Fix coerce_container_to_any false positive on autoderef (#15057)
Fixes the false positive reported in rust-lang/rust-clippy#15045. ~I
still need to work out how to fix the suggestion.~
changelog: none
diff --git a/.github/ISSUE_TEMPLATE/new_lint.yml b/.github/ISSUE_TEMPLATE/new_lint.yml
index b49493e..4647406 100644
--- a/.github/ISSUE_TEMPLATE/new_lint.yml
+++ b/.github/ISSUE_TEMPLATE/new_lint.yml
@@ -1,5 +1,7 @@
name: New lint suggestion
-description: Suggest a new Clippy lint.
+description: |
+ Suggest a new Clippy lint (currently not accepting new lints)
+ Check out the Clippy book for more information about the feature freeze.
labels: ["A-lint"]
body:
- type: markdown
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 9e49f60..83bfd8e 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -32,6 +32,10 @@
Delete this line and everything above before opening your PR.
+Note that we are currently not taking in new PRs that add new lints. We are in a
+feature freeze. Check out the book for more information. If you open a
+feature-adding pull request, its review will be delayed.
+
---
*Please write a short comment explaining your change (or "none" for internal only changes)*
diff --git a/.github/workflows/feature_freeze.yml b/.github/workflows/feature_freeze.yml
new file mode 100644
index 0000000..a5f8d4b
--- /dev/null
+++ b/.github/workflows/feature_freeze.yml
@@ -0,0 +1,25 @@
+name: Feature freeze check
+
+on:
+ pull_request:
+ paths:
+ - 'clippy_lints/src/declared_lints.rs'
+
+jobs:
+ auto-comment:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Check PR Changes
+ id: pr-changes
+ run: echo "::set-output name=changes::${{ toJson(github.event.pull_request.changed_files) }}"
+
+ - name: Create Comment
+ if: steps.pr-changes.outputs.changes != '[]'
+ run: |
+ # Use GitHub API to create a comment on the PR
+ PR_NUMBER=${{ github.event.pull_request.number }}
+ 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"
+ GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }}
+ COMMENT_URL="https://api.github.com/repos/${{ github.repository }}/issues/${PR_NUMBER}/comments"
+ curl -s -H "Authorization: token ${GITHUB_TOKEN}" -X POST $COMMENT_URL -d "{\"body\":\"$COMMENT\"}"
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0cfe89a..a92fbdc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,7 +6,94 @@
## Unreleased / Beta / In Rust Nightly
-[1e5237f4...master](https://github.com/rust-lang/rust-clippy/compare/1e5237f4...master)
+[03a5b6b9...master](https://github.com/rust-lang/rust-clippy/compare/03a5b6b9...master)
+
+## Rust 1.88
+
+Current stable, released 2025-06-26
+
+[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)
+
+### New Lints
+
+* Added [`swap_with_temporary`] to `complexity` [#14046](https://github.com/rust-lang/rust-clippy/pull/14046)
+* Added [`redundant_test_prefix`] to `restriction` [#13710](https://github.com/rust-lang/rust-clippy/pull/13710)
+* Added [`manual_dangling_ptr`] to `style` [#14107](https://github.com/rust-lang/rust-clippy/pull/14107)
+* Added [`char_indices_as_byte_indices`] to `correctness` [#13435](https://github.com/rust-lang/rust-clippy/pull/13435)
+* Added [`manual_abs_diff`] to `complexity` [#14482](https://github.com/rust-lang/rust-clippy/pull/14482)
+* Added [`ignore_without_reason`] to `pedantic` [#13931](https://github.com/rust-lang/rust-clippy/pull/13931)
+
+### Moves and Deprecations
+
+* Moved [`uninlined_format_args`] to `style` (from `pedantic`)
+ [#14160](https://github.com/rust-lang/rust-clippy/pull/14160)
+* [`match_on_vec_items`] deprecated in favor of [`indexing_slicing`]
+ [#14217](https://github.com/rust-lang/rust-clippy/pull/14217)
+* Removed superseded lints: `transmute_float_to_int`, `transmute_int_to_char`,
+ `transmute_int_to_float`, `transmute_num_to_bytes` (now in rustc)
+ [#14703](https://github.com/rust-lang/rust-clippy/pull/14703)
+
+### Enhancements
+
+* Configuration renamed from `lint-inconsistent-struct-field-initializers`
+ to `check-inconsistent-struct-field-initializers`
+ [#14280](https://github.com/rust-lang/rust-clippy/pull/14280)
+* Paths in `disallowed_*` configurations are now validated
+ [#14397](https://github.com/rust-lang/rust-clippy/pull/14397)
+* [`borrow_as_ptr`] now lints implicit casts as well
+ [#14408](https://github.com/rust-lang/rust-clippy/pull/14408)
+* [`iter_kv_map`] now recognizes references on maps
+ [#14596](https://github.com/rust-lang/rust-clippy/pull/14596)
+* [`empty_enum_variants_with_brackets`] no longer lints reachable enums or enums used
+ as functions within same crate [#12971](https://github.com/rust-lang/rust-clippy/pull/12971)
+* [`needless_lifetimes`] now checks for lifetime uses in closures
+ [#14608](https://github.com/rust-lang/rust-clippy/pull/14608)
+* [`wildcard_imports`] now lints on `pub use` when `warn_on_all_wildcard_imports` is enabled
+ [#14182](https://github.com/rust-lang/rust-clippy/pull/14182)
+* [`collapsible_if`] now recognizes the `let_chains` feature
+ [#14481](https://github.com/rust-lang/rust-clippy/pull/14481)
+* [`match_single_binding`] now allows macros in scrutinee and patterns
+ [#14635](https://github.com/rust-lang/rust-clippy/pull/14635)
+* [`needless_borrow`] does not contradict the compiler's
+ `dangerous_implicit_autorefs` lint even though the references
+ are not mandatory
+ [#14810](https://github.com/rust-lang/rust-clippy/pull/14810)
+
+### False Positive Fixes
+
+* [`double_ended_iterator_last`] and [`needless_collect`] fixed FP when iter has side effects
+ [#14490](https://github.com/rust-lang/rust-clippy/pull/14490)
+* [`mut_from_ref`] fixed FP where lifetimes nested in types were not considered
+ [#14471](https://github.com/rust-lang/rust-clippy/pull/14471)
+* [`redundant_clone`] fixed FP in overlapping lifetime
+ [#14237](https://github.com/rust-lang/rust-clippy/pull/14237)
+* [`map_entry`] fixed FP where lint would trigger without insert calls present
+ [#14568](https://github.com/rust-lang/rust-clippy/pull/14568)
+* [`iter_cloned_collect`] fixed FP with custom `From`/`IntoIterator` impl
+ [#14473](https://github.com/rust-lang/rust-clippy/pull/14473)
+* [`shadow_unrelated`] fixed FP in destructuring assignments
+ [#14381](https://github.com/rust-lang/rust-clippy/pull/14381)
+* [`redundant_clone`] fixed FP on enum cast
+ [#14395](https://github.com/rust-lang/rust-clippy/pull/14395)
+* [`collapsible_if`] fixed FP on block stmt before expr
+ [#14730](https://github.com/rust-lang/rust-clippy/pull/14730)
+
+### ICE Fixes
+
+* [`missing_const_for_fn`] fix ICE with `-Z validate-mir` compilation option
+ [#14776](https://github.com/rust-lang/rust-clippy/pull/14776)
+
+### Documentation Improvements
+
+* [`missing_asserts_for_indexing`] improved documentation and examples
+ [#14108](https://github.com/rust-lang/rust-clippy/pull/14108)
+
+### Others
+
+* We're testing with edition 2024 now
+ [#14602](https://github.com/rust-lang/rust-clippy/pull/14602)
+* Don't warn about unloaded crates in `clippy.toml` disallowed paths
+ [#14733](https://github.com/rust-lang/rust-clippy/pull/14733)
## Rust 1.87
@@ -5729,6 +5816,7 @@
[`disallowed_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_type
[`disallowed_types`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_types
[`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression
+[`doc_broken_link`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_broken_link
[`doc_comment_double_space_linebreaks`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_comment_double_space_linebreaks
[`doc_include_without_cfg`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_include_without_cfg
[`doc_lazy_continuation`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_lazy_continuation
@@ -5967,6 +6055,7 @@
[`manual_is_ascii_check`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_ascii_check
[`manual_is_finite`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_finite
[`manual_is_infinite`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_infinite
+[`manual_is_multiple_of`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_multiple_of
[`manual_is_power_of_two`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_power_of_two
[`manual_is_variant_and`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_variant_and
[`manual_let_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else
diff --git a/Cargo.toml b/Cargo.toml
index 5584ded..8cbdcf4 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "clippy"
-version = "0.1.89"
+version = "0.1.90"
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
repository = "https://github.com/rust-lang/rust-clippy"
readme = "README.md"
@@ -24,6 +24,7 @@
clippy_config = { path = "clippy_config" }
clippy_lints = { path = "clippy_lints" }
clippy_utils = { path = "clippy_utils" }
+declare_clippy_lint = { path = "declare_clippy_lint" }
rustc_tools_util = { path = "rustc_tools_util", version = "0.4.2" }
clippy_lints_internal = { path = "clippy_lints_internal", optional = true }
tempfile = { version = "3.20", optional = true }
@@ -58,6 +59,7 @@
[features]
integration = ["dep:tempfile"]
internal = ["dep:clippy_lints_internal", "dep:tempfile"]
+jemalloc = []
[package.metadata.rust-analyzer]
# This package uses #[feature(rustc_private)]
diff --git a/book/src/README.md b/book/src/README.md
index 5d2c397..db73b49 100644
--- a/book/src/README.md
+++ b/book/src/README.md
@@ -1,5 +1,9 @@
# Clippy
+[### IMPORTANT NOTE FOR CONTRIBUTORS ================](development/feature_freeze.md)
+
+----
+
[](https://github.com/rust-lang/rust-clippy#license)
A collection of lints to catch common mistakes and improve your
diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md
index 39fe735..b66c348 100644
--- a/book/src/SUMMARY.md
+++ b/book/src/SUMMARY.md
@@ -13,6 +13,7 @@
- [GitLab CI](continuous_integration/gitlab.md)
- [Travis CI](continuous_integration/travis.md)
- [Development](development/README.md)
+ - [IMPORTANT: FEATURE FREEZE](development/feature_freeze.md)
- [Basics](development/basics.md)
- [Adding Lints](development/adding_lints.md)
- [Defining Lints](development/defining_lints.md)
diff --git a/book/src/development/adding_lints.md b/book/src/development/adding_lints.md
index 2b89e94..a42a298 100644
--- a/book/src/development/adding_lints.md
+++ b/book/src/development/adding_lints.md
@@ -1,5 +1,8 @@
# Adding a new lint
+[### IMPORTANT NOTE FOR CONTRIBUTORS ================](feature_freeze.md)
+
+
You are probably here because you want to add a new lint to Clippy. If this is
the first time you're contributing to Clippy, this document guides you through
creating an example lint from scratch.
diff --git a/book/src/development/feature_freeze.md b/book/src/development/feature_freeze.md
new file mode 100644
index 0000000..260cb13
--- /dev/null
+++ b/book/src/development/feature_freeze.md
@@ -0,0 +1,55 @@
+# IMPORTANT: FEATURE FREEZE
+
+This is a temporary notice.
+
+From the 26th of June until the 18th of September we will perform a feature freeze. Only bugfix PRs will be reviewed
+except already open ones. Every feature-adding PR opened in between those dates will be moved into a
+milestone to be reviewed separately at another time.
+
+We do this because of the long backlog of bugs that need to be addressed
+in order to continue being the state-of-the-art linter that Clippy has become known for being.
+
+## For contributors
+
+If you are a contributor or are planning to become one, **please do not open a lint-adding PR**, we have lots of open
+bugs of all levels of difficulty that you can address instead!
+
+We currently have about 800 lints, each one posing a maintainability challenge that needs to account to every possible
+use case of the whole ecosystem. Bugs are natural in every software, but the Clippy team considers that Clippy needs a
+refinement period.
+
+If you open a PR at this time, we will not review it but push it into a milestone until the refinement period ends,
+adding additional load into our reviewing schedules.
+
+## I want to help, what can I do
+
+Thanks a lot to everyone who wants to help Clippy become better software in this feature freeze period!
+If you'd like to help, making a bugfix, making sure that it works, and opening a PR is a great step!
+
+To find things to fix, go to the [tracking issue][tracking_issue], find an issue that you like, go there and claim that
+issue with `@rustbot claim`.
+
+As a general metric and always taking into account your skill and knowledge level, you can use this guide:
+
+- 🟥 [ICEs][search_ice], these are compiler errors that causes Clippy to panic and crash. Usually involves high-level
+debugging, sometimes interacting directly with the upstream compiler. Difficult to fix but a great challenge that
+improves a lot developer workflows!
+
+- 🟧 [Suggestion causes bug][sugg_causes_bug], Clippy suggested code that changed logic in some silent way.
+Unacceptable, as this may have disastrous consequences. Easier to fix than ICEs
+
+- 🟨 [Suggestion causes error][sugg_causes_error], Clippy suggested code snippet that caused a compiler error
+when applied. We need to make sure that Clippy doesn't suggest using a variable twice at the same time or similar
+easy-to-happen occurrences.
+
+- 🟩 [False positives][false_positive], a lint should not have fired, the easiest of them all, as this is "just"
+identifying the root of a false positive and making an exception for those cases.
+
+Note that false negatives do not have priority unless the case is very clear, as they are a feature-request in a
+trench coat.
+
+[search_ice]: https://github.com/rust-lang/rust-clippy/issues?q=sort%3Aupdated-desc+state%3Aopen+label%3A%22I-ICE%22
+[sugg_causes_bug]: https://github.com/rust-lang/rust-clippy/issues?q=sort%3Aupdated-desc%20state%3Aopen%20label%3AI-suggestion-causes-bug
+[sugg_causes_error]: https://github.com/rust-lang/rust-clippy/issues?q=sort%3Aupdated-desc%20state%3Aopen%20label%3AI-suggestion-causes-error%20
+[false_positive]: https://github.com/rust-lang/rust-clippy/issues?q=sort%3Aupdated-desc%20state%3Aopen%20label%3AI-false-positive
+[tracking_issue]: https://github.com/rust-lang/rust-clippy/issues/15086
diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md
index ab46aab..992ed2c 100644
--- a/book/src/lint_configuration.md
+++ b/book/src/lint_configuration.md
@@ -488,6 +488,13 @@
## `disallowed-macros`
The list of disallowed macros, written as fully qualified paths.
+**Fields:**
+- `path` (required): the fully qualified path to the macro that should be disallowed
+- `reason` (optional): explanation why this macro is disallowed
+- `replacement` (optional): suggested alternative macro
+- `allow-invalid` (optional, `false` by default): when set to `true`, it will ignore this entry
+ if the path doesn't exist, instead of emitting an error
+
**Default Value:** `[]`
---
@@ -498,6 +505,13 @@
## `disallowed-methods`
The list of disallowed methods, written as fully qualified paths.
+**Fields:**
+- `path` (required): the fully qualified path to the method that should be disallowed
+- `reason` (optional): explanation why this method is disallowed
+- `replacement` (optional): suggested alternative method
+- `allow-invalid` (optional, `false` by default): when set to `true`, it will ignore this entry
+ if the path doesn't exist, instead of emitting an error
+
**Default Value:** `[]`
---
@@ -520,6 +534,13 @@
## `disallowed-types`
The list of disallowed types, written as fully qualified paths.
+**Fields:**
+- `path` (required): the fully qualified path to the type that should be disallowed
+- `reason` (optional): explanation why this type is disallowed
+- `replacement` (optional): suggested alternative type
+- `allow-invalid` (optional, `false` by default): when set to `true`, it will ignore this entry
+ if the path doesn't exist, instead of emitting an error
+
**Default Value:** `[]`
---
@@ -871,6 +892,7 @@
* [`unnested_or_patterns`](https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns)
* [`unused_trait_names`](https://rust-lang.github.io/rust-clippy/master/index.html#unused_trait_names)
* [`use_self`](https://rust-lang.github.io/rust-clippy/master/index.html#use_self)
+* [`zero_ptr`](https://rust-lang.github.io/rust-clippy/master/index.html#zero_ptr)
## `pass-by-value-size-limit`
diff --git a/clippy_config/Cargo.toml b/clippy_config/Cargo.toml
index 0606245..858366c 100644
--- a/clippy_config/Cargo.toml
+++ b/clippy_config/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "clippy_config"
-version = "0.1.89"
+version = "0.1.90"
edition = "2024"
publish = false
diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs
index 064825e..555f54b 100644
--- a/clippy_config/src/conf.rs
+++ b/clippy_config/src/conf.rs
@@ -575,10 +575,24 @@
#[conf_deprecated("Please use `cognitive-complexity-threshold` instead", cognitive_complexity_threshold)]
cyclomatic_complexity_threshold: u64 = 25,
/// The list of disallowed macros, written as fully qualified paths.
+ ///
+ /// **Fields:**
+ /// - `path` (required): the fully qualified path to the macro that should be disallowed
+ /// - `reason` (optional): explanation why this macro is disallowed
+ /// - `replacement` (optional): suggested alternative macro
+ /// - `allow-invalid` (optional, `false` by default): when set to `true`, it will ignore this entry
+ /// if the path doesn't exist, instead of emitting an error
#[disallowed_paths_allow_replacements = true]
#[lints(disallowed_macros)]
disallowed_macros: Vec<DisallowedPath> = Vec::new(),
/// The list of disallowed methods, written as fully qualified paths.
+ ///
+ /// **Fields:**
+ /// - `path` (required): the fully qualified path to the method that should be disallowed
+ /// - `reason` (optional): explanation why this method is disallowed
+ /// - `replacement` (optional): suggested alternative method
+ /// - `allow-invalid` (optional, `false` by default): when set to `true`, it will ignore this entry
+ /// if the path doesn't exist, instead of emitting an error
#[disallowed_paths_allow_replacements = true]
#[lints(disallowed_methods)]
disallowed_methods: Vec<DisallowedPath> = Vec::new(),
@@ -588,6 +602,13 @@
#[lints(disallowed_names)]
disallowed_names: Vec<String> = DEFAULT_DISALLOWED_NAMES.iter().map(ToString::to_string).collect(),
/// The list of disallowed types, written as fully qualified paths.
+ ///
+ /// **Fields:**
+ /// - `path` (required): the fully qualified path to the type that should be disallowed
+ /// - `reason` (optional): explanation why this type is disallowed
+ /// - `replacement` (optional): suggested alternative type
+ /// - `allow-invalid` (optional, `false` by default): when set to `true`, it will ignore this entry
+ /// if the path doesn't exist, instead of emitting an error
#[disallowed_paths_allow_replacements = true]
#[lints(disallowed_types)]
disallowed_types: Vec<DisallowedPath> = Vec::new(),
@@ -773,6 +794,7 @@
unnested_or_patterns,
unused_trait_names,
use_self,
+ zero_ptr,
)]
msrv: Msrv = Msrv::default(),
/// The minimum size (in bytes) to consider a type for passing by reference instead of by value.
diff --git a/clippy_dev/src/lint.rs b/clippy_dev/src/lint.rs
index e0e0367..0d66f16 100644
--- a/clippy_dev/src/lint.rs
+++ b/clippy_dev/src/lint.rs
@@ -13,7 +13,7 @@
if is_file {
exit_if_err(
- Command::new(env::var("CARGO").unwrap_or("cargo".into()))
+ Command::new(env::var("CARGO").unwrap_or_else(|_| "cargo".into()))
.args(["run", "--bin", "clippy-driver", "--"])
.args(["-L", "./target/debug"])
.args(["-Z", "no-codegen"])
@@ -26,7 +26,7 @@
);
} else {
exit_if_err(
- Command::new(env::var("CARGO").unwrap_or("cargo".into()))
+ Command::new(env::var("CARGO").unwrap_or_else(|_| "cargo".into()))
.arg("build")
.status(),
);
diff --git a/clippy_dev/src/release.rs b/clippy_dev/src/release.rs
index 62c1bee..15392dd 100644
--- a/clippy_dev/src/release.rs
+++ b/clippy_dev/src/release.rs
@@ -5,6 +5,7 @@
"clippy_config/Cargo.toml",
"clippy_lints/Cargo.toml",
"clippy_utils/Cargo.toml",
+ "declare_clippy_lint/Cargo.toml",
"Cargo.toml",
];
diff --git a/clippy_dev/src/serve.rs b/clippy_dev/src/serve.rs
index a2d1236..498ffeb 100644
--- a/clippy_dev/src/serve.rs
+++ b/clippy_dev/src/serve.rs
@@ -28,7 +28,7 @@
.map(mtime);
if times.iter().any(|&time| index_time < time) {
- Command::new(env::var("CARGO").unwrap_or("cargo".into()))
+ Command::new(env::var("CARGO").unwrap_or_else(|_| "cargo".into()))
.arg("collect-metadata")
.spawn()
.unwrap()
diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs
index 08592f2..5f6e874 100644
--- a/clippy_dev/src/update_lints.rs
+++ b/clippy_dev/src/update_lints.rs
@@ -4,8 +4,9 @@
use itertools::Itertools;
use std::collections::HashSet;
use std::fmt::Write;
+use std::fs;
use std::ops::Range;
-use std::path::{Path, PathBuf};
+use std::path::{self, Path, PathBuf};
use walkdir::{DirEntry, WalkDir};
const GENERATED_FILE_COMMENT: &str = "// This file was generated by `cargo dev update_lints`.\n\
@@ -36,123 +37,164 @@
deprecated: &[DeprecatedLint],
renamed: &[RenamedLint],
) {
- FileUpdater::default().update_files_checked(
+ let mut updater = FileUpdater::default();
+ updater.update_file_checked(
"cargo dev update_lints",
update_mode,
- &mut [
- (
- "README.md",
- &mut update_text_region_fn("[There are over ", " lints included in this crate!]", |dst| {
- write!(dst, "{}", round_to_fifty(lints.len())).unwrap();
- }),
- ),
- (
- "book/src/README.md",
- &mut update_text_region_fn("[There are over ", " lints included in this crate!]", |dst| {
- write!(dst, "{}", round_to_fifty(lints.len())).unwrap();
- }),
- ),
- (
- "CHANGELOG.md",
- &mut update_text_region_fn(
- "<!-- begin autogenerated links to lint list -->\n",
- "<!-- end autogenerated links to lint list -->",
- |dst| {
- for lint in lints
- .iter()
- .map(|l| &*l.name)
- .chain(deprecated.iter().filter_map(|l| l.name.strip_prefix("clippy::")))
- .chain(renamed.iter().filter_map(|l| l.old_name.strip_prefix("clippy::")))
- .sorted()
- {
- writeln!(dst, "[`{lint}`]: {DOCS_LINK}#{lint}").unwrap();
- }
- },
- ),
- ),
- (
- "clippy_lints/src/lib.rs",
- &mut update_text_region_fn(
- "// begin lints modules, do not remove this comment, it's used in `update_lints`\n",
- "// end lints modules, do not remove this comment, it's used in `update_lints`",
- |dst| {
- for lint_mod in lints.iter().map(|l| &l.module).sorted().dedup() {
- writeln!(dst, "mod {lint_mod};").unwrap();
- }
- },
- ),
- ),
- ("clippy_lints/src/declared_lints.rs", &mut |_, src, dst| {
- dst.push_str(GENERATED_FILE_COMMENT);
- dst.push_str("pub static LINTS: &[&crate::LintInfo] = &[\n");
- for (module_name, lint_name) in lints.iter().map(|l| (&l.module, l.name.to_uppercase())).sorted() {
- writeln!(dst, " crate::{module_name}::{lint_name}_INFO,").unwrap();
+ "README.md",
+ &mut update_text_region_fn("[There are over ", " lints included in this crate!]", |dst| {
+ write!(dst, "{}", round_to_fifty(lints.len())).unwrap();
+ }),
+ );
+ updater.update_file_checked(
+ "cargo dev update_lints",
+ update_mode,
+ "book/src/README.md",
+ &mut update_text_region_fn("[There are over ", " lints included in this crate!]", |dst| {
+ write!(dst, "{}", round_to_fifty(lints.len())).unwrap();
+ }),
+ );
+ updater.update_file_checked(
+ "cargo dev update_lints",
+ update_mode,
+ "CHANGELOG.md",
+ &mut update_text_region_fn(
+ "<!-- begin autogenerated links to lint list -->\n",
+ "<!-- end autogenerated links to lint list -->",
+ |dst| {
+ for lint in lints
+ .iter()
+ .map(|l| &*l.name)
+ .chain(deprecated.iter().filter_map(|l| l.name.strip_prefix("clippy::")))
+ .chain(renamed.iter().filter_map(|l| l.old_name.strip_prefix("clippy::")))
+ .sorted()
+ {
+ writeln!(dst, "[`{lint}`]: {DOCS_LINK}#{lint}").unwrap();
}
- dst.push_str("];\n");
- UpdateStatus::from_changed(src != dst)
- }),
- ("clippy_lints/src/deprecated_lints.rs", &mut |_, src, dst| {
- let mut searcher = RustSearcher::new(src);
- assert!(
- searcher.find_token(Token::Ident("declare_with_version"))
- && searcher.find_token(Token::Ident("declare_with_version")),
- "error reading deprecated lints"
- );
- dst.push_str(&src[..searcher.pos() as usize]);
- dst.push_str("! { DEPRECATED(DEPRECATED_VERSION) = [\n");
- for lint in deprecated {
- write!(
- dst,
- " #[clippy::version = \"{}\"]\n (\"{}\", \"{}\"),\n",
- lint.version, lint.name, lint.reason,
- )
- .unwrap();
- }
- dst.push_str(
- "]}\n\n\
+ },
+ ),
+ );
+ updater.update_file_checked(
+ "cargo dev update_lints",
+ update_mode,
+ "clippy_lints/src/deprecated_lints.rs",
+ &mut |_, src, dst| {
+ let mut searcher = RustSearcher::new(src);
+ assert!(
+ searcher.find_token(Token::Ident("declare_with_version"))
+ && searcher.find_token(Token::Ident("declare_with_version")),
+ "error reading deprecated lints"
+ );
+ dst.push_str(&src[..searcher.pos() as usize]);
+ dst.push_str("! { DEPRECATED(DEPRECATED_VERSION) = [\n");
+ for lint in deprecated {
+ write!(
+ dst,
+ " #[clippy::version = \"{}\"]\n (\"{}\", \"{}\"),\n",
+ lint.version, lint.name, lint.reason,
+ )
+ .unwrap();
+ }
+ dst.push_str(
+ "]}\n\n\
#[rustfmt::skip]\n\
declare_with_version! { RENAMED(RENAMED_VERSION) = [\n\
",
- );
- for lint in renamed {
- write!(
- dst,
- " #[clippy::version = \"{}\"]\n (\"{}\", \"{}\"),\n",
- lint.version, lint.old_name, lint.new_name,
- )
- .unwrap();
- }
- dst.push_str("]}\n");
- UpdateStatus::from_changed(src != dst)
- }),
- ("tests/ui/deprecated.rs", &mut |_, src, dst| {
- dst.push_str(GENERATED_FILE_COMMENT);
- for lint in deprecated {
- writeln!(dst, "#![warn({})] //~ ERROR: lint `{}`", lint.name, lint.name).unwrap();
- }
- dst.push_str("\nfn main() {}\n");
- UpdateStatus::from_changed(src != dst)
- }),
- ("tests/ui/rename.rs", &mut move |_, src, dst| {
- let mut seen_lints = HashSet::new();
- dst.push_str(GENERATED_FILE_COMMENT);
- dst.push_str("#![allow(clippy::duplicated_attributes)]\n");
- for lint in renamed {
- if seen_lints.insert(&lint.new_name) {
- writeln!(dst, "#![allow({})]", lint.new_name).unwrap();
- }
- }
- seen_lints.clear();
- for lint in renamed {
- if seen_lints.insert(&lint.old_name) {
- writeln!(dst, "#![warn({})] //~ ERROR: lint `{}`", lint.old_name, lint.old_name).unwrap();
- }
- }
- dst.push_str("\nfn main() {}\n");
- UpdateStatus::from_changed(src != dst)
- }),
- ],
+ );
+ for lint in renamed {
+ write!(
+ dst,
+ " #[clippy::version = \"{}\"]\n (\"{}\", \"{}\"),\n",
+ lint.version, lint.old_name, lint.new_name,
+ )
+ .unwrap();
+ }
+ dst.push_str("]}\n");
+ UpdateStatus::from_changed(src != dst)
+ },
);
+ updater.update_file_checked(
+ "cargo dev update_lints",
+ update_mode,
+ "tests/ui/deprecated.rs",
+ &mut |_, src, dst| {
+ dst.push_str(GENERATED_FILE_COMMENT);
+ for lint in deprecated {
+ writeln!(dst, "#![warn({})] //~ ERROR: lint `{}`", lint.name, lint.name).unwrap();
+ }
+ dst.push_str("\nfn main() {}\n");
+ UpdateStatus::from_changed(src != dst)
+ },
+ );
+ updater.update_file_checked(
+ "cargo dev update_lints",
+ update_mode,
+ "tests/ui/rename.rs",
+ &mut move |_, src, dst| {
+ let mut seen_lints = HashSet::new();
+ dst.push_str(GENERATED_FILE_COMMENT);
+ dst.push_str("#![allow(clippy::duplicated_attributes)]\n");
+ for lint in renamed {
+ if seen_lints.insert(&lint.new_name) {
+ writeln!(dst, "#![allow({})]", lint.new_name).unwrap();
+ }
+ }
+ seen_lints.clear();
+ for lint in renamed {
+ if seen_lints.insert(&lint.old_name) {
+ writeln!(dst, "#![warn({})] //~ ERROR: lint `{}`", lint.old_name, lint.old_name).unwrap();
+ }
+ }
+ dst.push_str("\nfn main() {}\n");
+ UpdateStatus::from_changed(src != dst)
+ },
+ );
+ for (crate_name, lints) in lints.iter().into_group_map_by(|&l| {
+ let Some(path::Component::Normal(name)) = l.path.components().next() else {
+ // All paths should start with `{crate_name}/src` when parsed from `find_lint_decls`
+ panic!("internal error: can't read crate name from path `{}`", l.path.display());
+ };
+ name
+ }) {
+ updater.update_file_checked(
+ "cargo dev update_lints",
+ update_mode,
+ Path::new(crate_name).join("src/lib.rs"),
+ &mut update_text_region_fn(
+ "// begin lints modules, do not remove this comment, it's used in `update_lints`\n",
+ "// end lints modules, do not remove this comment, it's used in `update_lints`",
+ |dst| {
+ for lint_mod in lints
+ .iter()
+ .filter(|l| !l.module.is_empty())
+ .map(|l| l.module.split_once("::").map_or(&*l.module, |x| x.0))
+ .sorted()
+ .dedup()
+ {
+ writeln!(dst, "mod {lint_mod};").unwrap();
+ }
+ },
+ ),
+ );
+ updater.update_file_checked(
+ "cargo dev update_lints",
+ update_mode,
+ Path::new(crate_name).join("src/declared_lints.rs"),
+ &mut |_, src, dst| {
+ dst.push_str(GENERATED_FILE_COMMENT);
+ dst.push_str("pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[\n");
+ for (module_path, lint_name) in lints.iter().map(|l| (&l.module, l.name.to_uppercase())).sorted() {
+ if module_path.is_empty() {
+ writeln!(dst, " crate::{lint_name}_INFO,").unwrap();
+ } else {
+ writeln!(dst, " crate::{module_path}::{lint_name}_INFO,").unwrap();
+ }
+ }
+ dst.push_str("];\n");
+ UpdateStatus::from_changed(src != dst)
+ },
+ );
+ }
}
fn round_to_fifty(count: usize) -> usize {
@@ -186,13 +228,25 @@
pub fn find_lint_decls() -> Vec<Lint> {
let mut lints = Vec::with_capacity(1000);
let mut contents = String::new();
- for (file, module) in read_src_with_module("clippy_lints/src".as_ref()) {
- parse_clippy_lint_decls(
- file.path(),
- File::open_read_to_cleared_string(file.path(), &mut contents),
- &module,
- &mut lints,
- );
+ for e in expect_action(fs::read_dir("."), ErrAction::Read, ".") {
+ let e = expect_action(e, ErrAction::Read, ".");
+ if !expect_action(e.file_type(), ErrAction::Read, ".").is_dir() {
+ continue;
+ }
+ let Ok(mut name) = e.file_name().into_string() else {
+ continue;
+ };
+ if name.starts_with("clippy_lints") && name != "clippy_lints_internal" {
+ name.push_str("/src");
+ for (file, module) in read_src_with_module(name.as_ref()) {
+ parse_clippy_lint_decls(
+ file.path(),
+ File::open_read_to_cleared_string(file.path(), &mut contents),
+ &module,
+ &mut lints,
+ );
+ }
+ }
}
lints.sort_by(|lhs, rhs| lhs.name.cmp(&rhs.name));
lints
@@ -204,7 +258,7 @@
let e = expect_action(e, ErrAction::Read, src_root);
let path = e.path().as_os_str().as_encoded_bytes();
if let Some(path) = path.strip_suffix(b".rs")
- && let Some(path) = path.get("clippy_lints/src/".len()..)
+ && let Some(path) = path.get(src_root.as_os_str().len() + 1..)
{
if path == b"lib" {
Some((e, String::new()))
@@ -332,17 +386,13 @@
/// Removes the line splices and surrounding quotes from a string literal
fn parse_str_lit(s: &str) -> String {
- let (s, mode) = if let Some(s) = s.strip_prefix("r") {
- (s.trim_matches('#'), rustc_literal_escaper::Mode::RawStr)
- } else {
- (s, rustc_literal_escaper::Mode::Str)
- };
+ let s = s.strip_prefix("r").unwrap_or(s).trim_matches('#');
let s = s
.strip_prefix('"')
.and_then(|s| s.strip_suffix('"'))
.unwrap_or_else(|| panic!("expected quoted string, found `{s}`"));
let mut res = String::with_capacity(s.len());
- rustc_literal_escaper::unescape_unicode(s, mode, &mut |_, ch| {
+ rustc_literal_escaper::unescape_str(s, &mut |_, ch| {
if let Ok(ch) = ch {
res.push(ch);
}
diff --git a/clippy_dev/src/utils.rs b/clippy_dev/src/utils.rs
index c4808b7..89962a1 100644
--- a/clippy_dev/src/utils.rs
+++ b/clippy_dev/src/utils.rs
@@ -383,21 +383,6 @@
self.update_file_checked_inner(tool, mode, path.as_ref(), update);
}
- #[expect(clippy::type_complexity)]
- pub fn update_files_checked(
- &mut self,
- tool: &str,
- mode: UpdateMode,
- files: &mut [(
- impl AsRef<Path>,
- &mut dyn FnMut(&Path, &str, &mut String) -> UpdateStatus,
- )],
- ) {
- for (path, update) in files {
- self.update_file_checked_inner(tool, mode, path.as_ref(), update);
- }
- }
-
pub fn update_file(
&mut self,
path: impl AsRef<Path>,
diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml
index 39e4e2e..c03cc99 100644
--- a/clippy_lints/Cargo.toml
+++ b/clippy_lints/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "clippy_lints"
-version = "0.1.89"
+version = "0.1.90"
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
repository = "https://github.com/rust-lang/rust-clippy"
readme = "README.md"
@@ -13,6 +13,7 @@
cargo_metadata = "0.18"
clippy_config = { path = "../clippy_config" }
clippy_utils = { path = "../clippy_utils" }
+declare_clippy_lint = { path = "../declare_clippy_lint" }
itertools = "0.12"
quine-mc_cluskey = "0.2"
regex-syntax = "0.8"
diff --git a/clippy_lints/src/attrs/inline_always.rs b/clippy_lints/src/attrs/inline_always.rs
index cb63fad..b8f93ee 100644
--- a/clippy_lints/src/attrs/inline_always.rs
+++ b/clippy_lints/src/attrs/inline_always.rs
@@ -1,29 +1,22 @@
use super::INLINE_ALWAYS;
-use super::utils::is_word;
use clippy_utils::diagnostics::span_lint;
+use rustc_attr_data_structures::{AttributeKind, InlineAttr, find_attr};
use rustc_hir::Attribute;
use rustc_lint::LateContext;
+use rustc_span::Span;
use rustc_span::symbol::Symbol;
-use rustc_span::{Span, sym};
pub(super) fn check(cx: &LateContext<'_>, span: Span, name: Symbol, attrs: &[Attribute]) {
if span.from_expansion() {
return;
}
- for attr in attrs {
- if let Some(values) = attr.meta_item_list() {
- if values.len() != 1 || !attr.has_name(sym::inline) {
- continue;
- }
- if is_word(&values[0], sym::always) {
- span_lint(
- cx,
- INLINE_ALWAYS,
- attr.span(),
- format!("you have declared `#[inline(always)]` on `{name}`. This is usually a bad idea"),
- );
- }
- }
+ if let Some(span) = find_attr!(attrs, AttributeKind::Inline(InlineAttr::Always, span) => *span) {
+ span_lint(
+ cx,
+ INLINE_ALWAYS,
+ span,
+ format!("you have declared `#[inline(always)]` on `{name}`. This is usually a bad idea"),
+ );
}
}
diff --git a/clippy_lints/src/attrs/mod.rs b/clippy_lints/src/attrs/mod.rs
index 9a12429..91c2dc7 100644
--- a/clippy_lints/src/attrs/mod.rs
+++ b/clippy_lints/src/attrs/mod.rs
@@ -207,7 +207,7 @@
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of the `#[allow]` attribute and suggests replacing it with
- /// the `#[expect]` (See [RFC 2383](https://rust-lang.github.io/rfcs/2383-lint-reasons.html))
+ /// the `#[expect]` attribute (See [RFC 2383](https://rust-lang.github.io/rfcs/2383-lint-reasons.html))
///
/// This lint only warns outer attributes (`#[allow]`), as inner attributes
/// (`#![allow]`) are usually used to enable or disable lints on a global scale.
diff --git a/clippy_lints/src/attrs/utils.rs b/clippy_lints/src/attrs/utils.rs
index a5ce213..7b66f91 100644
--- a/clippy_lints/src/attrs/utils.rs
+++ b/clippy_lints/src/attrs/utils.rs
@@ -46,11 +46,13 @@
}
fn is_relevant_block(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_>, block: &Block<'_>) -> bool {
- block.stmts.first().map_or(
- block
- .expr
- .as_ref()
- .is_some_and(|e| is_relevant_expr(cx, typeck_results, e)),
+ block.stmts.first().map_or_else(
+ || {
+ block
+ .expr
+ .as_ref()
+ .is_some_and(|e| is_relevant_expr(cx, typeck_results, e))
+ },
|stmt| match &stmt.kind {
StmtKind::Let(_) => true,
StmtKind::Expr(expr) | StmtKind::Semi(expr) => is_relevant_expr(cx, typeck_results, expr),
diff --git a/clippy_lints/src/bool_assert_comparison.rs b/clippy_lints/src/bool_assert_comparison.rs
index ae36bb7..8f95e44 100644
--- a/clippy_lints/src/bool_assert_comparison.rs
+++ b/clippy_lints/src/bool_assert_comparison.rs
@@ -56,7 +56,7 @@
.and_then(|trait_id| {
cx.tcx.associated_items(trait_id).find_by_ident_and_kind(
cx.tcx,
- Ident::from_str("Output"),
+ Ident::with_dummy_span(sym::Output),
ty::AssocTag::Type,
trait_id,
)
diff --git a/clippy_lints/src/borrow_deref_ref.rs b/clippy_lints/src/borrow_deref_ref.rs
index 7cde007..70c9c45 100644
--- a/clippy_lints/src/borrow_deref_ref.rs
+++ b/clippy_lints/src/borrow_deref_ref.rs
@@ -2,9 +2,9 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::SpanRangeExt;
use clippy_utils::ty::implements_trait;
-use clippy_utils::{get_parent_expr, is_from_proc_macro, is_lint_allowed, is_mutable};
+use clippy_utils::{get_parent_expr, is_expr_temporary_value, is_from_proc_macro, is_lint_allowed, is_mutable};
use rustc_errors::Applicability;
-use rustc_hir::{BorrowKind, ExprKind, UnOp};
+use rustc_hir::{BorrowKind, Expr, ExprKind, Node, UnOp};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::mir::Mutability;
use rustc_middle::ty;
@@ -48,7 +48,7 @@
declare_lint_pass!(BorrowDerefRef => [BORROW_DEREF_REF]);
impl<'tcx> LateLintPass<'tcx> for BorrowDerefRef {
- fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &rustc_hir::Expr<'tcx>) {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) {
if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, addrof_target) = e.kind
&& let ExprKind::Unary(UnOp::Deref, deref_target) = addrof_target.kind
&& !matches!(deref_target.kind, ExprKind::Unary(UnOp::Deref, ..))
@@ -76,6 +76,9 @@
&& let e_ty = cx.typeck_results().expr_ty_adjusted(e)
// check if the reference is coercing to a mutable reference
&& (!matches!(e_ty.kind(), ty::Ref(_, _, Mutability::Mut)) || is_mutable(cx, deref_target))
+ // If the new borrow might be itself borrowed mutably and the original reference is not a temporary
+ // value, do not propose to use it directly.
+ && (is_expr_temporary_value(cx, deref_target) || !potentially_bound_to_mutable_ref(cx, e))
&& let Some(deref_text) = deref_target.span.get_source_text(cx)
{
span_lint_and_then(
@@ -110,3 +113,9 @@
}
}
}
+
+/// Checks if `expr` is used as part of a `let` statement containing a `ref mut` binding.
+fn potentially_bound_to_mutable_ref<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
+ matches!(cx.tcx.parent_hir_node(expr.hir_id), Node::LetStmt(let_stmt)
+ if let_stmt.pat.contains_explicit_ref_binding() == Some(Mutability::Mut))
+}
diff --git a/clippy_lints/src/casts/cast_possible_truncation.rs b/clippy_lints/src/casts/cast_possible_truncation.rs
index a2ecb5f..2eebe84 100644
--- a/clippy_lints/src/casts/cast_possible_truncation.rs
+++ b/clippy_lints/src/casts/cast_possible_truncation.rs
@@ -3,7 +3,7 @@
use clippy_utils::source::snippet;
use clippy_utils::sugg::Sugg;
use clippy_utils::ty::{get_discriminant_value, is_isize_or_usize};
-use clippy_utils::{expr_or_init, sym};
+use clippy_utils::{expr_or_init, is_in_const_context, sym};
use rustc_abi::IntegerType;
use rustc_errors::{Applicability, Diag};
use rustc_hir::def::{DefKind, Res};
@@ -168,7 +168,9 @@
span_lint_and_then(cx, CAST_POSSIBLE_TRUNCATION, expr.span, msg, |diag| {
diag.help("if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...");
- if !cast_from.is_floating_point() {
+ // TODO: Remove the condition for const contexts when `try_from` and other commonly used methods
+ // become const fn.
+ if !is_in_const_context(cx) && !cast_from.is_floating_point() {
offer_suggestion(cx, expr, cast_expr, cast_to_span, diag);
}
});
diff --git a/clippy_lints/src/casts/cast_sign_loss.rs b/clippy_lints/src/casts/cast_sign_loss.rs
index 9a1ad8a..a70bd88 100644
--- a/clippy_lints/src/casts/cast_sign_loss.rs
+++ b/clippy_lints/src/casts/cast_sign_loss.rs
@@ -168,7 +168,7 @@
// Rust's integer pow() functions take an unsigned exponent.
let exponent_val = get_const_unsigned_int_eval(cx, exponent, None);
- let exponent_is_even = exponent_val.map(|val| val % 2 == 0);
+ let exponent_is_even = exponent_val.map(|val| val.is_multiple_of(2));
match (base_sign, exponent_is_even) {
// Non-negative bases always return non-negative results, ignoring overflow.
diff --git a/clippy_lints/src/casts/manual_dangling_ptr.rs b/clippy_lints/src/casts/manual_dangling_ptr.rs
index 61dfc0f..d9e88d6 100644
--- a/clippy_lints/src/casts/manual_dangling_ptr.rs
+++ b/clippy_lints/src/casts/manual_dangling_ptr.rs
@@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::SpanRangeExt;
-use clippy_utils::{expr_or_init, path_def_id, paths, std_or_core};
+use clippy_utils::{expr_or_init, is_path_diagnostic_item, std_or_core, sym};
use rustc_ast::LitKind;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, GenericArg, Mutability, QPath, Ty, TyKind};
@@ -53,8 +53,7 @@
fn is_align_of_call(cx: &LateContext<'_>, fun: &Expr<'_>, to: &Ty<'_>) -> bool {
if let ExprKind::Path(QPath::Resolved(_, path)) = fun.kind
- && let Some(fun_id) = path_def_id(cx, fun)
- && paths::ALIGN_OF.matches(cx, fun_id)
+ && is_path_diagnostic_item(cx, fun, sym::mem_align_of)
&& let Some(args) = path.segments.last().and_then(|seg| seg.args)
&& let [GenericArg::Type(generic_ty)] = args.args
{
diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs
index daae9a8..37accff 100644
--- a/clippy_lints/src/casts/mod.rs
+++ b/clippy_lints/src/casts/mod.rs
@@ -878,7 +878,7 @@
confusing_method_to_numeric_cast::check(cx, expr, cast_from_expr, cast_from, cast_to);
fn_to_numeric_cast::check(cx, expr, cast_from_expr, cast_from, cast_to);
fn_to_numeric_cast_with_truncation::check(cx, expr, cast_from_expr, cast_from, cast_to);
- zero_ptr::check(cx, expr, cast_from_expr, cast_to_hir);
+ zero_ptr::check(cx, expr, cast_from_expr, cast_to_hir, self.msrv);
if self.msrv.meets(cx, msrvs::MANUAL_DANGLING_PTR) {
manual_dangling_ptr::check(cx, expr, cast_from_expr, cast_to_hir);
diff --git a/clippy_lints/src/casts/unnecessary_cast.rs b/clippy_lints/src/casts/unnecessary_cast.rs
index 8e8c55c..010f09d 100644
--- a/clippy_lints/src/casts/unnecessary_cast.rs
+++ b/clippy_lints/src/casts/unnecessary_cast.rs
@@ -185,7 +185,7 @@
Node::Expr(parent) if is_borrow_expr(cx, parent) && !is_in_allowed_macro(cx, parent) => {
MaybeParenOrBlock::Block
},
- Node::Expr(parent) if cast_expr.precedence() < parent.precedence() => MaybeParenOrBlock::Paren,
+ Node::Expr(parent) if cx.precedence(cast_expr) < cx.precedence(parent) => MaybeParenOrBlock::Paren,
_ => MaybeParenOrBlock::Nothing,
};
diff --git a/clippy_lints/src/casts/zero_ptr.rs b/clippy_lints/src/casts/zero_ptr.rs
index a34af6b..f4738e7 100644
--- a/clippy_lints/src/casts/zero_ptr.rs
+++ b/clippy_lints/src/casts/zero_ptr.rs
@@ -1,4 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::SpanRangeExt;
use clippy_utils::{is_in_const_context, is_integer_literal, std_or_core};
use rustc_errors::Applicability;
@@ -7,10 +8,10 @@
use super::ZERO_PTR;
-pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, from: &Expr<'_>, to: &Ty<'_>) {
+pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, from: &Expr<'_>, to: &Ty<'_>, msrv: Msrv) {
if let TyKind::Ptr(ref mut_ty) = to.kind
&& is_integer_literal(from, 0)
- && !is_in_const_context(cx)
+ && (!is_in_const_context(cx) || msrv.meets(cx, msrvs::PTR_NULL))
&& let Some(std_or_core) = std_or_core(cx)
{
let (msg, sugg_fn) = match mut_ty.mutbl {
diff --git a/clippy_lints/src/coerce_container_to_any.rs b/clippy_lints/src/coerce_container_to_any.rs
index 3311a35..6217fc4 100644
--- a/clippy_lints/src/coerce_container_to_any.rs
+++ b/clippy_lints/src/coerce_container_to_any.rs
@@ -43,7 +43,7 @@
/// ```
#[clippy::version = "1.88.0"]
pub COERCE_CONTAINER_TO_ANY,
- suspicious,
+ nursery,
"coercing to `&dyn Any` when dereferencing could produce a `dyn Any` without coercion is usually not intended"
}
declare_lint_pass!(CoerceContainerToAny => [COERCE_CONTAINER_TO_ANY]);
diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs
index 5ef7266..2791869 100644
--- a/clippy_lints/src/copies.rs
+++ b/clippy_lints/src/copies.rs
@@ -11,7 +11,7 @@
use core::iter;
use core::ops::ControlFlow;
use rustc_errors::Applicability;
-use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, HirIdSet, Stmt, StmtKind, intravisit};
+use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, HirIdSet, LetStmt, Node, Stmt, StmtKind, intravisit};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::TyCtxt;
use rustc_session::impl_lint_pass;
@@ -295,7 +295,7 @@
sugg,
Applicability::Unspecified,
);
- if !cx.typeck_results().expr_ty(expr).is_unit() {
+ if is_expr_parent_assignment(cx, expr) || !cx.typeck_results().expr_ty(expr).is_unit() {
diag.note("the end suggestion probably needs some adjustments to use the expression result correctly");
}
}
@@ -660,3 +660,17 @@
);
}
}
+
+fn is_expr_parent_assignment(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+ let parent = cx.tcx.parent_hir_node(expr.hir_id);
+ if let Node::LetStmt(LetStmt { init: Some(e), .. })
+ | Node::Expr(Expr {
+ kind: ExprKind::Assign(_, e, _),
+ ..
+ }) = parent
+ {
+ return e.hir_id == expr.hir_id;
+ }
+
+ false
+}
diff --git a/clippy_lints/src/declare_clippy_lint.rs b/clippy_lints/src/declare_clippy_lint.rs
deleted file mode 100644
index 9f82f87..0000000
--- a/clippy_lints/src/declare_clippy_lint.rs
+++ /dev/null
@@ -1,168 +0,0 @@
-#[macro_export]
-#[allow(clippy::crate_in_macro_def)]
-macro_rules! declare_clippy_lint {
- (@
- $(#[doc = $lit:literal])*
- pub $lint_name:ident,
- $level:ident,
- $lintcategory:expr,
- $desc:literal,
- $version_expr:expr,
- $version_lit:literal
- $(, $eval_always: literal)?
- ) => {
- rustc_session::declare_tool_lint! {
- $(#[doc = $lit])*
- #[clippy::version = $version_lit]
- pub clippy::$lint_name,
- $level,
- $desc,
- report_in_external_macro:true
- $(, @eval_always = $eval_always)?
- }
-
- pub(crate) static ${concat($lint_name, _INFO)}: &'static crate::LintInfo = &crate::LintInfo {
- lint: &$lint_name,
- category: $lintcategory,
- explanation: concat!($($lit,"\n",)*),
- location: concat!(file!(), "#L", line!()),
- version: $version_expr
- };
- };
- (
- $(#[doc = $lit:literal])*
- #[clippy::version = $version:literal]
- pub $lint_name:ident,
- restriction,
- $desc:literal
- $(, @eval_always = $eval_always: literal)?
- ) => {
- declare_clippy_lint! {@
- $(#[doc = $lit])*
- pub $lint_name, Allow, crate::LintCategory::Restriction, $desc,
- Some($version), $version
- $(, $eval_always)?
- }
- };
- (
- $(#[doc = $lit:literal])*
- #[clippy::version = $version:literal]
- pub $lint_name:ident,
- style,
- $desc:literal
- $(, @eval_always = $eval_always: literal)?
- ) => {
- declare_clippy_lint! {@
- $(#[doc = $lit])*
- pub $lint_name, Warn, crate::LintCategory::Style, $desc,
- Some($version), $version
- $(, $eval_always)?
- }
- };
- (
- $(#[doc = $lit:literal])*
- #[clippy::version = $version:literal]
- pub $lint_name:ident,
- correctness,
- $desc:literal
- $(, @eval_always = $eval_always: literal)?
- ) => {
- declare_clippy_lint! {@
- $(#[doc = $lit])*
- pub $lint_name, Deny, crate::LintCategory::Correctness, $desc,
- Some($version), $version
- $(, $eval_always)?
-
- }
- };
- (
- $(#[doc = $lit:literal])*
- #[clippy::version = $version:literal]
- pub $lint_name:ident,
- perf,
- $desc:literal
- $(, @eval_always = $eval_always: literal)?
- ) => {
- declare_clippy_lint! {@
- $(#[doc = $lit])*
- pub $lint_name, Warn, crate::LintCategory::Perf, $desc,
- Some($version), $version
- $(, $eval_always)?
- }
- };
- (
- $(#[doc = $lit:literal])*
- #[clippy::version = $version:literal]
- pub $lint_name:ident,
- complexity,
- $desc:literal
- $(, @eval_always = $eval_always: literal)?
- ) => {
- declare_clippy_lint! {@
- $(#[doc = $lit])*
- pub $lint_name, Warn, crate::LintCategory::Complexity, $desc,
- Some($version), $version
- $(, $eval_always)?
- }
- };
- (
- $(#[doc = $lit:literal])*
- #[clippy::version = $version:literal]
- pub $lint_name:ident,
- suspicious,
- $desc:literal
- $(, @eval_always = $eval_always: literal)?
- ) => {
- declare_clippy_lint! {@
- $(#[doc = $lit])*
- pub $lint_name, Warn, crate::LintCategory::Suspicious, $desc,
- Some($version), $version
- $(, $eval_always)?
- }
- };
- (
- $(#[doc = $lit:literal])*
- #[clippy::version = $version:literal]
- pub $lint_name:ident,
- nursery,
- $desc:literal
- $(, @eval_always = $eval_always: literal)?
- ) => {
- declare_clippy_lint! {@
- $(#[doc = $lit])*
- pub $lint_name, Allow, crate::LintCategory::Nursery, $desc,
- Some($version), $version
- $(, $eval_always)?
- }
- };
- (
- $(#[doc = $lit:literal])*
- #[clippy::version = $version:literal]
- pub $lint_name:ident,
- pedantic,
- $desc:literal
- $(, @eval_always = $eval_always: literal)?
- ) => {
- declare_clippy_lint! {@
- $(#[doc = $lit])*
- pub $lint_name, Allow, crate::LintCategory::Pedantic, $desc,
- Some($version), $version
- $(, $eval_always)?
- }
- };
- (
- $(#[doc = $lit:literal])*
- #[clippy::version = $version:literal]
- pub $lint_name:ident,
- cargo,
- $desc:literal
- $(, @eval_always = $eval_always: literal)?
- ) => {
- declare_clippy_lint! {@
- $(#[doc = $lit])*
- pub $lint_name, Allow, crate::LintCategory::Cargo, $desc,
- Some($version), $version
- $(, $eval_always)?
- }
- };
-}
diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs
index 1e3907d..c3f8e02 100644
--- a/clippy_lints/src/declared_lints.rs
+++ b/clippy_lints/src/declared_lints.rs
@@ -2,7 +2,7 @@
// Use that command to update this file and do not edit by hand.
// Manual edits will be overwritten.
-pub static LINTS: &[&crate::LintInfo] = &[
+pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[
crate::absolute_paths::ABSOLUTE_PATHS_INFO,
crate::almost_complete_range::ALMOST_COMPLETE_RANGE_INFO,
crate::approx_const::APPROX_CONSTANT_INFO,
@@ -112,6 +112,7 @@
crate::disallowed_names::DISALLOWED_NAMES_INFO,
crate::disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS_INFO,
crate::disallowed_types::DISALLOWED_TYPES_INFO,
+ crate::doc::DOC_BROKEN_LINK_INFO,
crate::doc::DOC_COMMENT_DOUBLE_SPACE_LINEBREAKS_INFO,
crate::doc::DOC_INCLUDE_WITHOUT_CFG_INFO,
crate::doc::DOC_LAZY_CONTINUATION_INFO,
@@ -590,6 +591,7 @@
crate::operators::IMPOSSIBLE_COMPARISONS_INFO,
crate::operators::INEFFECTIVE_BIT_MASK_INFO,
crate::operators::INTEGER_DIVISION_INFO,
+ crate::operators::MANUAL_IS_MULTIPLE_OF_INFO,
crate::operators::MANUAL_MIDPOINT_INFO,
crate::operators::MISREFACTORED_ASSIGN_OP_INFO,
crate::operators::MODULO_ARITHMETIC_INFO,
diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs
index cde9528..7463d7b 100644
--- a/clippy_lints/src/dereference.rs
+++ b/clippy_lints/src/dereference.rs
@@ -972,7 +972,7 @@
"&"
};
- let expr_str = if !expr_is_macro_call && is_ufcs && expr.precedence() < ExprPrecedence::Prefix {
+ let expr_str = if !expr_is_macro_call && is_ufcs && cx.precedence(expr) < ExprPrecedence::Prefix {
Cow::Owned(format!("({expr_str})"))
} else {
expr_str
@@ -1015,10 +1015,10 @@
Node::Expr(e) => match e.kind {
ExprKind::Call(callee, _) if callee.hir_id != data.first_expr.hir_id => false,
ExprKind::Call(..) => {
- expr.precedence() < ExprPrecedence::Unambiguous
+ cx.precedence(expr) < ExprPrecedence::Unambiguous
|| matches!(expr.kind, ExprKind::Field(..))
},
- _ => expr.precedence() < e.precedence(),
+ _ => cx.precedence(expr) < cx.precedence(e),
},
_ => false,
};
@@ -1066,7 +1066,7 @@
Mutability::Not => "&",
Mutability::Mut => "&mut ",
};
- (prefix, expr.precedence() < ExprPrecedence::Prefix)
+ (prefix, cx.precedence(expr) < ExprPrecedence::Prefix)
},
None if !ty.is_ref() && data.adjusted_ty.is_ref() => ("&", false),
_ => ("", false),
@@ -1172,7 +1172,7 @@
},
Some(parent) if !parent.span.from_expansion() => {
// Double reference might be needed at this point.
- if parent.precedence() == ExprPrecedence::Unambiguous {
+ if cx.precedence(parent) == ExprPrecedence::Unambiguous {
// Parentheses would be needed here, don't lint.
*outer_pat = None;
} else {
diff --git a/clippy_lints/src/disallowed_macros.rs b/clippy_lints/src/disallowed_macros.rs
index 25b7099..37a1211 100644
--- a/clippy_lints/src/disallowed_macros.rs
+++ b/clippy_lints/src/disallowed_macros.rs
@@ -39,6 +39,9 @@
/// # When using an inline table, can add a `reason` for why the macro
/// # is disallowed.
/// { path = "serde::Serialize", reason = "no serializing" },
+ /// # This would normally error if the path is incorrect, but with `allow-invalid` = `true`,
+ /// # it will be silently ignored
+ /// { path = "std::invalid_macro", reason = "use alternative instead", allow-invalid = true }
/// ]
/// ```
/// ```no_run
diff --git a/clippy_lints/src/disallowed_methods.rs b/clippy_lints/src/disallowed_methods.rs
index fb970e1..8c06743 100644
--- a/clippy_lints/src/disallowed_methods.rs
+++ b/clippy_lints/src/disallowed_methods.rs
@@ -34,6 +34,9 @@
/// { path = "std::vec::Vec::leak", reason = "no leaking memory" },
/// # Can also add a `replacement` that will be offered as a suggestion.
/// { path = "std::sync::Mutex::new", reason = "prefer faster & simpler non-poisonable mutex", replacement = "parking_lot::Mutex::new" },
+ /// # This would normally error if the path is incorrect, but with `allow-invalid` = `true`,
+ /// # it will be silently ignored
+ /// { path = "std::fs::InvalidPath", reason = "use alternative instead", allow-invalid = true },
/// ]
/// ```
///
diff --git a/clippy_lints/src/disallowed_script_idents.rs b/clippy_lints/src/disallowed_script_idents.rs
index d1a8590..cf964d4 100644
--- a/clippy_lints/src/disallowed_script_idents.rs
+++ b/clippy_lints/src/disallowed_script_idents.rs
@@ -89,6 +89,10 @@
// Fast path for ascii-only idents.
if !symbol_str.is_ascii()
&& let Some(script) = symbol_str.chars().find_map(|c| {
+ if c.is_ascii() {
+ return None;
+ }
+
c.script_extension()
.iter()
.find(|script| !self.whitelist.contains(script))
diff --git a/clippy_lints/src/disallowed_types.rs b/clippy_lints/src/disallowed_types.rs
index 7875cdd..9a82327 100644
--- a/clippy_lints/src/disallowed_types.rs
+++ b/clippy_lints/src/disallowed_types.rs
@@ -35,6 +35,9 @@
/// { path = "std::net::Ipv4Addr", reason = "no IPv4 allowed" },
/// # Can also add a `replacement` that will be offered as a suggestion.
/// { path = "std::sync::Mutex", reason = "prefer faster & simpler non-poisonable mutex", replacement = "parking_lot::Mutex" },
+ /// # This would normally error if the path is incorrect, but with `allow-invalid` = `true`,
+ /// # it will be silently ignored
+ /// { path = "std::invalid::Type", reason = "use alternative instead", allow-invalid = true }
/// ]
/// ```
///
diff --git a/clippy_lints/src/doc/broken_link.rs b/clippy_lints/src/doc/broken_link.rs
new file mode 100644
index 0000000..a97b807
--- /dev/null
+++ b/clippy_lints/src/doc/broken_link.rs
@@ -0,0 +1,83 @@
+use clippy_utils::diagnostics::span_lint;
+use pulldown_cmark::BrokenLink as PullDownBrokenLink;
+use rustc_lint::LateContext;
+use rustc_resolve::rustdoc::{DocFragment, source_span_for_markdown_range};
+use rustc_span::{BytePos, Pos, Span};
+
+use super::DOC_BROKEN_LINK;
+
+/// Scan and report broken link on documents.
+/// It ignores false positives detected by `pulldown_cmark`, and only
+/// warns users when the broken link is consider a URL.
+// NOTE: We don't check these other cases because
+// rustdoc itself will check and warn about it:
+// - When a link url is broken across multiple lines in the URL path part
+// - When a link tag is missing the close parenthesis character at the end.
+// - When a link has whitespace within the url link.
+pub fn check(cx: &LateContext<'_>, bl: &PullDownBrokenLink<'_>, doc: &str, fragments: &[DocFragment]) {
+ warn_if_broken_link(cx, bl, doc, fragments);
+}
+
+fn warn_if_broken_link(cx: &LateContext<'_>, bl: &PullDownBrokenLink<'_>, doc: &str, fragments: &[DocFragment]) {
+ if let Some(span) = source_span_for_markdown_range(cx.tcx, doc, &bl.span, fragments) {
+ let mut len = 0;
+
+ // grab raw link data
+ let (_, raw_link) = doc.split_at(bl.span.start);
+
+ // strip off link text part
+ let raw_link = match raw_link.split_once(']') {
+ None => return,
+ Some((prefix, suffix)) => {
+ len += prefix.len() + 1;
+ suffix
+ },
+ };
+
+ let raw_link = match raw_link.split_once('(') {
+ None => return,
+ Some((prefix, suffix)) => {
+ if !prefix.is_empty() {
+ // there is text between ']' and '(' chars, so it is not a valid link
+ return;
+ }
+ len += prefix.len() + 1;
+ suffix
+ },
+ };
+
+ if raw_link.starts_with("(http") {
+ // reduce chances of false positive reports
+ // by limiting this checking only to http/https links.
+ return;
+ }
+
+ for c in raw_link.chars() {
+ if c == ')' {
+ // it is a valid link
+ return;
+ }
+
+ if c == '\n' {
+ report_broken_link(cx, span, len);
+ break;
+ }
+
+ len += 1;
+ }
+ }
+}
+
+fn report_broken_link(cx: &LateContext<'_>, frag_span: Span, offset: usize) {
+ let start = frag_span.lo();
+ let end = start + BytePos::from_usize(offset);
+
+ let span = Span::new(start, end, frag_span.ctxt(), frag_span.parent());
+
+ span_lint(
+ cx,
+ DOC_BROKEN_LINK,
+ span,
+ "possible broken doc link: broken across multiple lines",
+ );
+}
diff --git a/clippy_lints/src/doc/doc_suspicious_footnotes.rs b/clippy_lints/src/doc/doc_suspicious_footnotes.rs
index 289b6b9..3330cc5 100644
--- a/clippy_lints/src/doc/doc_suspicious_footnotes.rs
+++ b/clippy_lints/src/doc/doc_suspicious_footnotes.rs
@@ -1,4 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_then;
+use rustc_ast::attr::AttributeExt as _;
use rustc_ast::token::CommentKind;
use rustc_errors::Applicability;
use rustc_hir::{AttrStyle, Attribute};
@@ -43,13 +44,15 @@
"looks like a footnote ref, but has no matching footnote",
|diag| {
if this_fragment.kind == DocFragmentKind::SugaredDoc {
- let (doc_attr, (_, doc_attr_comment_kind)) = attrs
+ let (doc_attr, (_, doc_attr_comment_kind), attr_style) = attrs
.iter()
.filter(|attr| attr.span().overlaps(this_fragment.span))
.rev()
- .find_map(|attr| Some((attr, attr.doc_str_and_comment_kind()?)))
+ .find_map(|attr| {
+ Some((attr, attr.doc_str_and_comment_kind()?, attr.doc_resolution_scope()?))
+ })
.unwrap();
- let (to_add, terminator) = match (doc_attr_comment_kind, doc_attr.style()) {
+ let (to_add, terminator) = match (doc_attr_comment_kind, attr_style) {
(CommentKind::Line, AttrStyle::Outer) => ("\n///\n/// ", ""),
(CommentKind::Line, AttrStyle::Inner) => ("\n//!\n//! ", ""),
(CommentKind::Block, AttrStyle::Outer) => ("\n/** ", " */"),
diff --git a/clippy_lints/src/doc/missing_headers.rs b/clippy_lints/src/doc/missing_headers.rs
index 9ee32fc..3033ac0 100644
--- a/clippy_lints/src/doc/missing_headers.rs
+++ b/clippy_lints/src/doc/missing_headers.rs
@@ -3,7 +3,7 @@
use clippy_utils::macros::{is_panic, root_macro_call_first_node};
use clippy_utils::ty::{get_type_diagnostic_name, implements_trait_with_env, is_type_diagnostic_item};
use clippy_utils::visitors::for_each_expr;
-use clippy_utils::{fulfill_or_allowed, is_doc_hidden, method_chain_args, return_ty};
+use clippy_utils::{fulfill_or_allowed, is_doc_hidden, is_inside_always_const_context, method_chain_args, return_ty};
use rustc_hir::{BodyId, FnSig, OwnerId, Safety};
use rustc_lint::LateContext;
use rustc_middle::ty;
@@ -99,13 +99,16 @@
let mut panic_span = None;
let typeck = cx.tcx.typeck_body(body_id);
for_each_expr(cx, cx.tcx.hir_body(body_id), |expr| {
+ if is_inside_always_const_context(cx.tcx, expr.hir_id) {
+ return ControlFlow::<!>::Continue(());
+ }
+
if let Some(macro_call) = root_macro_call_first_node(cx, expr)
&& (is_panic(cx, macro_call.def_id)
|| matches!(
cx.tcx.get_diagnostic_name(macro_call.def_id),
Some(sym::assert_macro | sym::assert_eq_macro | sym::assert_ne_macro)
))
- && !cx.tcx.hir_is_inside_const_context(expr.hir_id)
&& !fulfill_or_allowed(cx, MISSING_PANICS_DOC, [expr.hir_id])
&& panic_span.is_none()
{
diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs
index e0fc2fd..1b6c48e 100644
--- a/clippy_lints/src/doc/mod.rs
+++ b/clippy_lints/src/doc/mod.rs
@@ -24,6 +24,7 @@
use std::ops::Range;
use url::Url;
+mod broken_link;
mod doc_comment_double_space_linebreaks;
mod doc_suspicious_footnotes;
mod include_in_doc_without_cfg;
@@ -294,6 +295,34 @@
declare_clippy_lint! {
/// ### What it does
+ /// Checks the doc comments have unbroken links, mostly caused
+ /// by bad formatted links such as broken across multiple lines.
+ ///
+ /// ### Why is this bad?
+ /// Because documentation generated by rustdoc will be broken
+ /// since expected links won't be links and just text.
+ ///
+ /// ### Examples
+ /// This link is broken:
+ /// ```no_run
+ /// /// [example of a bad link](https://
+ /// /// github.com/rust-lang/rust-clippy/)
+ /// pub fn do_something() {}
+ /// ```
+ ///
+ /// It shouldn't be broken across multiple lines to work:
+ /// ```no_run
+ /// /// [example of a good link](https://github.com/rust-lang/rust-clippy/)
+ /// pub fn do_something() {}
+ /// ```
+ #[clippy::version = "1.84.0"]
+ pub DOC_BROKEN_LINK,
+ pedantic,
+ "broken document link"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
/// Checks for the doc comments of publicly visible
/// safe functions and traits and warns if there is a `# Safety` section.
///
@@ -656,6 +685,7 @@
impl_lint_pass!(Documentation => [
DOC_LINK_CODE,
DOC_LINK_WITH_QUOTES,
+ DOC_BROKEN_LINK,
DOC_MARKDOWN,
DOC_NESTED_REFDEFS,
MISSING_SAFETY_DOC,
@@ -786,9 +816,9 @@
/// back in the various late lint pass methods if they need the final doc headers, like "Safety" or
/// "Panics" sections.
fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &[Attribute]) -> Option<DocHeaders> {
- /// We don't want the parser to choke on intra doc links. Since we don't
- /// actually care about rendering them, just pretend that all broken links
- /// point to a fake address.
+ // We don't want the parser to choke on intra doc links. Since we don't
+ // actually care about rendering them, just pretend that all broken links
+ // point to a fake address.
#[expect(clippy::unnecessary_wraps)] // we're following a type signature
fn fake_broken_link_callback<'a>(_: BrokenLink<'_>) -> Option<(CowStr<'a>, CowStr<'a>)> {
Some(("fake".into(), "fake".into()))
@@ -828,14 +858,12 @@
return Some(DocHeaders::default());
}
- let mut cb = fake_broken_link_callback;
-
check_for_code_clusters(
cx,
pulldown_cmark::Parser::new_with_broken_link_callback(
&doc,
main_body_opts() - Options::ENABLE_SMART_PUNCTUATION,
- Some(&mut cb),
+ Some(&mut fake_broken_link_callback),
)
.into_offset_iter(),
&doc,
@@ -845,9 +873,17 @@
},
);
+ // NOTE: check_doc uses it own cb function,
+ // to avoid causing duplicated diagnostics for the broken link checker.
+ let mut full_fake_broken_link_callback = |bl: BrokenLink<'_>| -> Option<(CowStr<'_>, CowStr<'_>)> {
+ broken_link::check(cx, &bl, &doc, &fragments);
+ Some(("fake".into(), "fake".into()))
+ };
+
// disable smart punctuation to pick up ['link'] more easily
let opts = main_body_opts() - Options::ENABLE_SMART_PUNCTUATION;
- let parser = pulldown_cmark::Parser::new_with_broken_link_callback(&doc, opts, Some(&mut cb));
+ let parser =
+ pulldown_cmark::Parser::new_with_broken_link_callback(&doc, opts, Some(&mut full_fake_broken_link_callback));
Some(check_doc(
cx,
diff --git a/clippy_lints/src/doc/needless_doctest_main.rs b/clippy_lints/src/doc/needless_doctest_main.rs
index 587ea63..74283d7 100644
--- a/clippy_lints/src/doc/needless_doctest_main.rs
+++ b/clippy_lints/src/doc/needless_doctest_main.rs
@@ -42,9 +42,8 @@
let mut test_attr_spans = vec![];
let filename = FileName::anon_source_code(&code);
- let fallback_bundle =
- rustc_errors::fallback_fluent_bundle(rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), false);
- let emitter = HumanEmitter::new(Box::new(io::sink()), fallback_bundle);
+ let translator = rustc_driver::default_translator();
+ let emitter = HumanEmitter::new(Box::new(io::sink()), translator);
let dcx = DiagCtxt::new(Box::new(emitter)).disable_warnings();
#[expect(clippy::arc_with_non_send_sync)] // `Arc` is expected by with_dcx
let sm = Arc::new(SourceMap::new(FilePathMapping::empty()));
@@ -72,6 +71,7 @@
if !ignore {
get_test_spans(&item, *ident, &mut test_attr_spans);
}
+
let is_async = matches!(sig.header.coroutine_kind, Some(CoroutineKind::Async { .. }));
let returns_nothing = match &sig.decl.output {
FnRetTy::Default(..) => true,
@@ -90,9 +90,14 @@
// Another function was found; this case is ignored for needless_doctest_main
ItemKind::Fn(fn_) => {
eligible = false;
- if !ignore {
- get_test_spans(&item, fn_.ident, &mut test_attr_spans);
+ if ignore {
+ // If ignore is active invalidating one lint,
+ // and we already found another function thus
+ // invalidating the other one, we have no
+ // business continuing.
+ return (false, test_attr_spans);
}
+ get_test_spans(&item, fn_.ident, &mut test_attr_spans);
},
// Tests with one of these items are ignored
ItemKind::Static(..)
@@ -123,6 +128,18 @@
let trailing_whitespace = text.len() - text.trim_end().len();
+ // We currently only test for "fn main". Checking for the real
+ // entrypoint (with tcx.entry_fn(())) in each block would be unnecessarily
+ // expensive, as those are probably intended and relevant. Same goes for
+ // macros and other weird ways of declaring a main function.
+ //
+ // Also, as we only check for attribute names and don't do macro expansion,
+ // we can check only for #[test]
+
+ if !((text.contains("main") && text.contains("fn")) || text.contains("#[test]")) {
+ return;
+ }
+
// Because of the global session, we need to create a new session in a different thread with
// the edition we need.
let text = text.to_owned();
diff --git a/clippy_lints/src/empty_line_after.rs b/clippy_lints/src/empty_line_after.rs
index 0c5f8bb..3bd7485 100644
--- a/clippy_lints/src/empty_line_after.rs
+++ b/clippy_lints/src/empty_line_after.rs
@@ -10,7 +10,7 @@
use rustc_lexer::TokenKind;
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
use rustc_session::impl_lint_pass;
-use rustc_span::{BytePos, ExpnKind, Ident, InnerSpan, Span, SpanData, Symbol, kw};
+use rustc_span::{BytePos, ExpnKind, Ident, InnerSpan, Span, SpanData, Symbol, kw, sym};
declare_clippy_lint! {
/// ### What it does
@@ -129,10 +129,55 @@
kind: StopKind,
first: usize,
last: usize,
+ name: Option<Symbol>,
}
impl Stop {
- fn convert_to_inner(&self) -> (Span, String) {
+ fn is_outer_attr_only(&self) -> bool {
+ let Some(name) = self.name else {
+ return false;
+ };
+ // Check if the attribute only has effect when as an outer attribute
+ // The below attributes are collected from the builtin attributes of The Rust Reference
+ // https://doc.rust-lang.org/reference/attributes.html#r-attributes.builtin
+ // And the comments below are from compiler errors and warnings
+ matches!(
+ name,
+ // Cannot be used at crate level
+ sym::repr | sym::test | sym::derive | sym::automatically_derived | sym::path | sym::global_allocator |
+ // Only has an effect on macro definitions
+ sym::macro_export |
+ // Only be applied to trait definitions
+ sym::on_unimplemented |
+ // Only be placed on trait implementations
+ sym::do_not_recommend |
+ // Only has an effect on items
+ sym::ignore | sym::should_panic | sym::proc_macro | sym::proc_macro_derive | sym::proc_macro_attribute |
+ // Has no effect when applied to a module
+ sym::must_use |
+ // Should be applied to a foreign function or static
+ sym::link_name | sym::link_ordinal | sym::link_section |
+ // Should be applied to an `extern crate` item
+ sym::no_link |
+ // Should be applied to a free function, impl method or static
+ sym::export_name | sym::no_mangle |
+ // Should be applied to a `static` variable
+ sym::used |
+ // Should be applied to function or closure
+ sym::inline |
+ // Should be applied to a function definition
+ sym::cold | sym::target_feature | sym::track_caller | sym::instruction_set |
+ // Should be applied to a struct or enum
+ sym::non_exhaustive |
+ // Note: No any warning when it as an inner attribute, but it has no effect
+ sym::panic_handler
+ )
+ }
+
+ fn convert_to_inner(&self) -> Option<(Span, String)> {
+ if self.is_outer_attr_only() {
+ return None;
+ }
let inner = match self.kind {
// #![...]
StopKind::Attr => InnerSpan::new(1, 1),
@@ -140,7 +185,7 @@
// ^ ^
StopKind::Doc(_) => InnerSpan::new(2, 3),
};
- (self.span.from_inner(inner), "!".into())
+ Some((self.span.from_inner(inner), "!".into()))
}
fn comment_out(&self, cx: &EarlyContext<'_>, suggestions: &mut Vec<(Span, String)>) {
@@ -177,6 +222,7 @@
},
first: file.lookup_line(file.relative_position(lo))?,
last: file.lookup_line(file.relative_position(hi))?,
+ name: attr.name(),
})
}
}
@@ -356,6 +402,12 @@
if let Some(parent) = self.items.iter().rev().nth(1)
&& (parent.kind == "module" || parent.kind == "crate")
&& parent.mod_items == Some(id)
+ && let suggestions = gaps
+ .iter()
+ .flat_map(|gap| gap.prev_chunk)
+ .filter_map(Stop::convert_to_inner)
+ .collect::<Vec<_>>()
+ && !suggestions.is_empty()
{
let desc = if parent.kind == "module" {
"parent module"
@@ -367,10 +419,7 @@
StopKind::Attr => format!("if the attribute should apply to the {desc} use an inner attribute"),
StopKind::Doc(_) => format!("if the comment should document the {desc} use an inner doc comment"),
},
- gaps.iter()
- .flat_map(|gap| gap.prev_chunk)
- .map(Stop::convert_to_inner)
- .collect(),
+ suggestions,
Applicability::MaybeIncorrect,
);
}
@@ -425,6 +474,7 @@
first: line.line,
// last doesn't need to be accurate here, we don't compare it with anything
last: line.line,
+ name: None,
});
}
diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs
index 6ed7c87..0288747 100644
--- a/clippy_lints/src/eta_reduction.rs
+++ b/clippy_lints/src/eta_reduction.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::diagnostics::span_lint_hir_and_then;
use clippy_utils::higher::VecArgs;
use clippy_utils::source::{snippet_opt, snippet_with_applicability};
use clippy_utils::ty::get_type_diagnostic_name;
@@ -7,6 +7,7 @@
get_path_from_caller_to_method_type, is_adjusted, is_no_std_crate, path_to_local, path_to_local_id,
};
use rustc_abi::ExternAbi;
+use rustc_attr_data_structures::{AttributeKind, find_attr};
use rustc_errors::Applicability;
use rustc_hir::{BindingMode, Expr, ExprKind, FnRetTy, GenericArgs, Param, PatKind, QPath, Safety, TyKind};
use rustc_infer::infer::TyCtxtInferExt;
@@ -108,14 +109,20 @@
{
let vec_crate = if is_no_std_crate(cx) { "alloc" } else { "std" };
// replace `|| vec![]` with `Vec::new`
- span_lint_and_sugg(
+ span_lint_hir_and_then(
cx,
REDUNDANT_CLOSURE,
+ expr.hir_id,
expr.span,
"redundant closure",
- "replace the closure with `Vec::new`",
- format!("{vec_crate}::vec::Vec::new"),
- Applicability::MachineApplicable,
+ |diag| {
+ diag.span_suggestion(
+ expr.span,
+ "replace the closure with `Vec::new`",
+ format!("{vec_crate}::vec::Vec::new"),
+ Applicability::MachineApplicable,
+ );
+ },
);
}
// skip `foo(|| macro!())`
@@ -155,7 +162,7 @@
let sig = match callee_ty_adjusted.kind() {
ty::FnDef(def, _) => {
// Rewriting `x(|| f())` to `x(f)` where f is marked `#[track_caller]` moves the `Location`
- if cx.tcx.has_attr(*def, sym::track_caller) {
+ if find_attr!(cx.tcx.get_all_attrs(*def), AttributeKind::TrackCaller(..)) {
return;
}
@@ -197,46 +204,53 @@
// For now ignore all callee types which reference a type parameter.
&& !generic_args.types().any(|t| matches!(t.kind(), ty::Param(_)))
{
- span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| {
- if let Some(mut snippet) = snippet_opt(cx, callee.span) {
- if path_to_local(callee).is_some_and(|l| {
- // FIXME: Do we really need this `local_used_in` check?
- // Isn't it checking something like... `callee(callee)`?
- // If somehow this check is needed, add some test for it,
- // 'cuz currently nothing changes after deleting this check.
- local_used_in(cx, l, args) || local_used_after_expr(cx, l, expr)
- }) {
- match cx
- .tcx
- .infer_ctxt()
- .build(cx.typing_mode())
- .err_ctxt()
- .type_implements_fn_trait(
- cx.param_env,
- Binder::bind_with_vars(callee_ty_adjusted, List::empty()),
- ty::PredicatePolarity::Positive,
- ) {
- // Mutable closure is used after current expr; we cannot consume it.
- Ok((ClosureKind::FnMut, _)) => snippet = format!("&mut {snippet}"),
- Ok((ClosureKind::Fn, _)) if !callee_ty_raw.is_ref() => {
- snippet = format!("&{snippet}");
- },
- _ => (),
+ span_lint_hir_and_then(
+ cx,
+ REDUNDANT_CLOSURE,
+ expr.hir_id,
+ expr.span,
+ "redundant closure",
+ |diag| {
+ if let Some(mut snippet) = snippet_opt(cx, callee.span) {
+ if path_to_local(callee).is_some_and(|l| {
+ // FIXME: Do we really need this `local_used_in` check?
+ // Isn't it checking something like... `callee(callee)`?
+ // If somehow this check is needed, add some test for it,
+ // 'cuz currently nothing changes after deleting this check.
+ local_used_in(cx, l, args) || local_used_after_expr(cx, l, expr)
+ }) {
+ match cx
+ .tcx
+ .infer_ctxt()
+ .build(cx.typing_mode())
+ .err_ctxt()
+ .type_implements_fn_trait(
+ cx.param_env,
+ Binder::bind_with_vars(callee_ty_adjusted, List::empty()),
+ ty::PredicatePolarity::Positive,
+ ) {
+ // Mutable closure is used after current expr; we cannot consume it.
+ Ok((ClosureKind::FnMut, _)) => snippet = format!("&mut {snippet}"),
+ Ok((ClosureKind::Fn, _)) if !callee_ty_raw.is_ref() => {
+ snippet = format!("&{snippet}");
+ },
+ _ => (),
+ }
}
+ diag.span_suggestion(
+ expr.span,
+ "replace the closure with the function itself",
+ snippet,
+ Applicability::MachineApplicable,
+ );
}
- diag.span_suggestion(
- expr.span,
- "replace the closure with the function itself",
- snippet,
- Applicability::MachineApplicable,
- );
- }
- });
+ },
+ );
}
},
ExprKind::MethodCall(path, self_, args, _) if check_inputs(typeck, body.params, Some(self_), args) => {
if let Some(method_def_id) = typeck.type_dependent_def_id(body.value.hir_id)
- && !cx.tcx.has_attr(method_def_id, sym::track_caller)
+ && !find_attr!(cx.tcx.get_all_attrs(method_def_id), AttributeKind::TrackCaller(..))
&& check_sig(closure_sig, cx.tcx.fn_sig(method_def_id).skip_binder().skip_binder())
{
let mut app = Applicability::MachineApplicable;
@@ -244,9 +258,10 @@
Some(span) => format!("::{}", snippet_with_applicability(cx, span, "<..>", &mut app)),
None => String::new(),
};
- span_lint_and_then(
+ span_lint_hir_and_then(
cx,
REDUNDANT_CLOSURE_FOR_METHOD_CALLS,
+ expr.hir_id,
expr.span,
"redundant closure",
|diag| {
diff --git a/clippy_lints/src/exhaustive_items.rs b/clippy_lints/src/exhaustive_items.rs
index 1fb0e4d..86d9038 100644
--- a/clippy_lints/src/exhaustive_items.rs
+++ b/clippy_lints/src/exhaustive_items.rs
@@ -76,7 +76,7 @@
"exported enums should not be exhaustive",
[].as_slice(),
),
- ItemKind::Struct(_, _, v) => (
+ ItemKind::Struct(_, _, v) if v.fields().iter().all(|f| f.default.is_none()) => (
EXHAUSTIVE_STRUCTS,
"exported structs should not be exhaustive",
v.fields(),
diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs
index 3c7e83b..b3c9e86 100644
--- a/clippy_lints/src/floating_point_arithmetic.rs
+++ b/clippy_lints/src/floating_point_arithmetic.rs
@@ -5,14 +5,13 @@
eq_expr_value, get_parent_expr, higher, is_in_const_context, is_inherent_method_call, is_no_std_crate,
numeric_literal, peel_blocks, sugg, sym,
};
+use rustc_ast::ast;
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_session::declare_lint_pass;
use rustc_span::source_map::Spanned;
-
-use rustc_ast::ast;
use std::f32::consts as f32_consts;
use std::f64::consts as f64_consts;
use sugg::Sugg;
diff --git a/clippy_lints/src/functions/must_use.rs b/clippy_lints/src/functions/must_use.rs
index 7065583..d959981 100644
--- a/clippy_lints/src/functions/must_use.rs
+++ b/clippy_lints/src/functions/must_use.rs
@@ -14,6 +14,8 @@
use clippy_utils::ty::is_must_use_ty;
use clippy_utils::visitors::for_each_expr_without_closures;
use clippy_utils::{return_ty, trait_ref_of_method};
+use rustc_attr_data_structures::{AttributeKind, find_attr};
+use rustc_span::Symbol;
use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
use core::ops::ControlFlow;
@@ -22,7 +24,7 @@
pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
let attrs = cx.tcx.hir_attrs(item.hir_id());
- let attr = cx.tcx.get_attr(item.owner_id, sym::must_use);
+ let attr = find_attr!(cx.tcx.hir_attrs(item.hir_id()), AttributeKind::MustUse { span, reason } => (span, reason));
if let hir::ItemKind::Fn {
ref sig,
body: ref body_id,
@@ -31,9 +33,19 @@
{
let is_public = cx.effective_visibilities.is_exported(item.owner_id.def_id);
let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
- if let Some(attr) = attr {
- check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, attr, attrs, sig);
- } else if is_public && !is_proc_macro(attrs) && !attrs.iter().any(|a| a.has_name(sym::no_mangle)) {
+ if let Some((attr_span, reason)) = attr {
+ check_needless_must_use(
+ cx,
+ sig.decl,
+ item.owner_id,
+ item.span,
+ fn_header_span,
+ *attr_span,
+ *reason,
+ attrs,
+ sig,
+ );
+ } else if is_public && !is_proc_macro(attrs) && !find_attr!(attrs, AttributeKind::NoMangle(..)) {
check_must_use_candidate(
cx,
sig.decl,
@@ -52,9 +64,20 @@
let is_public = cx.effective_visibilities.is_exported(item.owner_id.def_id);
let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
let attrs = cx.tcx.hir_attrs(item.hir_id());
- let attr = cx.tcx.get_attr(item.owner_id, sym::must_use);
- if let Some(attr) = attr {
- check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, attr, attrs, sig);
+ let attr =
+ find_attr!(cx.tcx.hir_attrs(item.hir_id()), AttributeKind::MustUse { span, reason } => (span, reason));
+ if let Some((attr_span, reason)) = attr {
+ check_needless_must_use(
+ cx,
+ sig.decl,
+ item.owner_id,
+ item.span,
+ fn_header_span,
+ *attr_span,
+ *reason,
+ attrs,
+ sig,
+ );
} else if is_public && !is_proc_macro(attrs) && trait_ref_of_method(cx, item.owner_id).is_none() {
check_must_use_candidate(
cx,
@@ -75,9 +98,20 @@
let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
let attrs = cx.tcx.hir_attrs(item.hir_id());
- let attr = cx.tcx.get_attr(item.owner_id, sym::must_use);
- if let Some(attr) = attr {
- check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, attr, attrs, sig);
+ let attr =
+ find_attr!(cx.tcx.hir_attrs(item.hir_id()), AttributeKind::MustUse { span, reason } => (span, reason));
+ if let Some((attr_span, reason)) = attr {
+ check_needless_must_use(
+ cx,
+ sig.decl,
+ item.owner_id,
+ item.span,
+ fn_header_span,
+ *attr_span,
+ *reason,
+ attrs,
+ sig,
+ );
} else if let hir::TraitFn::Provided(eid) = *eid {
let body = cx.tcx.hir_body(eid);
if attr.is_none() && is_public && !is_proc_macro(attrs) {
@@ -103,7 +137,8 @@
item_id: hir::OwnerId,
item_span: Span,
fn_header_span: Span,
- attr: &Attribute,
+ attr_span: Span,
+ reason: Option<Symbol>,
attrs: &[Attribute],
sig: &FnSig<'_>,
) {
@@ -118,12 +153,7 @@
fn_header_span,
"this unit-returning function has a `#[must_use]` attribute",
|diag| {
- diag.span_suggestion(
- attr.span(),
- "remove the attribute",
- "",
- Applicability::MachineApplicable,
- );
+ diag.span_suggestion(attr_span, "remove the attribute", "", Applicability::MachineApplicable);
},
);
} else {
@@ -137,11 +167,11 @@
MUST_USE_UNIT,
fn_header_span,
"this unit-returning function has a `#[must_use]` attribute",
- Some(attr.span()),
+ Some(attr_span),
"remove `must_use`",
);
}
- } else if attr.value_str().is_none() && is_must_use_ty(cx, return_ty(cx, item_id)) {
+ } else if reason.is_none() && is_must_use_ty(cx, return_ty(cx, item_id)) {
// Ignore async functions unless Future::Output type is a must_use type
if sig.header.is_async() {
let infcx = cx.tcx.infer_ctxt().build(cx.typing_mode());
diff --git a/clippy_lints/src/if_not_else.rs b/clippy_lints/src/if_not_else.rs
index 45f9aa0..ab7a965 100644
--- a/clippy_lints/src/if_not_else.rs
+++ b/clippy_lints/src/if_not_else.rs
@@ -1,4 +1,4 @@
-use clippy_utils::consts::{ConstEvalCtxt, Constant};
+use clippy_utils::consts::is_zero_integer_const;
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
use clippy_utils::is_else_clause;
use clippy_utils::source::{HasSession, indent_of, reindent_multiline, snippet};
@@ -48,13 +48,6 @@
declare_lint_pass!(IfNotElse => [IF_NOT_ELSE]);
-fn is_zero_const(expr: &Expr<'_>, cx: &LateContext<'_>) -> bool {
- if let Some(value) = ConstEvalCtxt::new(cx).eval_simple(expr) {
- return Constant::Int(0) == value;
- }
- false
-}
-
impl LateLintPass<'_> for IfNotElse {
fn check_expr(&mut self, cx: &LateContext<'_>, e: &Expr<'_>) {
if let ExprKind::If(cond, cond_inner, Some(els)) = e.kind
@@ -68,7 +61,7 @@
),
// Don't lint on `… != 0`, as these are likely to be bit tests.
// For example, `if foo & 0x0F00 != 0 { … } else { … }` is already in the "proper" order.
- ExprKind::Binary(op, _, rhs) if op.node == BinOpKind::Ne && !is_zero_const(rhs, cx) => (
+ ExprKind::Binary(op, _, rhs) if op.node == BinOpKind::Ne && !is_zero_integer_const(cx, rhs) => (
"unnecessary `!=` operation",
"change to `==` and swap the blocks of the `if`/`else`",
),
diff --git a/clippy_lints/src/inline_fn_without_body.rs b/clippy_lints/src/inline_fn_without_body.rs
index da5ca5e..ffe6ad1 100644
--- a/clippy_lints/src/inline_fn_without_body.rs
+++ b/clippy_lints/src/inline_fn_without_body.rs
@@ -1,10 +1,10 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::sugg::DiagExt;
+use rustc_attr_data_structures::{AttributeKind, find_attr};
use rustc_errors::Applicability;
use rustc_hir::{TraitFn, TraitItem, TraitItemKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass;
-use rustc_span::sym;
declare_clippy_lint! {
/// ### What it does
@@ -32,15 +32,19 @@
impl<'tcx> LateLintPass<'tcx> for InlineFnWithoutBody {
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
if let TraitItemKind::Fn(_, TraitFn::Required(_)) = item.kind
- && let Some(attr) = cx.tcx.hir_attrs(item.hir_id()).iter().find(|a| a.has_name(sym::inline))
+ && let Some(attr_span) = find_attr!(cx
+ .tcx
+ .hir_attrs(item.hir_id()),
+ AttributeKind::Inline(_, span) => *span
+ )
{
span_lint_and_then(
cx,
INLINE_FN_WITHOUT_BODY,
- attr.span(),
+ attr_span,
format!("use of `#[inline]` on trait method `{}` which has no body", item.ident),
|diag| {
- diag.suggest_remove_item(cx, attr.span(), "remove", Applicability::MachineApplicable);
+ diag.suggest_remove_item(cx, attr_span, "remove", Applicability::MachineApplicable);
},
);
}
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index be9142b..96a6dee 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -59,10 +59,10 @@
extern crate thin_vec;
#[macro_use]
-mod declare_clippy_lint;
+extern crate clippy_utils;
#[macro_use]
-extern crate clippy_utils;
+extern crate declare_clippy_lint;
mod utils;
@@ -411,108 +411,9 @@
use clippy_config::{Conf, get_configuration_metadata, sanitize_explanation};
use clippy_utils::macros::FormatArgsStorage;
use rustc_data_structures::fx::FxHashSet;
-use rustc_lint::{Lint, LintId};
+use rustc_lint::Lint;
use utils::attr_collector::{AttrCollector, AttrStorage};
-#[derive(Default)]
-struct RegistrationGroups {
- all: Vec<LintId>,
- cargo: Vec<LintId>,
- complexity: Vec<LintId>,
- correctness: Vec<LintId>,
- nursery: Vec<LintId>,
- pedantic: Vec<LintId>,
- perf: Vec<LintId>,
- restriction: Vec<LintId>,
- style: Vec<LintId>,
- suspicious: Vec<LintId>,
-}
-
-impl RegistrationGroups {
- #[rustfmt::skip]
- fn register(self, store: &mut rustc_lint::LintStore) {
- store.register_group(true, "clippy::all", Some("clippy_all"), self.all);
- store.register_group(true, "clippy::cargo", Some("clippy_cargo"), self.cargo);
- store.register_group(true, "clippy::complexity", Some("clippy_complexity"), self.complexity);
- store.register_group(true, "clippy::correctness", Some("clippy_correctness"), self.correctness);
- store.register_group(true, "clippy::nursery", Some("clippy_nursery"), self.nursery);
- store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), self.pedantic);
- store.register_group(true, "clippy::perf", Some("clippy_perf"), self.perf);
- store.register_group(true, "clippy::restriction", Some("clippy_restriction"), self.restriction);
- store.register_group(true, "clippy::style", Some("clippy_style"), self.style);
- store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), self.suspicious);
- }
-}
-
-#[derive(Copy, Clone, Debug)]
-pub(crate) enum LintCategory {
- Cargo,
- Complexity,
- Correctness,
- Nursery,
- Pedantic,
- Perf,
- Restriction,
- Style,
- Suspicious,
-}
-
-#[allow(clippy::enum_glob_use)]
-use LintCategory::*;
-
-impl LintCategory {
- fn is_all(self) -> bool {
- matches!(self, Correctness | Suspicious | Style | Complexity | Perf)
- }
-
- fn group(self, groups: &mut RegistrationGroups) -> &mut Vec<LintId> {
- match self {
- Cargo => &mut groups.cargo,
- Complexity => &mut groups.complexity,
- Correctness => &mut groups.correctness,
- Nursery => &mut groups.nursery,
- Pedantic => &mut groups.pedantic,
- Perf => &mut groups.perf,
- Restriction => &mut groups.restriction,
- Style => &mut groups.style,
- Suspicious => &mut groups.suspicious,
- }
- }
-}
-
-pub struct LintInfo {
- /// Double reference to maintain pointer equality
- pub lint: &'static &'static Lint,
- category: LintCategory,
- pub explanation: &'static str,
- /// e.g. `clippy_lints/src/absolute_paths.rs#43`
- pub location: &'static str,
- pub version: Option<&'static str>,
-}
-
-impl LintInfo {
- /// Returns the lint name in lowercase without the `clippy::` prefix
- #[allow(clippy::missing_panics_doc)]
- pub fn name_lower(&self) -> String {
- self.lint.name.strip_prefix("clippy::").unwrap().to_ascii_lowercase()
- }
-
- /// Returns the name of the lint's category in lowercase (`style`, `pedantic`)
- pub fn category_str(&self) -> &'static str {
- match self.category {
- Cargo => "cargo",
- Complexity => "complexity",
- Correctness => "correctness",
- Nursery => "nursery",
- Pedantic => "pedantic",
- Perf => "perf",
- Restriction => "restriction",
- Style => "style",
- Suspicious => "suspicious",
- }
- }
-}
-
pub fn explain(name: &str) -> i32 {
let target = format!("clippy::{}", name.to_ascii_uppercase());
@@ -535,30 +436,11 @@
}
}
-fn register_categories(store: &mut rustc_lint::LintStore) {
- let mut groups = RegistrationGroups::default();
-
- for LintInfo { lint, category, .. } in declared_lints::LINTS {
- if category.is_all() {
- groups.all.push(LintId::of(lint));
- }
-
- category.group(&mut groups).push(LintId::of(lint));
- }
-
- let lints: Vec<&'static Lint> = declared_lints::LINTS.iter().map(|info| *info.lint).collect();
-
- store.register_lints(&lints);
- groups.register(store);
-}
-
/// Register all lints and lint groups with the rustc lint store
///
/// Used in `./src/driver.rs`.
#[expect(clippy::too_many_lines)]
-pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
- register_categories(store);
-
+pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
for (old_name, new_name) in deprecated_lints::RENAMED {
store.register_renamed(old_name, new_name);
}
diff --git a/clippy_lints/src/loops/single_element_loop.rs b/clippy_lints/src/loops/single_element_loop.rs
index 12719c4..d66771a 100644
--- a/clippy_lints/src/loops/single_element_loop.rs
+++ b/clippy_lints/src/loops/single_element_loop.rs
@@ -84,7 +84,7 @@
if !prefix.is_empty()
&& (
// Precedence of internal expression is less than or equal to precedence of `&expr`.
- arg_expression.precedence() <= ExprPrecedence::Prefix || is_range_literal(arg_expression)
+ cx.precedence(arg_expression) <= ExprPrecedence::Prefix || is_range_literal(arg_expression)
)
{
arg_snip = format!("({arg_snip})").into();
diff --git a/clippy_lints/src/manual_let_else.rs b/clippy_lints/src/manual_let_else.rs
index 0b3bec7..9ff82cd 100644
--- a/clippy_lints/src/manual_let_else.rs
+++ b/clippy_lints/src/manual_let_else.rs
@@ -13,7 +13,6 @@
use rustc_hir::def::{CtorOf, DefKind, Res};
use rustc_hir::{Arm, Expr, ExprKind, HirId, MatchSource, Pat, PatExpr, PatExprKind, PatKind, QPath, Stmt, StmtKind};
use rustc_lint::{LateContext, LintContext};
-
use rustc_span::Span;
use rustc_span::symbol::{Symbol, sym};
use std::slice;
diff --git a/clippy_lints/src/manual_option_as_slice.rs b/clippy_lints/src/manual_option_as_slice.rs
index b55c11f..922db17 100644
--- a/clippy_lints/src/manual_option_as_slice.rs
+++ b/clippy_lints/src/manual_option_as_slice.rs
@@ -1,7 +1,7 @@
use clippy_config::Conf;
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
use clippy_utils::msrvs::Msrv;
-use clippy_utils::{is_none_arm, msrvs, paths, peel_hir_expr_refs, sym};
+use clippy_utils::{is_none_arm, msrvs, peel_hir_expr_refs, sym};
use rustc_errors::Applicability;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::{Arm, Expr, ExprKind, LangItem, Pat, PatKind, QPath, is_range_literal};
@@ -220,5 +220,5 @@
}
fn is_slice_from_ref(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
- paths::SLICE_FROM_REF.matches_path(cx, expr)
+ clippy_utils::is_path_diagnostic_item(cx, expr, sym::slice_from_ref)
}
diff --git a/clippy_lints/src/matches/manual_ok_err.rs b/clippy_lints/src/matches/manual_ok_err.rs
index 4959908..edbb556 100644
--- a/clippy_lints/src/matches/manual_ok_err.rs
+++ b/clippy_lints/src/matches/manual_ok_err.rs
@@ -1,9 +1,9 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::{indent_of, reindent_multiline};
use clippy_utils::sugg::Sugg;
-use clippy_utils::ty::option_arg_ty;
+use clippy_utils::ty::{option_arg_ty, peel_mid_ty_refs_is_mutable};
use clippy_utils::{get_parent_expr, is_res_lang_ctor, path_res, peel_blocks, span_contains_comment};
-use rustc_ast::BindingMode;
+use rustc_ast::{BindingMode, Mutability};
use rustc_errors::Applicability;
use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr};
use rustc_hir::def::{DefKind, Res};
@@ -133,7 +133,21 @@
Applicability::MachineApplicable
};
let scrut = Sugg::hir_with_applicability(cx, scrutinee, "..", &mut app).maybe_paren();
- let sugg = format!("{scrut}.{method}()");
+
+ let scrutinee_ty = cx.typeck_results().expr_ty(scrutinee);
+ let (_, n_ref, mutability) = peel_mid_ty_refs_is_mutable(scrutinee_ty);
+ let prefix = if n_ref > 0 {
+ if mutability == Mutability::Mut {
+ ".as_mut()"
+ } else {
+ ".as_ref()"
+ }
+ } else {
+ ""
+ };
+
+ let sugg = format!("{scrut}{prefix}.{method}()");
+
// If the expression being expanded is the `if …` part of an `else if …`, it must be blockified.
let sugg = if let Some(parent_expr) = get_parent_expr(cx, expr)
&& let ExprKind::If(_, _, Some(else_part)) = parent_expr.kind
diff --git a/clippy_lints/src/matches/manual_utils.rs b/clippy_lints/src/matches/manual_utils.rs
index d090573..dbae71b 100644
--- a/clippy_lints/src/matches/manual_utils.rs
+++ b/clippy_lints/src/matches/manual_utils.rs
@@ -117,7 +117,7 @@
// it's being passed by value.
let scrutinee = peel_hir_expr_refs(scrutinee).0;
let (scrutinee_str, _) = snippet_with_context(cx, scrutinee.span, expr_ctxt, "..", &mut app);
- let scrutinee_str = if scrutinee.span.eq_ctxt(expr.span) && scrutinee.precedence() < ExprPrecedence::Unambiguous {
+ let scrutinee_str = if scrutinee.span.eq_ctxt(expr.span) && cx.precedence(scrutinee) < ExprPrecedence::Unambiguous {
format!("({scrutinee_str})")
} else {
scrutinee_str.into()
diff --git a/clippy_lints/src/matches/match_wild_enum.rs b/clippy_lints/src/matches/match_wild_enum.rs
index 24b4a67..70a03ff 100644
--- a/clippy_lints/src/matches/match_wild_enum.rs
+++ b/clippy_lints/src/matches/match_wild_enum.rs
@@ -1,4 +1,5 @@
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::source::SpanRangeExt;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::{is_refutable, peel_hir_pat_refs, recurse_or_patterns};
use rustc_errors::Applicability;
@@ -116,11 +117,12 @@
let format_suggestion = |variant: &VariantDef| {
format!(
"{}{}{}{}",
- if let Some(ident) = wildcard_ident {
- format!("{} @ ", ident.name)
- } else {
- String::new()
- },
+ wildcard_ident.map_or(String::new(), |ident| {
+ ident
+ .span
+ .get_source_text(cx)
+ .map_or_else(|| format!("{} @ ", ident.name), |s| format!("{s} @ "))
+ }),
if let CommonPrefixSearcher::Path(path_prefix) = path_prefix {
let mut s = String::new();
for seg in path_prefix {
@@ -138,7 +140,7 @@
Some(CtorKind::Fn) if variant.fields.len() == 1 => "(_)",
Some(CtorKind::Fn) => "(..)",
Some(CtorKind::Const) => "",
- None => "{ .. }",
+ None => " { .. }",
}
)
};
diff --git a/clippy_lints/src/methods/io_other_error.rs b/clippy_lints/src/methods/io_other_error.rs
index ec4b9c7..9276261 100644
--- a/clippy_lints/src/methods/io_other_error.rs
+++ b/clippy_lints/src/methods/io_other_error.rs
@@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::msrvs::{self, Msrv};
-use clippy_utils::{expr_or_init, paths};
+use clippy_utils::{expr_or_init, is_path_diagnostic_item, sym};
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, QPath};
use rustc_lint::LateContext;
@@ -10,8 +10,11 @@
&& !expr.span.from_expansion()
&& !error_kind.span.from_expansion()
&& let ExprKind::Path(QPath::TypeRelative(_, new_segment)) = path.kind
- && paths::IO_ERROR_NEW.matches_path(cx, path)
- && paths::IO_ERRORKIND_OTHER_CTOR.matches_path(cx, expr_or_init(cx, error_kind))
+ && is_path_diagnostic_item(cx, path, sym::io_error_new)
+ && let ExprKind::Path(QPath::Resolved(_, init_path)) = &expr_or_init(cx, error_kind).kind
+ && let [.., error_kind_ty, error_kind_variant] = init_path.segments
+ && cx.tcx.is_diagnostic_item(sym::io_errorkind, error_kind_ty.res.def_id())
+ && error_kind_variant.ident.name == sym::Other
&& msrv.meets(cx, msrvs::IO_ERROR_OTHER)
{
span_lint_and_then(
diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs
index 7bdd999..2139466 100644
--- a/clippy_lints/src/methods/or_fun_call.rs
+++ b/clippy_lints/src/methods/or_fun_call.rs
@@ -136,7 +136,7 @@
fun_span: Option<Span>,
) -> bool {
// (path, fn_has_argument, methods, suffix)
- const KNOW_TYPES: [(Symbol, bool, &[Symbol], &str); 4] = [
+ const KNOW_TYPES: [(Symbol, bool, &[Symbol], &str); 5] = [
(sym::BTreeEntry, false, &[sym::or_insert], "with"),
(sym::HashMapEntry, false, &[sym::or_insert], "with"),
(
@@ -145,16 +145,17 @@
&[sym::map_or, sym::ok_or, sym::or, sym::unwrap_or],
"else",
),
- (sym::Result, true, &[sym::or, sym::unwrap_or], "else"),
+ (sym::Option, false, &[sym::get_or_insert], "with"),
+ (sym::Result, true, &[sym::map_or, sym::or, sym::unwrap_or], "else"),
];
if KNOW_TYPES.iter().any(|k| k.2.contains(&name))
&& switch_to_lazy_eval(cx, arg)
&& !contains_return(arg)
&& let self_ty = cx.typeck_results().expr_ty(self_expr)
- && let Some(&(_, fn_has_arguments, poss, suffix)) =
- KNOW_TYPES.iter().find(|&&i| is_type_diagnostic_item(cx, self_ty, i.0))
- && poss.contains(&name)
+ && let Some(&(_, fn_has_arguments, _, suffix)) = KNOW_TYPES
+ .iter()
+ .find(|&&i| is_type_diagnostic_item(cx, self_ty, i.0) && i.2.contains(&name))
{
let ctxt = span.ctxt();
let mut app = Applicability::HasPlaceholders;
diff --git a/clippy_lints/src/methods/swap_with_temporary.rs b/clippy_lints/src/methods/swap_with_temporary.rs
index de729fb..e378cbd 100644
--- a/clippy_lints/src/methods/swap_with_temporary.rs
+++ b/clippy_lints/src/methods/swap_with_temporary.rs
@@ -4,6 +4,7 @@
use rustc_errors::{Applicability, Diag};
use rustc_hir::{Expr, ExprKind, Node, QPath};
use rustc_lint::LateContext;
+use rustc_middle::ty::adjustment::Adjust;
use rustc_span::sym;
use super::SWAP_WITH_TEMPORARY;
@@ -11,12 +12,12 @@
const MSG_TEMPORARY: &str = "this expression returns a temporary value";
const MSG_TEMPORARY_REFMUT: &str = "this is a mutable reference to a temporary value";
-pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, func: &Expr<'_>, args: &[Expr<'_>]) {
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, func: &Expr<'_>, args: &'tcx [Expr<'_>]) {
if let ExprKind::Path(QPath::Resolved(_, func_path)) = func.kind
&& let Some(func_def_id) = func_path.res.opt_def_id()
&& cx.tcx.is_diagnostic_item(sym::mem_swap, func_def_id)
{
- match (ArgKind::new(&args[0]), ArgKind::new(&args[1])) {
+ match (ArgKind::new(cx, &args[0]), ArgKind::new(cx, &args[1])) {
(ArgKind::RefMutToTemp(left_temp), ArgKind::RefMutToTemp(right_temp)) => {
emit_lint_useless(cx, expr, &args[0], &args[1], left_temp, right_temp);
},
@@ -28,10 +29,10 @@
}
enum ArgKind<'tcx> {
- // Mutable reference to a place, coming from a macro
- RefMutToPlaceAsMacro(&'tcx Expr<'tcx>),
- // Place behind a mutable reference
- RefMutToPlace(&'tcx Expr<'tcx>),
+ // Mutable reference to a place, coming from a macro, and number of dereferences to use
+ RefMutToPlaceAsMacro(&'tcx Expr<'tcx>, usize),
+ // Place behind a mutable reference, and number of dereferences to use
+ RefMutToPlace(&'tcx Expr<'tcx>, usize),
// Temporary value behind a mutable reference
RefMutToTemp(&'tcx Expr<'tcx>),
// Any other case
@@ -39,13 +40,29 @@
}
impl<'tcx> ArgKind<'tcx> {
- fn new(arg: &'tcx Expr<'tcx>) -> Self {
- if let ExprKind::AddrOf(BorrowKind::Ref, _, target) = arg.kind {
- if target.is_syntactic_place_expr() {
+ /// Build a new `ArgKind` from `arg`. There must be no false positive when returning a
+ /// `ArgKind::RefMutToTemp` variant, as this may cause a spurious lint to be emitted.
+ fn new(cx: &LateContext<'tcx>, arg: &'tcx Expr<'tcx>) -> Self {
+ if let ExprKind::AddrOf(BorrowKind::Ref, _, target) = arg.kind
+ && let adjustments = cx.typeck_results().expr_adjustments(arg)
+ && adjustments
+ .first()
+ .is_some_and(|adj| matches!(adj.kind, Adjust::Deref(None)))
+ && adjustments
+ .last()
+ .is_some_and(|adj| matches!(adj.kind, Adjust::Borrow(_)))
+ {
+ let extra_derefs = adjustments[1..adjustments.len() - 1]
+ .iter()
+ .filter(|adj| matches!(adj.kind, Adjust::Deref(_)))
+ .count();
+ // If a deref is used, `arg` might be a place expression. For example, a mutex guard
+ // would dereference into the mutex content which is probably not temporary.
+ if target.is_syntactic_place_expr() || extra_derefs > 0 {
if arg.span.from_expansion() {
- ArgKind::RefMutToPlaceAsMacro(arg)
+ ArgKind::RefMutToPlaceAsMacro(arg, extra_derefs)
} else {
- ArgKind::RefMutToPlace(target)
+ ArgKind::RefMutToPlace(target, extra_derefs)
}
} else {
ArgKind::RefMutToTemp(target)
@@ -106,10 +123,15 @@
let mut applicability = Applicability::MachineApplicable;
let ctxt = expr.span.ctxt();
let assign_target = match target {
- ArgKind::Expr(target) | ArgKind::RefMutToPlaceAsMacro(target) => {
- Sugg::hir_with_context(cx, target, ctxt, "_", &mut applicability).deref()
- },
- ArgKind::RefMutToPlace(target) => Sugg::hir_with_context(cx, target, ctxt, "_", &mut applicability),
+ ArgKind::Expr(target) => Sugg::hir_with_context(cx, target, ctxt, "_", &mut applicability).deref(),
+ ArgKind::RefMutToPlaceAsMacro(arg, derefs) => (0..*derefs).fold(
+ Sugg::hir_with_context(cx, arg, ctxt, "_", &mut applicability).deref(),
+ |sugg, _| sugg.deref(),
+ ),
+ ArgKind::RefMutToPlace(target, derefs) => (0..*derefs).fold(
+ Sugg::hir_with_context(cx, target, ctxt, "_", &mut applicability),
+ |sugg, _| sugg.deref(),
+ ),
ArgKind::RefMutToTemp(_) => unreachable!(),
};
let assign_source = Sugg::hir_with_context(cx, temp, ctxt, "_", &mut applicability);
diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs
index fdccf1f..769526d 100644
--- a/clippy_lints/src/methods/unnecessary_to_owned.rs
+++ b/clippy_lints/src/methods/unnecessary_to_owned.rs
@@ -388,9 +388,11 @@
&& let (input, n_refs) = peel_middle_ty_refs(*input)
&& let (trait_predicates, _) = get_input_traits_and_projections(cx, callee_def_id, input)
&& let Some(sized_def_id) = cx.tcx.lang_items().sized_trait()
+ && let Some(meta_sized_def_id) = cx.tcx.lang_items().meta_sized_trait()
&& let [trait_predicate] = trait_predicates
.iter()
.filter(|trait_predicate| trait_predicate.def_id() != sized_def_id)
+ .filter(|trait_predicate| trait_predicate.def_id() != meta_sized_def_id)
.collect::<Vec<_>>()[..]
&& let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref)
&& let Some(as_ref_trait_id) = cx.tcx.get_diagnostic_item(sym::AsRef)
diff --git a/clippy_lints/src/missing_inline.rs b/clippy_lints/src/missing_inline.rs
index 1f61317..25c95d2 100644
--- a/clippy_lints/src/missing_inline.rs
+++ b/clippy_lints/src/missing_inline.rs
@@ -1,10 +1,11 @@
use clippy_utils::diagnostics::span_lint;
+use rustc_attr_data_structures::{AttributeKind, find_attr};
use rustc_hir as hir;
use rustc_hir::Attribute;
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::ty::AssocItemContainer;
use rustc_session::declare_lint_pass;
-use rustc_span::{Span, sym};
+use rustc_span::Span;
declare_clippy_lint! {
/// ### What it does
@@ -64,8 +65,7 @@
}
fn check_missing_inline_attrs(cx: &LateContext<'_>, attrs: &[Attribute], sp: Span, desc: &'static str) {
- let has_inline = attrs.iter().any(|a| a.has_name(sym::inline));
- if !has_inline {
+ if !find_attr!(attrs, AttributeKind::Inline(..)) {
span_lint(
cx,
MISSING_INLINE_IN_PUBLIC_ITEMS,
diff --git a/clippy_lints/src/needless_borrows_for_generic_args.rs b/clippy_lints/src/needless_borrows_for_generic_args.rs
index 2efb55b..17d251a 100644
--- a/clippy_lints/src/needless_borrows_for_generic_args.rs
+++ b/clippy_lints/src/needless_borrows_for_generic_args.rs
@@ -161,7 +161,7 @@
/// - `Copy` itself, or
/// - the only use of a mutable reference, or
/// - not a variable (created by a function call)
-#[expect(clippy::too_many_arguments)]
+#[expect(clippy::too_many_arguments, clippy::too_many_lines)]
fn needless_borrow_count<'tcx>(
cx: &LateContext<'tcx>,
possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>,
@@ -174,6 +174,7 @@
) -> usize {
let destruct_trait_def_id = cx.tcx.lang_items().destruct_trait();
let sized_trait_def_id = cx.tcx.lang_items().sized_trait();
+ let meta_sized_trait_def_id = cx.tcx.lang_items().meta_sized_trait();
let drop_trait_def_id = cx.tcx.lang_items().drop_trait();
let fn_sig = cx.tcx.fn_sig(fn_id).instantiate_identity().skip_binder();
@@ -209,6 +210,7 @@
.all(|trait_def_id| {
Some(trait_def_id) == destruct_trait_def_id
|| Some(trait_def_id) == sized_trait_def_id
+ || Some(trait_def_id) == meta_sized_trait_def_id
|| cx.tcx.is_diagnostic_item(sym::Any, trait_def_id)
})
{
@@ -230,11 +232,11 @@
let mut args_with_referent_ty = callee_args.to_vec();
let mut check_reference_and_referent = |reference: &Expr<'tcx>, referent: &Expr<'tcx>| {
- if let ExprKind::Field(base, _) = &referent.kind {
- let base_ty = cx.typeck_results().expr_ty(base);
- if drop_trait_def_id.is_some_and(|id| implements_trait(cx, base_ty, id, &[])) {
- return false;
- }
+ if let ExprKind::Field(base, _) = &referent.kind
+ && let base_ty = cx.typeck_results().expr_ty(base)
+ && drop_trait_def_id.is_some_and(|id| implements_trait(cx, base_ty, id, &[]))
+ {
+ return false;
}
let referent_ty = cx.typeck_results().expr_ty(referent);
diff --git a/clippy_lints/src/needless_parens_on_range_literals.rs b/clippy_lints/src/needless_parens_on_range_literals.rs
index 8a62106..021a115 100644
--- a/clippy_lints/src/needless_parens_on_range_literals.rs
+++ b/clippy_lints/src/needless_parens_on_range_literals.rs
@@ -5,7 +5,6 @@
use rustc_ast::ast;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind};
-
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass;
diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs
index 9562346..c97ecce 100644
--- a/clippy_lints/src/needless_pass_by_value.rs
+++ b/clippy_lints/src/needless_pass_by_value.rs
@@ -116,13 +116,18 @@
];
let sized_trait = need!(cx.tcx.lang_items().sized_trait());
+ let meta_sized_trait = need!(cx.tcx.lang_items().meta_sized_trait());
let preds = traits::elaborate(cx.tcx, cx.param_env.caller_bounds().iter())
.filter(|p| !p.is_global())
.filter_map(|pred| {
// Note that we do not want to deal with qualified predicates here.
match pred.kind().no_bound_vars() {
- Some(ty::ClauseKind::Trait(pred)) if pred.def_id() != sized_trait => Some(pred),
+ Some(ty::ClauseKind::Trait(pred))
+ if pred.def_id() != sized_trait && pred.def_id() != meta_sized_trait =>
+ {
+ Some(pred)
+ },
_ => None,
}
})
diff --git a/clippy_lints/src/neg_multiply.rs b/clippy_lints/src/neg_multiply.rs
index 74c8142..442280f 100644
--- a/clippy_lints/src/neg_multiply.rs
+++ b/clippy_lints/src/neg_multiply.rs
@@ -64,7 +64,7 @@
{
let mut applicability = Applicability::MachineApplicable;
let (snip, from_macro) = snippet_with_context(cx, exp.span, span.ctxt(), "..", &mut applicability);
- let suggestion = if !from_macro && exp.precedence() < ExprPrecedence::Prefix && !has_enclosing_paren(&snip) {
+ let suggestion = if !from_macro && cx.precedence(exp) < ExprPrecedence::Prefix && !has_enclosing_paren(&snip) {
format!("-({snip})")
} else {
format!("-{snip}")
diff --git a/clippy_lints/src/no_mangle_with_rust_abi.rs b/clippy_lints/src/no_mangle_with_rust_abi.rs
index b71dde9..dee8efe 100644
--- a/clippy_lints/src/no_mangle_with_rust_abi.rs
+++ b/clippy_lints/src/no_mangle_with_rust_abi.rs
@@ -1,8 +1,9 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::{snippet, snippet_with_applicability};
use rustc_abi::ExternAbi;
+use rustc_attr_data_structures::AttributeKind;
use rustc_errors::Applicability;
-use rustc_hir::{Item, ItemKind};
+use rustc_hir::{Attribute, Item, ItemKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass;
use rustc_span::{BytePos, Pos};
@@ -44,8 +45,7 @@
let mut app = Applicability::MaybeIncorrect;
let fn_snippet = snippet_with_applicability(cx, fn_sig.span.with_hi(ident.span.lo()), "..", &mut app);
for attr in attrs {
- if let Some(ident) = attr.ident()
- && ident.name == rustc_span::sym::no_mangle
+ if let Attribute::Parsed(AttributeKind::NoMangle(attr_span)) = attr
&& fn_sig.header.abi == ExternAbi::Rust
&& let Some((fn_attrs, _)) = fn_snippet.rsplit_once("fn")
&& !fn_attrs.contains("extern")
@@ -54,7 +54,7 @@
.span
.with_lo(fn_sig.span.lo() + BytePos::from_usize(fn_attrs.len()))
.shrink_to_lo();
- let attr_snippet = snippet(cx, attr.span(), "..");
+ let attr_snippet = snippet(cx, *attr_span, "..");
span_lint_and_then(
cx,
diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs
index a27c6aa..5f10e19 100644
--- a/clippy_lints/src/non_copy_const.rs
+++ b/clippy_lints/src/non_copy_const.rs
@@ -617,7 +617,7 @@
// Then a type check. Note we only check the type here as the result
// gets cached.
- let ty = EarlyBinder::bind(typeck.expr_ty(src_expr)).instantiate(tcx, init_args);
+ let ty = typeck.expr_ty(src_expr);
// Normalized as we need to check if this is an array later.
let ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty);
if self.is_ty_freeze(tcx, typing_env, ty).is_freeze() {
diff --git a/clippy_lints/src/operators/identity_op.rs b/clippy_lints/src/operators/identity_op.rs
index e1fd095..3efbb89 100644
--- a/clippy_lints/src/operators/identity_op.rs
+++ b/clippy_lints/src/operators/identity_op.rs
@@ -1,12 +1,13 @@
-use clippy_utils::consts::{ConstEvalCtxt, Constant, FullInt};
+use clippy_utils::consts::{ConstEvalCtxt, Constant, FullInt, integer_const, is_zero_integer_const};
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::{clip, peel_hir_expr_refs, unsext};
+use clippy_utils::{ExprUseNode, clip, expr_use_ctxt, peel_hir_expr_refs, unsext};
use rustc_errors::Applicability;
-use rustc_hir::{BinOpKind, Expr, ExprKind, Node};
+use rustc_hir::def::{DefKind, Res};
+use rustc_hir::{BinOpKind, Expr, ExprKind, Node, Path, QPath};
use rustc_lint::LateContext;
use rustc_middle::ty;
-use rustc_span::Span;
+use rustc_span::{Span, kw};
use super::IDENTITY_OP;
@@ -17,7 +18,7 @@
left: &'tcx Expr<'_>,
right: &'tcx Expr<'_>,
) {
- if !is_allowed(cx, op, left, right) {
+ if !is_allowed(cx, expr, op, left, right) {
return;
}
@@ -165,14 +166,27 @@
Parens::Needed
}
-fn is_allowed(cx: &LateContext<'_>, cmp: BinOpKind, left: &Expr<'_>, right: &Expr<'_>) -> bool {
+fn is_allowed<'tcx>(
+ cx: &LateContext<'tcx>,
+ expr: &'tcx Expr<'tcx>,
+ cmp: BinOpKind,
+ left: &Expr<'tcx>,
+ right: &Expr<'tcx>,
+) -> bool {
+ // Exclude case where the left or right side is associated function call returns a type which is
+ // `Self` that is not given explicitly, and the expression is not a let binding's init
+ // expression and the let binding has a type annotation, or a function's return value.
+ if (is_assoc_fn_without_type_instance(cx, left) || is_assoc_fn_without_type_instance(cx, right))
+ && !is_expr_used_with_type_annotation(cx, expr)
+ {
+ return false;
+ }
+
// This lint applies to integers and their references
cx.typeck_results().expr_ty(left).peel_refs().is_integral()
&& cx.typeck_results().expr_ty(right).peel_refs().is_integral()
// `1 << 0` is a common pattern in bit manipulation code
- && !(cmp == BinOpKind::Shl
- && ConstEvalCtxt::new(cx).eval_simple(right) == Some(Constant::Int(0))
- && ConstEvalCtxt::new(cx).eval_simple(left) == Some(Constant::Int(1)))
+ && !(cmp == BinOpKind::Shl && is_zero_integer_const(cx, right) && integer_const(cx, left) == Some(1))
}
fn check_remainder(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>, span: Span, arg: Span) {
@@ -234,3 +248,47 @@
applicability,
);
}
+
+fn is_expr_used_with_type_annotation<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
+ match expr_use_ctxt(cx, expr).use_node(cx) {
+ ExprUseNode::LetStmt(letstmt) => letstmt.ty.is_some(),
+ ExprUseNode::Return(_) => true,
+ _ => false,
+ }
+}
+
+/// Check if the expression is an associated function without a type instance.
+/// Example:
+/// ```
+/// trait Def {
+/// fn def() -> Self;
+/// }
+/// impl Def for usize {
+/// fn def() -> Self {
+/// 0
+/// }
+/// }
+/// fn test() {
+/// let _ = 0usize + &Default::default();
+/// let _ = 0usize + &Def::def();
+/// }
+/// ```
+fn is_assoc_fn_without_type_instance<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> bool {
+ if let ExprKind::Call(func, _) = peel_hir_expr_refs(expr).0.kind
+ && let ExprKind::Path(QPath::Resolved(
+ // If it's not None, don't need to go further.
+ None,
+ Path {
+ res: Res::Def(DefKind::AssocFn, def_id),
+ ..
+ },
+ )) = func.kind
+ && let output_ty = cx.tcx.fn_sig(def_id).instantiate_identity().skip_binder().output()
+ && let ty::Param(ty::ParamTy {
+ name: kw::SelfUpper, ..
+ }) = output_ty.kind()
+ {
+ return true;
+ }
+ false
+}
diff --git a/clippy_lints/src/operators/manual_is_multiple_of.rs b/clippy_lints/src/operators/manual_is_multiple_of.rs
new file mode 100644
index 0000000..821178a
--- /dev/null
+++ b/clippy_lints/src/operators/manual_is_multiple_of.rs
@@ -0,0 +1,66 @@
+use clippy_utils::consts::is_zero_integer_const;
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::msrvs::{self, Msrv};
+use clippy_utils::sugg::Sugg;
+use rustc_ast::BinOpKind;
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::LateContext;
+use rustc_middle::ty;
+
+use super::MANUAL_IS_MULTIPLE_OF;
+
+pub(super) fn check<'tcx>(
+ cx: &LateContext<'tcx>,
+ expr: &Expr<'_>,
+ op: BinOpKind,
+ lhs: &'tcx Expr<'tcx>,
+ rhs: &'tcx Expr<'tcx>,
+ msrv: Msrv,
+) {
+ if msrv.meets(cx, msrvs::UNSIGNED_IS_MULTIPLE_OF)
+ && let Some(operand) = uint_compare_to_zero(cx, op, lhs, rhs)
+ && let ExprKind::Binary(operand_op, operand_left, operand_right) = operand.kind
+ && operand_op.node == BinOpKind::Rem
+ {
+ let mut app = Applicability::MachineApplicable;
+ let divisor = Sugg::hir_with_applicability(cx, operand_right, "_", &mut app);
+ span_lint_and_sugg(
+ cx,
+ MANUAL_IS_MULTIPLE_OF,
+ expr.span,
+ "manual implementation of `.is_multiple_of()`",
+ "replace with",
+ format!(
+ "{}{}.is_multiple_of({divisor})",
+ if op == BinOpKind::Eq { "" } else { "!" },
+ Sugg::hir_with_applicability(cx, operand_left, "_", &mut app).maybe_paren()
+ ),
+ app,
+ );
+ }
+}
+
+// If we have a `x == 0`, `x != 0` or `x > 0` (or the reverted ones), return the non-zero operand
+fn uint_compare_to_zero<'tcx>(
+ cx: &LateContext<'tcx>,
+ op: BinOpKind,
+ lhs: &'tcx Expr<'tcx>,
+ rhs: &'tcx Expr<'tcx>,
+) -> Option<&'tcx Expr<'tcx>> {
+ let operand = if matches!(lhs.kind, ExprKind::Binary(..))
+ && matches!(op, BinOpKind::Eq | BinOpKind::Ne | BinOpKind::Gt)
+ && is_zero_integer_const(cx, rhs)
+ {
+ lhs
+ } else if matches!(rhs.kind, ExprKind::Binary(..))
+ && matches!(op, BinOpKind::Eq | BinOpKind::Ne | BinOpKind::Lt)
+ && is_zero_integer_const(cx, lhs)
+ {
+ rhs
+ } else {
+ return None;
+ };
+
+ matches!(cx.typeck_results().expr_ty_adjusted(operand).kind(), ty::Uint(_)).then_some(operand)
+}
diff --git a/clippy_lints/src/operators/mod.rs b/clippy_lints/src/operators/mod.rs
index 2f4e8e9..bdbbb34 100644
--- a/clippy_lints/src/operators/mod.rs
+++ b/clippy_lints/src/operators/mod.rs
@@ -11,6 +11,7 @@
mod float_equality_without_abs;
mod identity_op;
mod integer_division;
+mod manual_is_multiple_of;
mod manual_midpoint;
mod misrefactored_assign_op;
mod modulo_arithmetic;
@@ -830,12 +831,42 @@
"manual implementation of `midpoint` which can overflow"
}
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for manual implementation of `.is_multiple_of()` on
+ /// unsigned integer types.
+ ///
+ /// ### Why is this bad?
+ /// `a.is_multiple_of(b)` is a clearer way to check for divisibility
+ /// of `a` by `b`. This expression can never panic.
+ ///
+ /// ### Example
+ /// ```no_run
+ /// # let (a, b) = (3u64, 4u64);
+ /// if a % b == 0 {
+ /// println!("{a} is divisible by {b}");
+ /// }
+ /// ```
+ /// Use instead:
+ /// ```no_run
+ /// # let (a, b) = (3u64, 4u64);
+ /// if a.is_multiple_of(b) {
+ /// println!("{a} is divisible by {b}");
+ /// }
+ /// ```
+ #[clippy::version = "1.89.0"]
+ pub MANUAL_IS_MULTIPLE_OF,
+ complexity,
+ "manual implementation of `.is_multiple_of()`"
+}
+
pub struct Operators {
arithmetic_context: numeric_arithmetic::Context,
verbose_bit_mask_threshold: u64,
modulo_arithmetic_allow_comparison_to_zero: bool,
msrv: Msrv,
}
+
impl Operators {
pub fn new(conf: &'static Conf) -> Self {
Self {
@@ -874,6 +905,7 @@
NEEDLESS_BITWISE_BOOL,
SELF_ASSIGNMENT,
MANUAL_MIDPOINT,
+ MANUAL_IS_MULTIPLE_OF,
]);
impl<'tcx> LateLintPass<'tcx> for Operators {
@@ -891,6 +923,7 @@
identity_op::check(cx, e, op.node, lhs, rhs);
needless_bitwise_bool::check(cx, e, op.node, lhs, rhs);
manual_midpoint::check(cx, e, op.node, lhs, rhs, self.msrv);
+ manual_is_multiple_of::check(cx, e, op.node, lhs, rhs, self.msrv);
}
self.arithmetic_context.check_binary(cx, e, op.node, lhs, rhs);
bit_mask::check(cx, e, op.node, lhs, rhs);
diff --git a/clippy_lints/src/pass_by_ref_or_value.rs b/clippy_lints/src/pass_by_ref_or_value.rs
index dadf49b..b8005df 100644
--- a/clippy_lints/src/pass_by_ref_or_value.rs
+++ b/clippy_lints/src/pass_by_ref_or_value.rs
@@ -5,7 +5,7 @@
use clippy_utils::{is_self, is_self_ty};
use core::ops::ControlFlow;
use rustc_abi::ExternAbi;
-use rustc_ast::attr;
+use rustc_attr_data_structures::{AttributeKind, InlineAttr, find_attr};
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Applicability;
use rustc_hir as hir;
@@ -270,11 +270,13 @@
return;
}
let attrs = cx.tcx.hir_attrs(hir_id);
+ if find_attr!(attrs, AttributeKind::Inline(InlineAttr::Always, _)) {
+ return;
+ }
+
for a in attrs {
- if let Some(meta_items) = a.meta_item_list()
- && (a.has_name(sym::proc_macro_derive)
- || (a.has_name(sym::inline) && attr::list_contains_name(&meta_items, sym::always)))
- {
+ // FIXME(jdonszelmann): make part of the find_attr above
+ if a.has_name(sym::proc_macro_derive) {
return;
}
}
diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs
index c02e5e0..de12a25 100644
--- a/clippy_lints/src/question_mark.rs
+++ b/clippy_lints/src/question_mark.rs
@@ -142,6 +142,7 @@
&& let Some(ret) = find_let_else_ret_expression(els)
&& let Some(inner_pat) = pat_and_expr_can_be_question_mark(cx, pat, ret)
&& !span_contains_comment(cx.tcx.sess.source_map(), els.span)
+ && !span_contains_cfg(cx, els.span)
{
let mut applicability = Applicability::MaybeIncorrect;
let init_expr_str = Sugg::hir_with_applicability(cx, init_expr, "..", &mut applicability).maybe_paren();
diff --git a/clippy_lints/src/question_mark_used.rs b/clippy_lints/src/question_mark_used.rs
index 96ea485..7bbbd0d 100644
--- a/clippy_lints/src/question_mark_used.rs
+++ b/clippy_lints/src/question_mark_used.rs
@@ -1,5 +1,4 @@
use clippy_utils::diagnostics::span_lint_and_then;
-
use clippy_utils::macros::span_is_local;
use rustc_hir::{Expr, ExprKind, MatchSource};
use rustc_lint::{LateContext, LateLintPass};
diff --git a/clippy_lints/src/read_zero_byte_vec.rs b/clippy_lints/src/read_zero_byte_vec.rs
index 6b1dc86..acd8404 100644
--- a/clippy_lints/src/read_zero_byte_vec.rs
+++ b/clippy_lints/src/read_zero_byte_vec.rs
@@ -3,11 +3,10 @@
use clippy_utils::source::snippet;
use clippy_utils::{get_enclosing_block, sym};
-use hir::{Expr, ExprKind, HirId, LetStmt, PatKind, PathSegment, QPath, StmtKind};
use rustc_errors::Applicability;
-use rustc_hir as hir;
use rustc_hir::def::Res;
use rustc_hir::intravisit::{Visitor, walk_expr};
+use rustc_hir::{self as hir, Expr, ExprKind, HirId, LetStmt, PatKind, PathSegment, QPath, StmtKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass;
diff --git a/clippy_lints/src/redundant_slicing.rs b/clippy_lints/src/redundant_slicing.rs
index 1117dea..324a05c 100644
--- a/clippy_lints/src/redundant_slicing.rs
+++ b/clippy_lints/src/redundant_slicing.rs
@@ -86,7 +86,7 @@
let (indexed_ty, indexed_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(indexed));
let parent_expr = get_parent_expr(cx, expr);
let needs_parens_for_prefix =
- parent_expr.is_some_and(|parent| parent.precedence() > ExprPrecedence::Prefix);
+ parent_expr.is_some_and(|parent| cx.precedence(parent) > ExprPrecedence::Prefix);
if expr_ty == indexed_ty {
if expr_ref_count > indexed_ref_count {
diff --git a/clippy_lints/src/return_self_not_must_use.rs b/clippy_lints/src/return_self_not_must_use.rs
index 07ae92f..25929b8 100644
--- a/clippy_lints/src/return_self_not_must_use.rs
+++ b/clippy_lints/src/return_self_not_must_use.rs
@@ -1,12 +1,13 @@
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::ty::is_must_use_ty;
use clippy_utils::{nth_arg, return_ty};
+use rustc_attr_data_structures::{AttributeKind, find_attr};
use rustc_hir::def_id::LocalDefId;
use rustc_hir::intravisit::FnKind;
use rustc_hir::{Body, FnDecl, OwnerId, TraitItem, TraitItemKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_session::declare_lint_pass;
-use rustc_span::{Span, sym};
+use rustc_span::Span;
declare_clippy_lint! {
/// ### What it does
@@ -74,7 +75,10 @@
// We only show this warning for public exported methods.
&& cx.effective_visibilities.is_exported(fn_def)
// We don't want to emit this lint if the `#[must_use]` attribute is already there.
- && !cx.tcx.hir_attrs(owner_id.into()).iter().any(|attr| attr.has_name(sym::must_use))
+ && !find_attr!(
+ cx.tcx.hir_attrs(owner_id.into()),
+ AttributeKind::MustUse { .. }
+ )
&& cx.tcx.visibility(fn_def.to_def_id()).is_public()
&& let ret_ty = return_ty(cx, owner_id)
&& let self_arg = nth_arg(cx, owner_id, 0)
diff --git a/clippy_lints/src/single_range_in_vec_init.rs b/clippy_lints/src/single_range_in_vec_init.rs
index 54d09ff..dda2f8c 100644
--- a/clippy_lints/src/single_range_in_vec_init.rs
+++ b/clippy_lints/src/single_range_in_vec_init.rs
@@ -3,7 +3,7 @@
use clippy_utils::macros::root_macro_call_first_node;
use clippy_utils::source::SpanRangeExt;
use clippy_utils::ty::implements_trait;
-use clippy_utils::{is_no_std_crate, paths};
+use clippy_utils::{is_no_std_crate, sym};
use rustc_ast::{LitIntType, LitKind, UintTy};
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, LangItem, QPath, StructTailExpr};
@@ -100,7 +100,7 @@
&& let Some(start_snippet) = start.span.get_source_text(cx)
&& let Some(end_snippet) = end.span.get_source_text(cx)
{
- let should_emit_every_value = if let Some(step_def_id) = paths::ITER_STEP.only(cx)
+ let should_emit_every_value = if let Some(step_def_id) = cx.tcx.get_diagnostic_item(sym::range_step)
&& implements_trait(cx, ty, step_def_id, &[])
{
true
diff --git a/clippy_lints/src/to_digit_is_some.rs b/clippy_lints/src/to_digit_is_some.rs
index 7d7d74f..3e84754 100644
--- a/clippy_lints/src/to_digit_is_some.rs
+++ b/clippy_lints/src/to_digit_is_some.rs
@@ -2,7 +2,7 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::{is_in_const_context, paths, sym};
+use clippy_utils::{is_in_const_context, is_path_diagnostic_item, sym};
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass};
@@ -62,7 +62,7 @@
}
},
hir::ExprKind::Call(to_digits_call, [char_arg, radix_arg]) => {
- if paths::CHAR_TO_DIGIT.matches_path(cx, to_digits_call) {
+ if is_path_diagnostic_item(cx, to_digits_call, sym::char_to_digit) {
Some((false, char_arg, radix_arg))
} else {
None
diff --git a/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs b/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs
index 0d5cf45..18897fb 100644
--- a/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs
+++ b/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs
@@ -44,7 +44,7 @@
};
if let Node::Expr(parent) = cx.tcx.parent_hir_node(e.hir_id)
- && parent.precedence() > ExprPrecedence::Cast
+ && cx.precedence(parent) > ExprPrecedence::Cast
{
sugg = format!("({sugg})");
}
diff --git a/clippy_lints/src/unnested_or_patterns.rs b/clippy_lints/src/unnested_or_patterns.rs
index b839b6f..bd84209 100644
--- a/clippy_lints/src/unnested_or_patterns.rs
+++ b/clippy_lints/src/unnested_or_patterns.rs
@@ -128,7 +128,7 @@
}
impl MutVisitor for Visitor {
- fn visit_pat(&mut self, pat: &mut P<Pat>) {
+ fn visit_pat(&mut self, pat: &mut Pat) {
let is_inner = mem::replace(&mut self.is_inner, true);
walk_pat(self, pat);
let inner = match &mut pat.kind {
@@ -145,7 +145,7 @@
fn insert_necessary_parens(pat: &mut P<Pat>) {
struct Visitor;
impl MutVisitor for Visitor {
- fn visit_pat(&mut self, pat: &mut P<Pat>) {
+ fn visit_pat(&mut self, pat: &mut Pat) {
use ast::BindingMode;
walk_pat(self, pat);
let target = match &mut pat.kind {
@@ -167,7 +167,7 @@
changed: bool,
}
impl MutVisitor for Visitor {
- fn visit_pat(&mut self, p: &mut P<Pat>) {
+ fn visit_pat(&mut self, p: &mut Pat) {
// This is a bottom up transformation, so recurse first.
walk_pat(self, p);
diff --git a/clippy_lints/src/useless_concat.rs b/clippy_lints/src/useless_concat.rs
index 1ed1fbb..96845ad 100644
--- a/clippy_lints/src/useless_concat.rs
+++ b/clippy_lints/src/useless_concat.rs
@@ -1,8 +1,7 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::macros::macro_backtrace;
-use clippy_utils::paths::CONCAT;
use clippy_utils::source::snippet_opt;
-use clippy_utils::tokenize_with_text;
+use clippy_utils::{sym, tokenize_with_text};
use rustc_ast::LitKind;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind};
@@ -43,7 +42,7 @@
// Get the direct parent of the expression.
&& let Some(macro_call) = macro_backtrace(expr.span).next()
// Check if the `concat` macro from the `core` library.
- && CONCAT.matches(cx, macro_call.def_id)
+ && cx.tcx.is_diagnostic_item(sym::macro_concat, macro_call.def_id)
// We get the original code to parse it.
&& let Some(original_code) = snippet_opt(cx, macro_call.span)
// This check allows us to ensure that the code snippet:
diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml
index 615c099..73291aa 100644
--- a/clippy_utils/Cargo.toml
+++ b/clippy_utils/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "clippy_utils"
-version = "0.1.89"
+version = "0.1.90"
edition = "2024"
description = "Helpful tools for writing lints, provided as they are used in Clippy"
repository = "https://github.com/rust-lang/rust-clippy"
diff --git a/clippy_utils/README.md b/clippy_utils/README.md
index 1aa16e3..649748d 100644
--- a/clippy_utils/README.md
+++ b/clippy_utils/README.md
@@ -8,7 +8,7 @@
<!-- begin autogenerated nightly -->
```
-nightly-2025-06-12
+nightly-2025-06-26
```
<!-- end autogenerated nightly -->
diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs
index 1ec5d11..aaa071f 100644
--- a/clippy_utils/src/consts.rs
+++ b/clippy_utils/src/consts.rs
@@ -958,3 +958,18 @@
None
}
}
+
+/// If `expr` evaluates to an integer constant, return its value.
+pub fn integer_const(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<u128> {
+ if let Some(Constant::Int(value)) = ConstEvalCtxt::new(cx).eval_simple(expr) {
+ Some(value)
+ } else {
+ None
+ }
+}
+
+/// Check if `expr` evaluates to an integer constant of 0.
+#[inline]
+pub fn is_zero_integer_const(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+ integer_const(cx, expr) == Some(0)
+}
diff --git a/clippy_utils/src/diagnostics.rs b/clippy_utils/src/diagnostics.rs
index cd2098a..dc240dd 100644
--- a/clippy_utils/src/diagnostics.rs
+++ b/clippy_utils/src/diagnostics.rs
@@ -109,7 +109,7 @@
});
}
-/// Same as `span_lint` but with an extra `help` message.
+/// Same as [`span_lint`] but with an extra `help` message.
///
/// Use this if you want to provide some general help but
/// can't provide a specific machine applicable suggestion.
@@ -166,7 +166,7 @@
});
}
-/// Like `span_lint` but with a `note` section instead of a `help` message.
+/// Like [`span_lint`] but with a `note` section instead of a `help` message.
///
/// The `note` message is presented separately from the main lint message
/// and is attached to a specific span:
@@ -226,7 +226,7 @@
});
}
-/// Like `span_lint` but allows to add notes, help and suggestions using a closure.
+/// Like [`span_lint`] but allows to add notes, help and suggestions using a closure.
///
/// If you need to customize your lint output a lot, use this function.
/// If you change the signature, remember to update the internal lint `CollapsibleCalls`
diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs
index 7fa5222..a8b3341 100644
--- a/clippy_utils/src/lib.rs
+++ b/clippy_utils/src/lib.rs
@@ -1886,7 +1886,7 @@
_ => None,
};
- did.is_some_and(|did| cx.tcx.has_attr(did, sym::must_use))
+ did.is_some_and(|did| find_attr!(cx.tcx.get_all_attrs(did), AttributeKind::MustUse { .. }))
}
/// Checks if a function's body represents the identity function. Looks for bodies of the form:
diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs
index a5e66ad..24ed4c3 100644
--- a/clippy_utils/src/msrvs.rs
+++ b/clippy_utils/src/msrvs.rs
@@ -24,7 +24,7 @@
// names may refer to stabilized feature flags or library items
msrv_aliases! {
1,88,0 { LET_CHAINS }
- 1,87,0 { OS_STR_DISPLAY, INT_MIDPOINT, CONST_CHAR_IS_DIGIT }
+ 1,87,0 { OS_STR_DISPLAY, INT_MIDPOINT, CONST_CHAR_IS_DIGIT, UNSIGNED_IS_MULTIPLE_OF }
1,85,0 { UINT_FLOAT_MIDPOINT, CONST_SIZE_OF_VAL }
1,84,0 { CONST_OPTION_AS_SLICE, MANUAL_DANGLING_PTR }
1,83,0 { CONST_EXTERN_FN, CONST_FLOAT_BITS_CONV, CONST_FLOAT_CLASSIFY, CONST_MUT_REFS, CONST_UNWRAP }
@@ -42,6 +42,7 @@
1,65,0 { LET_ELSE, POINTER_CAST_CONSTNESS }
1,63,0 { CLONE_INTO, CONST_SLICE_FROM_REF }
1,62,0 { BOOL_THEN_SOME, DEFAULT_ENUM_ATTRIBUTE, CONST_EXTERN_C_FN }
+ 1,61,0 { CONST_FN_TRAIT_BOUND }
1,60,0 { ABS_DIFF }
1,59,0 { THREAD_LOCAL_CONST_INIT }
1,58,0 { FORMAT_ARGS_CAPTURE, PATTERN_TRAIT_CHAR_ARRAY, CONST_RAW_PTR_DEREF }
@@ -73,7 +74,7 @@
1,28,0 { FROM_BOOL, REPEAT_WITH, SLICE_FROM_REF }
1,27,0 { ITERATOR_TRY_FOLD }
1,26,0 { RANGE_INCLUSIVE, STRING_RETAIN }
- 1,24,0 { IS_ASCII_DIGIT }
+ 1,24,0 { IS_ASCII_DIGIT, PTR_NULL }
1,18,0 { HASH_MAP_RETAIN, HASH_SET_RETAIN }
1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST, EXPECT_ERR }
1,16,0 { STR_REPEAT }
diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs
index f37a609..8bbcb22 100644
--- a/clippy_utils/src/paths.rs
+++ b/clippy_utils/src/paths.rs
@@ -126,15 +126,6 @@
macro_path: PathNS::Macro,
}
-// Paths in `core`/`alloc`/`std`. This should be avoided and cleaned up by adding diagnostic items.
-pub static ALIGN_OF: PathLookup = value_path!(core::mem::align_of);
-pub static CHAR_TO_DIGIT: PathLookup = value_path!(char::to_digit);
-pub static CONCAT: PathLookup = macro_path!(core::concat);
-pub static IO_ERROR_NEW: PathLookup = value_path!(std::io::Error::new);
-pub static IO_ERRORKIND_OTHER_CTOR: PathLookup = value_path!(std::io::ErrorKind::Other);
-pub static ITER_STEP: PathLookup = type_path!(core::iter::Step);
-pub static SLICE_FROM_REF: PathLookup = value_path!(core::slice::from_ref);
-
// Paths in external crates
pub static FUTURES_IO_ASYNCREADEXT: PathLookup = type_path!(futures_util::AsyncReadExt);
pub static FUTURES_IO_ASYNCWRITEEXT: PathLookup = type_path!(futures_util::AsyncWriteExt);
diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs
index e629012..be93f27 100644
--- a/clippy_utils/src/qualify_min_const_fn.rs
+++ b/clippy_utils/src/qualify_min_const_fn.rs
@@ -32,6 +32,21 @@
for local in &body.local_decls {
check_ty(cx, local.ty, local.source_info.span, msrv)?;
}
+ if !msrv.meets(cx, msrvs::CONST_FN_TRAIT_BOUND)
+ && let Some(sized_did) = cx.tcx.lang_items().sized_trait()
+ && let Some(meta_sized_did) = cx.tcx.lang_items().meta_sized_trait()
+ && cx.tcx.param_env(def_id).caller_bounds().iter().any(|bound| {
+ bound.as_trait_clause().is_some_and(|clause| {
+ let did = clause.def_id();
+ did != sized_did && did != meta_sized_did
+ })
+ })
+ {
+ return Err((
+ body.span,
+ "non-`Sized` trait clause before `const_fn_trait_bound` is stabilized".into(),
+ ));
+ }
// impl trait is gone in MIR, so check the return type manually
check_ty(
cx,
diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs
index 6974e65..7a24d07 100644
--- a/clippy_utils/src/sugg.rs
+++ b/clippy_utils/src/sugg.rs
@@ -494,7 +494,17 @@
/// operators have the same
/// precedence.
pub fn make_unop(op: &str, expr: Sugg<'_>) -> Sugg<'static> {
- Sugg::MaybeParen(format!("{op}{}", expr.maybe_paren()).into())
+ // If the `expr` starts with `op` already, do not add wrap it in
+ // parentheses.
+ let expr = if let Sugg::MaybeParen(ref sugg) = expr
+ && !has_enclosing_paren(sugg)
+ && sugg.starts_with(op)
+ {
+ expr
+ } else {
+ expr.maybe_paren()
+ };
+ Sugg::MaybeParen(format!("{op}{expr}").into())
}
/// Builds the string for `<lhs> <op> <rhs>` adding parenthesis when necessary.
@@ -1016,6 +1026,16 @@
let sugg = Sugg::BinOp(AssocOp::Binary(ast::BinOpKind::Add), "(1 + 1)".into(), "(1 + 1)".into());
assert_eq!("((1 + 1) + (1 + 1))", sugg.maybe_paren().to_string());
}
+
+ #[test]
+ fn unop_parenthesize() {
+ let sugg = Sugg::NonParen("x".into()).mut_addr();
+ assert_eq!("&mut x", sugg.to_string());
+ let sugg = sugg.mut_addr();
+ assert_eq!("&mut &mut x", sugg.to_string());
+ assert_eq!("(&mut &mut x)", sugg.maybe_paren().to_string());
+ }
+
#[test]
fn not_op() {
use ast::BinOpKind::{Add, And, Eq, Ge, Gt, Le, Lt, Ne, Or};
diff --git a/clippy_utils/src/sym.rs b/clippy_utils/src/sym.rs
index a544954..8a8218c 100644
--- a/clippy_utils/src/sym.rs
+++ b/clippy_utils/src/sym.rs
@@ -46,7 +46,6 @@
DOUBLE_QUOTE: "\"",
Deserialize,
EarlyLintPass,
- ErrorKind,
IntoIter,
Itertools,
LF: "\n",
@@ -65,7 +64,6 @@
RegexBuilder,
RegexSet,
Start,
- Step,
Symbol,
SyntaxContext,
TBD,
@@ -76,7 +74,6 @@
Visitor,
Weak,
abs,
- align_of,
ambiguous_glob_reexports,
append,
arg,
@@ -159,7 +156,6 @@
from_ne_bytes,
from_ptr,
from_raw,
- from_ref,
from_str,
from_str_radix,
fs,
@@ -167,6 +163,7 @@
futures_util,
get,
get_mut,
+ get_or_insert,
get_or_insert_with,
get_unchecked,
get_unchecked_mut,
@@ -217,7 +214,6 @@
max_by_key,
max_value,
maximum,
- mem,
min,
min_by,
min_by_key,
diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs
index 1f0a0f2..bffbcf0 100644
--- a/clippy_utils/src/ty/mod.rs
+++ b/clippy_utils/src/ty/mod.rs
@@ -6,6 +6,7 @@
use itertools::Itertools;
use rustc_abi::VariantIdx;
use rustc_ast::ast::Mutability;
+use rustc_attr_data_structures::{AttributeKind, find_attr};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir as hir;
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
@@ -20,8 +21,8 @@
use rustc_middle::ty::layout::ValidityRequirement;
use rustc_middle::ty::{
self, AdtDef, AliasTy, AssocItem, AssocTag, Binder, BoundRegion, FnSig, GenericArg, GenericArgKind, GenericArgsRef,
- GenericParamDefKind, IntTy, Region, RegionKind, TraitRef, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable,
- TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, Upcast, VariantDef, VariantDiscr,
+ GenericParamDefKind, IntTy, Region, RegionKind, TraitRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable,
+ TypeVisitableExt, TypeVisitor, UintTy, Upcast, VariantDef, VariantDiscr,
};
use rustc_span::symbol::Ident;
use rustc_span::{DUMMY_SP, Span, Symbol, sym};
@@ -326,8 +327,8 @@
// Returns whether the type has #[must_use] attribute
pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
match ty.kind() {
- ty::Adt(adt, _) => cx.tcx.has_attr(adt.did(), sym::must_use),
- ty::Foreign(did) => cx.tcx.has_attr(*did, sym::must_use),
+ ty::Adt(adt, _) => find_attr!(cx.tcx.get_all_attrs(adt.did()), AttributeKind::MustUse { .. }),
+ ty::Foreign(did) => find_attr!(cx.tcx.get_all_attrs(*did), AttributeKind::MustUse { .. }),
ty::Slice(ty) | ty::Array(ty, _) | ty::RawPtr(ty, _) | ty::Ref(_, ty, _) => {
// for the Array case we don't need to care for the len == 0 case
// because we don't want to lint functions returning empty arrays
@@ -337,7 +338,10 @@
ty::Alias(ty::Opaque, AliasTy { def_id, .. }) => {
for (predicate, _) in cx.tcx.explicit_item_self_bounds(def_id).skip_binder() {
if let ty::ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder()
- && cx.tcx.has_attr(trait_predicate.trait_ref.def_id, sym::must_use)
+ && find_attr!(
+ cx.tcx.get_all_attrs(trait_predicate.trait_ref.def_id),
+ AttributeKind::MustUse { .. }
+ )
{
return true;
}
@@ -347,7 +351,7 @@
ty::Dynamic(binder, _, _) => {
for predicate in *binder {
if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder()
- && cx.tcx.has_attr(trait_ref.def_id, sym::must_use)
+ && find_attr!(cx.tcx.get_all_attrs(trait_ref.def_id), AttributeKind::MustUse { .. })
{
return true;
}
@@ -853,7 +857,7 @@
ControlFlow::Continue(())
}
}
- fn visit_binder<T: TypeFoldable<TyCtxt<'tcx>>>(&mut self, t: &Binder<'tcx, T>) -> Self::Result {
+ fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(&mut self, t: &Binder<'tcx, T>) -> Self::Result {
self.index += 1;
let res = t.super_visit_with(self);
self.index -= 1;
diff --git a/declare_clippy_lint/Cargo.toml b/declare_clippy_lint/Cargo.toml
new file mode 100644
index 0000000..bd6b4df
--- /dev/null
+++ b/declare_clippy_lint/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "declare_clippy_lint"
+version = "0.1.90"
+edition = "2024"
+repository = "https://github.com/rust-lang/rust-clippy"
+license = "MIT OR Apache-2.0"
+
+[package.metadata.rust-analyzer]
+# This crate uses #[feature(rustc_private)]
+rustc_private = true
diff --git a/declare_clippy_lint/src/lib.rs b/declare_clippy_lint/src/lib.rs
new file mode 100644
index 0000000..f7d9c64
--- /dev/null
+++ b/declare_clippy_lint/src/lib.rs
@@ -0,0 +1,280 @@
+#![feature(macro_metavar_expr_concat, rustc_private)]
+
+extern crate rustc_lint;
+
+use rustc_lint::{Lint, LintId, LintStore};
+
+// Needed by `declare_clippy_lint!`.
+pub extern crate rustc_session;
+
+#[derive(Default)]
+pub struct LintListBuilder {
+ lints: Vec<&'static Lint>,
+ all: Vec<LintId>,
+ cargo: Vec<LintId>,
+ complexity: Vec<LintId>,
+ correctness: Vec<LintId>,
+ nursery: Vec<LintId>,
+ pedantic: Vec<LintId>,
+ perf: Vec<LintId>,
+ restriction: Vec<LintId>,
+ style: Vec<LintId>,
+ suspicious: Vec<LintId>,
+}
+impl LintListBuilder {
+ pub fn insert(&mut self, lints: &[&LintInfo]) {
+ #[allow(clippy::enum_glob_use)]
+ use LintCategory::*;
+
+ self.lints.extend(lints.iter().map(|&x| x.lint));
+ for &&LintInfo { lint, category, .. } in lints {
+ let (all, cat) = match category {
+ Complexity => (Some(&mut self.all), &mut self.complexity),
+ Correctness => (Some(&mut self.all), &mut self.correctness),
+ Perf => (Some(&mut self.all), &mut self.perf),
+ Style => (Some(&mut self.all), &mut self.style),
+ Suspicious => (Some(&mut self.all), &mut self.suspicious),
+ Cargo => (None, &mut self.cargo),
+ Nursery => (None, &mut self.nursery),
+ Pedantic => (None, &mut self.pedantic),
+ Restriction => (None, &mut self.restriction),
+ };
+ if let Some(all) = all {
+ all.push(LintId::of(lint));
+ }
+ cat.push(LintId::of(lint));
+ }
+ }
+
+ pub fn register(self, store: &mut LintStore) {
+ store.register_lints(&self.lints);
+ store.register_group(true, "clippy::all", Some("clippy_all"), self.all);
+ store.register_group(true, "clippy::cargo", Some("clippy_cargo"), self.cargo);
+ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), self.complexity);
+ store.register_group(
+ true,
+ "clippy::correctness",
+ Some("clippy_correctness"),
+ self.correctness,
+ );
+ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), self.nursery);
+ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), self.pedantic);
+ store.register_group(true, "clippy::perf", Some("clippy_perf"), self.perf);
+ store.register_group(
+ true,
+ "clippy::restriction",
+ Some("clippy_restriction"),
+ self.restriction,
+ );
+ store.register_group(true, "clippy::style", Some("clippy_style"), self.style);
+ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), self.suspicious);
+ }
+}
+
+#[derive(Copy, Clone, Debug)]
+pub enum LintCategory {
+ Cargo,
+ Complexity,
+ Correctness,
+ Nursery,
+ Pedantic,
+ Perf,
+ Restriction,
+ Style,
+ Suspicious,
+}
+impl LintCategory {
+ #[must_use]
+ pub fn name(self) -> &'static str {
+ match self {
+ Self::Cargo => "cargo",
+ Self::Complexity => "complexity",
+ Self::Correctness => "correctness",
+ Self::Nursery => "nursery",
+ Self::Pedantic => "pedantic",
+ Self::Perf => "perf",
+ Self::Restriction => "restriction",
+ Self::Style => "style",
+ Self::Suspicious => "suspicious",
+ }
+ }
+}
+
+pub struct LintInfo {
+ pub lint: &'static Lint,
+ pub category: LintCategory,
+ pub explanation: &'static str,
+ /// e.g. `clippy_lints/src/absolute_paths.rs#43`
+ pub location: &'static str,
+ pub version: &'static str,
+}
+
+impl LintInfo {
+ /// Returns the lint name in lowercase without the `clippy::` prefix
+ #[must_use]
+ #[expect(clippy::missing_panics_doc)]
+ pub fn name_lower(&self) -> String {
+ self.lint.name.strip_prefix("clippy::").unwrap().to_ascii_lowercase()
+ }
+}
+
+#[macro_export]
+macro_rules! declare_clippy_lint_inner {
+ (
+ $(#[doc = $docs:literal])*
+ #[clippy::version = $version:literal]
+ $vis:vis $lint_name:ident,
+ $level:ident,
+ $category:ident,
+ $desc:literal
+ $(, @eval_always = $eval_always:literal)?
+ ) => {
+ $crate::rustc_session::declare_tool_lint! {
+ $(#[doc = $docs])*
+ #[clippy::version = $version]
+ $vis clippy::$lint_name,
+ $level,
+ $desc,
+ report_in_external_macro:true
+ $(, @eval_always = $eval_always)?
+ }
+
+ pub(crate) static ${concat($lint_name, _INFO)}: &'static $crate::LintInfo = &$crate::LintInfo {
+ lint: $lint_name,
+ category: $crate::LintCategory::$category,
+ explanation: concat!($($docs,"\n",)*),
+ location: concat!(file!(), "#L", line!()),
+ version: $version,
+ };
+ };
+}
+
+#[macro_export]
+macro_rules! declare_clippy_lint {
+ (
+ $(#[$($meta:tt)*])*
+ $vis:vis $lint_name:ident,
+ correctness,
+ $($rest:tt)*
+ ) => {
+ $crate::declare_clippy_lint_inner! {
+ $(#[$($meta)*])*
+ $vis $lint_name,
+ Deny,
+ Correctness,
+ $($rest)*
+ }
+ };
+ (
+ $(#[$($meta:tt)*])*
+ $vis:vis $lint_name:ident,
+ complexity,
+ $($rest:tt)*
+ ) => {
+ $crate::declare_clippy_lint_inner! {
+ $(#[$($meta)*])*
+ $vis $lint_name,
+ Warn,
+ Complexity,
+ $($rest)*
+ }
+ };
+ (
+ $(#[$($meta:tt)*])*
+ $vis:vis $lint_name:ident,
+ perf,
+ $($rest:tt)*
+ ) => {
+ $crate::declare_clippy_lint_inner! {
+ $(#[$($meta)*])*
+ $vis $lint_name,
+ Warn,
+ Perf,
+ $($rest)*
+ }
+ };
+ (
+ $(#[$($meta:tt)*])*
+ $vis:vis $lint_name:ident,
+ style,
+ $($rest:tt)*
+ ) => {
+ $crate::declare_clippy_lint_inner! {
+ $(#[$($meta)*])*
+ $vis $lint_name,
+ Warn,
+ Style,
+ $($rest)*
+ }
+ };
+ (
+ $(#[$($meta:tt)*])*
+ $vis:vis $lint_name:ident,
+ suspicious,
+ $($rest:tt)*
+ ) => {
+ $crate::declare_clippy_lint_inner! {
+ $(#[$($meta)*])*
+ $vis $lint_name,
+ Warn,
+ Suspicious,
+ $($rest)*
+ }
+ };
+ (
+ $(#[$($meta:tt)*])*
+ $vis:vis $lint_name:ident,
+ cargo,
+ $($rest:tt)*
+ ) => {
+ $crate::declare_clippy_lint_inner! {
+ $(#[$($meta)*])*
+ $vis $lint_name,
+ Allow,
+ Cargo,
+ $($rest)*
+ }
+ };
+ (
+ $(#[$($meta:tt)*])*
+ $vis:vis $lint_name:ident,
+ nursery,
+ $($rest:tt)*
+ ) => {
+ $crate::declare_clippy_lint_inner! {
+ $(#[$($meta)*])*
+ $vis $lint_name,
+ Allow,
+ Nursery,
+ $($rest)*
+ }
+ };
+ (
+ $(#[$($meta:tt)*])*
+ $vis:vis $lint_name:ident,
+ pedantic,
+ $($rest:tt)*
+ ) => {
+ $crate::declare_clippy_lint_inner! {
+ $(#[$($meta)*])*
+ $vis $lint_name,
+ Allow,
+ Pedantic,
+ $($rest)*
+ }
+ };
+ (
+ $(#[$($meta:tt)*])*
+ $vis:vis $lint_name:ident,
+ restriction,
+ $($rest:tt)*
+ ) => {
+ $crate::declare_clippy_lint_inner! {
+ $(#[$($meta)*])*
+ $vis $lint_name,
+ Allow,
+ Restriction,
+ $($rest)*
+ }
+ };
+}
diff --git a/lintcheck/src/main.rs b/lintcheck/src/main.rs
index 8418383..eb390ee 100644
--- a/lintcheck/src/main.rs
+++ b/lintcheck/src/main.rs
@@ -45,7 +45,7 @@
#[must_use]
pub fn target_dir() -> String {
- env::var("CARGO_TARGET_DIR").unwrap_or("target".to_owned())
+ env::var("CARGO_TARGET_DIR").unwrap_or_else(|_| "target".to_owned())
}
fn lintcheck_sources() -> String {
diff --git a/rust-toolchain.toml b/rust-toolchain.toml
index 3fc5a12..124756a 100644
--- a/rust-toolchain.toml
+++ b/rust-toolchain.toml
@@ -1,6 +1,6 @@
[toolchain]
# begin autogenerated nightly
-channel = "nightly-2025-06-12"
+channel = "nightly-2025-06-26"
# end autogenerated nightly
components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
profile = "minimal"
diff --git a/src/driver.rs b/src/driver.rs
index 37adb14..c4076cb 100644
--- a/src/driver.rs
+++ b/src/driver.rs
@@ -13,7 +13,13 @@
extern crate rustc_session;
extern crate rustc_span;
+// See docs in https://github.com/rust-lang/rust/blob/master/compiler/rustc/src/main.rs
+// about jemalloc.
+#[cfg(feature = "jemalloc")]
+extern crate tikv_jemalloc_sys as jemalloc_sys;
+
use clippy_utils::sym;
+use declare_clippy_lint::LintListBuilder;
use rustc_interface::interface;
use rustc_session::EarlyDiagCtxt;
use rustc_session::config::ErrorOutputType;
@@ -151,8 +157,13 @@
(previous)(sess, lint_store);
}
+ let mut list_builder = LintListBuilder::default();
+ list_builder.insert(clippy_lints::declared_lints::LINTS);
+ list_builder.register(lint_store);
+
let conf = clippy_config::Conf::read(sess, &conf_path);
- clippy_lints::register_lints(lint_store, conf);
+ clippy_lints::register_lint_passes(lint_store, conf);
+
#[cfg(feature = "internal")]
clippy_lints_internal::register_lints(lint_store);
}));
@@ -181,6 +192,36 @@
#[allow(clippy::too_many_lines)]
#[allow(clippy::ignored_unit_patterns)]
pub fn main() {
+ // See docs in https://github.com/rust-lang/rust/blob/master/compiler/rustc/src/main.rs
+ // about jemalloc.
+ #[cfg(feature = "jemalloc")]
+ {
+ use std::os::raw::{c_int, c_void};
+
+ #[used]
+ static _F1: unsafe extern "C" fn(usize, usize) -> *mut c_void = jemalloc_sys::calloc;
+ #[used]
+ static _F2: unsafe extern "C" fn(*mut *mut c_void, usize, usize) -> c_int = jemalloc_sys::posix_memalign;
+ #[used]
+ static _F3: unsafe extern "C" fn(usize, usize) -> *mut c_void = jemalloc_sys::aligned_alloc;
+ #[used]
+ static _F4: unsafe extern "C" fn(usize) -> *mut c_void = jemalloc_sys::malloc;
+ #[used]
+ static _F5: unsafe extern "C" fn(*mut c_void, usize) -> *mut c_void = jemalloc_sys::realloc;
+ #[used]
+ static _F6: unsafe extern "C" fn(*mut c_void) = jemalloc_sys::free;
+
+ #[cfg(target_os = "macos")]
+ {
+ unsafe extern "C" {
+ fn _rjem_je_zone_register();
+ }
+
+ #[used]
+ static _F7: unsafe extern "C" fn() = _rjem_je_zone_register;
+ }
+ }
+
let early_dcx = EarlyDiagCtxt::new(ErrorOutputType::default());
rustc_driver::init_rustc_env_logger(&early_dcx);
diff --git a/src/main.rs b/src/main.rs
index c9853e5..3c2eec1 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -107,7 +107,7 @@
}
fn into_std_cmd(self) -> Command {
- let mut cmd = Command::new(env::var("CARGO").unwrap_or("cargo".into()));
+ let mut cmd = Command::new(env::var("CARGO").unwrap_or_else(|_| "cargo".into()));
let clippy_args: String = self
.clippy_args
.iter()
diff --git a/tests/compile-test.rs b/tests/compile-test.rs
index 99a0125..cefe654 100644
--- a/tests/compile-test.rs
+++ b/tests/compile-test.rs
@@ -7,9 +7,9 @@
use cargo_metadata::Message;
use cargo_metadata::diagnostic::{Applicability, Diagnostic};
use clippy_config::ClippyConfiguration;
-use clippy_lints::LintInfo;
use clippy_lints::declared_lints::LINTS;
use clippy_lints::deprecated_lints::{DEPRECATED, DEPRECATED_VERSION, RENAMED};
+use declare_clippy_lint::LintInfo;
use pulldown_cmark::{Options, Parser, html};
use serde::Deserialize;
use test_utils::IS_RUSTC_TEST_SUITE;
@@ -568,10 +568,10 @@
Self {
id: name,
id_location: Some(lint.location),
- group: lint.category_str(),
+ group: lint.category.name(),
level: lint.lint.default_level.as_str(),
docs,
- version: lint.version.unwrap(),
+ version: lint.version,
applicability,
}
}
diff --git a/tests/dogfood.rs b/tests/dogfood.rs
index 4ac2bd5..3896168 100644
--- a/tests/dogfood.rs
+++ b/tests/dogfood.rs
@@ -40,6 +40,7 @@
"clippy_lints",
"clippy_utils",
"clippy_config",
+ "declare_clippy_lint",
"lintcheck",
"rustc_tools_util",
] {
diff --git a/tests/ui/author/macro_in_closure.stdout b/tests/ui/author/macro_in_closure.stdout
index 5f8a4ce..49595e2 100644
--- a/tests/ui/author/macro_in_closure.stdout
+++ b/tests/ui/author/macro_in_closure.stdout
@@ -9,28 +9,35 @@
&& let ExprKind::Call(func, args) = e.kind
&& paths::STD_IO_STDIO__PRINT.matches_path(cx, func) // Add the path to `clippy_utils::paths` if needed
&& args.len() == 1
- && let ExprKind::Call(func1, args1) = args[0].kind
- && paths::CORE_FMT_RT_NEW_V1.matches_path(cx, func1) // Add the path to `clippy_utils::paths` if needed
- && args1.len() == 2
+ && let ExprKind::Block(block1, None) = args[0].kind
+ && block1.stmts.len() == 1
+ && let StmtKind::Let(local1) = block1.stmts[0].kind
+ && let Some(init1) = local1.init
+ && let ExprKind::Array(elements) = init1.kind
+ && elements.len() == 1
+ && let ExprKind::Call(func1, args1) = elements[0].kind
+ && paths::CORE_FMT_RT_ARGUMENT_NEW_DISPLAY.matches_path(cx, func1) // Add the path to `clippy_utils::paths` if needed
+ && args1.len() == 1
&& let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = args1[0].kind
- && let ExprKind::Array(elements) = inner.kind
- && elements.len() == 2
- && let ExprKind::Lit(ref lit) = elements[0].kind
+ && let PatKind::Binding(BindingMode::NONE, _, name, None) = local1.pat.kind
+ && name.as_str() == "args"
+ && let Some(trailing_expr) = block1.expr
+ && let ExprKind::Call(func2, args2) = trailing_expr.kind
+ && paths::CORE_FMT_RT_NEW_V1.matches_path(cx, func2) // Add the path to `clippy_utils::paths` if needed
+ && args2.len() == 2
+ && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner1) = args2[0].kind
+ && let ExprKind::Array(elements1) = inner1.kind
+ && elements1.len() == 2
+ && let ExprKind::Lit(ref lit) = elements1[0].kind
&& let LitKind::Str(s, _) = lit.node
&& s.as_str() == ""
- && let ExprKind::Lit(ref lit1) = elements[1].kind
+ && let ExprKind::Lit(ref lit1) = elements1[1].kind
&& let LitKind::Str(s1, _) = lit1.node
&& s1.as_str() == "\n"
- && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner1) = args1[1].kind
- && let ExprKind::Array(elements1) = inner1.kind
- && elements1.len() == 1
- && let ExprKind::Call(func2, args2) = elements1[0].kind
- && paths::CORE_FMT_RT_ARGUMENT_NEW_DISPLAY.matches_path(cx, func2) // Add the path to `clippy_utils::paths` if needed
- && args2.len() == 1
- && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner2) = args2[0].kind
+ && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner2) = args2[1].kind
&& block.expr.is_none()
- && let PatKind::Binding(BindingMode::NONE, _, name, None) = local.pat.kind
- && name.as_str() == "print_text"
+ && let PatKind::Binding(BindingMode::NONE, _, name1, None) = local.pat.kind
+ && name1.as_str() == "print_text"
{
// report your lint here
}
diff --git a/tests/ui/author/macro_in_loop.stdout b/tests/ui/author/macro_in_loop.stdout
index ecc2525..4fc7b49 100644
--- a/tests/ui/author/macro_in_loop.stdout
+++ b/tests/ui/author/macro_in_loop.stdout
@@ -19,25 +19,32 @@
&& let ExprKind::Call(func, args) = e1.kind
&& paths::STD_IO_STDIO__PRINT.matches_path(cx, func) // Add the path to `clippy_utils::paths` if needed
&& args.len() == 1
- && let ExprKind::Call(func1, args1) = args[0].kind
- && paths::CORE_FMT_RT_NEW_V1.matches_path(cx, func1) // Add the path to `clippy_utils::paths` if needed
- && args1.len() == 2
+ && let ExprKind::Block(block2, None) = args[0].kind
+ && block2.stmts.len() == 1
+ && let StmtKind::Let(local) = block2.stmts[0].kind
+ && let Some(init) = local.init
+ && let ExprKind::Array(elements) = init.kind
+ && elements.len() == 1
+ && let ExprKind::Call(func1, args1) = elements[0].kind
+ && paths::CORE_FMT_RT_ARGUMENT_NEW_DISPLAY.matches_path(cx, func1) // Add the path to `clippy_utils::paths` if needed
+ && args1.len() == 1
&& let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = args1[0].kind
- && let ExprKind::Array(elements) = inner.kind
- && elements.len() == 2
- && let ExprKind::Lit(ref lit2) = elements[0].kind
+ && let PatKind::Binding(BindingMode::NONE, _, name1, None) = local.pat.kind
+ && name1.as_str() == "args"
+ && let Some(trailing_expr) = block2.expr
+ && let ExprKind::Call(func2, args2) = trailing_expr.kind
+ && paths::CORE_FMT_RT_NEW_V1.matches_path(cx, func2) // Add the path to `clippy_utils::paths` if needed
+ && args2.len() == 2
+ && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner1) = args2[0].kind
+ && let ExprKind::Array(elements1) = inner1.kind
+ && elements1.len() == 2
+ && let ExprKind::Lit(ref lit2) = elements1[0].kind
&& let LitKind::Str(s, _) = lit2.node
&& s.as_str() == ""
- && let ExprKind::Lit(ref lit3) = elements[1].kind
+ && let ExprKind::Lit(ref lit3) = elements1[1].kind
&& let LitKind::Str(s1, _) = lit3.node
&& s1.as_str() == "\n"
- && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner1) = args1[1].kind
- && let ExprKind::Array(elements1) = inner1.kind
- && elements1.len() == 1
- && let ExprKind::Call(func2, args2) = elements1[0].kind
- && paths::CORE_FMT_RT_ARGUMENT_NEW_DISPLAY.matches_path(cx, func2) // Add the path to `clippy_utils::paths` if needed
- && args2.len() == 1
- && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner2) = args2[0].kind
+ && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner2) = args2[1].kind
&& block1.expr.is_none()
&& block.expr.is_none()
{
diff --git a/tests/ui/borrow_deref_ref.fixed b/tests/ui/borrow_deref_ref.fixed
index 765dd75..6d06fcc 100644
--- a/tests/ui/borrow_deref_ref.fixed
+++ b/tests/ui/borrow_deref_ref.fixed
@@ -124,3 +124,50 @@
//~^ borrow_deref_ref
}
}
+
+fn issue_14934() {
+ let x: &'static str = "x";
+ let y = "y".to_string();
+ {
+ #[expect(clippy::toplevel_ref_arg)]
+ let ref mut x = &*x; // Do not lint
+ *x = &*y;
+ }
+ {
+ let mut x = x;
+ //~^ borrow_deref_ref
+ x = &*y;
+ }
+ {
+ #[expect(clippy::toplevel_ref_arg, clippy::needless_borrow)]
+ let ref x = x;
+ //~^ borrow_deref_ref
+ }
+ {
+ #[expect(clippy::toplevel_ref_arg)]
+ let ref mut x = std::convert::identity(x);
+ //~^ borrow_deref_ref
+ *x = &*y;
+ }
+ {
+ #[derive(Clone)]
+ struct S(&'static str);
+ let s = S("foo");
+ #[expect(clippy::toplevel_ref_arg)]
+ let ref mut x = &*s.0; // Do not lint
+ *x = "bar";
+ #[expect(clippy::toplevel_ref_arg)]
+ let ref mut x = s.clone().0;
+ //~^ borrow_deref_ref
+ *x = "bar";
+ #[expect(clippy::toplevel_ref_arg)]
+ let ref mut x = &*std::convert::identity(&s).0;
+ *x = "bar";
+ }
+ {
+ let y = &1;
+ #[expect(clippy::toplevel_ref_arg)]
+ let ref mut x = { y };
+ //~^ borrow_deref_ref
+ }
+}
diff --git a/tests/ui/borrow_deref_ref.rs b/tests/ui/borrow_deref_ref.rs
index 8ee66bf..b43f4c9 100644
--- a/tests/ui/borrow_deref_ref.rs
+++ b/tests/ui/borrow_deref_ref.rs
@@ -124,3 +124,50 @@
//~^ borrow_deref_ref
}
}
+
+fn issue_14934() {
+ let x: &'static str = "x";
+ let y = "y".to_string();
+ {
+ #[expect(clippy::toplevel_ref_arg)]
+ let ref mut x = &*x; // Do not lint
+ *x = &*y;
+ }
+ {
+ let mut x = &*x;
+ //~^ borrow_deref_ref
+ x = &*y;
+ }
+ {
+ #[expect(clippy::toplevel_ref_arg, clippy::needless_borrow)]
+ let ref x = &*x;
+ //~^ borrow_deref_ref
+ }
+ {
+ #[expect(clippy::toplevel_ref_arg)]
+ let ref mut x = &*std::convert::identity(x);
+ //~^ borrow_deref_ref
+ *x = &*y;
+ }
+ {
+ #[derive(Clone)]
+ struct S(&'static str);
+ let s = S("foo");
+ #[expect(clippy::toplevel_ref_arg)]
+ let ref mut x = &*s.0; // Do not lint
+ *x = "bar";
+ #[expect(clippy::toplevel_ref_arg)]
+ let ref mut x = &*s.clone().0;
+ //~^ borrow_deref_ref
+ *x = "bar";
+ #[expect(clippy::toplevel_ref_arg)]
+ let ref mut x = &*std::convert::identity(&s).0;
+ *x = "bar";
+ }
+ {
+ let y = &1;
+ #[expect(clippy::toplevel_ref_arg)]
+ let ref mut x = { &*y };
+ //~^ borrow_deref_ref
+ }
+}
diff --git a/tests/ui/borrow_deref_ref.stderr b/tests/ui/borrow_deref_ref.stderr
index 3d55da2..3a1f968 100644
--- a/tests/ui/borrow_deref_ref.stderr
+++ b/tests/ui/borrow_deref_ref.stderr
@@ -25,5 +25,35 @@
LL | (&*s).foo();
| ^^^^^ help: if you would like to reborrow, try removing `&*`: `s`
-error: aborting due to 4 previous errors
+error: deref on an immutable reference
+ --> tests/ui/borrow_deref_ref.rs:137:21
+ |
+LL | let mut x = &*x;
+ | ^^^ help: if you would like to reborrow, try removing `&*`: `x`
+
+error: deref on an immutable reference
+ --> tests/ui/borrow_deref_ref.rs:143:21
+ |
+LL | let ref x = &*x;
+ | ^^^ help: if you would like to reborrow, try removing `&*`: `x`
+
+error: deref on an immutable reference
+ --> tests/ui/borrow_deref_ref.rs:148:25
+ |
+LL | let ref mut x = &*std::convert::identity(x);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if you would like to reborrow, try removing `&*`: `std::convert::identity(x)`
+
+error: deref on an immutable reference
+ --> tests/ui/borrow_deref_ref.rs:160:25
+ |
+LL | let ref mut x = &*s.clone().0;
+ | ^^^^^^^^^^^^^ help: if you would like to reborrow, try removing `&*`: `s.clone().0`
+
+error: deref on an immutable reference
+ --> tests/ui/borrow_deref_ref.rs:170:27
+ |
+LL | let ref mut x = { &*y };
+ | ^^^ help: if you would like to reborrow, try removing `&*`: `y`
+
+error: aborting due to 9 previous errors
diff --git a/tests/ui/borrow_interior_mutable_const.rs b/tests/ui/borrow_interior_mutable_const.rs
index 0f439f7..674450a 100644
--- a/tests/ui/borrow_interior_mutable_const.rs
+++ b/tests/ui/borrow_interior_mutable_const.rs
@@ -218,4 +218,20 @@
let _ = &S::VALUE.1; //~ borrow_interior_mutable_const
let _ = &S::VALUE.2;
}
+ {
+ pub struct Foo<T, const N: usize>(pub Entry<N>, pub T);
+
+ pub struct Entry<const N: usize>(pub Cell<[u32; N]>);
+
+ impl<const N: usize> Entry<N> {
+ const INIT: Self = Self(Cell::new([42; N]));
+ }
+
+ impl<T, const N: usize> Foo<T, N> {
+ pub fn make_foo(v: T) -> Self {
+ // Used to ICE due to incorrect instantiation.
+ Foo(Entry::INIT, v)
+ }
+ }
+ }
}
diff --git a/tests/ui/box_default.fixed b/tests/ui/box_default.fixed
index 80000f5..ed00494 100644
--- a/tests/ui/box_default.fixed
+++ b/tests/ui/box_default.fixed
@@ -126,7 +126,7 @@
impl Bar for Foo {}
fn maybe_get_bar(i: u32) -> Option<Box<dyn Bar>> {
- if i % 2 == 0 {
+ if i.is_multiple_of(2) {
Some(Box::new(Foo::default()))
} else {
None
diff --git a/tests/ui/box_default.rs b/tests/ui/box_default.rs
index 4681016..801d92f 100644
--- a/tests/ui/box_default.rs
+++ b/tests/ui/box_default.rs
@@ -126,7 +126,7 @@
impl Bar for Foo {}
fn maybe_get_bar(i: u32) -> Option<Box<dyn Bar>> {
- if i % 2 == 0 {
+ if i.is_multiple_of(2) {
Some(Box::new(Foo::default()))
} else {
None
diff --git a/tests/ui/branches_sharing_code/shared_at_bottom.rs b/tests/ui/branches_sharing_code/shared_at_bottom.rs
index 922d304..fa322dc 100644
--- a/tests/ui/branches_sharing_code/shared_at_bottom.rs
+++ b/tests/ui/branches_sharing_code/shared_at_bottom.rs
@@ -276,3 +276,27 @@
}
}
}
+
+fn issue15004() {
+ let a = 12u32;
+ let b = 13u32;
+ let mut c = 8u32;
+
+ let mut result = if b > a {
+ c += 1;
+ 0
+ } else {
+ c += 2;
+ 0
+ //~^ branches_sharing_code
+ };
+
+ result = if b > a {
+ c += 1;
+ 1
+ } else {
+ c += 2;
+ 1
+ //~^ branches_sharing_code
+ };
+}
diff --git a/tests/ui/branches_sharing_code/shared_at_bottom.stderr b/tests/ui/branches_sharing_code/shared_at_bottom.stderr
index f437db8..1c470fb 100644
--- a/tests/ui/branches_sharing_code/shared_at_bottom.stderr
+++ b/tests/ui/branches_sharing_code/shared_at_bottom.stderr
@@ -172,5 +172,35 @@
LL + let y = 1;
|
-error: aborting due to 10 previous errors
+error: all if blocks contain the same code at the end
+ --> tests/ui/branches_sharing_code/shared_at_bottom.rs:290:5
+ |
+LL | / 0
+LL | |
+LL | | };
+ | |_____^
+ |
+ = note: the end suggestion probably needs some adjustments to use the expression result correctly
+help: consider moving these statements after the if
+ |
+LL ~ }
+LL ~ 0;
+ |
+
+error: all if blocks contain the same code at the end
+ --> tests/ui/branches_sharing_code/shared_at_bottom.rs:299:5
+ |
+LL | / 1
+LL | |
+LL | | };
+ | |_____^
+ |
+ = note: the end suggestion probably needs some adjustments to use the expression result correctly
+help: consider moving these statements after the if
+ |
+LL ~ }
+LL ~ 1;
+ |
+
+error: aborting due to 12 previous errors
diff --git a/tests/ui/cast_size.32bit.stderr b/tests/ui/cast_size.32bit.stderr
index cb1620e..5811cb3 100644
--- a/tests/ui/cast_size.32bit.stderr
+++ b/tests/ui/cast_size.32bit.stderr
@@ -177,6 +177,14 @@
LL | 9_999_999_999_999_999usize as f64;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+error: casting `usize` to `u16` may truncate the value
+ --> tests/ui/cast_size.rs:71:20
+ |
+LL | const N: u16 = M as u16;
+ | ^^^^^^^^
+ |
+ = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
+
error: literal out of range for `usize`
--> tests/ui/cast_size.rs:63:5
|
@@ -186,5 +194,5 @@
= note: the literal `9_999_999_999_999_999usize` does not fit into the type `usize` whose range is `0..=4294967295`
= note: `#[deny(overflowing_literals)]` on by default
-error: aborting due to 19 previous errors
+error: aborting due to 20 previous errors
diff --git a/tests/ui/cast_size.64bit.stderr b/tests/ui/cast_size.64bit.stderr
index b6000a5..ba14195 100644
--- a/tests/ui/cast_size.64bit.stderr
+++ b/tests/ui/cast_size.64bit.stderr
@@ -177,5 +177,13 @@
LL | 9_999_999_999_999_999usize as f64;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-error: aborting due to 18 previous errors
+error: casting `usize` to `u16` may truncate the value
+ --> tests/ui/cast_size.rs:71:20
+ |
+LL | const N: u16 = M as u16;
+ | ^^^^^^^^
+ |
+ = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
+
+error: aborting due to 19 previous errors
diff --git a/tests/ui/cast_size.rs b/tests/ui/cast_size.rs
index e5bef2a..ecc5866 100644
--- a/tests/ui/cast_size.rs
+++ b/tests/ui/cast_size.rs
@@ -65,3 +65,9 @@
//~[32bit]^^ ERROR: literal out of range for `usize`
// 999_999_999_999_999_999_999_999_999_999u128 as f128;
}
+
+fn issue15163() {
+ const M: usize = 100;
+ const N: u16 = M as u16;
+ //~^ cast_possible_truncation
+}
diff --git a/tests/ui/def_id_nocore.rs b/tests/ui/def_id_nocore.rs
index 40f40f7..5c13d86 100644
--- a/tests/ui/def_id_nocore.rs
+++ b/tests/ui/def_id_nocore.rs
@@ -7,8 +7,14 @@
#[link(name = "c")]
unsafe extern "C" {}
+#[lang = "pointee_sized"]
+pub trait PointeeSized {}
+
+#[lang = "meta_sized"]
+pub trait MetaSized: PointeeSized {}
+
#[lang = "sized"]
-pub trait Sized {}
+pub trait Sized: MetaSized {}
#[lang = "copy"]
pub trait Copy {}
#[lang = "freeze"]
diff --git a/tests/ui/def_id_nocore.stderr b/tests/ui/def_id_nocore.stderr
index 2718217..175dd07 100644
--- a/tests/ui/def_id_nocore.stderr
+++ b/tests/ui/def_id_nocore.stderr
@@ -1,5 +1,5 @@
error: methods called `as_*` usually take `self` by reference or `self` by mutable reference
- --> tests/ui/def_id_nocore.rs:27:19
+ --> tests/ui/def_id_nocore.rs:33:19
|
LL | pub fn as_ref(self) -> &'static str {
| ^^^^
diff --git a/tests/ui/disallowed_script_idents.rs b/tests/ui/disallowed_script_idents.rs
index 08fd1d9..dae3800 100644
--- a/tests/ui/disallowed_script_idents.rs
+++ b/tests/ui/disallowed_script_idents.rs
@@ -15,3 +15,17 @@
let カウンタ = 10;
//~^ disallowed_script_idents
}
+
+fn issue15116() {
+ const ÄÖÜ: u8 = 0;
+ const _ÄÖÜ: u8 = 0;
+ const Ä_ÖÜ: u8 = 0;
+ const ÄÖ_Ü: u8 = 0;
+ const ÄÖÜ_: u8 = 0;
+ let äöüß = 1;
+ let _äöüß = 1;
+ let ä_öüß = 1;
+ let äö_üß = 1;
+ let äöü_ß = 1;
+ let äöüß_ = 1;
+}
diff --git a/tests/ui/doc/needless_doctest_main.rs b/tests/ui/doc/needless_doctest_main.rs
index 21396cb..8c32176 100644
--- a/tests/ui/doc/needless_doctest_main.rs
+++ b/tests/ui/doc/needless_doctest_main.rs
@@ -1,5 +1,3 @@
-//@ check-pass
-
#![warn(clippy::needless_doctest_main)]
//! issue 10491:
//! ```rust,no_test
@@ -19,6 +17,100 @@
/// ```
fn foo() {}
+#[rustfmt::skip]
+/// Description
+/// ```rust
+/// fn main() {
+//~^ error: needless `fn main` in doctest
+/// let a = 0;
+/// }
+/// ```
+fn mulpipulpi() {}
+
+#[rustfmt::skip]
+/// With a `#[no_main]`
+/// ```rust
+/// #[no_main]
+/// fn a() {
+/// let _ = 0;
+/// }
+/// ```
+fn pulpimulpi() {}
+
+// Without a `#[no_main]` attribute
+/// ```rust
+/// fn a() {
+/// let _ = 0;
+/// }
+/// ```
+fn plumilupi() {}
+
+#[rustfmt::skip]
+/// Additional function, shouldn't trigger
+/// ```rust
+/// fn additional_function() {
+/// let _ = 0;
+/// // Thus `fn main` is actually relevant!
+/// }
+/// fn main() {
+/// let _ = 0;
+/// }
+/// ```
+fn mlupipupi() {}
+
+#[rustfmt::skip]
+/// Additional function AFTER main, shouldn't trigger
+/// ```rust
+/// fn main() {
+/// let _ = 0;
+/// }
+/// fn additional_function() {
+/// let _ = 0;
+/// // Thus `fn main` is actually relevant!
+/// }
+/// ```
+fn lumpimupli() {}
+
+#[rustfmt::skip]
+/// Ignore code block, should not lint at all
+/// ```rust, ignore
+/// fn main() {
+//~^ error: needless `fn main` in doctest
+/// // Hi!
+/// let _ = 0;
+/// }
+/// ```
+fn mpulpilumi() {}
+
+#[rustfmt::skip]
+/// Spaces in weird positions (including an \u{A0} after `main`)
+/// ```rust
+/// fn main (){
+//~^ error: needless `fn main` in doctest
+/// let _ = 0;
+/// }
+/// ```
+fn plumpiplupi() {}
+
+/// 4 Functions, this should not lint because there are several function
+///
+/// ```rust
+/// fn a() {let _ = 0; }
+/// fn b() {let _ = 0; }
+/// fn main() { let _ = 0; }
+/// fn d() { let _ = 0; }
+/// ```
+fn pulmipulmip() {}
+
+/// 3 Functions but main is first, should also not lint
+///
+///```rust
+/// fn main() { let _ = 0; }
+/// fn b() { let _ = 0; }
+/// fn c() { let _ = 0; }
+/// ```
+fn pmuplimulip() {}
+
fn main() {}
fn issue8244() -> Result<(), ()> {
diff --git a/tests/ui/doc/needless_doctest_main.stderr b/tests/ui/doc/needless_doctest_main.stderr
new file mode 100644
index 0000000..dd5474c
--- /dev/null
+++ b/tests/ui/doc/needless_doctest_main.stderr
@@ -0,0 +1,36 @@
+error: needless `fn main` in doctest
+ --> tests/ui/doc/needless_doctest_main.rs:23:5
+ |
+LL | /// fn main() {
+ | _____^
+LL | |
+LL | | /// let a = 0;
+LL | | /// }
+ | |_____^
+ |
+ = note: `-D clippy::needless-doctest-main` implied by `-D warnings`
+ = help: to override `-D warnings` add `#[allow(clippy::needless_doctest_main)]`
+
+error: needless `fn main` in doctest
+ --> tests/ui/doc/needless_doctest_main.rs:77:5
+ |
+LL | /// fn main() {
+ | _____^
+LL | |
+LL | | /// // Hi!
+LL | | /// let _ = 0;
+LL | | /// }
+ | |_____^
+
+error: needless `fn main` in doctest
+ --> tests/ui/doc/needless_doctest_main.rs:88:5
+ |
+LL | /// fn main (){
+ | _____^
+LL | |
+LL | | /// let _ = 0;
+LL | | /// }
+ | |_____^
+
+error: aborting due to 3 previous errors
+
diff --git a/tests/ui/doc_broken_link.rs b/tests/ui/doc_broken_link.rs
new file mode 100644
index 0000000..7d9c0ef
--- /dev/null
+++ b/tests/ui/doc_broken_link.rs
@@ -0,0 +1,72 @@
+#![warn(clippy::doc_broken_link)]
+
+fn main() {}
+
+pub struct FakeType {}
+
+/// This might be considered a link false positive
+/// and should be ignored by this lint rule:
+/// Example of referencing some code with brackets [FakeType].
+pub fn doc_ignore_link_false_positive_1() {}
+
+/// This might be considered a link false positive
+/// and should be ignored by this lint rule:
+/// [`FakeType`]. Continue text after brackets,
+/// then (something in
+/// parenthesis).
+pub fn doc_ignore_link_false_positive_2() {}
+
+/// Test valid link, whole link single line.
+/// [doc valid link](https://test.fake/doc_valid_link)
+pub fn doc_valid_link() {}
+
+/// Test valid link, whole link single line but it has special chars such as brackets and
+/// parenthesis. [doc invalid link url invalid char](https://test.fake/doc_valid_link_url_invalid_char?foo[bar]=1&bar(foo)=2)
+pub fn doc_valid_link_url_invalid_char() {}
+
+/// Test valid link, text tag broken across multiple lines.
+/// [doc valid link broken
+/// text](https://test.fake/doc_valid_link_broken_text)
+pub fn doc_valid_link_broken_text() {}
+
+/// Test valid link, url tag broken across multiple lines, but
+/// the whole url part in a single line.
+/// [doc valid link broken url tag two lines first](https://test.fake/doc_valid_link_broken_url_tag_two_lines_first
+/// )
+pub fn doc_valid_link_broken_url_tag_two_lines_first() {}
+
+/// Test valid link, url tag broken across multiple lines, but
+/// the whole url part in a single line.
+/// [doc valid link broken url tag two lines second](
+/// https://test.fake/doc_valid_link_broken_url_tag_two_lines_second)
+pub fn doc_valid_link_broken_url_tag_two_lines_second() {}
+
+/// Test valid link, url tag broken across multiple lines, but
+/// the whole url part in a single line, but the closing pharentesis
+/// in a third line.
+/// [doc valid link broken url tag three lines](
+/// https://test.fake/doc_valid_link_broken_url_tag_three_lines
+/// )
+pub fn doc_valid_link_broken_url_tag_three_lines() {}
+
+/// Test invalid link, url part broken across multiple lines.
+/// [doc invalid link broken url scheme part](https://
+/// test.fake/doc_invalid_link_broken_url_scheme_part)
+//~^^ ERROR: possible broken doc link: broken across multiple lines
+pub fn doc_invalid_link_broken_url_scheme_part() {}
+
+/// Test invalid link, url part broken across multiple lines.
+/// [doc invalid link broken url host part](https://test
+/// .fake/doc_invalid_link_broken_url_host_part)
+//~^^ ERROR: possible broken doc link: broken across multiple lines
+pub fn doc_invalid_link_broken_url_host_part() {}
+
+/// Test invalid link, for multiple urls in the same block of comment.
+/// There is a [fist link - invalid](https://test
+/// .fake) then it continues
+//~^^ ERROR: possible broken doc link: broken across multiple lines
+/// with a [second link - valid](https://test.fake/doc_valid_link) and another [third link - invalid](https://test
+/// .fake). It ends with another
+//~^^ ERROR: possible broken doc link: broken across multiple lines
+/// line of comment.
+pub fn doc_multiple_invalid_link_broken_url() {}
diff --git a/tests/ui/doc_broken_link.stderr b/tests/ui/doc_broken_link.stderr
new file mode 100644
index 0000000..179ed97
--- /dev/null
+++ b/tests/ui/doc_broken_link.stderr
@@ -0,0 +1,29 @@
+error: possible broken doc link: broken across multiple lines
+ --> tests/ui/doc_broken_link.rs:53:5
+ |
+LL | /// [doc invalid link broken url scheme part](https://
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::doc-broken-link` implied by `-D warnings`
+ = help: to override `-D warnings` add `#[allow(clippy::doc_broken_link)]`
+
+error: possible broken doc link: broken across multiple lines
+ --> tests/ui/doc_broken_link.rs:59:5
+ |
+LL | /// [doc invalid link broken url host part](https://test
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: possible broken doc link: broken across multiple lines
+ --> tests/ui/doc_broken_link.rs:65:16
+ |
+LL | /// There is a [fist link - invalid](https://test
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: possible broken doc link: broken across multiple lines
+ --> tests/ui/doc_broken_link.rs:68:80
+ |
+LL | /// with a [second link - valid](https://test.fake/doc_valid_link) and another [third link - invalid](https://test
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 4 previous errors
+
diff --git a/tests/ui/empty_line_after/outer_attribute.1.fixed b/tests/ui/empty_line_after/outer_attribute.1.fixed
index 36d80a2..e36e3c2 100644
--- a/tests/ui/empty_line_after/outer_attribute.1.fixed
+++ b/tests/ui/empty_line_after/outer_attribute.1.fixed
@@ -105,4 +105,13 @@
")]
pub struct Args;
+mod issue_14980 {
+ //~v empty_line_after_outer_attr
+ #[repr(align(536870912))]
+ enum Aligned {
+ Zero = 0,
+ One = 1,
+ }
+}
+
fn main() {}
diff --git a/tests/ui/empty_line_after/outer_attribute.2.fixed b/tests/ui/empty_line_after/outer_attribute.2.fixed
index 0e8e412..b0908fc 100644
--- a/tests/ui/empty_line_after/outer_attribute.2.fixed
+++ b/tests/ui/empty_line_after/outer_attribute.2.fixed
@@ -108,4 +108,13 @@
")]
pub struct Args;
+mod issue_14980 {
+ //~v empty_line_after_outer_attr
+ #[repr(align(536870912))]
+ enum Aligned {
+ Zero = 0,
+ One = 1,
+ }
+}
+
fn main() {}
diff --git a/tests/ui/empty_line_after/outer_attribute.rs b/tests/ui/empty_line_after/outer_attribute.rs
index 1295088..4ae113c 100644
--- a/tests/ui/empty_line_after/outer_attribute.rs
+++ b/tests/ui/empty_line_after/outer_attribute.rs
@@ -116,4 +116,14 @@
")]
pub struct Args;
+mod issue_14980 {
+ //~v empty_line_after_outer_attr
+ #[repr(align(536870912))]
+
+ enum Aligned {
+ Zero = 0,
+ One = 1,
+ }
+}
+
fn main() {}
diff --git a/tests/ui/empty_line_after/outer_attribute.stderr b/tests/ui/empty_line_after/outer_attribute.stderr
index 519ba6e..331bc7c 100644
--- a/tests/ui/empty_line_after/outer_attribute.stderr
+++ b/tests/ui/empty_line_after/outer_attribute.stderr
@@ -111,5 +111,16 @@
|
= help: if the empty lines are unintentional, remove them
-error: aborting due to 9 previous errors
+error: empty line after outer attribute
+ --> tests/ui/empty_line_after/outer_attribute.rs:121:5
+ |
+LL | / #[repr(align(536870912))]
+LL | |
+ | |_^
+LL | enum Aligned {
+ | ------------ the attribute applies to this enum
+ |
+ = help: if the empty line is unintentional, remove it
+
+error: aborting due to 10 previous errors
diff --git a/tests/ui/eta.fixed b/tests/ui/eta.fixed
index 0ba631f..c93b83f 100644
--- a/tests/ui/eta.fixed
+++ b/tests/ui/eta.fixed
@@ -543,3 +543,21 @@
//~^ redundant_closure
}
}
+
+fn issue_14789() {
+ _ = Some(1u8).map(
+ #[expect(clippy::redundant_closure)]
+ |a| foo(a),
+ );
+
+ _ = Some("foo").map(
+ #[expect(clippy::redundant_closure_for_method_calls)]
+ |s| s.to_owned(),
+ );
+
+ let _: Vec<u8> = None.map_or_else(
+ #[expect(clippy::redundant_closure)]
+ || vec![],
+ std::convert::identity,
+ );
+}
diff --git a/tests/ui/eta.rs b/tests/ui/eta.rs
index 4d8b29d..273c8b2 100644
--- a/tests/ui/eta.rs
+++ b/tests/ui/eta.rs
@@ -543,3 +543,21 @@
//~^ redundant_closure
}
}
+
+fn issue_14789() {
+ _ = Some(1u8).map(
+ #[expect(clippy::redundant_closure)]
+ |a| foo(a),
+ );
+
+ _ = Some("foo").map(
+ #[expect(clippy::redundant_closure_for_method_calls)]
+ |s| s.to_owned(),
+ );
+
+ let _: Vec<u8> = None.map_or_else(
+ #[expect(clippy::redundant_closure)]
+ || vec![],
+ std::convert::identity,
+ );
+}
diff --git a/tests/ui/exhaustive_items.fixed b/tests/ui/exhaustive_items.fixed
index 79c74ae..3b2f33d 100644
--- a/tests/ui/exhaustive_items.fixed
+++ b/tests/ui/exhaustive_items.fixed
@@ -1,3 +1,4 @@
+#![feature(default_field_values)]
#![deny(clippy::exhaustive_enums, clippy::exhaustive_structs)]
#![allow(unused)]
@@ -90,3 +91,9 @@
pub bar: String,
}
}
+
+pub mod issue14992 {
+ pub struct A {
+ pub a: isize = 42,
+ }
+}
diff --git a/tests/ui/exhaustive_items.rs b/tests/ui/exhaustive_items.rs
index 4e851f4..b0a6a71 100644
--- a/tests/ui/exhaustive_items.rs
+++ b/tests/ui/exhaustive_items.rs
@@ -1,3 +1,4 @@
+#![feature(default_field_values)]
#![deny(clippy::exhaustive_enums, clippy::exhaustive_structs)]
#![allow(unused)]
@@ -87,3 +88,9 @@
pub bar: String,
}
}
+
+pub mod issue14992 {
+ pub struct A {
+ pub a: isize = 42,
+ }
+}
diff --git a/tests/ui/exhaustive_items.stderr b/tests/ui/exhaustive_items.stderr
index c92c8a9..55928fa 100644
--- a/tests/ui/exhaustive_items.stderr
+++ b/tests/ui/exhaustive_items.stderr
@@ -1,5 +1,5 @@
error: exported enums should not be exhaustive
- --> tests/ui/exhaustive_items.rs:9:5
+ --> tests/ui/exhaustive_items.rs:10:5
|
LL | / pub enum Exhaustive {
LL | |
@@ -11,7 +11,7 @@
| |_____^
|
note: the lint level is defined here
- --> tests/ui/exhaustive_items.rs:1:9
+ --> tests/ui/exhaustive_items.rs:2:9
|
LL | #![deny(clippy::exhaustive_enums, clippy::exhaustive_structs)]
| ^^^^^^^^^^^^^^^^^^^^^^^^
@@ -22,7 +22,7 @@
|
error: exported enums should not be exhaustive
- --> tests/ui/exhaustive_items.rs:19:5
+ --> tests/ui/exhaustive_items.rs:20:5
|
LL | / pub enum ExhaustiveWithAttrs {
LL | |
@@ -40,7 +40,7 @@
|
error: exported structs should not be exhaustive
- --> tests/ui/exhaustive_items.rs:55:5
+ --> tests/ui/exhaustive_items.rs:56:5
|
LL | / pub struct Exhaustive {
LL | |
@@ -50,7 +50,7 @@
| |_____^
|
note: the lint level is defined here
- --> tests/ui/exhaustive_items.rs:1:35
+ --> tests/ui/exhaustive_items.rs:2:35
|
LL | #![deny(clippy::exhaustive_enums, clippy::exhaustive_structs)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/identity_op.fixed b/tests/ui/identity_op.fixed
index a1b5560..4e14e1a 100644
--- a/tests/ui/identity_op.fixed
+++ b/tests/ui/identity_op.fixed
@@ -312,3 +312,49 @@
let _: u64 = 1u64 + ((x as i32 + y as i32) as u64);
//~^ identity_op
}
+
+fn issue_14932() {
+ let _ = 0usize + &Default::default(); // no error
+
+ 0usize + &Default::default(); // no error
+
+ <usize as Default>::default();
+ //~^ identity_op
+
+ let _ = usize::default();
+ //~^ identity_op
+
+ let _n: usize = Default::default();
+ //~^ identity_op
+}
+
+// Expr's type can be inferred by the function's return type
+fn issue_14932_2() -> usize {
+ Default::default()
+ //~^ identity_op
+}
+
+trait Def {
+ fn def() -> Self;
+}
+
+impl Def for usize {
+ fn def() -> Self {
+ 0
+ }
+}
+
+fn issue_14932_3() {
+ let _ = 0usize + &Def::def(); // no error
+
+ 0usize + &Def::def(); // no error
+
+ <usize as Def>::def();
+ //~^ identity_op
+
+ let _ = usize::def();
+ //~^ identity_op
+
+ let _n: usize = Def::def();
+ //~^ identity_op
+}
diff --git a/tests/ui/identity_op.rs b/tests/ui/identity_op.rs
index f603e10..ebbef57 100644
--- a/tests/ui/identity_op.rs
+++ b/tests/ui/identity_op.rs
@@ -312,3 +312,49 @@
let _: u64 = 1u64 + ((x as i32 + y as i32) as u64 + 0u64);
//~^ identity_op
}
+
+fn issue_14932() {
+ let _ = 0usize + &Default::default(); // no error
+
+ 0usize + &Default::default(); // no error
+
+ 0usize + &<usize as Default>::default();
+ //~^ identity_op
+
+ let _ = 0usize + &usize::default();
+ //~^ identity_op
+
+ let _n: usize = 0usize + &Default::default();
+ //~^ identity_op
+}
+
+// Expr's type can be inferred by the function's return type
+fn issue_14932_2() -> usize {
+ 0usize + &Default::default()
+ //~^ identity_op
+}
+
+trait Def {
+ fn def() -> Self;
+}
+
+impl Def for usize {
+ fn def() -> Self {
+ 0
+ }
+}
+
+fn issue_14932_3() {
+ let _ = 0usize + &Def::def(); // no error
+
+ 0usize + &Def::def(); // no error
+
+ 0usize + &<usize as Def>::def();
+ //~^ identity_op
+
+ let _ = 0usize + &usize::def();
+ //~^ identity_op
+
+ let _n: usize = 0usize + &Def::def();
+ //~^ identity_op
+}
diff --git a/tests/ui/identity_op.stderr b/tests/ui/identity_op.stderr
index 8f9c2b6..24fa5db 100644
--- a/tests/ui/identity_op.stderr
+++ b/tests/ui/identity_op.stderr
@@ -379,5 +379,47 @@
LL | let _: u64 = 1u64 + ((x as i32 + y as i32) as u64 + 0u64);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `((x as i32 + y as i32) as u64)`
-error: aborting due to 63 previous errors
+error: this operation has no effect
+ --> tests/ui/identity_op.rs:321:5
+ |
+LL | 0usize + &<usize as Default>::default();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `<usize as Default>::default()`
+
+error: this operation has no effect
+ --> tests/ui/identity_op.rs:324:13
+ |
+LL | let _ = 0usize + &usize::default();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `usize::default()`
+
+error: this operation has no effect
+ --> tests/ui/identity_op.rs:327:21
+ |
+LL | let _n: usize = 0usize + &Default::default();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `Default::default()`
+
+error: this operation has no effect
+ --> tests/ui/identity_op.rs:333:5
+ |
+LL | 0usize + &Default::default()
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `Default::default()`
+
+error: this operation has no effect
+ --> tests/ui/identity_op.rs:352:5
+ |
+LL | 0usize + &<usize as Def>::def();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `<usize as Def>::def()`
+
+error: this operation has no effect
+ --> tests/ui/identity_op.rs:355:13
+ |
+LL | let _ = 0usize + &usize::def();
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `usize::def()`
+
+error: this operation has no effect
+ --> tests/ui/identity_op.rs:358:21
+ |
+LL | let _n: usize = 0usize + &Def::def();
+ | ^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `Def::def()`
+
+error: aborting due to 70 previous errors
diff --git a/tests/ui/infinite_iter.rs b/tests/ui/infinite_iter.rs
index 002a791..701a865 100644
--- a/tests/ui/infinite_iter.rs
+++ b/tests/ui/infinite_iter.rs
@@ -38,7 +38,7 @@
//~^ infinite_iter
// infinite iter
- (0_u64..).filter(|x| x % 2 == 0).last();
+ (0_u64..).filter(|x| x.is_multiple_of(2)).last();
//~^ infinite_iter
// not an infinite, because ranges are double-ended
diff --git a/tests/ui/infinite_iter.stderr b/tests/ui/infinite_iter.stderr
index 47133a2..b9e7c00 100644
--- a/tests/ui/infinite_iter.stderr
+++ b/tests/ui/infinite_iter.stderr
@@ -42,8 +42,8 @@
error: infinite iteration detected
--> tests/ui/infinite_iter.rs:41:5
|
-LL | (0_u64..).filter(|x| x % 2 == 0).last();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | (0_u64..).filter(|x| x.is_multiple_of(2)).last();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: possible infinite iteration detected
--> tests/ui/infinite_iter.rs:53:5
diff --git a/tests/ui/iter_kv_map.fixed b/tests/ui/iter_kv_map.fixed
index 874f749..b18dda3 100644
--- a/tests/ui/iter_kv_map.fixed
+++ b/tests/ui/iter_kv_map.fixed
@@ -30,15 +30,19 @@
let _ = map.clone().values().collect::<Vec<_>>();
//~^ iter_kv_map
- let _ = map.keys().filter(|x| *x % 2 == 0).count();
+ let _ = map.keys().filter(|x| x.is_multiple_of(2)).count();
//~^ iter_kv_map
// Don't lint
- let _ = map.iter().filter(|(_, val)| *val % 2 == 0).map(|(key, _)| key).count();
+ let _ = map
+ .iter()
+ .filter(|(_, val)| val.is_multiple_of(2))
+ .map(|(key, _)| key)
+ .count();
let _ = map.iter().map(get_key).collect::<Vec<_>>();
// Linting the following could be an improvement to the lint
- // map.iter().filter_map(|(_, val)| (val % 2 == 0).then(val * 17)).count();
+ // map.iter().filter_map(|(_, val)| (val.is_multiple_of(2)).then(val * 17)).count();
// Lint
let _ = map.keys().map(|key| key * 9).count();
@@ -84,15 +88,19 @@
let _ = map.clone().values().collect::<Vec<_>>();
//~^ iter_kv_map
- let _ = map.keys().filter(|x| *x % 2 == 0).count();
+ let _ = map.keys().filter(|x| x.is_multiple_of(2)).count();
//~^ iter_kv_map
// Don't lint
- let _ = map.iter().filter(|(_, val)| *val % 2 == 0).map(|(key, _)| key).count();
+ let _ = map
+ .iter()
+ .filter(|(_, val)| val.is_multiple_of(2))
+ .map(|(key, _)| key)
+ .count();
let _ = map.iter().map(get_key).collect::<Vec<_>>();
// Linting the following could be an improvement to the lint
- // map.iter().filter_map(|(_, val)| (val % 2 == 0).then(val * 17)).count();
+ // map.iter().filter_map(|(_, val)| (val.is_multiple_of(2)).then(val * 17)).count();
// Lint
let _ = map.keys().map(|key| key * 9).count();
diff --git a/tests/ui/iter_kv_map.rs b/tests/ui/iter_kv_map.rs
index f570e3c..729e4e8 100644
--- a/tests/ui/iter_kv_map.rs
+++ b/tests/ui/iter_kv_map.rs
@@ -30,15 +30,19 @@
let _ = map.clone().iter().map(|(_, val)| val).collect::<Vec<_>>();
//~^ iter_kv_map
- let _ = map.iter().map(|(key, _)| key).filter(|x| *x % 2 == 0).count();
+ let _ = map.iter().map(|(key, _)| key).filter(|x| x.is_multiple_of(2)).count();
//~^ iter_kv_map
// Don't lint
- let _ = map.iter().filter(|(_, val)| *val % 2 == 0).map(|(key, _)| key).count();
+ let _ = map
+ .iter()
+ .filter(|(_, val)| val.is_multiple_of(2))
+ .map(|(key, _)| key)
+ .count();
let _ = map.iter().map(get_key).collect::<Vec<_>>();
// Linting the following could be an improvement to the lint
- // map.iter().filter_map(|(_, val)| (val % 2 == 0).then(val * 17)).count();
+ // map.iter().filter_map(|(_, val)| (val.is_multiple_of(2)).then(val * 17)).count();
// Lint
let _ = map.iter().map(|(key, _value)| key * 9).count();
@@ -86,15 +90,19 @@
let _ = map.clone().iter().map(|(_, val)| val).collect::<Vec<_>>();
//~^ iter_kv_map
- let _ = map.iter().map(|(key, _)| key).filter(|x| *x % 2 == 0).count();
+ let _ = map.iter().map(|(key, _)| key).filter(|x| x.is_multiple_of(2)).count();
//~^ iter_kv_map
// Don't lint
- let _ = map.iter().filter(|(_, val)| *val % 2 == 0).map(|(key, _)| key).count();
+ let _ = map
+ .iter()
+ .filter(|(_, val)| val.is_multiple_of(2))
+ .map(|(key, _)| key)
+ .count();
let _ = map.iter().map(get_key).collect::<Vec<_>>();
// Linting the following could be an improvement to the lint
- // map.iter().filter_map(|(_, val)| (val % 2 == 0).then(val * 17)).count();
+ // map.iter().filter_map(|(_, val)| (val.is_multiple_of(2)).then(val * 17)).count();
// Lint
let _ = map.iter().map(|(key, _value)| key * 9).count();
diff --git a/tests/ui/iter_kv_map.stderr b/tests/ui/iter_kv_map.stderr
index 31ee76c..8f73541 100644
--- a/tests/ui/iter_kv_map.stderr
+++ b/tests/ui/iter_kv_map.stderr
@@ -52,29 +52,29 @@
error: iterating on a map's keys
--> tests/ui/iter_kv_map.rs:33:13
|
-LL | let _ = map.iter().map(|(key, _)| key).filter(|x| *x % 2 == 0).count();
+LL | let _ = map.iter().map(|(key, _)| key).filter(|x| x.is_multiple_of(2)).count();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()`
error: iterating on a map's keys
- --> tests/ui/iter_kv_map.rs:44:13
+ --> tests/ui/iter_kv_map.rs:48:13
|
LL | let _ = map.iter().map(|(key, _value)| key * 9).count();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys().map(|key| key * 9)`
error: iterating on a map's values
- --> tests/ui/iter_kv_map.rs:46:13
+ --> tests/ui/iter_kv_map.rs:50:13
|
LL | let _ = map.iter().map(|(_key, value)| value * 17).count();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|value| value * 17)`
error: iterating on a map's values
- --> tests/ui/iter_kv_map.rs:50:13
+ --> tests/ui/iter_kv_map.rs:54:13
|
LL | let _ = map.clone().into_iter().map(|(_, ref val)| ref_acceptor(val)).count();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values().map(|ref val| ref_acceptor(val))`
error: iterating on a map's values
- --> tests/ui/iter_kv_map.rs:54:13
+ --> tests/ui/iter_kv_map.rs:58:13
|
LL | let _ = map
| _____________^
@@ -97,85 +97,85 @@
|
error: iterating on a map's values
- --> tests/ui/iter_kv_map.rs:65:13
+ --> tests/ui/iter_kv_map.rs:69:13
|
LL | let _ = map.clone().into_iter().map(|(_, mut val)| val).count();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values()`
error: iterating on a map's keys
- --> tests/ui/iter_kv_map.rs:70:13
+ --> tests/ui/iter_kv_map.rs:74:13
|
LL | let _ = map.iter().map(|(key, _)| key).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()`
error: iterating on a map's values
- --> tests/ui/iter_kv_map.rs:72:13
+ --> tests/ui/iter_kv_map.rs:76:13
|
LL | let _ = map.iter().map(|(_, value)| value).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values()`
error: iterating on a map's values
- --> tests/ui/iter_kv_map.rs:74:13
+ --> tests/ui/iter_kv_map.rs:78:13
|
LL | let _ = map.iter().map(|(_, v)| v + 2).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|v| v + 2)`
error: iterating on a map's keys
- --> tests/ui/iter_kv_map.rs:77:13
+ --> tests/ui/iter_kv_map.rs:81:13
|
LL | let _ = map.clone().into_iter().map(|(key, _)| key).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys()`
error: iterating on a map's keys
- --> tests/ui/iter_kv_map.rs:79:13
+ --> tests/ui/iter_kv_map.rs:83:13
|
LL | let _ = map.clone().into_iter().map(|(key, _)| key + 2).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys().map(|key| key + 2)`
error: iterating on a map's values
- --> tests/ui/iter_kv_map.rs:82:13
+ --> tests/ui/iter_kv_map.rs:86:13
|
LL | let _ = map.clone().into_iter().map(|(_, val)| val).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values()`
error: iterating on a map's values
- --> tests/ui/iter_kv_map.rs:84:13
+ --> tests/ui/iter_kv_map.rs:88:13
|
LL | let _ = map.clone().into_iter().map(|(_, val)| val + 2).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values().map(|val| val + 2)`
error: iterating on a map's values
- --> tests/ui/iter_kv_map.rs:87:13
+ --> tests/ui/iter_kv_map.rs:91:13
|
LL | let _ = map.clone().iter().map(|(_, val)| val).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().values()`
error: iterating on a map's keys
- --> tests/ui/iter_kv_map.rs:89:13
+ --> tests/ui/iter_kv_map.rs:93:13
|
-LL | let _ = map.iter().map(|(key, _)| key).filter(|x| *x % 2 == 0).count();
+LL | let _ = map.iter().map(|(key, _)| key).filter(|x| x.is_multiple_of(2)).count();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()`
error: iterating on a map's keys
- --> tests/ui/iter_kv_map.rs:100:13
+ --> tests/ui/iter_kv_map.rs:108:13
|
LL | let _ = map.iter().map(|(key, _value)| key * 9).count();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys().map(|key| key * 9)`
error: iterating on a map's values
- --> tests/ui/iter_kv_map.rs:102:13
+ --> tests/ui/iter_kv_map.rs:110:13
|
LL | let _ = map.iter().map(|(_key, value)| value * 17).count();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|value| value * 17)`
error: iterating on a map's values
- --> tests/ui/iter_kv_map.rs:106:13
+ --> tests/ui/iter_kv_map.rs:114:13
|
LL | let _ = map.clone().into_iter().map(|(_, ref val)| ref_acceptor(val)).count();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values().map(|ref val| ref_acceptor(val))`
error: iterating on a map's values
- --> tests/ui/iter_kv_map.rs:110:13
+ --> tests/ui/iter_kv_map.rs:118:13
|
LL | let _ = map
| _____________^
@@ -198,73 +198,73 @@
|
error: iterating on a map's values
- --> tests/ui/iter_kv_map.rs:121:13
+ --> tests/ui/iter_kv_map.rs:129:13
|
LL | let _ = map.clone().into_iter().map(|(_, mut val)| val).count();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values()`
error: iterating on a map's keys
- --> tests/ui/iter_kv_map.rs:137:13
+ --> tests/ui/iter_kv_map.rs:145:13
|
LL | let _ = map.iter().map(|(key, _)| key).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()`
error: iterating on a map's values
- --> tests/ui/iter_kv_map.rs:140:13
+ --> tests/ui/iter_kv_map.rs:148:13
|
LL | let _ = map.iter().map(|(_, value)| value).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values()`
error: iterating on a map's values
- --> tests/ui/iter_kv_map.rs:143:13
+ --> tests/ui/iter_kv_map.rs:151:13
|
LL | let _ = map.iter().map(|(_, v)| v + 2).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|v| v + 2)`
error: iterating on a map's keys
- --> tests/ui/iter_kv_map.rs:152:13
+ --> tests/ui/iter_kv_map.rs:160:13
|
LL | let _ = map.clone().into_iter().map(|(key, _)| key).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys()`
error: iterating on a map's keys
- --> tests/ui/iter_kv_map.rs:155:13
+ --> tests/ui/iter_kv_map.rs:163:13
|
LL | let _ = map.clone().into_iter().map(|(key, _)| key + 2).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys().map(|key| key + 2)`
error: iterating on a map's values
- --> tests/ui/iter_kv_map.rs:158:13
+ --> tests/ui/iter_kv_map.rs:166:13
|
LL | let _ = map.clone().into_iter().map(|(_, val)| val).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values()`
error: iterating on a map's values
- --> tests/ui/iter_kv_map.rs:161:13
+ --> tests/ui/iter_kv_map.rs:169:13
|
LL | let _ = map.clone().into_iter().map(|(_, val)| val + 2).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values().map(|val| val + 2)`
error: iterating on a map's keys
- --> tests/ui/iter_kv_map.rs:164:13
+ --> tests/ui/iter_kv_map.rs:172:13
|
LL | let _ = map.iter().map(|(key, _)| key).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()`
error: iterating on a map's values
- --> tests/ui/iter_kv_map.rs:167:13
+ --> tests/ui/iter_kv_map.rs:175:13
|
LL | let _ = map.iter().map(|(_, value)| value).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values()`
error: iterating on a map's values
- --> tests/ui/iter_kv_map.rs:170:13
+ --> tests/ui/iter_kv_map.rs:178:13
|
LL | let _ = map.iter().map(|(_, v)| v + 2).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|v| v + 2)`
error: iterating on a map's values
- --> tests/ui/iter_kv_map.rs:185:13
+ --> tests/ui/iter_kv_map.rs:193:13
|
LL | let _ = map.as_ref().iter().map(|(_, v)| v).copied().collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.as_ref().values()`
diff --git a/tests/ui/large_stack_frames.rs b/tests/ui/large_stack_frames.rs
index 3ed124f..132f145 100644
--- a/tests/ui/large_stack_frames.rs
+++ b/tests/ui/large_stack_frames.rs
@@ -1,8 +1,7 @@
//@ normalize-stderr-test: "\b10000(08|16|32)\b" -> "100$$PTR"
//@ normalize-stderr-test: "\b2500(060|120)\b" -> "250$$PTR"
-#![allow(unused, incomplete_features)]
+#![allow(unused)]
#![warn(clippy::large_stack_frames)]
-#![feature(unsized_locals)]
use std::hint::black_box;
@@ -11,11 +10,6 @@
black_box(&x);
}
-fn unsized_local() {
- let x: dyn std::fmt::Display = *(Box::new(1) as Box<dyn std::fmt::Display>);
- black_box(&x);
-}
-
struct ArrayDefault<const N: usize>([u8; N]);
impl<const N: usize> Default for ArrayDefault<N> {
diff --git a/tests/ui/large_stack_frames.stderr b/tests/ui/large_stack_frames.stderr
index 0ff49e9..79482e6 100644
--- a/tests/ui/large_stack_frames.stderr
+++ b/tests/ui/large_stack_frames.stderr
@@ -1,5 +1,5 @@
error: this function may allocate 250$PTR bytes on the stack
- --> tests/ui/large_stack_frames.rs:27:4
+ --> tests/ui/large_stack_frames.rs:21:4
|
LL | fn many_small_arrays() {
| ^^^^^^^^^^^^^^^^^
@@ -13,7 +13,7 @@
= help: to override `-D warnings` add `#[allow(clippy::large_stack_frames)]`
error: this function may allocate 1000000 bytes on the stack
- --> tests/ui/large_stack_frames.rs:38:4
+ --> tests/ui/large_stack_frames.rs:32:4
|
LL | fn large_return_value() -> ArrayDefault<1_000_000> {
| ^^^^^^^^^^^^^^^^^^ ----------------------- this is the largest part, at 1000000 bytes for type `ArrayDefault<1000000>`
@@ -21,7 +21,7 @@
= note: 1000000 bytes is larger than Clippy's configured `stack-size-threshold` of 512000
error: this function may allocate 100$PTR bytes on the stack
- --> tests/ui/large_stack_frames.rs:44:4
+ --> tests/ui/large_stack_frames.rs:38:4
|
LL | fn large_fn_arg(x: ArrayDefault<1_000_000>) {
| ^^^^^^^^^^^^ - `x` is the largest part, at 1000000 bytes for type `ArrayDefault<1000000>`
@@ -29,7 +29,7 @@
= note: 100$PTR bytes is larger than Clippy's configured `stack-size-threshold` of 512000
error: this function may allocate 100$PTR bytes on the stack
- --> tests/ui/large_stack_frames.rs:51:13
+ --> tests/ui/large_stack_frames.rs:45:13
|
LL | let f = || black_box(&[0u8; 1_000_000]);
| ^^^^^^^^^^^^^^----------------^
diff --git a/tests/ui/let_unit.fixed b/tests/ui/let_unit.fixed
index 5e7a2ad..304eace 100644
--- a/tests/ui/let_unit.fixed
+++ b/tests/ui/let_unit.fixed
@@ -61,7 +61,7 @@
//~^ let_unit_value
.into_iter()
.map(|i| i * 2)
- .filter(|i| i % 2 == 0)
+ .filter(|i| i.is_multiple_of(2))
.map(|_| ())
.next()
.unwrap();
diff --git a/tests/ui/let_unit.rs b/tests/ui/let_unit.rs
index 7b06f69..a02cb34 100644
--- a/tests/ui/let_unit.rs
+++ b/tests/ui/let_unit.rs
@@ -61,7 +61,7 @@
//~^ let_unit_value
.into_iter()
.map(|i| i * 2)
- .filter(|i| i % 2 == 0)
+ .filter(|i| i.is_multiple_of(2))
.map(|_| ())
.next()
.unwrap();
diff --git a/tests/ui/let_unit.stderr b/tests/ui/let_unit.stderr
index d7d01d3..d743110 100644
--- a/tests/ui/let_unit.stderr
+++ b/tests/ui/let_unit.stderr
@@ -25,7 +25,7 @@
LL +
LL + .into_iter()
LL + .map(|i| i * 2)
-LL + .filter(|i| i % 2 == 0)
+LL + .filter(|i| i.is_multiple_of(2))
LL + .map(|_| ())
LL + .next()
LL + .unwrap();
diff --git a/tests/ui/manual_contains.fixed b/tests/ui/manual_contains.fixed
index d26c948..18171f0 100644
--- a/tests/ui/manual_contains.fixed
+++ b/tests/ui/manual_contains.fixed
@@ -58,7 +58,7 @@
let vec: Vec<u32> = vec![1, 2, 3, 4, 5, 6];
let values = &vec[..];
- let _ = values.iter().any(|&v| v % 2 == 0);
+ let _ = values.iter().any(|&v| v.is_multiple_of(2));
let _ = values.iter().any(|&v| v * 2 == 6);
let _ = values.iter().any(|&v| v == v);
let _ = values.iter().any(|&v| 4 == 4);
diff --git a/tests/ui/manual_contains.rs b/tests/ui/manual_contains.rs
index fe67d2e..918f4d6 100644
--- a/tests/ui/manual_contains.rs
+++ b/tests/ui/manual_contains.rs
@@ -58,7 +58,7 @@
let vec: Vec<u32> = vec![1, 2, 3, 4, 5, 6];
let values = &vec[..];
- let _ = values.iter().any(|&v| v % 2 == 0);
+ let _ = values.iter().any(|&v| v.is_multiple_of(2));
let _ = values.iter().any(|&v| v * 2 == 6);
let _ = values.iter().any(|&v| v == v);
let _ = values.iter().any(|&v| 4 == 4);
diff --git a/tests/ui/manual_find_fixable.fixed b/tests/ui/manual_find_fixable.fixed
index 01b3eba..c69b0cb 100644
--- a/tests/ui/manual_find_fixable.fixed
+++ b/tests/ui/manual_find_fixable.fixed
@@ -11,7 +11,7 @@
}
fn with_pat(arr: Vec<(u32, u32)>) -> Option<u32> {
- arr.into_iter().map(|(a, _)| a).find(|&a| a % 2 == 0)
+ arr.into_iter().map(|(a, _)| a).find(|&a| a.is_multiple_of(2))
}
struct Data {
@@ -63,7 +63,7 @@
fn with_else(arr: Vec<u32>) -> Option<u32> {
for el in arr {
- if el % 2 == 0 {
+ if el.is_multiple_of(2) {
return Some(el);
} else {
println!("{}", el);
diff --git a/tests/ui/manual_find_fixable.rs b/tests/ui/manual_find_fixable.rs
index ce62a4b..db7092f 100644
--- a/tests/ui/manual_find_fixable.rs
+++ b/tests/ui/manual_find_fixable.rs
@@ -19,7 +19,7 @@
fn with_pat(arr: Vec<(u32, u32)>) -> Option<u32> {
for (a, _) in arr {
//~^ manual_find
- if a % 2 == 0 {
+ if a.is_multiple_of(2) {
return Some(a);
}
}
@@ -111,7 +111,7 @@
fn with_else(arr: Vec<u32>) -> Option<u32> {
for el in arr {
- if el % 2 == 0 {
+ if el.is_multiple_of(2) {
return Some(el);
} else {
println!("{}", el);
diff --git a/tests/ui/manual_find_fixable.stderr b/tests/ui/manual_find_fixable.stderr
index 020635d..0c05c0d 100644
--- a/tests/ui/manual_find_fixable.stderr
+++ b/tests/ui/manual_find_fixable.stderr
@@ -17,11 +17,11 @@
|
LL | / for (a, _) in arr {
LL | |
-LL | | if a % 2 == 0 {
+LL | | if a.is_multiple_of(2) {
LL | | return Some(a);
... |
LL | | None
- | |________^ help: replace with an iterator: `arr.into_iter().map(|(a, _)| a).find(|&a| a % 2 == 0)`
+ | |________^ help: replace with an iterator: `arr.into_iter().map(|(a, _)| a).find(|&a| a.is_multiple_of(2))`
error: manual implementation of `Iterator::find`
--> tests/ui/manual_find_fixable.rs:34:5
diff --git a/tests/ui/manual_inspect.fixed b/tests/ui/manual_inspect.fixed
index 9b768db..00a1915 100644
--- a/tests/ui/manual_inspect.fixed
+++ b/tests/ui/manual_inspect.fixed
@@ -154,7 +154,6 @@
});
let _ = [0]
- //~^ suspicious_map
.into_iter()
.inspect(|&x| {
//~^ manual_inspect
diff --git a/tests/ui/manual_inspect.rs b/tests/ui/manual_inspect.rs
index e679636..b3b1713 100644
--- a/tests/ui/manual_inspect.rs
+++ b/tests/ui/manual_inspect.rs
@@ -165,7 +165,6 @@
});
let _ = [0]
- //~^ suspicious_map
.into_iter()
.map(|x| {
//~^ manual_inspect
diff --git a/tests/ui/manual_inspect.stderr b/tests/ui/manual_inspect.stderr
index 78b085f..70c00c1 100644
--- a/tests/ui/manual_inspect.stderr
+++ b/tests/ui/manual_inspect.stderr
@@ -157,25 +157,8 @@
LL ~ println!("{}", x);
|
-error: this call to `map()` won't have an effect on the call to `count()`
- --> tests/ui/manual_inspect.rs:167:13
- |
-LL | let _ = [0]
- | _____________^
-LL | |
-LL | | .into_iter()
-LL | | .map(|x| {
-... |
-LL | | })
-LL | | .count();
- | |________________^
- |
- = help: make sure you did not confuse `map` with `filter`, `for_each` or `inspect`
- = note: `-D clippy::suspicious-map` implied by `-D warnings`
- = help: to override `-D warnings` add `#[allow(clippy::suspicious_map)]`
-
error: using `map` over `inspect`
- --> tests/ui/manual_inspect.rs:170:10
+ --> tests/ui/manual_inspect.rs:169:10
|
LL | .map(|x| {
| ^^^
@@ -188,7 +171,7 @@
|
error: using `map` over `inspect`
- --> tests/ui/manual_inspect.rs:203:30
+ --> tests/ui/manual_inspect.rs:202:30
|
LL | if let Some(x) = Some(1).map(|x| { println!("{x}");
| ^^^
@@ -200,5 +183,5 @@
LL ~ }) {
|
-error: aborting due to 14 previous errors
+error: aborting due to 13 previous errors
diff --git a/tests/ui/manual_is_multiple_of.fixed b/tests/ui/manual_is_multiple_of.fixed
new file mode 100644
index 0000000..6735b99
--- /dev/null
+++ b/tests/ui/manual_is_multiple_of.fixed
@@ -0,0 +1,25 @@
+//@aux-build: proc_macros.rs
+#![warn(clippy::manual_is_multiple_of)]
+
+fn main() {}
+
+#[clippy::msrv = "1.87"]
+fn f(a: u64, b: u64) {
+ let _ = a.is_multiple_of(b); //~ manual_is_multiple_of
+ let _ = (a + 1).is_multiple_of(b + 1); //~ manual_is_multiple_of
+ let _ = !a.is_multiple_of(b); //~ manual_is_multiple_of
+ let _ = !(a + 1).is_multiple_of(b + 1); //~ manual_is_multiple_of
+
+ let _ = !a.is_multiple_of(b); //~ manual_is_multiple_of
+ let _ = !a.is_multiple_of(b); //~ manual_is_multiple_of
+
+ proc_macros::external! {
+ let a: u64 = 23424;
+ let _ = a % 4096 == 0;
+ }
+}
+
+#[clippy::msrv = "1.86"]
+fn g(a: u64, b: u64) {
+ let _ = a % b == 0;
+}
diff --git a/tests/ui/manual_is_multiple_of.rs b/tests/ui/manual_is_multiple_of.rs
new file mode 100644
index 0000000..00b638e
--- /dev/null
+++ b/tests/ui/manual_is_multiple_of.rs
@@ -0,0 +1,25 @@
+//@aux-build: proc_macros.rs
+#![warn(clippy::manual_is_multiple_of)]
+
+fn main() {}
+
+#[clippy::msrv = "1.87"]
+fn f(a: u64, b: u64) {
+ let _ = a % b == 0; //~ manual_is_multiple_of
+ let _ = (a + 1) % (b + 1) == 0; //~ manual_is_multiple_of
+ let _ = a % b != 0; //~ manual_is_multiple_of
+ let _ = (a + 1) % (b + 1) != 0; //~ manual_is_multiple_of
+
+ let _ = a % b > 0; //~ manual_is_multiple_of
+ let _ = 0 < a % b; //~ manual_is_multiple_of
+
+ proc_macros::external! {
+ let a: u64 = 23424;
+ let _ = a % 4096 == 0;
+ }
+}
+
+#[clippy::msrv = "1.86"]
+fn g(a: u64, b: u64) {
+ let _ = a % b == 0;
+}
diff --git a/tests/ui/manual_is_multiple_of.stderr b/tests/ui/manual_is_multiple_of.stderr
new file mode 100644
index 0000000..0b1ae70
--- /dev/null
+++ b/tests/ui/manual_is_multiple_of.stderr
@@ -0,0 +1,41 @@
+error: manual implementation of `.is_multiple_of()`
+ --> tests/ui/manual_is_multiple_of.rs:8:13
+ |
+LL | let _ = a % b == 0;
+ | ^^^^^^^^^^ help: replace with: `a.is_multiple_of(b)`
+ |
+ = note: `-D clippy::manual-is-multiple-of` implied by `-D warnings`
+ = help: to override `-D warnings` add `#[allow(clippy::manual_is_multiple_of)]`
+
+error: manual implementation of `.is_multiple_of()`
+ --> tests/ui/manual_is_multiple_of.rs:9:13
+ |
+LL | let _ = (a + 1) % (b + 1) == 0;
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `(a + 1).is_multiple_of(b + 1)`
+
+error: manual implementation of `.is_multiple_of()`
+ --> tests/ui/manual_is_multiple_of.rs:10:13
+ |
+LL | let _ = a % b != 0;
+ | ^^^^^^^^^^ help: replace with: `!a.is_multiple_of(b)`
+
+error: manual implementation of `.is_multiple_of()`
+ --> tests/ui/manual_is_multiple_of.rs:11:13
+ |
+LL | let _ = (a + 1) % (b + 1) != 0;
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `!(a + 1).is_multiple_of(b + 1)`
+
+error: manual implementation of `.is_multiple_of()`
+ --> tests/ui/manual_is_multiple_of.rs:13:13
+ |
+LL | let _ = a % b > 0;
+ | ^^^^^^^^^ help: replace with: `!a.is_multiple_of(b)`
+
+error: manual implementation of `.is_multiple_of()`
+ --> tests/ui/manual_is_multiple_of.rs:14:13
+ |
+LL | let _ = 0 < a % b;
+ | ^^^^^^^^^ help: replace with: `!a.is_multiple_of(b)`
+
+error: aborting due to 6 previous errors
+
diff --git a/tests/ui/manual_is_variant_and.fixed b/tests/ui/manual_is_variant_and.fixed
index 18a7218..6425f32 100644
--- a/tests/ui/manual_is_variant_and.fixed
+++ b/tests/ui/manual_is_variant_and.fixed
@@ -77,7 +77,7 @@
let _ = opt_map!(opt2, |x| x == 'a').unwrap_or_default(); // should not lint
// Should not lint.
- let _ = Foo::<u32>(0).map(|x| x % 2 == 0) == Some(true);
+ let _ = Foo::<u32>(0).map(|x| x.is_multiple_of(2)) == Some(true);
let _ = Some(2).map(|x| x % 2 == 0) != foo();
let _ = mac!(eq Some(2).map(|x| x % 2 == 0), Some(true));
let _ = mac!(some 2).map(|x| x % 2 == 0) == Some(true);
@@ -96,11 +96,11 @@
});
let _ = res.is_ok_and(|x| x > 1);
- let _ = Ok::<usize, ()>(2).is_ok_and(|x| x % 2 == 0);
+ let _ = Ok::<usize, ()>(2).is_ok_and(|x| x.is_multiple_of(2));
//~^ manual_is_variant_and
- let _ = !Ok::<usize, ()>(2).is_ok_and(|x| x % 2 == 0);
+ let _ = !Ok::<usize, ()>(2).is_ok_and(|x| x.is_multiple_of(2));
//~^ manual_is_variant_and
- let _ = !Ok::<usize, ()>(2).is_ok_and(|x| x % 2 == 0);
+ let _ = !Ok::<usize, ()>(2).is_ok_and(|x| x.is_multiple_of(2));
//~^ manual_is_variant_and
// won't fix because the return type of the closure is not `bool`
diff --git a/tests/ui/manual_is_variant_and.rs b/tests/ui/manual_is_variant_and.rs
index a92f7c0..e069e97 100644
--- a/tests/ui/manual_is_variant_and.rs
+++ b/tests/ui/manual_is_variant_and.rs
@@ -83,7 +83,7 @@
let _ = opt_map!(opt2, |x| x == 'a').unwrap_or_default(); // should not lint
// Should not lint.
- let _ = Foo::<u32>(0).map(|x| x % 2 == 0) == Some(true);
+ let _ = Foo::<u32>(0).map(|x| x.is_multiple_of(2)) == Some(true);
let _ = Some(2).map(|x| x % 2 == 0) != foo();
let _ = mac!(eq Some(2).map(|x| x % 2 == 0), Some(true));
let _ = mac!(some 2).map(|x| x % 2 == 0) == Some(true);
@@ -105,11 +105,11 @@
//~^ manual_is_variant_and
.unwrap_or_default();
- let _ = Ok::<usize, ()>(2).map(|x| x % 2 == 0) == Ok(true);
+ let _ = Ok::<usize, ()>(2).map(|x| x.is_multiple_of(2)) == Ok(true);
//~^ manual_is_variant_and
- let _ = Ok::<usize, ()>(2).map(|x| x % 2 == 0) != Ok(true);
+ let _ = Ok::<usize, ()>(2).map(|x| x.is_multiple_of(2)) != Ok(true);
//~^ manual_is_variant_and
- let _ = Ok::<usize, ()>(2).map(|x| x % 2 == 0) != Ok(true);
+ let _ = Ok::<usize, ()>(2).map(|x| x.is_multiple_of(2)) != Ok(true);
//~^ manual_is_variant_and
// won't fix because the return type of the closure is not `bool`
diff --git a/tests/ui/manual_is_variant_and.stderr b/tests/ui/manual_is_variant_and.stderr
index 1fb437a..f770319 100644
--- a/tests/ui/manual_is_variant_and.stderr
+++ b/tests/ui/manual_is_variant_and.stderr
@@ -105,20 +105,20 @@
error: called `.map() == Ok()`
--> tests/ui/manual_is_variant_and.rs:108:13
|
-LL | let _ = Ok::<usize, ()>(2).map(|x| x % 2 == 0) == Ok(true);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `Ok::<usize, ()>(2).is_ok_and(|x| x % 2 == 0)`
+LL | let _ = Ok::<usize, ()>(2).map(|x| x.is_multiple_of(2)) == Ok(true);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `Ok::<usize, ()>(2).is_ok_and(|x| x.is_multiple_of(2))`
error: called `.map() != Ok()`
--> tests/ui/manual_is_variant_and.rs:110:13
|
-LL | let _ = Ok::<usize, ()>(2).map(|x| x % 2 == 0) != Ok(true);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `!Ok::<usize, ()>(2).is_ok_and(|x| x % 2 == 0)`
+LL | let _ = Ok::<usize, ()>(2).map(|x| x.is_multiple_of(2)) != Ok(true);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `!Ok::<usize, ()>(2).is_ok_and(|x| x.is_multiple_of(2))`
error: called `.map() != Ok()`
--> tests/ui/manual_is_variant_and.rs:112:13
|
-LL | let _ = Ok::<usize, ()>(2).map(|x| x % 2 == 0) != Ok(true);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `!Ok::<usize, ()>(2).is_ok_and(|x| x % 2 == 0)`
+LL | let _ = Ok::<usize, ()>(2).map(|x| x.is_multiple_of(2)) != Ok(true);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `!Ok::<usize, ()>(2).is_ok_and(|x| x.is_multiple_of(2))`
error: called `map(<f>).unwrap_or_default()` on a `Result` value
--> tests/ui/manual_is_variant_and.rs:119:18
diff --git a/tests/ui/manual_ok_err.fixed b/tests/ui/manual_ok_err.fixed
index e6f799a..9b70ce0 100644
--- a/tests/ui/manual_ok_err.fixed
+++ b/tests/ui/manual_ok_err.fixed
@@ -103,3 +103,27 @@
};
//~^^^^^ manual_ok_err
}
+
+mod issue15051 {
+ struct Container {
+ field: Result<bool, ()>,
+ }
+
+ #[allow(clippy::needless_borrow)]
+ fn with_addr_of(x: &Container) -> Option<&bool> {
+ (&x.field).as_ref().ok()
+ }
+
+ fn from_fn(x: &Container) -> Option<&bool> {
+ let result_with_ref = || &x.field;
+ result_with_ref().as_ref().ok()
+ }
+
+ fn result_with_ref_mut(x: &mut Container) -> &mut Result<bool, ()> {
+ &mut x.field
+ }
+
+ fn from_fn_mut(x: &mut Container) -> Option<&mut bool> {
+ result_with_ref_mut(x).as_mut().ok()
+ }
+}
diff --git a/tests/ui/manual_ok_err.rs b/tests/ui/manual_ok_err.rs
index 972b2c4..dee9046 100644
--- a/tests/ui/manual_ok_err.rs
+++ b/tests/ui/manual_ok_err.rs
@@ -141,3 +141,39 @@
};
//~^^^^^ manual_ok_err
}
+
+mod issue15051 {
+ struct Container {
+ field: Result<bool, ()>,
+ }
+
+ #[allow(clippy::needless_borrow)]
+ fn with_addr_of(x: &Container) -> Option<&bool> {
+ match &x.field {
+ //~^ manual_ok_err
+ Ok(panel) => Some(panel),
+ Err(_) => None,
+ }
+ }
+
+ fn from_fn(x: &Container) -> Option<&bool> {
+ let result_with_ref = || &x.field;
+ match result_with_ref() {
+ //~^ manual_ok_err
+ Ok(panel) => Some(panel),
+ Err(_) => None,
+ }
+ }
+
+ fn result_with_ref_mut(x: &mut Container) -> &mut Result<bool, ()> {
+ &mut x.field
+ }
+
+ fn from_fn_mut(x: &mut Container) -> Option<&mut bool> {
+ match result_with_ref_mut(x) {
+ //~^ manual_ok_err
+ Ok(panel) => Some(panel),
+ Err(_) => None,
+ }
+ }
+}
diff --git a/tests/ui/manual_ok_err.stderr b/tests/ui/manual_ok_err.stderr
index 040e170..448fbff 100644
--- a/tests/ui/manual_ok_err.stderr
+++ b/tests/ui/manual_ok_err.stderr
@@ -111,5 +111,35 @@
LL ~ };
|
-error: aborting due to 9 previous errors
+error: manual implementation of `ok`
+ --> tests/ui/manual_ok_err.rs:152:9
+ |
+LL | / match &x.field {
+LL | |
+LL | | Ok(panel) => Some(panel),
+LL | | Err(_) => None,
+LL | | }
+ | |_________^ help: replace with: `(&x.field).as_ref().ok()`
+
+error: manual implementation of `ok`
+ --> tests/ui/manual_ok_err.rs:161:9
+ |
+LL | / match result_with_ref() {
+LL | |
+LL | | Ok(panel) => Some(panel),
+LL | | Err(_) => None,
+LL | | }
+ | |_________^ help: replace with: `result_with_ref().as_ref().ok()`
+
+error: manual implementation of `ok`
+ --> tests/ui/manual_ok_err.rs:173:9
+ |
+LL | / match result_with_ref_mut(x) {
+LL | |
+LL | | Ok(panel) => Some(panel),
+LL | | Err(_) => None,
+LL | | }
+ | |_________^ help: replace with: `result_with_ref_mut(x).as_mut().ok()`
+
+error: aborting due to 12 previous errors
diff --git a/tests/ui/missing_const_for_fn/const_trait.fixed b/tests/ui/missing_const_for_fn/const_trait.fixed
index 7e0d4fc..f1d5579 100644
--- a/tests/ui/missing_const_for_fn/const_trait.fixed
+++ b/tests/ui/missing_const_for_fn/const_trait.fixed
@@ -25,7 +25,7 @@
0u64.method();
}
-// False negative, see FIXME comment in `clipy_utils::qualify_min_const`
+// False negative, see FIXME comment in `clippy_utils::qualify_min_const_fn`
fn could_be_const_but_does_not_trigger<T>(t: T)
where
T: const ConstTrait,
diff --git a/tests/ui/missing_const_for_fn/const_trait.rs b/tests/ui/missing_const_for_fn/const_trait.rs
index 439da46..d495759 100644
--- a/tests/ui/missing_const_for_fn/const_trait.rs
+++ b/tests/ui/missing_const_for_fn/const_trait.rs
@@ -25,7 +25,7 @@
0u64.method();
}
-// False negative, see FIXME comment in `clipy_utils::qualify_min_const`
+// False negative, see FIXME comment in `clippy_utils::qualify_min_const_fn`
fn could_be_const_but_does_not_trigger<T>(t: T)
where
T: const ConstTrait,
diff --git a/tests/ui/missing_const_for_fn/could_be_const.fixed b/tests/ui/missing_const_for_fn/could_be_const.fixed
index 65eb2d5..95bf63e 100644
--- a/tests/ui/missing_const_for_fn/could_be_const.fixed
+++ b/tests/ui/missing_const_for_fn/could_be_const.fixed
@@ -221,3 +221,60 @@
//~^ missing_const_for_fn
*x += 1;
}
+
+mod issue_15079 {
+ pub trait Trait {}
+
+ pub struct Struct<T: Trait> {
+ _t: Option<T>,
+ }
+
+ impl<T: Trait> Struct<T> {
+ #[clippy::msrv = "1.60"]
+ pub fn new_1_60() -> Self {
+ Self { _t: None }
+ }
+
+ #[clippy::msrv = "1.61"]
+ pub const fn new_1_61() -> Self {
+ //~^ missing_const_for_fn
+ Self { _t: None }
+ }
+ }
+
+ pub struct S2<T> {
+ _t: Option<T>,
+ }
+
+ impl<T> S2<T> {
+ #[clippy::msrv = "1.60"]
+ pub const fn new_1_60() -> Self {
+ //~^ missing_const_for_fn
+ Self { _t: None }
+ }
+
+ #[clippy::msrv = "1.61"]
+ pub const fn new_1_61() -> Self {
+ //~^ missing_const_for_fn
+ Self { _t: None }
+ }
+ }
+
+ pub struct S3<T: ?Sized + 'static> {
+ _t: Option<&'static T>,
+ }
+
+ impl<T: ?Sized + 'static> S3<T> {
+ #[clippy::msrv = "1.60"]
+ pub const fn new_1_60() -> Self {
+ //~^ missing_const_for_fn
+ Self { _t: None }
+ }
+
+ #[clippy::msrv = "1.61"]
+ pub const fn new_1_61() -> Self {
+ //~^ missing_const_for_fn
+ Self { _t: None }
+ }
+ }
+}
diff --git a/tests/ui/missing_const_for_fn/could_be_const.rs b/tests/ui/missing_const_for_fn/could_be_const.rs
index 3690d2f..8290be6 100644
--- a/tests/ui/missing_const_for_fn/could_be_const.rs
+++ b/tests/ui/missing_const_for_fn/could_be_const.rs
@@ -221,3 +221,60 @@
//~^ missing_const_for_fn
*x += 1;
}
+
+mod issue_15079 {
+ pub trait Trait {}
+
+ pub struct Struct<T: Trait> {
+ _t: Option<T>,
+ }
+
+ impl<T: Trait> Struct<T> {
+ #[clippy::msrv = "1.60"]
+ pub fn new_1_60() -> Self {
+ Self { _t: None }
+ }
+
+ #[clippy::msrv = "1.61"]
+ pub fn new_1_61() -> Self {
+ //~^ missing_const_for_fn
+ Self { _t: None }
+ }
+ }
+
+ pub struct S2<T> {
+ _t: Option<T>,
+ }
+
+ impl<T> S2<T> {
+ #[clippy::msrv = "1.60"]
+ pub fn new_1_60() -> Self {
+ //~^ missing_const_for_fn
+ Self { _t: None }
+ }
+
+ #[clippy::msrv = "1.61"]
+ pub fn new_1_61() -> Self {
+ //~^ missing_const_for_fn
+ Self { _t: None }
+ }
+ }
+
+ pub struct S3<T: ?Sized + 'static> {
+ _t: Option<&'static T>,
+ }
+
+ impl<T: ?Sized + 'static> S3<T> {
+ #[clippy::msrv = "1.60"]
+ pub fn new_1_60() -> Self {
+ //~^ missing_const_for_fn
+ Self { _t: None }
+ }
+
+ #[clippy::msrv = "1.61"]
+ pub fn new_1_61() -> Self {
+ //~^ missing_const_for_fn
+ Self { _t: None }
+ }
+ }
+}
diff --git a/tests/ui/missing_const_for_fn/could_be_const.stderr b/tests/ui/missing_const_for_fn/could_be_const.stderr
index 10e07d1..17cbc43 100644
--- a/tests/ui/missing_const_for_fn/could_be_const.stderr
+++ b/tests/ui/missing_const_for_fn/could_be_const.stderr
@@ -332,5 +332,75 @@
LL | const fn mut_add(x: &mut i32) {
| +++++
-error: aborting due to 25 previous errors
+error: this could be a `const fn`
+ --> tests/ui/missing_const_for_fn/could_be_const.rs:239:9
+ |
+LL | / pub fn new_1_61() -> Self {
+LL | |
+LL | | Self { _t: None }
+LL | | }
+ | |_________^
+ |
+help: make the function `const`
+ |
+LL | pub const fn new_1_61() -> Self {
+ | +++++
+
+error: this could be a `const fn`
+ --> tests/ui/missing_const_for_fn/could_be_const.rs:251:9
+ |
+LL | / pub fn new_1_60() -> Self {
+LL | |
+LL | | Self { _t: None }
+LL | | }
+ | |_________^
+ |
+help: make the function `const`
+ |
+LL | pub const fn new_1_60() -> Self {
+ | +++++
+
+error: this could be a `const fn`
+ --> tests/ui/missing_const_for_fn/could_be_const.rs:257:9
+ |
+LL | / pub fn new_1_61() -> Self {
+LL | |
+LL | | Self { _t: None }
+LL | | }
+ | |_________^
+ |
+help: make the function `const`
+ |
+LL | pub const fn new_1_61() -> Self {
+ | +++++
+
+error: this could be a `const fn`
+ --> tests/ui/missing_const_for_fn/could_be_const.rs:269:9
+ |
+LL | / pub fn new_1_60() -> Self {
+LL | |
+LL | | Self { _t: None }
+LL | | }
+ | |_________^
+ |
+help: make the function `const`
+ |
+LL | pub const fn new_1_60() -> Self {
+ | +++++
+
+error: this could be a `const fn`
+ --> tests/ui/missing_const_for_fn/could_be_const.rs:275:9
+ |
+LL | / pub fn new_1_61() -> Self {
+LL | |
+LL | | Self { _t: None }
+LL | | }
+ | |_________^
+ |
+help: make the function `const`
+ |
+LL | pub const fn new_1_61() -> Self {
+ | +++++
+
+error: aborting due to 30 previous errors
diff --git a/tests/ui/missing_panics_doc.rs b/tests/ui/missing_panics_doc.rs
index ffdae85..d016e09 100644
--- a/tests/ui/missing_panics_doc.rs
+++ b/tests/ui/missing_panics_doc.rs
@@ -250,3 +250,31 @@
}
}
}
+
+/// This needs documenting
+pub fn unwrap_expect_etc_in_const() {
+ let a = const { std::num::NonZeroUsize::new(1).unwrap() };
+ // This should still pass the lint even if it is guaranteed to panic at compile-time
+ let b = const { std::num::NonZeroUsize::new(0).unwrap() };
+}
+
+/// This needs documenting
+pub const fn unwrap_expect_etc_in_const_fn_fails() {
+ //~^ missing_panics_doc
+ let a = std::num::NonZeroUsize::new(1).unwrap();
+}
+
+/// This needs documenting
+pub const fn assert_in_const_fn_fails() {
+ //~^ missing_panics_doc
+ let x = 0;
+ if x == 0 {
+ panic!();
+ }
+}
+
+/// This needs documenting
+pub const fn in_const_fn<const N: usize>(n: usize) {
+ //~^ missing_panics_doc
+ assert!(N > n);
+}
diff --git a/tests/ui/missing_panics_doc.stderr b/tests/ui/missing_panics_doc.stderr
index 7f0acf8..85a0091 100644
--- a/tests/ui/missing_panics_doc.stderr
+++ b/tests/ui/missing_panics_doc.stderr
@@ -180,5 +180,41 @@
LL | *v.last().expect("passed an empty thing")
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-error: aborting due to 15 previous errors
+error: docs for function which may panic missing `# Panics` section
+ --> tests/ui/missing_panics_doc.rs:262:1
+ |
+LL | pub const fn unwrap_expect_etc_in_const_fn_fails() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: first possible panic found here
+ --> tests/ui/missing_panics_doc.rs:264:13
+ |
+LL | let a = std::num::NonZeroUsize::new(1).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: docs for function which may panic missing `# Panics` section
+ --> tests/ui/missing_panics_doc.rs:268:1
+ |
+LL | pub const fn assert_in_const_fn_fails() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: first possible panic found here
+ --> tests/ui/missing_panics_doc.rs:272:9
+ |
+LL | panic!();
+ | ^^^^^^^^
+
+error: docs for function which may panic missing `# Panics` section
+ --> tests/ui/missing_panics_doc.rs:277:1
+ |
+LL | pub const fn in_const_fn<const N: usize>(n: usize) {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: first possible panic found here
+ --> tests/ui/missing_panics_doc.rs:279:5
+ |
+LL | assert!(N > n);
+ | ^^^^^^^^^^^^^^
+
+error: aborting due to 18 previous errors
diff --git a/tests/ui/nonminimal_bool.stderr b/tests/ui/nonminimal_bool.stderr
index 0e3e4cf..ecb82a2 100644
--- a/tests/ui/nonminimal_bool.stderr
+++ b/tests/ui/nonminimal_bool.stderr
@@ -179,7 +179,7 @@
--> tests/ui/nonminimal_bool.rs:186:8
|
LL | if !b != true {}
- | ^^^^^^^^^^ help: try simplifying it as shown: `!(!b)`
+ | ^^^^^^^^^^ help: try simplifying it as shown: `!!b`
error: this boolean expression can be simplified
--> tests/ui/nonminimal_bool.rs:189:8
@@ -209,7 +209,7 @@
--> tests/ui/nonminimal_bool.rs:193:8
|
LL | if true != !b {}
- | ^^^^^^^^^^ help: try simplifying it as shown: `!(!b)`
+ | ^^^^^^^^^^ help: try simplifying it as shown: `!!b`
error: this boolean expression can be simplified
--> tests/ui/nonminimal_bool.rs:196:8
diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed
index a1119d7..34f3e04 100644
--- a/tests/ui/or_fun_call.fixed
+++ b/tests/ui/or_fun_call.fixed
@@ -5,6 +5,7 @@
clippy::uninlined_format_args,
clippy::unnecessary_wraps,
clippy::unnecessary_literal_unwrap,
+ clippy::unnecessary_result_map_or_else,
clippy::useless_vec
)]
@@ -409,4 +410,33 @@
//~^ or_fun_call
}
+mod result_map_or {
+ fn g() -> i32 {
+ 3
+ }
+
+ fn f(n: i32) -> i32 {
+ n
+ }
+
+ fn test_map_or() {
+ let x: Result<i32, ()> = Ok(4);
+ let _ = x.map_or_else(|_| g(), |v| v);
+ //~^ or_fun_call
+ let _ = x.map_or_else(|_| g(), f);
+ //~^ or_fun_call
+ let _ = x.map_or(0, f);
+ }
+}
+
+fn test_option_get_or_insert() {
+ // assume that this is slow call
+ fn g() -> u8 {
+ 99
+ }
+ let mut x = Some(42_u8);
+ let _ = x.get_or_insert_with(g);
+ //~^ or_fun_call
+}
+
fn main() {}
diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs
index a7cd632..dc57bd6 100644
--- a/tests/ui/or_fun_call.rs
+++ b/tests/ui/or_fun_call.rs
@@ -5,6 +5,7 @@
clippy::uninlined_format_args,
clippy::unnecessary_wraps,
clippy::unnecessary_literal_unwrap,
+ clippy::unnecessary_result_map_or_else,
clippy::useless_vec
)]
@@ -409,4 +410,33 @@
//~^ or_fun_call
}
+mod result_map_or {
+ fn g() -> i32 {
+ 3
+ }
+
+ fn f(n: i32) -> i32 {
+ n
+ }
+
+ fn test_map_or() {
+ let x: Result<i32, ()> = Ok(4);
+ let _ = x.map_or(g(), |v| v);
+ //~^ or_fun_call
+ let _ = x.map_or(g(), f);
+ //~^ or_fun_call
+ let _ = x.map_or(0, f);
+ }
+}
+
+fn test_option_get_or_insert() {
+ // assume that this is slow call
+ fn g() -> u8 {
+ 99
+ }
+ let mut x = Some(42_u8);
+ let _ = x.get_or_insert(g());
+ //~^ or_fun_call
+}
+
fn main() {}
diff --git a/tests/ui/or_fun_call.stderr b/tests/ui/or_fun_call.stderr
index 35bda7e..0f159fe 100644
--- a/tests/ui/or_fun_call.stderr
+++ b/tests/ui/or_fun_call.stderr
@@ -1,5 +1,5 @@
error: function call inside of `unwrap_or`
- --> tests/ui/or_fun_call.rs:52:22
+ --> tests/ui/or_fun_call.rs:53:22
|
LL | with_constructor.unwrap_or(make());
| ^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(make)`
@@ -8,7 +8,7 @@
= help: to override `-D warnings` add `#[allow(clippy::or_fun_call)]`
error: use of `unwrap_or` to construct default value
- --> tests/ui/or_fun_call.rs:56:14
+ --> tests/ui/or_fun_call.rs:57:14
|
LL | with_new.unwrap_or(Vec::new());
| ^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()`
@@ -17,199 +17,199 @@
= help: to override `-D warnings` add `#[allow(clippy::unwrap_or_default)]`
error: function call inside of `unwrap_or`
- --> tests/ui/or_fun_call.rs:60:21
+ --> tests/ui/or_fun_call.rs:61:21
|
LL | with_const_args.unwrap_or(Vec::with_capacity(12));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| Vec::with_capacity(12))`
error: function call inside of `unwrap_or`
- --> tests/ui/or_fun_call.rs:64:14
+ --> tests/ui/or_fun_call.rs:65:14
|
LL | with_err.unwrap_or(make());
| ^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|_| make())`
error: function call inside of `unwrap_or`
- --> tests/ui/or_fun_call.rs:68:19
+ --> tests/ui/or_fun_call.rs:69:19
|
LL | with_err_args.unwrap_or(Vec::with_capacity(12));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|_| Vec::with_capacity(12))`
error: use of `unwrap_or` to construct default value
- --> tests/ui/or_fun_call.rs:72:24
+ --> tests/ui/or_fun_call.rs:73:24
|
LL | with_default_trait.unwrap_or(Default::default());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()`
error: use of `unwrap_or` to construct default value
- --> tests/ui/or_fun_call.rs:76:23
+ --> tests/ui/or_fun_call.rs:77:23
|
LL | with_default_type.unwrap_or(u64::default());
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()`
error: function call inside of `unwrap_or`
- --> tests/ui/or_fun_call.rs:80:18
+ --> tests/ui/or_fun_call.rs:81:18
|
LL | self_default.unwrap_or(<FakeDefault>::default());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(<FakeDefault>::default)`
error: use of `unwrap_or` to construct default value
- --> tests/ui/or_fun_call.rs:84:18
+ --> tests/ui/or_fun_call.rs:85:18
|
LL | real_default.unwrap_or(<FakeDefault as Default>::default());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()`
error: use of `unwrap_or` to construct default value
- --> tests/ui/or_fun_call.rs:88:14
+ --> tests/ui/or_fun_call.rs:89:14
|
LL | with_vec.unwrap_or(vec![]);
| ^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()`
error: function call inside of `unwrap_or`
- --> tests/ui/or_fun_call.rs:92:21
+ --> tests/ui/or_fun_call.rs:93:21
|
LL | without_default.unwrap_or(Foo::new());
| ^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(Foo::new)`
error: use of `or_insert` to construct default value
- --> tests/ui/or_fun_call.rs:96:19
+ --> tests/ui/or_fun_call.rs:97:19
|
LL | map.entry(42).or_insert(String::new());
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()`
error: use of `or_insert` to construct default value
- --> tests/ui/or_fun_call.rs:100:23
+ --> tests/ui/or_fun_call.rs:101:23
|
LL | map_vec.entry(42).or_insert(vec![]);
| ^^^^^^^^^^^^^^^^^ help: try: `or_default()`
error: use of `or_insert` to construct default value
- --> tests/ui/or_fun_call.rs:104:21
+ --> tests/ui/or_fun_call.rs:105:21
|
LL | btree.entry(42).or_insert(String::new());
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()`
error: use of `or_insert` to construct default value
- --> tests/ui/or_fun_call.rs:108:25
+ --> tests/ui/or_fun_call.rs:109:25
|
LL | btree_vec.entry(42).or_insert(vec![]);
| ^^^^^^^^^^^^^^^^^ help: try: `or_default()`
error: use of `unwrap_or` to construct default value
- --> tests/ui/or_fun_call.rs:112:21
+ --> tests/ui/or_fun_call.rs:113:21
|
LL | let _ = stringy.unwrap_or(String::new());
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()`
error: function call inside of `ok_or`
- --> tests/ui/or_fun_call.rs:117:17
+ --> tests/ui/or_fun_call.rs:118:17
|
LL | let _ = opt.ok_or(format!("{} world.", hello));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ok_or_else(|| format!("{} world.", hello))`
error: function call inside of `unwrap_or`
- --> tests/ui/or_fun_call.rs:122:21
+ --> tests/ui/or_fun_call.rs:123:21
|
LL | let _ = Some(1).unwrap_or(map[&1]);
| ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| map[&1])`
error: function call inside of `unwrap_or`
- --> tests/ui/or_fun_call.rs:125:21
+ --> tests/ui/or_fun_call.rs:126:21
|
LL | let _ = Some(1).unwrap_or(map[&1]);
| ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| map[&1])`
error: function call inside of `or`
- --> tests/ui/or_fun_call.rs:150:35
+ --> tests/ui/or_fun_call.rs:151:35
|
LL | let _ = Some("a".to_string()).or(Some("b".to_string()));
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_else(|| Some("b".to_string()))`
error: function call inside of `unwrap_or`
- --> tests/ui/or_fun_call.rs:193:18
+ --> tests/ui/or_fun_call.rs:194:18
|
LL | None.unwrap_or(ptr_to_ref(s));
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| ptr_to_ref(s))`
error: function call inside of `unwrap_or`
- --> tests/ui/or_fun_call.rs:201:14
+ --> tests/ui/or_fun_call.rs:202:14
|
LL | None.unwrap_or(unsafe { ptr_to_ref(s) });
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })`
error: function call inside of `unwrap_or`
- --> tests/ui/or_fun_call.rs:204:14
+ --> tests/ui/or_fun_call.rs:205:14
|
LL | None.unwrap_or( unsafe { ptr_to_ref(s) } );
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })`
error: function call inside of `map_or`
- --> tests/ui/or_fun_call.rs:280:25
+ --> tests/ui/or_fun_call.rs:281:25
|
LL | let _ = Some(4).map_or(g(), |v| v);
| ^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(g, |v| v)`
error: function call inside of `map_or`
- --> tests/ui/or_fun_call.rs:282:25
+ --> tests/ui/or_fun_call.rs:283:25
|
LL | let _ = Some(4).map_or(g(), f);
| ^^^^^^^^^^^^^^ help: try: `map_or_else(g, f)`
error: use of `unwrap_or_else` to construct default value
- --> tests/ui/or_fun_call.rs:314:18
+ --> tests/ui/or_fun_call.rs:315:18
|
LL | with_new.unwrap_or_else(Vec::new);
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()`
error: use of `unwrap_or_else` to construct default value
- --> tests/ui/or_fun_call.rs:318:28
+ --> tests/ui/or_fun_call.rs:319:28
|
LL | with_default_trait.unwrap_or_else(Default::default);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()`
error: use of `unwrap_or_else` to construct default value
- --> tests/ui/or_fun_call.rs:322:27
+ --> tests/ui/or_fun_call.rs:323:27
|
LL | with_default_type.unwrap_or_else(u64::default);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()`
error: use of `unwrap_or_else` to construct default value
- --> tests/ui/or_fun_call.rs:326:22
+ --> tests/ui/or_fun_call.rs:327:22
|
LL | real_default.unwrap_or_else(<FakeDefault as Default>::default);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()`
error: use of `or_insert_with` to construct default value
- --> tests/ui/or_fun_call.rs:330:23
+ --> tests/ui/or_fun_call.rs:331:23
|
LL | map.entry(42).or_insert_with(String::new);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()`
error: use of `or_insert_with` to construct default value
- --> tests/ui/or_fun_call.rs:334:25
+ --> tests/ui/or_fun_call.rs:335:25
|
LL | btree.entry(42).or_insert_with(String::new);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()`
error: use of `unwrap_or_else` to construct default value
- --> tests/ui/or_fun_call.rs:338:25
+ --> tests/ui/or_fun_call.rs:339:25
|
LL | let _ = stringy.unwrap_or_else(String::new);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()`
error: function call inside of `unwrap_or`
- --> tests/ui/or_fun_call.rs:380:17
+ --> tests/ui/or_fun_call.rs:381:17
|
LL | let _ = opt.unwrap_or({ f() }); // suggest `.unwrap_or_else(f)`
| ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(f)`
error: function call inside of `unwrap_or`
- --> tests/ui/or_fun_call.rs:385:17
+ --> tests/ui/or_fun_call.rs:386:17
|
LL | let _ = opt.unwrap_or(f() + 1); // suggest `.unwrap_or_else(|| f() + 1)`
| ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| f() + 1)`
error: function call inside of `unwrap_or`
- --> tests/ui/or_fun_call.rs:390:17
+ --> tests/ui/or_fun_call.rs:391:17
|
LL | let _ = opt.unwrap_or({
| _________________^
@@ -229,22 +229,40 @@
|
error: function call inside of `map_or`
- --> tests/ui/or_fun_call.rs:396:17
+ --> tests/ui/or_fun_call.rs:397:17
|
LL | let _ = opt.map_or(f() + 1, |v| v); // suggest `.map_or_else(|| f() + 1, |v| v)`
| ^^^^^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|| f() + 1, |v| v)`
error: use of `unwrap_or` to construct default value
- --> tests/ui/or_fun_call.rs:401:17
+ --> tests/ui/or_fun_call.rs:402:17
|
LL | let _ = opt.unwrap_or({ i32::default() });
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()`
error: function call inside of `unwrap_or`
- --> tests/ui/or_fun_call.rs:408:21
+ --> tests/ui/or_fun_call.rs:409:21
|
LL | let _ = opt_foo.unwrap_or(Foo { val: String::default() });
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| Foo { val: String::default() })`
-error: aborting due to 38 previous errors
+error: function call inside of `map_or`
+ --> tests/ui/or_fun_call.rs:424:19
+ |
+LL | let _ = x.map_or(g(), |v| v);
+ | ^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|_| g(), |v| v)`
+
+error: function call inside of `map_or`
+ --> tests/ui/or_fun_call.rs:426:19
+ |
+LL | let _ = x.map_or(g(), f);
+ | ^^^^^^^^^^^^^^ help: try: `map_or_else(|_| g(), f)`
+
+error: function call inside of `get_or_insert`
+ --> tests/ui/or_fun_call.rs:438:15
+ |
+LL | let _ = x.get_or_insert(g());
+ | ^^^^^^^^^^^^^^^^^^ help: try: `get_or_insert_with(g)`
+
+error: aborting due to 41 previous errors
diff --git a/tests/ui/question_mark.fixed b/tests/ui/question_mark.fixed
index 60dc1c1..8d6f5fb 100644
--- a/tests/ui/question_mark.fixed
+++ b/tests/ui/question_mark.fixed
@@ -453,3 +453,15 @@
None
}
+
+fn issue_13642(x: Option<i32>) -> Option<()> {
+ let Some(x) = x else {
+ #[cfg(false)]
+ panic!();
+
+ #[cfg(true)]
+ return None;
+ };
+
+ None
+}
diff --git a/tests/ui/question_mark.rs b/tests/ui/question_mark.rs
index 99d0122..f13eee2 100644
--- a/tests/ui/question_mark.rs
+++ b/tests/ui/question_mark.rs
@@ -549,3 +549,15 @@
None
}
+
+fn issue_13642(x: Option<i32>) -> Option<()> {
+ let Some(x) = x else {
+ #[cfg(false)]
+ panic!();
+
+ #[cfg(true)]
+ return None;
+ };
+
+ None
+}
diff --git a/tests/ui/single_range_in_vec_init.rs b/tests/ui/single_range_in_vec_init.rs
index 2588445..0888019 100644
--- a/tests/ui/single_range_in_vec_init.rs
+++ b/tests/ui/single_range_in_vec_init.rs
@@ -2,7 +2,6 @@
//@no-rustfix: overlapping suggestions
#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::useless_vec, unused)]
#![warn(clippy::single_range_in_vec_init)]
-#![feature(generic_arg_infer)]
#[macro_use]
extern crate proc_macros;
diff --git a/tests/ui/single_range_in_vec_init.stderr b/tests/ui/single_range_in_vec_init.stderr
index a99127a..b21338e 100644
--- a/tests/ui/single_range_in_vec_init.stderr
+++ b/tests/ui/single_range_in_vec_init.stderr
@@ -1,5 +1,5 @@
error: an array of `Range` that is only one element
- --> tests/ui/single_range_in_vec_init.rs:26:5
+ --> tests/ui/single_range_in_vec_init.rs:25:5
|
LL | [0..200];
| ^^^^^^^^
@@ -18,7 +18,7 @@
|
error: a `Vec` of `Range` that is only one element
- --> tests/ui/single_range_in_vec_init.rs:28:5
+ --> tests/ui/single_range_in_vec_init.rs:27:5
|
LL | vec![0..200];
| ^^^^^^^^^^^^
@@ -35,7 +35,7 @@
|
error: an array of `Range` that is only one element
- --> tests/ui/single_range_in_vec_init.rs:30:5
+ --> tests/ui/single_range_in_vec_init.rs:29:5
|
LL | [0u8..200];
| ^^^^^^^^^^
@@ -52,7 +52,7 @@
|
error: an array of `Range` that is only one element
- --> tests/ui/single_range_in_vec_init.rs:32:5
+ --> tests/ui/single_range_in_vec_init.rs:31:5
|
LL | [0usize..200];
| ^^^^^^^^^^^^^
@@ -69,7 +69,7 @@
|
error: an array of `Range` that is only one element
- --> tests/ui/single_range_in_vec_init.rs:34:5
+ --> tests/ui/single_range_in_vec_init.rs:33:5
|
LL | [0..200usize];
| ^^^^^^^^^^^^^
@@ -86,7 +86,7 @@
|
error: a `Vec` of `Range` that is only one element
- --> tests/ui/single_range_in_vec_init.rs:36:5
+ --> tests/ui/single_range_in_vec_init.rs:35:5
|
LL | vec![0u8..200];
| ^^^^^^^^^^^^^^
@@ -103,7 +103,7 @@
|
error: a `Vec` of `Range` that is only one element
- --> tests/ui/single_range_in_vec_init.rs:38:5
+ --> tests/ui/single_range_in_vec_init.rs:37:5
|
LL | vec![0usize..200];
| ^^^^^^^^^^^^^^^^^
@@ -120,7 +120,7 @@
|
error: a `Vec` of `Range` that is only one element
- --> tests/ui/single_range_in_vec_init.rs:40:5
+ --> tests/ui/single_range_in_vec_init.rs:39:5
|
LL | vec![0..200usize];
| ^^^^^^^^^^^^^^^^^
@@ -137,7 +137,7 @@
|
error: an array of `Range` that is only one element
- --> tests/ui/single_range_in_vec_init.rs:43:5
+ --> tests/ui/single_range_in_vec_init.rs:42:5
|
LL | [0..200isize];
| ^^^^^^^^^^^^^
@@ -149,7 +149,7 @@
|
error: a `Vec` of `Range` that is only one element
- --> tests/ui/single_range_in_vec_init.rs:45:5
+ --> tests/ui/single_range_in_vec_init.rs:44:5
|
LL | vec![0..200isize];
| ^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/swap_with_temporary.fixed b/tests/ui/swap_with_temporary.fixed
index 4007d99..4b4b0d4 100644
--- a/tests/ui/swap_with_temporary.fixed
+++ b/tests/ui/swap_with_temporary.fixed
@@ -72,3 +72,49 @@
swap(&mut s.t, v.get_mut(0).unwrap());
swap(w.unwrap(), &mut s.t);
}
+
+fn issue15166() {
+ use std::sync::Mutex;
+
+ struct A {
+ thing: Mutex<Vec<u8>>,
+ }
+
+ impl A {
+ fn a(&self) {
+ let mut new_vec = vec![42];
+ // Do not lint here, as neither `new_vec` nor the result of `.lock().unwrap()` are temporaries
+ swap(&mut new_vec, &mut self.thing.lock().unwrap());
+ for v in new_vec {
+ // Do something with v
+ }
+ // Here `vec![42]` is temporary though, and a proper dereference will have to be used in the fix
+ *self.thing.lock().unwrap() = vec![42];
+ //~^ ERROR: swapping with a temporary value is inefficient
+ }
+ }
+}
+
+fn multiple_deref() {
+ let mut v1 = &mut &mut &mut vec![42];
+ ***v1 = vec![];
+ //~^ ERROR: swapping with a temporary value is inefficient
+
+ struct Wrapper<T: ?Sized>(T);
+ impl<T: ?Sized> std::ops::Deref for Wrapper<T> {
+ type Target = T;
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+ }
+ impl<T: ?Sized> std::ops::DerefMut for Wrapper<T> {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.0
+ }
+ }
+
+ use std::sync::Mutex;
+ let mut v1 = Mutex::new(Wrapper(Wrapper(vec![42])));
+ ***v1.lock().unwrap() = vec![];
+ //~^ ERROR: swapping with a temporary value is inefficient
+}
diff --git a/tests/ui/swap_with_temporary.rs b/tests/ui/swap_with_temporary.rs
index d403c08..8e35e61 100644
--- a/tests/ui/swap_with_temporary.rs
+++ b/tests/ui/swap_with_temporary.rs
@@ -72,3 +72,49 @@
swap(&mut s.t, v.get_mut(0).unwrap());
swap(w.unwrap(), &mut s.t);
}
+
+fn issue15166() {
+ use std::sync::Mutex;
+
+ struct A {
+ thing: Mutex<Vec<u8>>,
+ }
+
+ impl A {
+ fn a(&self) {
+ let mut new_vec = vec![42];
+ // Do not lint here, as neither `new_vec` nor the result of `.lock().unwrap()` are temporaries
+ swap(&mut new_vec, &mut self.thing.lock().unwrap());
+ for v in new_vec {
+ // Do something with v
+ }
+ // Here `vec![42]` is temporary though, and a proper dereference will have to be used in the fix
+ swap(&mut vec![42], &mut self.thing.lock().unwrap());
+ //~^ ERROR: swapping with a temporary value is inefficient
+ }
+ }
+}
+
+fn multiple_deref() {
+ let mut v1 = &mut &mut &mut vec![42];
+ swap(&mut ***v1, &mut vec![]);
+ //~^ ERROR: swapping with a temporary value is inefficient
+
+ struct Wrapper<T: ?Sized>(T);
+ impl<T: ?Sized> std::ops::Deref for Wrapper<T> {
+ type Target = T;
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+ }
+ impl<T: ?Sized> std::ops::DerefMut for Wrapper<T> {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.0
+ }
+ }
+
+ use std::sync::Mutex;
+ let mut v1 = Mutex::new(Wrapper(Wrapper(vec![42])));
+ swap(&mut vec![], &mut v1.lock().unwrap());
+ //~^ ERROR: swapping with a temporary value is inefficient
+}
diff --git a/tests/ui/swap_with_temporary.stderr b/tests/ui/swap_with_temporary.stderr
index 5935577..5ca4fcc 100644
--- a/tests/ui/swap_with_temporary.stderr
+++ b/tests/ui/swap_with_temporary.stderr
@@ -96,5 +96,41 @@
LL | swap(mac!(refmut y), &mut func());
| ^^^^^^
-error: aborting due to 8 previous errors
+error: swapping with a temporary value is inefficient
+ --> tests/ui/swap_with_temporary.rs:92:13
+ |
+LL | swap(&mut vec![42], &mut self.thing.lock().unwrap());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use assignment instead: `*self.thing.lock().unwrap() = vec![42]`
+ |
+note: this expression returns a temporary value
+ --> tests/ui/swap_with_temporary.rs:92:23
+ |
+LL | swap(&mut vec![42], &mut self.thing.lock().unwrap());
+ | ^^^^^^^^
+
+error: swapping with a temporary value is inefficient
+ --> tests/ui/swap_with_temporary.rs:100:5
+ |
+LL | swap(&mut ***v1, &mut vec![]);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use assignment instead: `***v1 = vec![]`
+ |
+note: this expression returns a temporary value
+ --> tests/ui/swap_with_temporary.rs:100:27
+ |
+LL | swap(&mut ***v1, &mut vec![]);
+ | ^^^^^^
+
+error: swapping with a temporary value is inefficient
+ --> tests/ui/swap_with_temporary.rs:118:5
+ |
+LL | swap(&mut vec![], &mut v1.lock().unwrap());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use assignment instead: `***v1.lock().unwrap() = vec![]`
+ |
+note: this expression returns a temporary value
+ --> tests/ui/swap_with_temporary.rs:118:15
+ |
+LL | swap(&mut vec![], &mut v1.lock().unwrap());
+ | ^^^^^^
+
+error: aborting due to 11 previous errors
diff --git a/tests/ui/unnecessary_os_str_debug_formatting.rs b/tests/ui/unnecessary_os_str_debug_formatting.rs
index 6652efd..66590be 100644
--- a/tests/ui/unnecessary_os_str_debug_formatting.rs
+++ b/tests/ui/unnecessary_os_str_debug_formatting.rs
@@ -21,3 +21,16 @@
let _: String = format!("{:?}", os_str); //~ unnecessary_debug_formatting
let _: String = format!("{:?}", os_string); //~ unnecessary_debug_formatting
}
+
+#[clippy::msrv = "1.86"]
+fn msrv_1_86() {
+ let os_str = OsStr::new("test");
+ println!("{:?}", os_str);
+}
+
+#[clippy::msrv = "1.87"]
+fn msrv_1_87() {
+ let os_str = OsStr::new("test");
+ println!("{:?}", os_str);
+ //~^ unnecessary_debug_formatting
+}
diff --git a/tests/ui/unnecessary_os_str_debug_formatting.stderr b/tests/ui/unnecessary_os_str_debug_formatting.stderr
index 382e59b..f04d2d5 100644
--- a/tests/ui/unnecessary_os_str_debug_formatting.stderr
+++ b/tests/ui/unnecessary_os_str_debug_formatting.stderr
@@ -54,5 +54,14 @@
= help: use `Display` formatting and change this to `os_string.display()`
= 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
-error: aborting due to 6 previous errors
+error: unnecessary `Debug` formatting in `println!` args
+ --> tests/ui/unnecessary_os_str_debug_formatting.rs:34:22
+ |
+LL | println!("{:?}", os_str);
+ | ^^^^^^
+ |
+ = help: use `Display` formatting and change this to `os_str.display()`
+ = 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
+
+error: aborting due to 7 previous errors
diff --git a/tests/ui/wildcard_enum_match_arm.fixed b/tests/ui/wildcard_enum_match_arm.fixed
index 141ff6e..5f738a2 100644
--- a/tests/ui/wildcard_enum_match_arm.fixed
+++ b/tests/ui/wildcard_enum_match_arm.fixed
@@ -91,6 +91,21 @@
}
{
+ pub enum Enum {
+ A,
+ B,
+ C(u8),
+ D(u8, u8),
+ E { e: u8 },
+ };
+ match Enum::A {
+ Enum::A => (),
+ Enum::B | Enum::C(_) | Enum::D(..) | Enum::E { .. } => (),
+ //~^ wildcard_enum_match_arm
+ }
+ }
+
+ {
#![allow(clippy::manual_non_exhaustive)]
pub enum Enum {
A,
@@ -105,3 +120,17 @@
}
}
}
+
+fn issue15091() {
+ enum Foo {
+ A,
+ B,
+ C,
+ }
+
+ match Foo::A {
+ Foo::A => {},
+ r#type @ Foo::B | r#type @ Foo::C => {},
+ //~^ wildcard_enum_match_arm
+ }
+}
diff --git a/tests/ui/wildcard_enum_match_arm.rs b/tests/ui/wildcard_enum_match_arm.rs
index a13684e..4bc4bfd 100644
--- a/tests/ui/wildcard_enum_match_arm.rs
+++ b/tests/ui/wildcard_enum_match_arm.rs
@@ -91,6 +91,21 @@
}
{
+ pub enum Enum {
+ A,
+ B,
+ C(u8),
+ D(u8, u8),
+ E { e: u8 },
+ };
+ match Enum::A {
+ Enum::A => (),
+ _ => (),
+ //~^ wildcard_enum_match_arm
+ }
+ }
+
+ {
#![allow(clippy::manual_non_exhaustive)]
pub enum Enum {
A,
@@ -105,3 +120,17 @@
}
}
}
+
+fn issue15091() {
+ enum Foo {
+ A,
+ B,
+ C,
+ }
+
+ match Foo::A {
+ Foo::A => {},
+ r#type => {},
+ //~^ wildcard_enum_match_arm
+ }
+}
diff --git a/tests/ui/wildcard_enum_match_arm.stderr b/tests/ui/wildcard_enum_match_arm.stderr
index 088c6b7..d092998 100644
--- a/tests/ui/wildcard_enum_match_arm.stderr
+++ b/tests/ui/wildcard_enum_match_arm.stderr
@@ -38,7 +38,19 @@
--> tests/ui/wildcard_enum_match_arm.rs:103:13
|
LL | _ => (),
+ | ^ help: try: `Enum::B | Enum::C(_) | Enum::D(..) | Enum::E { .. }`
+
+error: wildcard match will also match any future added variants
+ --> tests/ui/wildcard_enum_match_arm.rs:118:13
+ |
+LL | _ => (),
| ^ help: try: `Enum::B | Enum::__Private`
-error: aborting due to 6 previous errors
+error: wildcard match will also match any future added variants
+ --> tests/ui/wildcard_enum_match_arm.rs:133:9
+ |
+LL | r#type => {},
+ | ^^^^^^ help: try: `r#type @ Foo::B | r#type @ Foo::C`
+
+error: aborting due to 8 previous errors
diff --git a/tests/ui/zero_ptr.fixed b/tests/ui/zero_ptr.fixed
index f2375d5..f9d9d2d 100644
--- a/tests/ui/zero_ptr.fixed
+++ b/tests/ui/zero_ptr.fixed
@@ -16,3 +16,11 @@
let z = 0;
let _ = z as *const usize; // this is currently not caught
}
+
+const fn in_const_context() {
+ #[clippy::msrv = "1.23"]
+ let _: *const usize = 0 as *const _;
+ #[clippy::msrv = "1.24"]
+ let _: *const usize = std::ptr::null();
+ //~^ zero_ptr
+}
diff --git a/tests/ui/zero_ptr.rs b/tests/ui/zero_ptr.rs
index ee01e42..41455fe 100644
--- a/tests/ui/zero_ptr.rs
+++ b/tests/ui/zero_ptr.rs
@@ -16,3 +16,11 @@
let z = 0;
let _ = z as *const usize; // this is currently not caught
}
+
+const fn in_const_context() {
+ #[clippy::msrv = "1.23"]
+ let _: *const usize = 0 as *const _;
+ #[clippy::msrv = "1.24"]
+ let _: *const usize = 0 as *const _;
+ //~^ zero_ptr
+}
diff --git a/tests/ui/zero_ptr.stderr b/tests/ui/zero_ptr.stderr
index 8dc781f..81269de 100644
--- a/tests/ui/zero_ptr.stderr
+++ b/tests/ui/zero_ptr.stderr
@@ -31,5 +31,11 @@
LL | foo(0 as *const _, 0 as *mut _);
| ^^^^^^^^^^^ help: try: `std::ptr::null_mut()`
-error: aborting due to 5 previous errors
+error: `0 as *const _` detected
+ --> tests/ui/zero_ptr.rs:24:27
+ |
+LL | let _: *const usize = 0 as *const _;
+ | ^^^^^^^^^^^^^ help: try: `std::ptr::null()`
+
+error: aborting due to 6 previous errors
diff --git a/tests/versioncheck.rs b/tests/versioncheck.rs
index f6fc235..b017938 100644
--- a/tests/versioncheck.rs
+++ b/tests/versioncheck.rs
@@ -27,6 +27,7 @@
"clippy_config/Cargo.toml",
"clippy_lints/Cargo.toml",
"clippy_utils/Cargo.toml",
+ "declare_clippy_lint/Cargo.toml",
];
for path in paths {
diff --git a/triagebot.toml b/triagebot.toml
index 16557a4..4f37075 100644
--- a/triagebot.toml
+++ b/triagebot.toml
@@ -17,6 +17,9 @@
[issue-links]
+[mentions."clippy_lints/src/doc"]
+cc = ["@notriddle"]
+
# Prevents mentions in commits to avoid users being spammed
[no-mentions]
diff --git a/util/versions.py b/util/versions.py
index fee0d29..6e06d77 100755
--- a/util/versions.py
+++ b/util/versions.py
@@ -6,11 +6,11 @@
import sys
def key(v):
- if v == "master":
- return sys.maxsize
if v == "stable":
- return sys.maxsize - 1
+ return sys.maxsize
if v == "beta":
+ return sys.maxsize - 1
+ if v == "master":
return sys.maxsize - 2
if v == "pre-1.29.0":
return -1