Properly check that an expression might be the one returned (#15115) The `TyCtxt::hir_get_fn_id_for_return_block()` function was too broad, as it will return positively even when given part of an expression that can be used as a return value. A new `potential_return_of_enclosing_body()` utility function has been made to represent the fact that an expression might be directly returned from its enclosing body. changelog: [`return_and_then`]: prevent false positives in case of a partially used expression Fixes rust-lang/rust-clippy#15111 Fixes rust-lang/rust-clippy#14927 <!-- TRIAGEBOT_START --> <!-- TRIAGEBOT_SUMMARY_START --> ### Summary Notes - [Beta-nomination](https://github.com/rust-lang/rust-clippy/pull/15115#issuecomment-2996222332) by [samueltardieu](https://github.com/samueltardieu) *Managed by `@rustbot`—see [help](https://forge.rust-lang.org/triagebot/note.html) for details* <!-- TRIAGEBOT_SUMMARY_END --> <!-- TRIAGEBOT_END -->
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..7ad58af --- /dev/null +++ b/.github/workflows/feature_freeze.yml
@@ -0,0 +1,35 @@ +name: Feature freeze check + +on: + pull_request_target: + types: + - opened + branches: + - master + paths: + - 'clippy_lints/src/declared_lints.rs' + +jobs: + auto-comment: + runs-on: ubuntu-latest + + permissions: + pull-requests: write + + # Do not in any case add code that runs anything coming from the the content + # of the pull request, as malicious code would be able to access the private + # GitHub token. + 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 September 18 and focusing on bugfixes.\nThanks a lot for your contribution, and sorry for the inconvenience.\nWith ❤ from the Clippy team\n\n@rustbot note Feature-freeze\n@rustbot blocked\n@rustbot label +A-lint\n" + 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 5ad1a71..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
diff --git a/Cargo.toml b/Cargo.toml index 8cd648c..2b6139e 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" @@ -28,13 +28,13 @@ 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 } -termize = "0.1" +termize = "0.2" color-print = "0.3.4" anstream = "0.6.18" [dev-dependencies] cargo_metadata = "0.18.1" -ui_test = "0.29.2" +ui_test = "0.30.2" regex = "1.5.5" serde = { version = "1.0.145", features = ["derive"] } serde_json = "1.0.122" @@ -45,20 +45,13 @@ pulldown-cmark = { version = "0.11", default-features = false, features = ["html"] } askama = { version = "0.14", default-features = false, features = ["alloc", "config", "derive"] } -# UI test dependencies -if_chain = "1.0" -quote = "1.0.25" -syn = { version = "2.0", features = ["full"] } -futures = "0.3" -parking_lot = "0.12" -tokio = { version = "1", features = ["io-util"] } - [build-dependencies] rustc_tools_util = { path = "rustc_tools_util", version = "0.4.2" } [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/development/infrastructure/backport.md b/book/src/development/infrastructure/backport.md index 9526d8a..47ea6a4 100644 --- a/book/src/development/infrastructure/backport.md +++ b/book/src/development/infrastructure/backport.md
@@ -109,4 +109,4 @@ When a PR is backported to Rust `beta`, label the PR with `beta-accepted`. This will then get picked up when [writing the changelog]. -[writing the changelog]: changelog_update.md#31-include-beta-accepted-prs +[writing the changelog]: changelog_update.md#4-include-beta-accepted-prs
diff --git a/book/src/development/infrastructure/changelog_update.md b/book/src/development/infrastructure/changelog_update.md index eede6b7..c96ff22 100644 --- a/book/src/development/infrastructure/changelog_update.md +++ b/book/src/development/infrastructure/changelog_update.md
@@ -38,7 +38,7 @@ sure though, that `beta` was already branched in the Rust repository. To find the commit hash, issue the following command when in a `rust-lang/rust` -checkout: +checkout (most of the time on the `upstream/beta` branch): ``` git log --oneline -- src/tools/clippy/ | grep -o "Merge commit '[a-f0-9]*' into .*" | head -1 | sed -e "s/Merge commit '\([a-f0-9]*\)' into .*/\1/g" ``` @@ -48,16 +48,13 @@ Once you've got the correct commit range, run ``` -util/fetch_prs_between.sh commit1 commit2 > changes.txt +util/fetch_prs_between.sh start_commit end_commit > changes.txt ``` -where `commit2` is the commit hash from the previous command and `commit1` -is the commit hash from the current CHANGELOG file. +where `end_commit` is the commit hash from the previous command and `start_commit` +is [the commit hash][beta_section] from the current CHANGELOG file. Open `changes.txt` file in your editor of choice. -When updating the changelog it's also a good idea to make sure that `commit1` is -already correct in the current changelog. - ### 3. Authoring the final changelog The above script should have dumped all the relevant PRs to the file you @@ -70,17 +67,7 @@ content to `CHANGELOG.md`. Adapt the wording as you see fit but try to keep it somewhat coherent. -The order should roughly be: - -1. New lints -2. Moves or deprecations of lints -3. Changes that expand what code existing lints cover -4. False positive fixes -5. ICE fixes -6. Documentation improvements -7. Others - -As section headers, we use: +The sections order should roughly be: ``` ### New Lints @@ -97,10 +84,10 @@ ### Others ``` -Please also be sure to update the Beta/Unreleased sections at the top with the -relevant commit ranges. +Please also be sure to update [the `Unreleased/Beta/In Rust Nightly` section][beta_section] at the top with the +relevant commits ranges and to add the `Rust <version>` section with release date and PR ranges. -#### 3.1 Include `beta-accepted` PRs +### 4. Include `beta-accepted` PRs Look for the [`beta-accepted`] label and make sure to also include the PRs with that label in the changelog. If you can, remove the `beta-accepted` labels @@ -109,7 +96,7 @@ > _Note:_ Some of those PRs might even get backported to the previous `beta`. > Those have to be included in the changelog of the _previous_ release. -### 4. Update `clippy::version` attributes +### 5. Update `clippy::version` attributes Next, make sure to check that the `#[clippy::version]` attributes for the added lints contain the correct version. @@ -129,3 +116,4 @@ [rust_beta_tools]: https://github.com/rust-lang/rust/tree/beta/src/tools/clippy [rust_stable_tools]: https://github.com/rust-lang/rust/releases [`beta-accepted`]: https://github.com/rust-lang/rust-clippy/issues?q=label%3Abeta-accepted+ +[beta_section]: https://github.com/rust-lang/rust-clippy/blob/master/CHANGELOG.md#unreleased--beta--in-rust-nightly
diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index e9b7f42..992ed2c 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md
@@ -892,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 841facd..555f54b 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs
@@ -794,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/fmt.rs b/clippy_dev/src/fmt.rs index c1b6b37..bd9e57c 100644 --- a/clippy_dev/src/fmt.rs +++ b/clippy_dev/src/fmt.rs
@@ -223,7 +223,7 @@ if check { return Err(Error::CheckFailed); } - fs::write(path, new_text.as_bytes())?; + fs::write(path, new_text)?; } Ok(()) }
diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs index 6dbee33..5f6e874 100644 --- a/clippy_dev/src/update_lints.rs +++ b/clippy_dev/src/update_lints.rs
@@ -386,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_lints/Cargo.toml b/clippy_lints/Cargo.toml index 37b554f..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"
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/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/casts/borrow_as_ptr.rs b/clippy_lints/src/casts/borrow_as_ptr.rs index ad0a4f8..e3b125a 100644 --- a/clippy_lints/src/casts/borrow_as_ptr.rs +++ b/clippy_lints/src/casts/borrow_as_ptr.rs
@@ -18,7 +18,8 @@ cast_to: &'tcx Ty<'_>, msrv: Msrv, ) -> bool { - if matches!(cast_to.kind, TyKind::Ptr(_)) + if let TyKind::Ptr(target) = cast_to.kind + && !matches!(target.ty.kind, TyKind::TraitObject(..)) && let ExprKind::AddrOf(BorrowKind::Ref, mutability, e) = cast_expr.kind && !is_lint_allowed(cx, BORROW_AS_PTR, expr.hir_id) {
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/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 8c12a42..6217fc4 100644 --- a/clippy_lints/src/coerce_container_to_any.rs +++ b/clippy_lints/src/coerce_container_to_any.rs
@@ -1,9 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet; +use clippy_utils::sugg::{self, Sugg}; use clippy_utils::sym; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::adjustment::{Adjust, PointerCoercion}; use rustc_middle::ty::{self, ExistentialPredicate, Ty, TyCtxt}; use rustc_session::declare_lint_pass; @@ -42,30 +43,25 @@ /// ``` #[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]); impl<'tcx> LateLintPass<'tcx> for CoerceContainerToAny { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - // If this expression has an effective type of `&dyn Any` ... - { - let coerced_ty = cx.typeck_results().expr_ty_adjusted(e); - - let ty::Ref(_, coerced_ref_ty, _) = *coerced_ty.kind() else { - return; - }; - if !is_dyn_any(cx.tcx, coerced_ref_ty) { - return; - } + // If this expression was coerced to `&dyn Any` ... + if !cx.typeck_results().expr_adjustments(e).last().is_some_and(|adj| { + matches!(adj.kind, Adjust::Pointer(PointerCoercion::Unsize)) && is_ref_dyn_any(cx.tcx, adj.target) + }) { + return; } let expr_ty = cx.typeck_results().expr_ty(e); let ty::Ref(_, expr_ref_ty, _) = *expr_ty.kind() else { return; }; - // ... but only due to coercion ... + // ... but it's not actually `&dyn Any` ... if is_dyn_any(cx.tcx, expr_ref_ty) { return; } @@ -78,23 +74,37 @@ } // ... that's probably not intended. - let (span, deref_count) = match e.kind { + let (target_expr, deref_count) = match e.kind { // If `e` was already an `&` expression, skip `*&` in the suggestion - ExprKind::AddrOf(_, _, referent) => (referent.span, depth), - _ => (e.span, depth + 1), + ExprKind::AddrOf(_, _, referent) => (referent, depth), + _ => (e, depth + 1), }; + let ty::Ref(_, _, mutability) = *cx.typeck_results().expr_ty_adjusted(e).kind() else { + return; + }; + let sugg = sugg::make_unop( + &format!("{}{}", mutability.ref_prefix_str(), str::repeat("*", deref_count)), + Sugg::hir(cx, target_expr, ".."), + ); span_lint_and_sugg( cx, COERCE_CONTAINER_TO_ANY, e.span, - format!("coercing `{expr_ty}` to `&dyn Any`"), + format!("coercing `{expr_ty}` to `{}dyn Any`", mutability.ref_prefix_str()), "consider dereferencing", - format!("&{}{}", str::repeat("*", deref_count), snippet(cx, span, "x")), + sugg.to_string(), Applicability::MaybeIncorrect, ); } } +fn is_ref_dyn_any(tcx: TyCtxt<'_>, ty: Ty<'_>) -> bool { + let ty::Ref(_, ref_ty, _) = *ty.kind() else { + return false; + }; + is_dyn_any(tcx, ref_ty) +} + fn is_dyn_any(tcx: TyCtxt<'_>, ty: Ty<'_>) -> bool { let ty::Dynamic(traits, ..) = ty.kind() else { return false;
diff --git a/clippy_lints/src/cognitive_complexity.rs b/clippy_lints/src/cognitive_complexity.rs index 5c64216..d5d937d 100644 --- a/clippy_lints/src/cognitive_complexity.rs +++ b/clippy_lints/src/cognitive_complexity.rs
@@ -14,18 +14,25 @@ declare_clippy_lint! { /// ### What it does - /// Checks for methods with high cognitive complexity. + /// We used to think it measured how hard a method is to understand. /// /// ### Why is this bad? - /// Methods of high cognitive complexity tend to be hard to - /// both read and maintain. Also LLVM will tend to optimize small methods better. + /// Ideally, we would like to be able to measure how hard a function is + /// to understand given its context (what we call its Cognitive Complexity). + /// But that's not what this lint does. See "Known problems" /// /// ### Known problems - /// Sometimes it's hard to find a way to reduce the - /// complexity. + /// The true Cognitive Complexity of a method is not something we can + /// calculate using modern technology. This lint has been left in the + /// `nursery` so as to not mislead users into using this lint as a + /// measurement tool. /// - /// ### Example - /// You'll see it when you get the warning. + /// For more detailed information, see [rust-clippy#3793](https://github.com/rust-lang/rust-clippy/issues/3793) + /// + /// ### Lints to consider instead of this + /// + /// * [`excessive_nesting`](https://rust-lang.github.io/rust-clippy/master/index.html#excessive_nesting) + /// * [`too_many_lines`](https://rust-lang.github.io/rust-clippy/master/index.html#too_many_lines) #[clippy::version = "1.35.0"] pub COGNITIVE_COMPLEXITY, nursery,
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_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/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 1b6c48e..4939793 100644 --- a/clippy_lints/src/doc/mod.rs +++ b/clippy_lints/src/doc/mod.rs
@@ -1249,7 +1249,9 @@ b'[' => { start = Some(i + offset); }, - b']' if let Some(start) = start => { + b']' if let Some(start) = start + && doc.as_bytes().get(i + offset + 1) == Some(&b':') => + { return Some(start..i + offset + 1); }, _ => {},
diff --git a/clippy_lints/src/doc/needless_doctest_main.rs b/clippy_lints/src/doc/needless_doctest_main.rs index 4bf545b..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()));
diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index fe85ae7..0288747 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs
@@ -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; @@ -161,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; } @@ -249,7 +250,7 @@ }, 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;
diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index b3c9e86..d5abaa5 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs
@@ -2,8 +2,8 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::{ - 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, + eq_expr_value, get_parent_expr, has_ambiguous_literal_in_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; @@ -455,7 +455,6 @@ None } -// TODO: Fix rust-lang/rust-clippy#4735 fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) { if let ExprKind::Binary( Spanned { @@ -491,6 +490,14 @@ return; }; + // Check if any variable in the expression has an ambiguous type (could be f32 or f64) + // see: https://github.com/rust-lang/rust-clippy/issues/14897 + if (matches!(recv.kind, ExprKind::Path(_)) || matches!(recv.kind, ExprKind::Call(_, _))) + && has_ambiguous_literal_in_expr(cx, recv) + { + return; + } + span_lint_and_sugg( cx, SUBOPTIMAL_FLOPS,
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/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/loops/empty_loop.rs b/clippy_lints/src/loops/empty_loop.rs index 823cf0f..e809987 100644 --- a/clippy_lints/src/loops/empty_loop.rs +++ b/clippy_lints/src/loops/empty_loop.rs
@@ -1,11 +1,22 @@ use super::EMPTY_LOOP; use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::{is_in_panic_handler, is_no_std_crate}; +use clippy_utils::{is_in_panic_handler, is_no_std_crate, sym}; -use rustc_hir::{Block, Expr}; +use rustc_hir::{Block, Expr, ItemKind, Node}; use rustc_lint::LateContext; pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, loop_block: &Block<'_>) { + let parent_hir_id = cx.tcx.parent_hir_id(expr.hir_id); + if let Node::Item(parent_node) = cx.tcx.hir_node(parent_hir_id) + && matches!(parent_node.kind, ItemKind::Fn { .. }) + && let attrs = cx.tcx.hir_attrs(parent_hir_id) + && attrs.iter().any(|attr| attr.has_name(sym::rustc_intrinsic)) + { + // Intrinsic functions are expanded into an empty loop when lowering the AST + // to simplify the job of later passes which might expect any function to have a body. + return; + } + if loop_block.stmts.is_empty() && loop_block.expr.is_none() && !is_in_panic_handler(cx, expr) { let msg = "empty `loop {}` wastes CPU cycles"; let help = if is_no_std_crate(cx) {
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 9ff82cd..1f9a943 100644 --- a/clippy_lints/src/manual_let_else.rs +++ b/clippy_lints/src/manual_let_else.rs
@@ -245,17 +245,36 @@ } match pat.kind { - PatKind::Binding(_ann, _id, binding_name, opt_subpt) => { - let Some((pat_to_put, binding_mode)) = ident_map.get(&binding_name.name) else { - break 'a; - }; - let sn_pfx = binding_mode.prefix_str(); - let (sn_ptp, _) = snippet_with_context(cx, pat_to_put.span, span.ctxt(), "", app); - if let Some(subpt) = opt_subpt { - let subpt = replace_in_pattern(cx, span, ident_map, subpt, app, false); - return format!("{sn_pfx}{sn_ptp} @ {subpt}"); + PatKind::Binding(ann, _id, binding_name, opt_subpt) => { + match (ident_map.get(&binding_name.name), opt_subpt) { + (Some((pat_to_put, binding_mode)), opt_subpt) => { + let sn_pfx = binding_mode.prefix_str(); + let (sn_ptp, _) = snippet_with_context(cx, pat_to_put.span, span.ctxt(), "", app); + if let Some(subpt) = opt_subpt { + let subpt = replace_in_pattern(cx, span, ident_map, subpt, app, false); + return format!("{sn_pfx}{sn_ptp} @ {subpt}"); + } + return format!("{sn_pfx}{sn_ptp}"); + }, + (None, Some(subpt)) => { + let subpt = replace_in_pattern(cx, span, ident_map, subpt, app, false); + // scanning for a value that matches is not sensitive to order + #[expect(rustc::potential_query_instability)] + if ident_map.values().any(|(other_pat, _)| { + if let PatKind::Binding(_, _, other_name, _) = other_pat.kind { + other_name == binding_name + } else { + false + } + }) { + // this name is shadowed, and, therefore, not usable + return subpt; + } + let binding_pfx = ann.prefix_str(); + return format!("{binding_pfx}{binding_name} @ {subpt}"); + }, + (None, None) => break 'a, } - return format!("{sn_pfx}{sn_ptp}"); }, PatKind::Or(pats) => { let patterns = pats
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_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/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/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_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 95623467..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..946114e 100644 --- a/clippy_lints/src/neg_multiply.rs +++ b/clippy_lints/src/neg_multiply.rs
@@ -1,13 +1,13 @@ use clippy_utils::consts::{self, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet_with_context; +use clippy_utils::get_parent_expr; +use clippy_utils::source::{snippet, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; use rustc_ast::util::parser::ExprPrecedence; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::Span; declare_clippy_lint! { /// ### What it does @@ -33,6 +33,19 @@ declare_lint_pass!(NegMultiply => [NEG_MULTIPLY]); +fn is_in_parens_with_postfix(cx: &LateContext<'_>, mul_expr: &Expr<'_>) -> bool { + if let Some(parent) = get_parent_expr(cx, mul_expr) { + let mult_snippet = snippet(cx, mul_expr.span, ""); + if has_enclosing_paren(&mult_snippet) + && let ExprKind::MethodCall(_, _, _, _) = parent.kind + { + return true; + } + } + + false +} + impl<'tcx> LateLintPass<'tcx> for NegMultiply { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { if let ExprKind::Binary(ref op, left, right) = e.kind @@ -40,15 +53,15 @@ { match (&left.kind, &right.kind) { (&ExprKind::Unary(..), &ExprKind::Unary(..)) => {}, - (&ExprKind::Unary(UnOp::Neg, lit), _) => check_mul(cx, e.span, lit, right), - (_, &ExprKind::Unary(UnOp::Neg, lit)) => check_mul(cx, e.span, lit, left), + (&ExprKind::Unary(UnOp::Neg, lit), _) => check_mul(cx, e, lit, right), + (_, &ExprKind::Unary(UnOp::Neg, lit)) => check_mul(cx, e, lit, left), _ => {}, } } } } -fn check_mul(cx: &LateContext<'_>, span: Span, lit: &Expr<'_>, exp: &Expr<'_>) { +fn check_mul(cx: &LateContext<'_>, mul_expr: &Expr<'_>, lit: &Expr<'_>, exp: &Expr<'_>) { const F16_ONE: u16 = 1.0_f16.to_bits(); const F128_ONE: u128 = 1.0_f128.to_bits(); if let ExprKind::Lit(l) = lit.kind @@ -63,8 +76,19 @@ && cx.typeck_results().expr_ty(exp).is_numeric() { 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 (snip, from_macro) = snippet_with_context(cx, exp.span, mul_expr.span.ctxt(), "..", &mut applicability); + + let needs_parens_for_postfix = is_in_parens_with_postfix(cx, mul_expr); + + let suggestion = if needs_parens_for_postfix { + // Special case: when the multiplication is in parentheses followed by a method call + // we need to preserve the grouping but negate the inner expression. + // Consider this expression: `((a.delta - 0.5).abs() * -1.0).total_cmp(&1.0)` + // We need to end up with: `(-(a.delta - 0.5).abs()).total_cmp(&1.0)` + // Otherwise, without the parentheses we would try to negate an Ordering: + // `-(a.delta - 0.5).abs().total_cmp(&1.0)` + format!("(-{snip})") + } else if !from_macro && cx.precedence(exp) < ExprPrecedence::Prefix && !has_enclosing_paren(&snip) { format!("-({snip})") } else { format!("-{snip}") @@ -72,7 +96,7 @@ span_lint_and_sugg( cx, NEG_MULTIPLY, - span, + mul_expr.span, "this multiplication by -1 can be written more succinctly", "consider using", suggestion,
diff --git a/clippy_lints/src/no_effect.rs b/clippy_lints/src/no_effect.rs index 02c4816..72e6503 100644 --- a/clippy_lints/src/no_effect.rs +++ b/clippy_lints/src/no_effect.rs
@@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; use clippy_utils::source::SpanRangeExt; -use clippy_utils::ty::has_drop; +use clippy_utils::ty::{expr_type_is_certain, has_drop}; use clippy_utils::{ in_automatically_derived, is_inside_always_const_context, is_lint_allowed, path_to_local, peel_blocks, }; @@ -340,11 +340,13 @@ }, ExprKind::Array(v) | ExprKind::Tup(v) => Some(v.iter().collect()), ExprKind::Repeat(inner, _) - | ExprKind::Cast(inner, _) | ExprKind::Type(inner, _) | ExprKind::Unary(_, inner) | ExprKind::Field(inner, _) | ExprKind::AddrOf(_, _, inner) => reduce_expression(cx, inner).or_else(|| Some(vec![inner])), + ExprKind::Cast(inner, _) if expr_type_is_certain(cx, inner) => { + reduce_expression(cx, inner).or_else(|| Some(vec![inner])) + }, ExprKind::Struct(_, fields, ref base) => { if has_drop(cx, cx.typeck_results().expr_ty(expr)) { None
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/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/redundant_closure_call.rs b/clippy_lints/src/redundant_closure_call.rs index 8459726..1c23fe9 100644 --- a/clippy_lints/src/redundant_closure_call.rs +++ b/clippy_lints/src/redundant_closure_call.rs
@@ -5,9 +5,7 @@ use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::intravisit::{Visitor as HirVisitor, Visitor}; -use rustc_hir::{ - ClosureKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, ExprKind, Node, intravisit as hir_visit, -}; +use rustc_hir::{ClosureKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, ExprKind, intravisit as hir_visit}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::nested_filter; use rustc_middle::ty; @@ -198,15 +196,15 @@ hint = hint.asyncify(); } - let is_in_fn_call_arg = if let Node::Expr(expr) = cx.tcx.parent_hir_node(expr.hir_id) { - matches!(expr.kind, ExprKind::Call(_, _)) - } else { - false - }; - - // avoid clippy::double_parens - if !is_in_fn_call_arg { - hint = hint.maybe_paren(); + // If the closure body is a block with a single expression, suggest just the inner expression, + // not the block. Example: `(|| { Some(true) })()` should suggest + // `Some(true)` + if let ExprKind::Block(block, _) = body.kind + && block.stmts.is_empty() + && let Some(expr) = block.expr + { + hint = Sugg::hir_with_context(cx, expr, full_expr.span.ctxt(), "..", &mut applicability) + .maybe_paren(); } diag.span_suggestion(full_expr.span, "try doing something like", hint, applicability);
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/std_instead_of_core.rs b/clippy_lints/src/std_instead_of_core.rs index 442b325..cf70e88 100644 --- a/clippy_lints/src/std_instead_of_core.rs +++ b/clippy_lints/src/std_instead_of_core.rs
@@ -1,10 +1,10 @@ use clippy_config::Conf; -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; use clippy_utils::is_from_proc_macro; use clippy_utils::msrvs::Msrv; use rustc_attr_data_structures::{StabilityLevel, StableSince}; use rustc_errors::Applicability; -use rustc_hir::def::Res; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::{Block, Body, HirId, Path, PathSegment}; use rustc_lint::{LateContext, LateLintPass, Lint, LintContext}; @@ -88,49 +88,52 @@ } pub struct StdReexports { - lint_point: (Span, Option<LintPoint>), + lint_points: Option<(Span, Vec<LintPoint>)>, msrv: Msrv, } impl StdReexports { pub fn new(conf: &'static Conf) -> Self { Self { - lint_point: Default::default(), + lint_points: Option::default(), msrv: conf.msrv, } } - fn lint_if_finish(&mut self, cx: &LateContext<'_>, (span, item): (Span, Option<LintPoint>)) { - if span.source_equal(self.lint_point.0) { - return; + fn lint_if_finish(&mut self, cx: &LateContext<'_>, krate: Span, lint_point: LintPoint) { + match &mut self.lint_points { + Some((prev_krate, prev_lints)) if prev_krate.overlaps(krate) => { + prev_lints.push(lint_point); + }, + _ => emit_lints(cx, self.lint_points.replace((krate, vec![lint_point]))), } - - if !self.lint_point.0.is_dummy() { - emit_lints(cx, &self.lint_point); - } - - self.lint_point = (span, item); } } impl_lint_pass!(StdReexports => [STD_INSTEAD_OF_CORE, STD_INSTEAD_OF_ALLOC, ALLOC_INSTEAD_OF_CORE]); -type LintPoint = (&'static Lint, &'static str, &'static str); +#[derive(Debug)] +enum LintPoint { + Available(Span, &'static Lint, &'static str, &'static str), + Conflict, +} impl<'tcx> LateLintPass<'tcx> for StdReexports { fn check_path(&mut self, cx: &LateContext<'tcx>, path: &Path<'tcx>, _: HirId) { - if let Res::Def(_, def_id) = path.res + if let Res::Def(def_kind, def_id) = path.res && let Some(first_segment) = get_first_segment(path) && is_stable(cx, def_id, self.msrv) && !path.span.in_external_macro(cx.sess().source_map()) && !is_from_proc_macro(cx, &first_segment.ident) + && !matches!(def_kind, DefKind::Macro(_)) + && let Some(last_segment) = path.segments.last() { let (lint, used_mod, replace_with) = match first_segment.ident.name { sym::std => match cx.tcx.crate_name(def_id.krate) { sym::core => (STD_INSTEAD_OF_CORE, "std", "core"), sym::alloc => (STD_INSTEAD_OF_ALLOC, "std", "alloc"), _ => { - self.lint_if_finish(cx, (first_segment.ident.span, None)); + self.lint_if_finish(cx, first_segment.ident.span, LintPoint::Conflict); return; }, }, @@ -138,44 +141,84 @@ if cx.tcx.crate_name(def_id.krate) == sym::core { (ALLOC_INSTEAD_OF_CORE, "alloc", "core") } else { - self.lint_if_finish(cx, (first_segment.ident.span, None)); + self.lint_if_finish(cx, first_segment.ident.span, LintPoint::Conflict); return; } }, - _ => return, + _ => { + self.lint_if_finish(cx, first_segment.ident.span, LintPoint::Conflict); + return; + }, }; - self.lint_if_finish(cx, (first_segment.ident.span, Some((lint, used_mod, replace_with)))); + self.lint_if_finish( + cx, + first_segment.ident.span, + LintPoint::Available(last_segment.ident.span, lint, used_mod, replace_with), + ); } } fn check_block_post(&mut self, cx: &LateContext<'tcx>, _: &Block<'tcx>) { - self.lint_if_finish(cx, Default::default()); + emit_lints(cx, self.lint_points.take()); } fn check_body_post(&mut self, cx: &LateContext<'tcx>, _: &Body<'tcx>) { - self.lint_if_finish(cx, Default::default()); + emit_lints(cx, self.lint_points.take()); } fn check_crate_post(&mut self, cx: &LateContext<'tcx>) { - self.lint_if_finish(cx, Default::default()); + emit_lints(cx, self.lint_points.take()); } } -fn emit_lints(cx: &LateContext<'_>, (span, item): &(Span, Option<LintPoint>)) { - let Some((lint, used_mod, replace_with)) = item else { +fn emit_lints(cx: &LateContext<'_>, lint_points: Option<(Span, Vec<LintPoint>)>) { + let Some((krate_span, lint_points)) = lint_points else { return; }; - span_lint_and_sugg( - cx, - lint, - *span, - format!("used import from `{used_mod}` instead of `{replace_with}`"), - format!("consider importing the item from `{replace_with}`"), - (*replace_with).to_string(), - Applicability::MachineApplicable, - ); + let mut lint: Option<(&'static Lint, &'static str, &'static str)> = None; + let mut has_conflict = false; + for lint_point in &lint_points { + match lint_point { + LintPoint::Available(_, l, used_mod, replace_with) + if lint.is_none_or(|(prev_l, ..)| l.name == prev_l.name) => + { + lint = Some((l, used_mod, replace_with)); + }, + _ => { + has_conflict = true; + break; + }, + } + } + + if !has_conflict && let Some((lint, used_mod, replace_with)) = lint { + span_lint_and_sugg( + cx, + lint, + krate_span, + format!("used import from `{used_mod}` instead of `{replace_with}`"), + format!("consider importing the item from `{replace_with}`"), + (*replace_with).to_string(), + Applicability::MachineApplicable, + ); + return; + } + + for lint_point in lint_points { + let LintPoint::Available(span, lint, used_mod, replace_with) = lint_point else { + continue; + }; + span_lint_and_help( + cx, + lint, + span, + format!("used import from `{used_mod}` instead of `{replace_with}`"), + None, + format!("consider importing the item from `{replace_with}`"), + ); + } } /// Returns the first named segment of a [`Path`].
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/undocumented_unsafe_blocks.rs b/clippy_lints/src/undocumented_unsafe_blocks.rs index 9242747..d4f8b1b 100644 --- a/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/clippy_lints/src/undocumented_unsafe_blocks.rs
@@ -143,7 +143,8 @@ if let Some(tail) = block.expr && !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, tail.hir_id) && !tail.span.in_external_macro(cx.tcx.sess.source_map()) - && let HasSafetyComment::Yes(pos) = stmt_has_safety_comment(cx, tail.span, tail.hir_id) + && let HasSafetyComment::Yes(pos) = + stmt_has_safety_comment(cx, tail.span, tail.hir_id, self.accept_comment_above_attributes) && let Some(help_span) = expr_has_unnecessary_safety_comment(cx, tail, pos) { span_lint_and_then( @@ -167,7 +168,8 @@ }; if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, stmt.hir_id) && !stmt.span.in_external_macro(cx.tcx.sess.source_map()) - && let HasSafetyComment::Yes(pos) = stmt_has_safety_comment(cx, stmt.span, stmt.hir_id) + && let HasSafetyComment::Yes(pos) = + stmt_has_safety_comment(cx, stmt.span, stmt.hir_id, self.accept_comment_above_attributes) && let Some(help_span) = expr_has_unnecessary_safety_comment(cx, expr, pos) { span_lint_and_then( @@ -534,7 +536,12 @@ /// Checks if the lines immediately preceding the item contain a safety comment. #[allow(clippy::collapsible_match)] -fn stmt_has_safety_comment(cx: &LateContext<'_>, span: Span, hir_id: HirId) -> HasSafetyComment { +fn stmt_has_safety_comment( + cx: &LateContext<'_>, + span: Span, + hir_id: HirId, + accept_comment_above_attributes: bool, +) -> HasSafetyComment { match span_from_macro_expansion_has_safety_comment(cx, span) { HasSafetyComment::Maybe => (), has_safety_comment => return has_safety_comment, @@ -549,6 +556,13 @@ _ => return HasSafetyComment::Maybe, }; + // if span_with_attrs_has_safety_comment(cx, span, hir_id, accept_comment_above_attrib + // } + let mut span = span; + if accept_comment_above_attributes { + span = include_attrs_in_span(cx, hir_id, span); + } + let source_map = cx.sess().source_map(); if let Some(comment_start) = comment_start && let Ok(unsafe_line) = source_map.lookup_line(span.lo())
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_test_deps/Cargo.toml b/clippy_test_deps/Cargo.toml new file mode 100644 index 0000000..f41334f --- /dev/null +++ b/clippy_test_deps/Cargo.toml
@@ -0,0 +1,17 @@ +[package] +name = "clippy_test_deps" +version = "0.1.0" +edition = "2021" + +# Add dependencies here to make them available in ui tests. + +[dependencies] +regex = "1.5.5" +serde = { version = "1.0.145", features = ["derive"] } +if_chain = "1.0" +quote = "1.0.25" +syn = { version = "2.0", features = ["full"] } +futures = "0.3" +parking_lot = "0.12" +tokio = { version = "1", features = ["io-util"] } +itertools = "0.12"
diff --git a/clippy_test_deps/src/main.rs b/clippy_test_deps/src/main.rs new file mode 100644 index 0000000..f328e4d --- /dev/null +++ b/clippy_test_deps/src/main.rs
@@ -0,0 +1 @@ +fn main() {}
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/hir_utils.rs b/clippy_utils/src/hir_utils.rs index c37231d..e249f87 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs
@@ -2,6 +2,7 @@ use crate::macros::macro_backtrace; use crate::source::{SpanRange, SpanRangeExt, walk_span_to_context}; use crate::tokenize_with_text; +use rustc_ast::ast; use rustc_ast::ast::InlineAsmTemplatePiece; use rustc_data_structures::fx::FxHasher; use rustc_hir::MatchSource::TryDesugar; @@ -9,8 +10,8 @@ use rustc_hir::{ AssocItemConstraint, BinOpKind, BindingMode, Block, BodyId, Closure, ConstArg, ConstArgKind, Expr, ExprField, ExprKind, FnRetTy, GenericArg, GenericArgs, HirId, HirIdMap, InlineAsmOperand, LetExpr, Lifetime, LifetimeKind, - Pat, PatExpr, PatExprKind, PatField, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, StructTailExpr, - TraitBoundModifiers, Ty, TyKind, TyPat, TyPatKind, + Node, Pat, PatExpr, PatExprKind, PatField, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, + StructTailExpr, TraitBoundModifiers, Ty, TyKind, TyPat, TyPatKind, }; use rustc_lexer::{TokenKind, tokenize}; use rustc_lint::LateContext; @@ -1004,8 +1005,8 @@ self.hash_expr(e); } }, - ExprKind::Match(e, arms, s) => { - self.hash_expr(e); + ExprKind::Match(scrutinee, arms, _) => { + self.hash_expr(scrutinee); for arm in *arms { self.hash_pat(arm.pat); @@ -1014,8 +1015,6 @@ } self.hash_expr(arm.body); } - - s.hash(&mut self.s); }, ExprKind::MethodCall(path, receiver, args, _fn_span) => { self.hash_name(path.ident.name); @@ -1058,8 +1057,8 @@ ExprKind::Use(expr, _) => { self.hash_expr(expr); }, - ExprKind::Unary(lop, le) => { - std::mem::discriminant(lop).hash(&mut self.s); + ExprKind::Unary(l_op, le) => { + std::mem::discriminant(l_op).hash(&mut self.s); self.hash_expr(le); }, ExprKind::UnsafeBinderCast(kind, expr, ty) => { @@ -1394,3 +1393,70 @@ } f(cx, left.into_range(), right.into_range(), pred) } + +/// Returns true if the expression contains ambiguous literals (unsuffixed float or int literals) +/// that could be interpreted as either f32/f64 or i32/i64 depending on context. +pub fn has_ambiguous_literal_in_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + match expr.kind { + ExprKind::Path(ref qpath) => { + if let Res::Local(hir_id) = cx.qpath_res(qpath, expr.hir_id) + && let Node::LetStmt(local) = cx.tcx.parent_hir_node(hir_id) + && local.ty.is_none() + && let Some(init) = local.init + { + return has_ambiguous_literal_in_expr(cx, init); + } + false + }, + ExprKind::Lit(lit) => matches!( + lit.node, + ast::LitKind::Float(_, ast::LitFloatType::Unsuffixed) | ast::LitKind::Int(_, ast::LitIntType::Unsuffixed) + ), + + ExprKind::Array(exprs) | ExprKind::Tup(exprs) => exprs.iter().any(|e| has_ambiguous_literal_in_expr(cx, e)), + + ExprKind::Assign(lhs, rhs, _) | ExprKind::AssignOp(_, lhs, rhs) | ExprKind::Binary(_, lhs, rhs) => { + has_ambiguous_literal_in_expr(cx, lhs) || has_ambiguous_literal_in_expr(cx, rhs) + }, + + ExprKind::Unary(_, e) + | ExprKind::Cast(e, _) + | ExprKind::Type(e, _) + | ExprKind::DropTemps(e) + | ExprKind::AddrOf(_, _, e) + | ExprKind::Field(e, _) + | ExprKind::Index(e, _, _) + | ExprKind::Yield(e, _) => has_ambiguous_literal_in_expr(cx, e), + + ExprKind::MethodCall(_, receiver, args, _) | ExprKind::Call(receiver, args) => { + has_ambiguous_literal_in_expr(cx, receiver) || args.iter().any(|e| has_ambiguous_literal_in_expr(cx, e)) + }, + + ExprKind::Closure(Closure { body, .. }) => { + let body = cx.tcx.hir_body(*body); + let closure_expr = crate::peel_blocks(body.value); + has_ambiguous_literal_in_expr(cx, closure_expr) + }, + + ExprKind::Block(blk, _) => blk.expr.as_ref().is_some_and(|e| has_ambiguous_literal_in_expr(cx, e)), + + ExprKind::If(cond, then_expr, else_expr) => { + has_ambiguous_literal_in_expr(cx, cond) + || has_ambiguous_literal_in_expr(cx, then_expr) + || else_expr.as_ref().is_some_and(|e| has_ambiguous_literal_in_expr(cx, e)) + }, + + ExprKind::Match(scrutinee, arms, _) => { + has_ambiguous_literal_in_expr(cx, scrutinee) + || arms.iter().any(|arm| has_ambiguous_literal_in_expr(cx, arm.body)) + }, + + ExprKind::Loop(body, ..) => body.expr.is_some_and(|e| has_ambiguous_literal_in_expr(cx, e)), + + ExprKind::Ret(opt_expr) | ExprKind::Break(_, opt_expr) => { + opt_expr.as_ref().is_some_and(|e| has_ambiguous_literal_in_expr(cx, e)) + }, + + _ => false, + } +}
diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 9ac8514..8a7f699 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs
@@ -77,7 +77,8 @@ pub use self::attrs::*; pub use self::check_proc_macro::{is_from_proc_macro, is_span_if, is_span_match}; pub use self::hir_utils::{ - HirEqInterExpr, SpanlessEq, SpanlessHash, both, count_eq, eq_expr_value, hash_expr, hash_stmt, is_bool, over, + HirEqInterExpr, SpanlessEq, SpanlessHash, both, count_eq, eq_expr_value, has_ambiguous_literal_in_expr, hash_expr, + hash_stmt, is_bool, over, }; use core::mem; @@ -1886,7 +1887,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 7a0bef1..24ed4c3 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs
@@ -74,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 05c24d7..be93f27 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs
@@ -34,10 +34,12 @@ } 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| clause.def_id() != sized_did) + bound.as_trait_clause().is_some_and(|clause| { + let did = clause.def_id(); + did != sized_did && did != meta_sized_did + }) }) { return Err((
diff --git a/clippy_utils/src/sym.rs b/clippy_utils/src/sym.rs index 60582ee..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, @@ -218,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/clippy_utils/src/visitors.rs b/clippy_utils/src/visitors.rs index fc6e30a..48f1c36 100644 --- a/clippy_utils/src/visitors.rs +++ b/clippy_utils/src/visitors.rs
@@ -353,7 +353,7 @@ ExprKind::Binary(_, lhs, rhs) if self.cx.typeck_results().expr_ty(lhs).peel_refs().is_primitive_ty() && self.cx.typeck_results().expr_ty(rhs).peel_refs().is_primitive_ty() => {}, - ExprKind::Unary(UnOp::Deref, e) if self.cx.typeck_results().expr_ty(e).is_ref() => (), + ExprKind::Unary(UnOp::Deref, e) if self.cx.typeck_results().expr_ty(e).is_raw_ptr() => (), ExprKind::Unary(_, e) if self.cx.typeck_results().expr_ty(e).peel_refs().is_primitive_ty() => (), ExprKind::Index(base, _, _) if matches!( @@ -388,7 +388,8 @@ | ExprKind::Repeat(..) | ExprKind::Struct(..) | ExprKind::Tup(_) - | ExprKind::Type(..) => (), + | ExprKind::Type(..) + | ExprKind::UnsafeBinderCast(..) => (), _ => { return ControlFlow::Break(()); @@ -676,10 +677,7 @@ helper(typeck, true, else_expr, f)?; } }, - ExprKind::Type(e, _) => { - helper(typeck, consume, e, f)?; - }, - ExprKind::UnsafeBinderCast(_, e, _) => { + ExprKind::Type(e, _) | ExprKind::UnsafeBinderCast(_, e, _) => { helper(typeck, consume, e, f)?; },
diff --git a/declare_clippy_lint/Cargo.toml b/declare_clippy_lint/Cargo.toml index c8a9b13..bd6b4df 100644 --- a/declare_clippy_lint/Cargo.toml +++ b/declare_clippy_lint/Cargo.toml
@@ -1,6 +1,6 @@ [package] name = "declare_clippy_lint" -version = "0.1.89" +version = "0.1.90" edition = "2024" repository = "https://github.com/rust-lang/rust-clippy" license = "MIT OR Apache-2.0"
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 202c7441..c4076cb 100644 --- a/src/driver.rs +++ b/src/driver.rs
@@ -13,6 +13,11 @@ 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; @@ -187,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/tests/compile-test.rs b/tests/compile-test.rs index cefe654..aa78633 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs
@@ -16,8 +16,10 @@ use ui_test::custom_flags::Flag; use ui_test::custom_flags::edition::Edition; use ui_test::custom_flags::rustfix::RustfixMode; +use ui_test::dependencies::DependencyBuilder; use ui_test::spanned::Spanned; -use ui_test::{Args, CommandBuilder, Config, Match, error_on_output_conflict, status_emitter}; +use ui_test::status_emitter::StatusEmitter; +use ui_test::{Args, CommandBuilder, Config, Match, error_on_output_conflict}; use std::collections::{BTreeMap, HashMap}; use std::env::{self, set_var, var_os}; @@ -27,46 +29,26 @@ use std::sync::mpsc::{Sender, channel}; use std::{fs, iter, thread}; -// Test dependencies may need an `extern crate` here to ensure that they show up -// in the depinfo file (otherwise cargo thinks they are unused) -extern crate futures; -extern crate if_chain; -extern crate itertools; -extern crate parking_lot; -extern crate quote; -extern crate syn; -extern crate tokio; - mod test_utils; -/// All crates used in UI tests are listed here -static TEST_DEPENDENCIES: &[&str] = &[ - "clippy_config", - "clippy_lints", - "clippy_utils", - "futures", - "if_chain", - "itertools", - "parking_lot", - "quote", - "regex", - "serde_derive", - "serde", - "syn", - "tokio", -]; +/// All crates used in internal UI tests are listed here. +/// We directly re-use these crates from their normal clippy builds, so we don't have them +/// in `clippy_test_devs`. That saves a lot of time but also means they don't work in a stage 1 +/// test in rustc bootstrap. +static INTERNAL_TEST_DEPENDENCIES: &[&str] = &["clippy_config", "clippy_lints", "clippy_utils"]; -/// Produces a string with an `--extern` flag for all UI test crate -/// dependencies. +/// Produces a string with an `--extern` flag for all `INTERNAL_TEST_DEPENDENCIES`. /// /// The dependency files are located by parsing the depinfo file for this test /// module. This assumes the `-Z binary-dep-depinfo` flag is enabled. All test /// dependencies must be added to Cargo.toml at the project root. Test /// dependencies that are not *directly* used by this test module require an /// `extern crate` declaration. -fn extern_flags() -> Vec<String> { +fn internal_extern_flags() -> Vec<String> { + let current_exe_path = env::current_exe().unwrap(); + let deps_path = current_exe_path.parent().unwrap(); let current_exe_depinfo = { - let mut path = env::current_exe().unwrap(); + let mut path = current_exe_path.clone(); path.set_extension("d"); fs::read_to_string(path).unwrap() }; @@ -88,7 +70,7 @@ Some((name, path_str)) }; if let Some((name, path)) = parse_name_path() - && TEST_DEPENDENCIES.contains(&name) + && INTERNAL_TEST_DEPENDENCIES.contains(&name) { // A dependency may be listed twice if it is available in sysroot, // and the sysroot dependencies are listed first. As of the writing, @@ -96,7 +78,7 @@ crates.insert(name, path); } } - let not_found: Vec<&str> = TEST_DEPENDENCIES + let not_found: Vec<&str> = INTERNAL_TEST_DEPENDENCIES .iter() .copied() .filter(|n| !crates.contains_key(n)) @@ -111,6 +93,7 @@ crates .into_iter() .map(|(name, path)| format!("--extern={name}={path}")) + .chain([format!("-Ldependency={}", deps_path.display())]) .collect() } @@ -119,7 +102,6 @@ struct TestContext { args: Args, - extern_flags: Vec<String>, diagnostic_collector: Option<DiagnosticCollector>, collector_thread: Option<thread::JoinHandle<()>>, } @@ -134,7 +116,6 @@ .unzip(); Self { args, - extern_flags: extern_flags(), diagnostic_collector, collector_thread, } @@ -158,6 +139,15 @@ }; let defaults = config.comment_defaults.base(); defaults.set_custom("edition", Edition("2024".into())); + defaults.set_custom( + "dependencies", + DependencyBuilder { + program: CommandBuilder::cargo(), + crate_manifest_path: Path::new("clippy_test_deps").join("Cargo.toml"), + build_std: None, + bless_lockfile: self.args.bless, + }, + ); defaults.exit_status = None.into(); if mandatory_annotations { defaults.require_annotations = Some(Spanned::dummy(true)).into(); @@ -182,12 +172,10 @@ "-Zui-testing", "-Zdeduplicate-diagnostics=no", "-Dwarnings", - &format!("-Ldependency={}", deps_path.display()), ] .map(OsString::from), ); - config.program.args.extend(self.extern_flags.iter().map(OsString::from)); // Prevent rustc from creating `rustc-ice-*` files the console output is enough. config.program.envs.push(("RUSTC_ICE".into(), Some("0".into()))); @@ -217,7 +205,7 @@ vec![config], ui_test::default_file_filter, ui_test::default_per_file_config, - status_emitter::Text::from(cx.args.format), + Box::<dyn StatusEmitter>::from(cx.args.format), ) .unwrap(); } @@ -227,13 +215,17 @@ return; } let mut config = cx.base_config("ui-internal", true); + config + .program + .args + .extend(internal_extern_flags().iter().map(OsString::from)); config.bless_command = Some("cargo uitest --features internal -- -- --bless".into()); ui_test::run_tests_generic( vec![config], ui_test::default_file_filter, ui_test::default_per_file_config, - status_emitter::Text::from(cx.args.format), + Box::<dyn StatusEmitter>::from(cx.args.format), ) .unwrap(); } @@ -257,7 +249,7 @@ .envs .push(("CLIPPY_CONF_DIR".into(), Some(path.parent().unwrap().into()))); }, - status_emitter::Text::from(cx.args.format), + Box::<dyn StatusEmitter>::from(cx.args.format), ) .unwrap(); } @@ -304,7 +296,7 @@ .then(|| ui_test::default_any_file_filter(path, config) && !ignored_32bit(path)) }, |_config, _file_contents| {}, - status_emitter::Text::from(cx.args.format), + Box::<dyn StatusEmitter>::from(cx.args.format), ) .unwrap(); }
diff --git a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.disabled.stderr b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.disabled.stderr index e9c5e5f..453bcf1 100644 --- a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.disabled.stderr +++ b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.disabled.stderr
@@ -450,5 +450,13 @@ LL | // SAFETY: unnecessary_safety_comment triggers here | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 52 previous errors +error: unsafe block missing a safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:733:12 + | +LL | return unsafe { h() }; + | ^^^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: aborting due to 53 previous errors
diff --git a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs index 91a02bc..a2d7c1b 100644 --- a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs +++ b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs
@@ -723,4 +723,15 @@ _ = unsafe { foo() } } +fn rfl_issue15034() -> i32 { + unsafe fn h() -> i32 { + 1i32 + } + // This shouldn't lint with accept-comment-above-attributes! Thus fixing a false positive! + // SAFETY: My safety comment! + #[allow(clippy::unnecessary_cast)] + return unsafe { h() }; + //~[disabled]^ ERROR: unsafe block missing a safety comment +} + fn main() {}
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_as_ptr.fixed b/tests/ui/borrow_as_ptr.fixed index 3ba2eea..3f6e524 100644 --- a/tests/ui/borrow_as_ptr.fixed +++ b/tests/ui/borrow_as_ptr.fixed
@@ -47,3 +47,9 @@ // Do not lint references to temporaries core::ptr::eq(&0i32, &1i32); } + +fn issue_15141() { + let a = String::new(); + // Don't lint cast to dyn trait pointers + let b = &a as *const dyn std::any::Any; +}
diff --git a/tests/ui/borrow_as_ptr.rs b/tests/ui/borrow_as_ptr.rs index 8cdd051..20f4f40 100644 --- a/tests/ui/borrow_as_ptr.rs +++ b/tests/ui/borrow_as_ptr.rs
@@ -47,3 +47,9 @@ // Do not lint references to temporaries core::ptr::eq(&0i32, &1i32); } + +fn issue_15141() { + let a = String::new(); + // Don't lint cast to dyn trait pointers + let b = &a as *const dyn std::any::Any; +}
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/coerce_container_to_any.fixed b/tests/ui/coerce_container_to_any.fixed index ae9d3ef..b5b3f15 100644 --- a/tests/ui/coerce_container_to_any.fixed +++ b/tests/ui/coerce_container_to_any.fixed
@@ -3,7 +3,7 @@ use std::any::Any; fn main() { - let x: Box<dyn Any> = Box::new(()); + let mut x: Box<dyn Any> = Box::new(()); let ref_x = &x; f(&*x); @@ -15,12 +15,23 @@ let _: &dyn Any = &*x; //~^ coerce_container_to_any + let _: &dyn Any = &*x; + //~^ coerce_container_to_any + + let _: &mut dyn Any = &mut *x; + //~^ coerce_container_to_any + f(&42); f(&Box::new(())); f(&Box::new(Box::new(()))); + let ref_x = &x; f(&**ref_x); f(&*x); let _: &dyn Any = &*x; + + // https://github.com/rust-lang/rust-clippy/issues/15045 + #[allow(clippy::needless_borrow)] + (&x).downcast_ref::<()>().unwrap(); } fn f(_: &dyn Any) {}
diff --git a/tests/ui/coerce_container_to_any.rs b/tests/ui/coerce_container_to_any.rs index 9948bd4..4d6527b 100644 --- a/tests/ui/coerce_container_to_any.rs +++ b/tests/ui/coerce_container_to_any.rs
@@ -3,7 +3,7 @@ use std::any::Any; fn main() { - let x: Box<dyn Any> = Box::new(()); + let mut x: Box<dyn Any> = Box::new(()); let ref_x = &x; f(&x); @@ -15,12 +15,23 @@ let _: &dyn Any = &x; //~^ coerce_container_to_any + let _: &dyn Any = &mut x; + //~^ coerce_container_to_any + + let _: &mut dyn Any = &mut x; + //~^ coerce_container_to_any + f(&42); f(&Box::new(())); f(&Box::new(Box::new(()))); + let ref_x = &x; f(&**ref_x); f(&*x); let _: &dyn Any = &*x; + + // https://github.com/rust-lang/rust-clippy/issues/15045 + #[allow(clippy::needless_borrow)] + (&x).downcast_ref::<()>().unwrap(); } fn f(_: &dyn Any) {}
diff --git a/tests/ui/coerce_container_to_any.stderr b/tests/ui/coerce_container_to_any.stderr index 00ab77e..26389c9 100644 --- a/tests/ui/coerce_container_to_any.stderr +++ b/tests/ui/coerce_container_to_any.stderr
@@ -19,5 +19,17 @@ LL | let _: &dyn Any = &x; | ^^ help: consider dereferencing: `&*x` -error: aborting due to 3 previous errors +error: coercing `&mut std::boxed::Box<dyn std::any::Any>` to `&dyn Any` + --> tests/ui/coerce_container_to_any.rs:18:23 + | +LL | let _: &dyn Any = &mut x; + | ^^^^^^ help: consider dereferencing: `&*x` + +error: coercing `&mut std::boxed::Box<dyn std::any::Any>` to `&mut dyn Any` + --> tests/ui/coerce_container_to_any.rs:21:27 + | +LL | let _: &mut dyn Any = &mut x; + | ^^^^^^ help: consider dereferencing: `&mut *x` + +error: aborting due to 5 previous errors
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/doc_nested_refdef_list_item.fixed b/tests/ui/doc/doc_nested_refdef_list_item.fixed index 065f448..5c57c58 100644 --- a/tests/ui/doc/doc_nested_refdef_list_item.fixed +++ b/tests/ui/doc/doc_nested_refdef_list_item.fixed
@@ -72,8 +72,6 @@ /// ## Heading /// -/// - [x][] - Done -//~^ ERROR: link reference defined in list item -/// - [ ][] - Not Done -//~^ ERROR: link reference defined in list item +/// - [x] - Done +/// - [ ] - Not Done pub struct GithubCheckboxes;
diff --git a/tests/ui/doc/doc_nested_refdef_list_item.rs b/tests/ui/doc/doc_nested_refdef_list_item.rs index c7eab50..06b6ba4 100644 --- a/tests/ui/doc/doc_nested_refdef_list_item.rs +++ b/tests/ui/doc/doc_nested_refdef_list_item.rs
@@ -73,7 +73,5 @@ /// ## Heading /// /// - [x] - Done -//~^ ERROR: link reference defined in list item /// - [ ] - Not Done -//~^ ERROR: link reference defined in list item pub struct GithubCheckboxes;
diff --git a/tests/ui/doc/doc_nested_refdef_list_item.stderr b/tests/ui/doc/doc_nested_refdef_list_item.stderr index 5a815da..27314c7 100644 --- a/tests/ui/doc/doc_nested_refdef_list_item.stderr +++ b/tests/ui/doc/doc_nested_refdef_list_item.stderr
@@ -144,29 +144,5 @@ LL | /// - [link][]: def "title" | ++ -error: link reference defined in list item - --> tests/ui/doc/doc_nested_refdef_list_item.rs:75:7 - | -LL | /// - [x] - Done - | ^^^ - | - = help: link definitions are not shown in rendered documentation -help: for an intra-doc link, add `[]` between the label and the colon - | -LL | /// - [x][] - Done - | ++ - -error: link reference defined in list item - --> tests/ui/doc/doc_nested_refdef_list_item.rs:77:7 - | -LL | /// - [ ] - Not Done - | ^^^ - | - = help: link definitions are not shown in rendered documentation -help: for an intra-doc link, add `[]` between the label and the colon - | -LL | /// - [ ][] - Not Done - | ++ - -error: aborting due to 14 previous errors +error: aborting due to 12 previous errors
diff --git a/tests/ui/empty_loop_intrinsic.rs b/tests/ui/empty_loop_intrinsic.rs new file mode 100644 index 0000000..a550e56 --- /dev/null +++ b/tests/ui/empty_loop_intrinsic.rs
@@ -0,0 +1,13 @@ +//@check-pass + +#![warn(clippy::empty_loop)] +#![feature(intrinsics)] +#![feature(rustc_attrs)] + +// From issue #15200 +#[rustc_intrinsic] +#[rustc_nounwind] +/// # Safety +pub const unsafe fn simd_insert<T, U>(x: T, idx: u32, val: U) -> T; + +fn main() {}
diff --git a/tests/ui/floating_point_mul_add.fixed b/tests/ui/floating_point_mul_add.fixed index 83aeddb..884bae0 100644 --- a/tests/ui/floating_point_mul_add.fixed +++ b/tests/ui/floating_point_mul_add.fixed
@@ -69,3 +69,47 @@ let _ = a + b * c; } + +fn _issue14897() { + let x = 1.0; + let _ = x * 2.0 + 0.5; // should not suggest mul_add + let _ = 0.5 + x * 2.0; // should not suggest mul_add + let _ = 0.5 + x * 1.2; // should not suggest mul_add + let _ = 1.2 + x * 1.2; // should not suggest mul_add + + let x = -1.0; + let _ = 0.5 + x * 1.2; // should not suggest mul_add + + let x = { 4.0 }; + let _ = 0.5 + x * 1.2; // should not suggest mul_add + + let x = if 1 > 2 { 1.0 } else { 2.0 }; + let _ = 0.5 + x * 1.2; // should not suggest mul_add + + let x = 2.4 + 1.2; + let _ = 0.5 + x * 1.2; // should not suggest mul_add + + let f = || 4.0; + let x = f(); + let _ = 0.5 + f() * 1.2; // should not suggest mul_add + let _ = 0.5 + x * 1.2; // should not suggest mul_add + + let x = 0.1; + let y = x; + let z = y; + let _ = 0.5 + z * 1.2; // should not suggest mul_add + + let _ = 2.0f64.mul_add(x, 0.5); + //~^ suboptimal_flops + let _ = 2.0f64.mul_add(x, 0.5); + //~^ suboptimal_flops + + let _ = 2.0f64.mul_add(4.0, x); + //~^ suboptimal_flops + + let y: f64 = 1.0; + let _ = y.mul_add(2.0, 0.5); + //~^ suboptimal_flops + let _ = 1.0f64.mul_add(2.0, 0.5); + //~^ suboptimal_flops +}
diff --git a/tests/ui/floating_point_mul_add.rs b/tests/ui/floating_point_mul_add.rs index 039ee8d..9ceb2ec 100644 --- a/tests/ui/floating_point_mul_add.rs +++ b/tests/ui/floating_point_mul_add.rs
@@ -69,3 +69,47 @@ let _ = a + b * c; } + +fn _issue14897() { + let x = 1.0; + let _ = x * 2.0 + 0.5; // should not suggest mul_add + let _ = 0.5 + x * 2.0; // should not suggest mul_add + let _ = 0.5 + x * 1.2; // should not suggest mul_add + let _ = 1.2 + x * 1.2; // should not suggest mul_add + + let x = -1.0; + let _ = 0.5 + x * 1.2; // should not suggest mul_add + + let x = { 4.0 }; + let _ = 0.5 + x * 1.2; // should not suggest mul_add + + let x = if 1 > 2 { 1.0 } else { 2.0 }; + let _ = 0.5 + x * 1.2; // should not suggest mul_add + + let x = 2.4 + 1.2; + let _ = 0.5 + x * 1.2; // should not suggest mul_add + + let f = || 4.0; + let x = f(); + let _ = 0.5 + f() * 1.2; // should not suggest mul_add + let _ = 0.5 + x * 1.2; // should not suggest mul_add + + let x = 0.1; + let y = x; + let z = y; + let _ = 0.5 + z * 1.2; // should not suggest mul_add + + let _ = 0.5 + 2.0 * x; + //~^ suboptimal_flops + let _ = 2.0 * x + 0.5; + //~^ suboptimal_flops + + let _ = x + 2.0 * 4.0; + //~^ suboptimal_flops + + let y: f64 = 1.0; + let _ = y * 2.0 + 0.5; + //~^ suboptimal_flops + let _ = 1.0 * 2.0 + 0.5; + //~^ suboptimal_flops +}
diff --git a/tests/ui/floating_point_mul_add.stderr b/tests/ui/floating_point_mul_add.stderr index 6482127..dad65dd 100644 --- a/tests/ui/floating_point_mul_add.stderr +++ b/tests/ui/floating_point_mul_add.stderr
@@ -79,5 +79,35 @@ LL | let _ = a - (b * u as f64); | ^^^^^^^^^^^^^^^^^^ help: consider using: `b.mul_add(-(u as f64), a)` -error: aborting due to 13 previous errors +error: multiply and add expressions can be calculated more efficiently and accurately + --> tests/ui/floating_point_mul_add.rs:102:13 + | +LL | let _ = 0.5 + 2.0 * x; + | ^^^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(x, 0.5)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> tests/ui/floating_point_mul_add.rs:104:13 + | +LL | let _ = 2.0 * x + 0.5; + | ^^^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(x, 0.5)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> tests/ui/floating_point_mul_add.rs:107:13 + | +LL | let _ = x + 2.0 * 4.0; + | ^^^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(4.0, x)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> tests/ui/floating_point_mul_add.rs:111:13 + | +LL | let _ = y * 2.0 + 0.5; + | ^^^^^^^^^^^^^ help: consider using: `y.mul_add(2.0, 0.5)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> tests/ui/floating_point_mul_add.rs:113:13 + | +LL | let _ = 1.0 * 2.0 + 0.5; + | ^^^^^^^^^^^^^^^ help: consider using: `1.0f64.mul_add(2.0, 0.5)` + +error: aborting due to 18 previous errors
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/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_let_else_match.fixed b/tests/ui/manual_let_else_match.fixed index 588ba5e..15f604a 100644 --- a/tests/ui/manual_let_else_match.fixed +++ b/tests/ui/manual_let_else_match.fixed
@@ -137,3 +137,48 @@ fn issue11579() { let Some(msg) = Some("hi") else { unreachable!("can't happen") }; } + +#[derive(Clone, Copy)] +struct Issue9939<T> { + avalanche: T, +} + +fn issue9939() { + let issue = Some(Issue9939 { avalanche: 1 }); + let Some(Issue9939 { avalanche: tornado }) = issue else { unreachable!("can't happen") }; + let issue = Some(Issue9939 { avalanche: true }); + let Some(Issue9939 { avalanche: acid_rain }) = issue else { unreachable!("can't happen") }; + assert_eq!(tornado, 1); + assert!(acid_rain); + + // without shadowing + let _x @ Some(Issue9939 { avalanche: _y }) = issue else { unreachable!("can't happen") }; + + // with shadowing + let Some(Issue9939 { avalanche: _x }) = issue else { unreachable!("can't happen") }; +} + +#[derive(Clone, Copy)] +struct Issue9939b<T, U> { + earthquake: T, + hurricane: U, +} + +fn issue9939b() { + let issue = Some(Issue9939b { + earthquake: true, + hurricane: 1, + }); + let issue @ Some(Issue9939b { earthquake: flood, hurricane: drought }) = issue else { unreachable!("can't happen") }; + assert_eq!(drought, 1); + assert!(flood); + assert!(issue.is_some()); + + // without shadowing + let _x @ Some(Issue9939b { earthquake: erosion, hurricane: _y }) = issue else { unreachable!("can't happen") }; + assert!(erosion); + + // with shadowing + let Some(Issue9939b { earthquake: erosion, hurricane: _x }) = issue else { unreachable!("can't happen") }; + assert!(erosion); +}
diff --git a/tests/ui/manual_let_else_match.rs b/tests/ui/manual_let_else_match.rs index 6416753..44a044b 100644 --- a/tests/ui/manual_let_else_match.rs +++ b/tests/ui/manual_let_else_match.rs
@@ -177,3 +177,76 @@ _ => unreachable!("can't happen"), }; } + +#[derive(Clone, Copy)] +struct Issue9939<T> { + avalanche: T, +} + +fn issue9939() { + let issue = Some(Issue9939 { avalanche: 1 }); + let tornado = match issue { + //~^ manual_let_else + Some(Issue9939 { avalanche }) => avalanche, + _ => unreachable!("can't happen"), + }; + let issue = Some(Issue9939 { avalanche: true }); + let acid_rain = match issue { + //~^ manual_let_else + Some(Issue9939 { avalanche: tornado }) => tornado, + _ => unreachable!("can't happen"), + }; + assert_eq!(tornado, 1); + assert!(acid_rain); + + // without shadowing + let _y = match issue { + //~^ manual_let_else + _x @ Some(Issue9939 { avalanche }) => avalanche, + None => unreachable!("can't happen"), + }; + + // with shadowing + let _x = match issue { + //~^ manual_let_else + _x @ Some(Issue9939 { avalanche }) => avalanche, + None => unreachable!("can't happen"), + }; +} + +#[derive(Clone, Copy)] +struct Issue9939b<T, U> { + earthquake: T, + hurricane: U, +} + +fn issue9939b() { + let issue = Some(Issue9939b { + earthquake: true, + hurricane: 1, + }); + let (issue, drought, flood) = match issue { + //~^ manual_let_else + flood @ Some(Issue9939b { earthquake, hurricane }) => (flood, hurricane, earthquake), + None => unreachable!("can't happen"), + }; + assert_eq!(drought, 1); + assert!(flood); + assert!(issue.is_some()); + + // without shadowing + let (_y, erosion) = match issue { + //~^ manual_let_else + _x @ Some(Issue9939b { earthquake, hurricane }) => (hurricane, earthquake), + None => unreachable!("can't happen"), + }; + assert!(erosion); + + // with shadowing + let (_x, erosion) = match issue { + //~^ manual_let_else + _x @ Some(Issue9939b { earthquake, hurricane }) => (hurricane, earthquake), + None => unreachable!("can't happen"), + }; + assert!(erosion); +}
diff --git a/tests/ui/manual_let_else_match.stderr b/tests/ui/manual_let_else_match.stderr index 393562c..ed6117e 100644 --- a/tests/ui/manual_let_else_match.stderr +++ b/tests/ui/manual_let_else_match.stderr
@@ -101,5 +101,75 @@ LL | | }; | |______^ help: consider writing: `let Some(msg) = Some("hi") else { unreachable!("can't happen") };` -error: aborting due to 10 previous errors +error: this could be rewritten as `let...else` + --> tests/ui/manual_let_else_match.rs:188:5 + | +LL | / let tornado = match issue { +LL | | +LL | | Some(Issue9939 { avalanche }) => avalanche, +LL | | _ => unreachable!("can't happen"), +LL | | }; + | |______^ help: consider writing: `let Some(Issue9939 { avalanche: tornado }) = issue else { unreachable!("can't happen") };` + +error: this could be rewritten as `let...else` + --> tests/ui/manual_let_else_match.rs:194:5 + | +LL | / let acid_rain = match issue { +LL | | +LL | | Some(Issue9939 { avalanche: tornado }) => tornado, +LL | | _ => unreachable!("can't happen"), +LL | | }; + | |______^ help: consider writing: `let Some(Issue9939 { avalanche: acid_rain }) = issue else { unreachable!("can't happen") };` + +error: this could be rewritten as `let...else` + --> tests/ui/manual_let_else_match.rs:203:5 + | +LL | / let _y = match issue { +LL | | +LL | | _x @ Some(Issue9939 { avalanche }) => avalanche, +LL | | None => unreachable!("can't happen"), +LL | | }; + | |______^ help: consider writing: `let _x @ Some(Issue9939 { avalanche: _y }) = issue else { unreachable!("can't happen") };` + +error: this could be rewritten as `let...else` + --> tests/ui/manual_let_else_match.rs:210:5 + | +LL | / let _x = match issue { +LL | | +LL | | _x @ Some(Issue9939 { avalanche }) => avalanche, +LL | | None => unreachable!("can't happen"), +LL | | }; + | |______^ help: consider writing: `let Some(Issue9939 { avalanche: _x }) = issue else { unreachable!("can't happen") };` + +error: this could be rewritten as `let...else` + --> tests/ui/manual_let_else_match.rs:228:5 + | +LL | / let (issue, drought, flood) = match issue { +LL | | +LL | | flood @ Some(Issue9939b { earthquake, hurricane }) => (flood, hurricane, earthquake), +LL | | None => unreachable!("can't happen"), +LL | | }; + | |______^ help: consider writing: `let issue @ Some(Issue9939b { earthquake: flood, hurricane: drought }) = issue else { unreachable!("can't happen") };` + +error: this could be rewritten as `let...else` + --> tests/ui/manual_let_else_match.rs:238:5 + | +LL | / let (_y, erosion) = match issue { +LL | | +LL | | _x @ Some(Issue9939b { earthquake, hurricane }) => (hurricane, earthquake), +LL | | None => unreachable!("can't happen"), +LL | | }; + | |______^ help: consider writing: `let _x @ Some(Issue9939b { earthquake: erosion, hurricane: _y }) = issue else { unreachable!("can't happen") };` + +error: this could be rewritten as `let...else` + --> tests/ui/manual_let_else_match.rs:246:5 + | +LL | / let (_x, erosion) = match issue { +LL | | +LL | | _x @ Some(Issue9939b { earthquake, hurricane }) => (hurricane, earthquake), +LL | | None => unreachable!("can't happen"), +LL | | }; + | |______^ help: consider writing: `let Some(Issue9939b { earthquake: erosion, hurricane: _x }) = issue else { unreachable!("can't happen") };` + +error: aborting due to 17 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/neg_multiply.fixed b/tests/ui/neg_multiply.fixed index ff6e083..32d466e 100644 --- a/tests/ui/neg_multiply.fixed +++ b/tests/ui/neg_multiply.fixed
@@ -82,3 +82,15 @@ -1.0 * -1.0; // should be ok } + +struct Y { + delta: f64, +} + +fn nested() { + let a = Y { delta: 1.0 }; + let b = Y { delta: 1.0 }; + let _ = (-(a.delta - 0.5).abs()).total_cmp(&1.0); + //~^ neg_multiply + let _ = (-(a.delta - 0.5).abs()).total_cmp(&1.0); +}
diff --git a/tests/ui/neg_multiply.rs b/tests/ui/neg_multiply.rs index b0f4e85..241a72c 100644 --- a/tests/ui/neg_multiply.rs +++ b/tests/ui/neg_multiply.rs
@@ -82,3 +82,15 @@ -1.0 * -1.0; // should be ok } + +struct Y { + delta: f64, +} + +fn nested() { + let a = Y { delta: 1.0 }; + let b = Y { delta: 1.0 }; + let _ = ((a.delta - 0.5).abs() * -1.0).total_cmp(&1.0); + //~^ neg_multiply + let _ = (-(a.delta - 0.5).abs()).total_cmp(&1.0); +}
diff --git a/tests/ui/neg_multiply.stderr b/tests/ui/neg_multiply.stderr index 2ef7e32..f4fb6d3 100644 --- a/tests/ui/neg_multiply.stderr +++ b/tests/ui/neg_multiply.stderr
@@ -97,5 +97,11 @@ LL | (3.0_f32 as f64) * -1.0; | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `-(3.0_f32 as f64)` -error: aborting due to 16 previous errors +error: this multiplication by -1 can be written more succinctly + --> tests/ui/neg_multiply.rs:93:13 + | +LL | let _ = ((a.delta - 0.5).abs() * -1.0).total_cmp(&1.0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(-(a.delta - 0.5).abs())` + +error: aborting due to 17 previous errors
diff --git a/tests/ui/redundant_closure_call_fixable.fixed b/tests/ui/redundant_closure_call_fixable.fixed index 099c118..9f6643e 100644 --- a/tests/ui/redundant_closure_call_fixable.fixed +++ b/tests/ui/redundant_closure_call_fixable.fixed
@@ -60,7 +60,7 @@ //~^ redundant_closure_call // immediately calling only one closure, so we can't remove the other ones - let a = (|| || 123); + let a = || || 123; //~^ redundant_closure_call dbg!(a()()); @@ -144,3 +144,15 @@ // different. make_closure!(x)(); } + +#[rustfmt::skip] +fn issue_9583() { + Some(true) == Some(true); + //~^ redundant_closure_call + Some(true) == Some(true); + //~^ redundant_closure_call + Some(if 1 > 2 {1} else {2}) == Some(2); + //~^ redundant_closure_call + Some( 1 > 2 ) == Some(true); + //~^ redundant_closure_call +}
diff --git a/tests/ui/redundant_closure_call_fixable.rs b/tests/ui/redundant_closure_call_fixable.rs index da5dd7e..34f2287 100644 --- a/tests/ui/redundant_closure_call_fixable.rs +++ b/tests/ui/redundant_closure_call_fixable.rs
@@ -144,3 +144,15 @@ // different. make_closure!(x)(); } + +#[rustfmt::skip] +fn issue_9583() { + (|| { Some(true) })() == Some(true); + //~^ redundant_closure_call + (|| Some(true))() == Some(true); + //~^ redundant_closure_call + (|| { Some(if 1 > 2 {1} else {2}) })() == Some(2); + //~^ redundant_closure_call + (|| { Some( 1 > 2 ) })() == Some(true); + //~^ redundant_closure_call +}
diff --git a/tests/ui/redundant_closure_call_fixable.stderr b/tests/ui/redundant_closure_call_fixable.stderr index 2c35aaf..a5591cf 100644 --- a/tests/ui/redundant_closure_call_fixable.stderr +++ b/tests/ui/redundant_closure_call_fixable.stderr
@@ -95,7 +95,7 @@ --> tests/ui/redundant_closure_call_fixable.rs:63:13 | LL | let a = (|| || || 123)(); - | ^^^^^^^^^^^^^^^^ help: try doing something like: `(|| || 123)` + | ^^^^^^^^^^^^^^^^ help: try doing something like: `|| || 123` error: try not to call a closure in the expression where it is declared --> tests/ui/redundant_closure_call_fixable.rs:68:13 @@ -145,5 +145,29 @@ LL | std::convert::identity((|| 13_i32 + 36_i32)()).leading_zeros(); | ^^^^^^^^^^^^^^^^^^^^^^ help: try doing something like: `13_i32 + 36_i32` -error: aborting due to 17 previous errors +error: try not to call a closure in the expression where it is declared + --> tests/ui/redundant_closure_call_fixable.rs:150:5 + | +LL | (|| { Some(true) })() == Some(true); + | ^^^^^^^^^^^^^^^^^^^^^ help: try doing something like: `Some(true)` + +error: try not to call a closure in the expression where it is declared + --> tests/ui/redundant_closure_call_fixable.rs:152:5 + | +LL | (|| Some(true))() == Some(true); + | ^^^^^^^^^^^^^^^^^ help: try doing something like: `Some(true)` + +error: try not to call a closure in the expression where it is declared + --> tests/ui/redundant_closure_call_fixable.rs:154:5 + | +LL | (|| { Some(if 1 > 2 {1} else {2}) })() == Some(2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try doing something like: `Some(if 1 > 2 {1} else {2})` + +error: try not to call a closure in the expression where it is declared + --> tests/ui/redundant_closure_call_fixable.rs:156:5 + | +LL | (|| { Some( 1 > 2 ) })() == Some(true); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try doing something like: `Some( 1 > 2 )` + +error: aborting due to 21 previous errors
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/std_instead_of_core.fixed b/tests/ui/std_instead_of_core.fixed index 1820ade..603ab0a 100644 --- a/tests/ui/std_instead_of_core.fixed +++ b/tests/ui/std_instead_of_core.fixed
@@ -8,7 +8,6 @@ #[macro_use] extern crate proc_macro_derive; -#[warn(clippy::std_instead_of_core)] fn std_instead_of_core() { // Regular import use core::hash::Hasher; @@ -90,9 +89,3 @@ #[clippy::msrv = "1.77"] fn msrv_1_77(_: core::net::IpAddr) {} //~^ std_instead_of_core - -#[warn(clippy::std_instead_of_core)] -#[rustfmt::skip] -fn issue14982() { - use std::{collections::HashMap, hash::Hash}; -}
diff --git a/tests/ui/std_instead_of_core.rs b/tests/ui/std_instead_of_core.rs index 32c4933..b6d4aba 100644 --- a/tests/ui/std_instead_of_core.rs +++ b/tests/ui/std_instead_of_core.rs
@@ -8,7 +8,6 @@ #[macro_use] extern crate proc_macro_derive; -#[warn(clippy::std_instead_of_core)] fn std_instead_of_core() { // Regular import use std::hash::Hasher; @@ -90,9 +89,3 @@ #[clippy::msrv = "1.77"] fn msrv_1_77(_: std::net::IpAddr) {} //~^ std_instead_of_core - -#[warn(clippy::std_instead_of_core)] -#[rustfmt::skip] -fn issue14982() { - use std::{collections::HashMap, hash::Hash}; -}
diff --git a/tests/ui/std_instead_of_core.stderr b/tests/ui/std_instead_of_core.stderr index 45d60d23..a5f8fbb 100644 --- a/tests/ui/std_instead_of_core.stderr +++ b/tests/ui/std_instead_of_core.stderr
@@ -1,5 +1,5 @@ error: used import from `std` instead of `core` - --> tests/ui/std_instead_of_core.rs:14:9 + --> tests/ui/std_instead_of_core.rs:13:9 | LL | use std::hash::Hasher; | ^^^ help: consider importing the item from `core`: `core` @@ -8,61 +8,61 @@ = help: to override `-D warnings` add `#[allow(clippy::std_instead_of_core)]` error: used import from `std` instead of `core` - --> tests/ui/std_instead_of_core.rs:17:11 + --> tests/ui/std_instead_of_core.rs:16:11 | LL | use ::std::hash::Hash; | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> tests/ui/std_instead_of_core.rs:23:9 + --> tests/ui/std_instead_of_core.rs:22:9 | LL | use std::fmt::{Debug, Result}; | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> tests/ui/std_instead_of_core.rs:28:9 + --> tests/ui/std_instead_of_core.rs:27:9 | LL | use std::{ | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> tests/ui/std_instead_of_core.rs:35:15 + --> tests/ui/std_instead_of_core.rs:34:15 | LL | let ptr = std::ptr::null::<u32>(); | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> tests/ui/std_instead_of_core.rs:37:21 + --> tests/ui/std_instead_of_core.rs:36:21 | LL | let ptr_mut = ::std::ptr::null_mut::<usize>(); | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> tests/ui/std_instead_of_core.rs:41:16 + --> tests/ui/std_instead_of_core.rs:40:16 | LL | let cell = std::cell::Cell::new(8u32); | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> tests/ui/std_instead_of_core.rs:43:27 + --> tests/ui/std_instead_of_core.rs:42:27 | LL | let cell_absolute = ::std::cell::Cell::new(8u32); | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> tests/ui/std_instead_of_core.rs:48:9 + --> tests/ui/std_instead_of_core.rs:47:9 | LL | use std::error::Error; | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> tests/ui/std_instead_of_core.rs:52:9 + --> tests/ui/std_instead_of_core.rs:51:9 | LL | use std::iter::Iterator; | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `alloc` - --> tests/ui/std_instead_of_core.rs:59:9 + --> tests/ui/std_instead_of_core.rs:58:9 | LL | use std::vec; | ^^^ help: consider importing the item from `alloc`: `alloc` @@ -71,13 +71,13 @@ = help: to override `-D warnings` add `#[allow(clippy::std_instead_of_alloc)]` error: used import from `std` instead of `alloc` - --> tests/ui/std_instead_of_core.rs:61:9 + --> tests/ui/std_instead_of_core.rs:60:9 | LL | use std::vec::Vec; | ^^^ help: consider importing the item from `alloc`: `alloc` error: used import from `alloc` instead of `core` - --> tests/ui/std_instead_of_core.rs:67:9 + --> tests/ui/std_instead_of_core.rs:66:9 | LL | use alloc::slice::from_ref; | ^^^^^ help: consider importing the item from `core`: `core` @@ -86,13 +86,13 @@ = help: to override `-D warnings` add `#[allow(clippy::alloc_instead_of_core)]` error: used import from `std` instead of `core` - --> tests/ui/std_instead_of_core.rs:82:9 + --> tests/ui/std_instead_of_core.rs:81:9 | LL | std::intrinsics::copy(a, b, 1); | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> tests/ui/std_instead_of_core.rs:91:17 + --> tests/ui/std_instead_of_core.rs:90:17 | LL | fn msrv_1_77(_: std::net::IpAddr) {} | ^^^ help: consider importing the item from `core`: `core`
diff --git a/tests/ui/std_instead_of_core_unfixable.rs b/tests/ui/std_instead_of_core_unfixable.rs new file mode 100644 index 0000000..957f472 --- /dev/null +++ b/tests/ui/std_instead_of_core_unfixable.rs
@@ -0,0 +1,18 @@ +//@no-rustfix + +#![warn(clippy::std_instead_of_core)] +#![warn(clippy::std_instead_of_alloc)] +#![allow(unused_imports)] + +#[rustfmt::skip] +fn issue14982() { + use std::{collections::HashMap, hash::Hash}; + //~^ std_instead_of_core +} + +#[rustfmt::skip] +fn issue15143() { + use std::{error::Error, vec::Vec, fs::File}; + //~^ std_instead_of_core + //~| std_instead_of_alloc +}
diff --git a/tests/ui/std_instead_of_core_unfixable.stderr b/tests/ui/std_instead_of_core_unfixable.stderr new file mode 100644 index 0000000..0cdec56 --- /dev/null +++ b/tests/ui/std_instead_of_core_unfixable.stderr
@@ -0,0 +1,30 @@ +error: used import from `std` instead of `core` + --> tests/ui/std_instead_of_core_unfixable.rs:9:43 + | +LL | use std::{collections::HashMap, hash::Hash}; + | ^^^^ + | + = help: consider importing the item from `core` + = note: `-D clippy::std-instead-of-core` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::std_instead_of_core)]` + +error: used import from `std` instead of `core` + --> tests/ui/std_instead_of_core_unfixable.rs:15:22 + | +LL | use std::{error::Error, vec::Vec, fs::File}; + | ^^^^^ + | + = help: consider importing the item from `core` + +error: used import from `std` instead of `alloc` + --> tests/ui/std_instead_of_core_unfixable.rs:15:34 + | +LL | use std::{error::Error, vec::Vec, fs::File}; + | ^^^ + | + = help: consider importing the item from `alloc` + = note: `-D clippy::std-instead-of-alloc` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::std_instead_of_alloc)]` + +error: aborting due to 3 previous errors +
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_operation.fixed b/tests/ui/unnecessary_operation.fixed index 645b56f..ac9fa4d 100644 --- a/tests/ui/unnecessary_operation.fixed +++ b/tests/ui/unnecessary_operation.fixed
@@ -144,3 +144,16 @@ assert!([42, 55].len() > get_usize()); //~^ unnecessary_operation } + +fn issue15173() { + // No lint as `Box::new(None)` alone would be ambiguous + Box::new(None) as Box<Option<i32>>; +} + +#[expect(clippy::redundant_closure_call)] +fn issue15173_original<MsU>(handler: impl FnOnce() -> MsU + Clone + 'static) { + Box::new(move |value| { + (|_| handler.clone()())(value); + None + }) as Box<dyn Fn(i32) -> Option<i32>>; +}
diff --git a/tests/ui/unnecessary_operation.rs b/tests/ui/unnecessary_operation.rs index 97e9026..a3e6c62 100644 --- a/tests/ui/unnecessary_operation.rs +++ b/tests/ui/unnecessary_operation.rs
@@ -150,3 +150,16 @@ [42, 55][get_usize()]; //~^ unnecessary_operation } + +fn issue15173() { + // No lint as `Box::new(None)` alone would be ambiguous + Box::new(None) as Box<Option<i32>>; +} + +#[expect(clippy::redundant_closure_call)] +fn issue15173_original<MsU>(handler: impl FnOnce() -> MsU + Clone + 'static) { + Box::new(move |value| { + (|_| handler.clone()())(value); + None + }) as Box<dyn Fn(i32) -> Option<i32>>; +}
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/triagebot.toml b/triagebot.toml index 16557a4..8171841 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] @@ -40,12 +43,15 @@ [autolabel."S-waiting-on-review"] new_pr = true +[concern] +# These labels are set when there are unresolved concerns, removed otherwise +labels = ["S-waiting-on-concerns"] + [assign] contributing_url = "https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md" users_on_vacation = [ "matthiaskrgr", "Manishearth", - "blyxyas", ] [assign.owners]
diff --git a/util/gh-pages/index_template.html b/util/gh-pages/index_template.html index 865b952..6f380ec 100644 --- a/util/gh-pages/index_template.html +++ b/util/gh-pages/index_template.html
@@ -149,49 +149,45 @@ <article class="panel panel-default" id="{{lint.id}}"> {# #} <input id="label-{{lint.id}}" type="checkbox"> {# #} <label for="label-{{lint.id}}"> {# #} - <header class="panel-heading"> {# #} - <h2 class="panel-title"> {# #} - <div class="panel-title-name" id="lint-{{lint.id}}"> {# #} - <span>{{lint.id}}</span> {#+ #} - <a href="#{{lint.id}}" class="lint-anchor anchor label label-default">¶</a> {#+ #} - <a href="" class="copy-to-clipboard anchor label label-default"> {# #} - 📋 {# #} - </a> {# #} - </div> {# #} + <h2 class="lint-title"> {# #} + <div class="panel-title-name" id="lint-{{lint.id}}"> {# #} + {{lint.id +}} + <a href="#{{lint.id}}" class="anchor label label-default">¶</a> {#+ #} + <a href="" class="copy-to-clipboard anchor label label-default"> {# #} + 📋 {# #} + </a> {# #} + </div> {# #} - <div class="panel-title-addons"> {# #} - <span class="label label-lint-group label-default label-group-{{lint.group}}">{{lint.group}}</span> {#+ #} + <span class="label label-lint-group label-default label-group-{{lint.group}}">{{lint.group}}</span> {#+ #} - <span class="label label-lint-level label-lint-level-{{lint.level}}">{{lint.level}}</span> {#+ #} + <span class="label label-lint-level label-lint-level-{{lint.level}}">{{lint.level}}</span> {#+ #} - <span class="label label-doc-folding"></span> {# #} - </div> {# #} - </h2> {# #} - </header> {# #} + <span class="label label-doc-folding"></span> {# #} + </h2> {# #} </label> {# #} <div class="list-group lint-docs"> {# #} <div class="list-group-item lint-doc-md">{{Self::markdown(lint.docs)}}</div> {# #} <div class="lint-additional-info-container"> {# Applicability #} - <div class="lint-additional-info-item"> {# #} - <span> Applicability: </span> {# #} + <div> {# #} + Applicability: {#+ #} <span class="label label-default label-applicability">{{ lint.applicability_str() }}</span> {# #} <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint_defs/enum.Applicability.html#variants">(?)</a> {# #} </div> {# Clippy version #} - <div class="lint-additional-info-item"> {# #} - <span>{% if lint.group == "deprecated" %}Deprecated{% else %} Added{% endif +%} in: </span> {# #} + <div> {# #} + {% if lint.group == "deprecated" %}Deprecated{% else %} Added{% endif +%} in: {#+ #} <span class="label label-default label-version">{{lint.version}}</span> {# #} </div> {# Open related issues #} - <div class="lint-additional-info-item"> {# #} + <div> {# #} <a href="https://github.com/rust-lang/rust-clippy/issues?q=is%3Aissue+{{lint.id}}">Related Issues</a> {# #} </div> {# Jump to source #} {% if let Some(id_location) = lint.id_location %} - <div class="lint-additional-info-item"> {# #} + <div> {# #} <a href="https://github.com/rust-lang/rust-clippy/blob/master/{{id_location}}">View Source</a> {# #} </div> {% endif %}
diff --git a/util/gh-pages/script.js b/util/gh-pages/script.js index 285aa34..ee13f1c 100644 --- a/util/gh-pages/script.js +++ b/util/gh-pages/script.js
@@ -554,10 +554,10 @@ return; } - if (event.target.classList.contains("lint-anchor")) { - lintAnchor(event); - } else if (event.target.classList.contains("copy-to-clipboard")) { + if (event.target.classList.contains("copy-to-clipboard")) { copyToClipboard(event); + } else if (event.target.classList.contains("anchor")) { + lintAnchor(event); } });
diff --git a/util/gh-pages/style.css b/util/gh-pages/style.css index 3cc7a91..022ea87 100644 --- a/util/gh-pages/style.css +++ b/util/gh-pages/style.css
@@ -50,11 +50,25 @@ .panel-heading { cursor: pointer; } -.panel-title { display: flex; flex-wrap: wrap;} -.panel-title .label { display: inline-block; } +.lint-title { + cursor: pointer; + margin-top: 0; + margin-bottom: 0; + font-size: 16px; + display: flex; + flex-wrap: wrap; + background: var(--theme-hover); + color: var(--fg); + border: 1px solid var(--theme-popup-border); + padding: 10px 15px; + border-top-left-radius: 3px; + border-top-right-radius: 3px; + gap: 4px; +} + +.lint-title .label { display: inline-block; } .panel-title-name { flex: 1; min-width: 400px;} -.panel-title-name span { vertical-align: bottom; } .panel .panel-title-name .anchor { display: none; } .panel:hover .panel-title-name .anchor { display: inline;} @@ -147,7 +161,7 @@ display: flex; flex-flow: column; } - .lint-additional-info-item + .lint-additional-info-item { + .lint-additional-info-container > div + div { border-top: 1px solid var(--theme-popup-border); } } @@ -156,12 +170,12 @@ display: flex; flex-flow: row; } - .lint-additional-info-item + .lint-additional-info-item { + .lint-additional-info-container > div + div { border-left: 1px solid var(--theme-popup-border); } } -.lint-additional-info-item { +.lint-additional-info-container > div { display: inline-flex; min-width: 200px; flex-grow: 1;
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