diff --git a/compiler/rustc_codegen_gcc/src/declare.rs b/compiler/rustc_codegen_gcc/src/declare.rs index e4130b221ee3b..6450e2d4039ca 100644 --- a/compiler/rustc_codegen_gcc/src/declare.rs +++ b/compiler/rustc_codegen_gcc/src/declare.rs @@ -151,7 +151,6 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { /// /// If there’s a value with the same name already declared, the function will /// update the declaration and return existing Value instead. -#[expect(clippy::let_and_return)] fn declare_raw_fn<'gcc>( cx: &CodegenCx<'gcc, '_>, name: &str, diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml index 560f1e8d7fbe5..8f5ef9ca2f8f6 100644 --- a/src/tools/clippy/Cargo.toml +++ b/src/tools/clippy/Cargo.toml @@ -42,7 +42,7 @@ walkdir = "2.3" filetime = "0.2.9" itertools = "0.12" pulldown-cmark = { version = "0.11", default-features = false, features = ["html"] } -askama = { version = "0.15", default-features = false, features = ["alloc", "config", "derive"] } +askama = { version = "0.15.4", default-features = false, features = ["alloc", "config", "derive"] } [dev-dependencies.toml] version = "0.9.7" diff --git a/src/tools/clippy/book/src/development/infrastructure/sync.md b/src/tools/clippy/book/src/development/infrastructure/sync.md index 2bbdf47a83581..4506ff15d8ff9 100644 --- a/src/tools/clippy/book/src/development/infrastructure/sync.md +++ b/src/tools/clippy/book/src/development/infrastructure/sync.md @@ -79,7 +79,7 @@ to be run inside the `rust` directory): ```bash git fetch upstream # assuming upstream is the rust-lang/rust remote git switch rustup - git merge upstream/master --no-ff + git merge upstream/main --no-ff ``` > Note: This is one of the few instances where a merge commit is allowed in > a PR. @@ -99,7 +99,7 @@ to be run inside the `rust` directory): All the following commands have to be run inside the `rust` directory. -1. Make sure you have checked out the latest `master` of `rust-lang/rust`. +1. Make sure you have checked out the latest `main` of `rust-lang/rust`. 2. Sync the `rust-lang/rust-clippy` master to the rust-copy of Clippy: ```bash git switch -c clippy-subtree-update diff --git a/src/tools/clippy/book/src/development/type_checking.md b/src/tools/clippy/book/src/development/type_checking.md index 578836ecc5686..e92e766988858 100644 --- a/src/tools/clippy/book/src/development/type_checking.md +++ b/src/tools/clippy/book/src/development/type_checking.md @@ -146,7 +146,7 @@ in this chapter: - [Stages of compilation](https://rustc-dev-guide.rust-lang.org/compiler-src.html#the-main-stages-of-compilation) - [Diagnostic items](https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-items.html) -- [Type checking](https://rustc-dev-guide.rust-lang.org/type-checking.html) +- [Type checking](https://rustc-dev-guide.rust-lang.org/hir-typeck/summary.html) - [Ty module](https://rustc-dev-guide.rust-lang.org/ty.html) [Adt]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/ty_kind/enum.TyKind.html#variant.Adt @@ -154,7 +154,7 @@ in this chapter: [expr_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckResults.html#method.expr_ty [node_type]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckResults.html#method.node_type [is_char]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Ty.html#method.is_char -[is_char_source]: https://doc.rust-lang.org/nightly/nightly-rustc/src/rustc_middle/ty/sty.rs.html#1831-1834 +[is_char_source]: https://github.com/rust-lang/rust/blob/d34f1f931489618efffc4007e6b6bdb9e10f6467/compiler/rustc_middle/src/ty/sty.rs#L1429-L1432 [kind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Ty.html#method.kind [LateContext]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LateContext.html [LateLintPass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html @@ -163,5 +163,5 @@ in this chapter: [TyKind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/ty_kind/enum.TyKind.html [TypeckResults]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckResults.html [middle_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Ty.html -[hir_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/struct.Ty.html +[hir_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/hir/struct.Ty.html [lower_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_analysis/fn.lower_ty.html diff --git a/src/tools/clippy/book/src/lint_configuration.md b/src/tools/clippy/book/src/lint_configuration.md index f81dd421f59b9..57ac01828e597 100644 --- a/src/tools/clippy/book/src/lint_configuration.md +++ b/src/tools/clippy/book/src/lint_configuration.md @@ -246,7 +246,7 @@ A list of crate names to allow duplicates of ## `allowed-idents-below-min-chars` Allowed names below the minimum allowed characters. The value `".."` can be used as part of -the list to indicate, that the configured values should be appended to the default +the list to indicate that the configured values should be appended to the default configuration of Clippy. By default, any configuration will replace the default value. **Default Value:** `["i", "j", "x", "y", "z", "w", "n"]` @@ -570,12 +570,12 @@ The list of disallowed types, written as fully qualified paths. ## `doc-valid-idents` The list of words this lint should not consider as identifiers needing ticks. The value -`".."` can be used as part of the list to indicate, that the configured values should be appended to the +`".."` can be used as part of the list to indicate that the configured values should be appended to the default configuration of Clippy. By default, any configuration will replace the default value. For example: * `doc-valid-idents = ["ClipPy"]` would replace the default list with `["ClipPy"]`. * `doc-valid-idents = ["ClipPy", ".."]` would append `ClipPy` to the default list. -**Default Value:** `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "MHz", "GHz", "THz", "AccessKit", "CoAP", "CoreFoundation", "CoreGraphics", "CoreText", "DevOps", "Direct2D", "Direct3D", "DirectWrite", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "InfiniBand", "RoCE", "ClojureScript", "CoffeeScript", "JavaScript", "PostScript", "PureScript", "TypeScript", "PowerPC", "WebAssembly", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", "OpenAL", "OpenDNS", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenTelemetry", "OpenType", "WebGL", "WebGL2", "WebGPU", "WebRTC", "WebSocket", "WebTransport", "WebP", "OpenExr", "YCbCr", "sRGB", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "NetBSD", "OpenBSD", "NixOS", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]` +**Default Value:** `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "MHz", "GHz", "THz", "AccessKit", "CoAP", "CoreFoundation", "CoreGraphics", "CoreText", "DevOps", "Direct2D", "Direct3D", "DirectWrite", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "InfiniBand", "RoCE", "ClojureScript", "CoffeeScript", "JavaScript", "PostScript", "PureScript", "TypeScript", "PowerPC", "PowerShell", "WebAssembly", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", "OpenAL", "OpenDNS", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenTelemetry", "OpenType", "WebGL", "WebGL2", "WebGPU", "WebRTC", "WebSocket", "WebTransport", "WebP", "OpenExr", "YCbCr", "sRGB", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "NetBSD", "OpenBSD", "NixOS", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]` --- **Affected lints:** diff --git a/src/tools/clippy/clippy_config/src/conf.rs b/src/tools/clippy/clippy_config/src/conf.rs index 849d9a613d806..3f4997a395a8e 100644 --- a/src/tools/clippy/clippy_config/src/conf.rs +++ b/src/tools/clippy/clippy_config/src/conf.rs @@ -35,7 +35,7 @@ const DEFAULT_DOC_VALID_IDENTS: &[&str] = &[ "IPv4", "IPv6", "InfiniBand", "RoCE", "ClojureScript", "CoffeeScript", "JavaScript", "PostScript", "PureScript", "TypeScript", - "PowerPC", "WebAssembly", + "PowerPC", "PowerShell", "WebAssembly", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", @@ -423,7 +423,7 @@ define_Conf! { #[lints(multiple_crate_versions)] allowed_duplicate_crates: Vec = Vec::new(), /// Allowed names below the minimum allowed characters. The value `".."` can be used as part of - /// the list to indicate, that the configured values should be appended to the default + /// the list to indicate that the configured values should be appended to the default /// configuration of Clippy. By default, any configuration will replace the default value. #[lints(min_ident_chars)] allowed_idents_below_min_chars: Vec = @@ -620,7 +620,7 @@ define_Conf! { #[lints(disallowed_types)] disallowed_types: Vec = Vec::new(), /// The list of words this lint should not consider as identifiers needing ticks. The value - /// `".."` can be used as part of the list to indicate, that the configured values should be appended to the + /// `".."` can be used as part of the list to indicate that the configured values should be appended to the /// default configuration of Clippy. By default, any configuration will replace the default value. For example: /// * `doc-valid-idents = ["ClipPy"]` would replace the default list with `["ClipPy"]`. /// * `doc-valid-idents = ["ClipPy", ".."]` would append `ClipPy` to the default list. diff --git a/src/tools/clippy/clippy_dev/src/new_lint.rs b/src/tools/clippy/clippy_dev/src/new_lint.rs index 0b6d702d77218..72f281ca4d9d7 100644 --- a/src/tools/clippy/clippy_dev/src/new_lint.rs +++ b/src/tools/clippy/clippy_dev/src/new_lint.rs @@ -167,9 +167,9 @@ fn add_lint(lint: &LintData<'_>, enable_msrv: bool) -> io::Result<()> { let camel_name = to_camel_case(lint.name); let new_lint = if enable_msrv { - format!("Box::new(move |{ctor_arg}| Box::new({module_name}::{camel_name}::new(conf))),\n ",) + format!("Box::new(move |{ctor_arg}| Box::new({module_name}::{camel_name}::new(conf))),\n ") } else { - format!("Box::new(|{ctor_arg}| Box::new({module_name}::{camel_name})),\n ",) + format!("Box::new(|{ctor_arg}| Box::new({module_name}::{camel_name})),\n ") }; lib_rs.insert_str(comment_start, &new_lint); diff --git a/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs b/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs index aa9a6654bee32..9a1e315ae5306 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs @@ -31,6 +31,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, item: &Item, attrs: &[Attribute]) { | sym::dead_code | sym::deprecated | sym::deprecated_in_future + | sym::exported_private_dependencies | sym::hidden_glob_reexports | sym::unreachable_pub | sym::unused diff --git a/src/tools/clippy/clippy_lints/src/cargo/lint_groups_priority.rs b/src/tools/clippy/clippy_lints/src/cargo/lint_groups_priority.rs index 14c5e22fb9cd5..f937f065d6e0d 100644 --- a/src/tools/clippy/clippy_lints/src/cargo/lint_groups_priority.rs +++ b/src/tools/clippy/clippy_lints/src/cargo/lint_groups_priority.rs @@ -100,7 +100,7 @@ fn check_table(cx: &LateContext<'_>, table: &DeTable<'_>, known_groups: &FxHashS "to have lints override the group set `{}` to a lower priority", group.as_ref() ), - format!("{{ level = {:?}, priority = {low_priority} }}", group_config.level,), + format!("{{ level = {:?}, priority = {low_priority} }}", group_config.level), Applicability::MaybeIncorrect, ); }, diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs b/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs index 2eebe84923274..0591afaa6c585 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs @@ -117,7 +117,7 @@ pub(super) fn check( return; } - format!("casting `{cast_from}` to `{cast_to}` may truncate the value{suffix}",) + format!("casting `{cast_from}` to `{cast_to}` may truncate the value{suffix}") }, (ty::Adt(def, _), Some(to_nbits)) if def.is_enum() => { diff --git a/src/tools/clippy/clippy_lints/src/casts/manual_dangling_ptr.rs b/src/tools/clippy/clippy_lints/src/casts/manual_dangling_ptr.rs index be1f406770ce6..56779e8ce3d95 100644 --- a/src/tools/clippy/clippy_lints/src/casts/manual_dangling_ptr.rs +++ b/src/tools/clippy/clippy_lints/src/casts/manual_dangling_ptr.rs @@ -15,6 +15,8 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, from: &Expr<'_>, to: let init_expr = expr_or_init(cx, from); if is_expr_const_aligned(cx, init_expr, ptr_ty.ty) && let Some(std_or_core) = std_or_core(cx) + && let pointee_ty = cx.typeck_results().node_type(ptr_ty.ty.hir_id) + && pointee_ty.is_sized(cx.tcx, cx.typing_env()) { let sugg_fn = match ptr_ty.mutbl { Mutability::Not => "ptr::dangling", diff --git a/src/tools/clippy/clippy_lints/src/dbg_macro.rs b/src/tools/clippy/clippy_lints/src/dbg_macro.rs index 9197870cb6952..45de4035d992e 100644 --- a/src/tools/clippy/clippy_lints/src/dbg_macro.rs +++ b/src/tools/clippy/clippy_lints/src/dbg_macro.rs @@ -75,45 +75,47 @@ impl LateLintPass<'_> for DbgMacro { "the `dbg!` macro is intended as a debugging tool", |diag| { let mut applicability = Applicability::MachineApplicable; - let (sugg_span, suggestion) = - match is_async_move_desugar(expr).unwrap_or(expr).peel_drop_temps().kind { - // dbg!() - ExprKind::Block(..) => { - // If the `dbg!` macro is a "free" statement and not contained within other expressions, - // remove the whole statement. - if let Node::Stmt(_) = cx.tcx.parent_hir_node(expr.hir_id) - && let Some(semi_span) = - cx.sess().source_map().mac_call_stmt_semi_span(macro_call.span) - { - (macro_call.span.to(semi_span), String::new()) - } else { - (macro_call.span, String::from("()")) - } - }, - ExprKind::Match(first, arms, _) => { - let vals = collect_vals(first, arms); - let suggestion = match vals.as_slice() { - // dbg!(1) => 1 - &[val] => { - snippet_with_applicability(cx, val.span.source_callsite(), "..", &mut applicability) - .to_string() - } - // dbg!(2, 3) => (2, 3) - &[first, .., last] => { - let snippet = snippet_with_applicability( - cx, - first.span.source_callsite().to(last.span.source_callsite()), - "..", - &mut applicability, - ); - format!("({snippet})") - } - _ => unreachable!(), - }; - (macro_call.span, suggestion) - }, - _ => unreachable!(), - }; + let (sugg_span, suggestion) = match is_async_move_desugar(expr) + .unwrap_or(expr) + .peel_drop_temps() + .kind + { + // dbg!() + ExprKind::Block(..) => { + // If the `dbg!` macro is a "free" statement and not contained within other expressions, + // remove the whole statement. + if let Node::Stmt(_) = cx.tcx.parent_hir_node(expr.hir_id) + && let Some(semi_span) = cx.sess().source_map().mac_call_stmt_semi_span(macro_call.span) + { + (macro_call.span.to(semi_span), String::new()) + } else { + (macro_call.span, String::from("()")) + } + }, + ExprKind::Match(first, arms, _) => { + let vals = collect_vals(first, arms); + let suggestion = match *vals.as_slice() { + // dbg!(1) => 1 + [val] => { + snippet_with_applicability(cx, val.span.source_callsite(), "..", &mut applicability) + .to_string() + }, + // dbg!(2, 3) => (2, 3) + [first, .., last] => { + let snippet = snippet_with_applicability( + cx, + first.span.source_callsite().to(last.span.source_callsite()), + "..", + &mut applicability, + ); + format!("({snippet})") + }, + _ => unreachable!(), + }; + (macro_call.span, suggestion) + }, + _ => unreachable!(), + }; diag.span_suggestion( sugg_span, @@ -165,7 +167,7 @@ fn first_dbg_macro_in_expansion(cx: &LateContext<'_>, span: Span) -> Option, span: Span) -> Option(first: &'hir Expr<'hir>, mut arms: &'hir [Arm<'hir>]) -> Vec<&'hir Expr<'hir>> { let mut vals = vec![first]; loop { - let [arm] = arms else { unreachable!("dbg! macro expansion only has single-arm matches") }; + let [arm] = arms else { + unreachable!("dbg! macro expansion only has single-arm matches") + }; - match is_async_move_desugar(arm.body).unwrap_or(arm.body).peel_drop_temps().kind { + match is_async_move_desugar(arm.body) + .unwrap_or(arm.body) + .peel_drop_temps() + .kind + { ExprKind::Block(..) => return vals, ExprKind::Match(val, a, _) => { vals.push(val); arms = a; - } + }, _ => unreachable!("dbg! macro expansion only results in block or match expressions"), } } diff --git a/src/tools/clippy/clippy_lints/src/doc/doc_paragraphs_missing_punctuation.rs b/src/tools/clippy/clippy_lints/src/doc/doc_paragraphs_missing_punctuation.rs index a8f7346376728..605d35f48baf2 100644 --- a/src/tools/clippy/clippy_lints/src/doc/doc_paragraphs_missing_punctuation.rs +++ b/src/tools/clippy/clippy_lints/src/doc/doc_paragraphs_missing_punctuation.rs @@ -53,21 +53,26 @@ fn is_missing_punctuation(doc_string: &str) -> Vec { let mut no_report_depth = 0; let mut missing_punctuation = Vec::new(); let mut current_paragraph = None; + let mut current_event_is_missing_punctuation = false; for (event, offset) in Parser::new_ext(doc_string, main_body_opts() - Options::ENABLE_SMART_PUNCTUATION).into_offset_iter() { + let last_event_was_missing_punctuation = current_event_is_missing_punctuation; + current_event_is_missing_punctuation = false; + match event { - Event::Start( - Tag::CodeBlock(..) - | Tag::FootnoteDefinition(_) - | Tag::Heading { .. } - | Tag::HtmlBlock - | Tag::List(..) - | Tag::Table(_), - ) => { + Event::Start(Tag::FootnoteDefinition(_) | Tag::Heading { .. } | Tag::HtmlBlock | Tag::Table(_)) => { no_report_depth += 1; }, + Event::Start(Tag::CodeBlock(..) | Tag::List(..)) => { + no_report_depth += 1; + if last_event_was_missing_punctuation { + // Remove the error from the previous paragraph as it is followed by a code + // block or a list. + missing_punctuation.pop(); + } + }, Event::End(TagEnd::FootnoteDefinition) => { no_report_depth -= 1; }, @@ -83,6 +88,7 @@ fn is_missing_punctuation(doc_string: &str) -> Vec { Event::End(TagEnd::Paragraph) => { if let Some(mp) = current_paragraph { missing_punctuation.push(mp); + current_event_is_missing_punctuation = true; } }, Event::Code(..) | Event::Start(Tag::Link { .. }) | Event::End(TagEnd::Link) diff --git a/src/tools/clippy/clippy_lints/src/doc/doc_suspicious_footnotes.rs b/src/tools/clippy/clippy_lints/src/doc/doc_suspicious_footnotes.rs index deca29a1885f0..dfa6c96378644 100644 --- a/src/tools/clippy/clippy_lints/src/doc/doc_suspicious_footnotes.rs +++ b/src/tools/clippy/clippy_lints/src/doc/doc_suspicious_footnotes.rs @@ -91,7 +91,7 @@ pub fn check(cx: &LateContext<'_>, doc: &str, range: Range, fragments: &F diag.span_suggestion_verbose( this_fragment.span.shrink_to_hi(), "add footnote definition", - format!("\n\n{label}: ", label = &doc[start..end],), + format!("\n\n{label}: ", label = &doc[start..end]), Applicability::HasPlaceholders, ); } else { diff --git a/src/tools/clippy/clippy_lints/src/doc/mod.rs b/src/tools/clippy/clippy_lints/src/doc/mod.rs index ecf7acbd7ce6e..e7a984694831b 100644 --- a/src/tools/clippy/clippy_lints/src/doc/mod.rs +++ b/src/tools/clippy/clippy_lints/src/doc/mod.rs @@ -1016,6 +1016,7 @@ struct CodeTags { no_run: bool, ignore: bool, compile_fail: bool, + test_harness: bool, rust: bool, } @@ -1026,6 +1027,7 @@ impl Default for CodeTags { no_run: false, ignore: false, compile_fail: false, + test_harness: false, rust: true, } @@ -1059,7 +1061,11 @@ impl CodeTags { tags.compile_fail = true; seen_rust_tags = !seen_other_tags || seen_rust_tags; }, - "test_harness" | "standalone_crate" => { + "test_harness" => { + tags.test_harness = true; + seen_rust_tags = !seen_other_tags || seen_rust_tags; + }, + "standalone_crate" => { seen_rust_tags = !seen_other_tags || seen_rust_tags; }, _ if item.starts_with("ignore-") => seen_rust_tags = true, @@ -1295,7 +1301,7 @@ fn check_doc<'a, Events: Iterator, Range for DurationSuboptimalUnits { .typeck_results() .node_type(func_ty.hir_id) .is_diag_item(cx, sym::Duration) + && matches!(cx.typeck_results().expr_ty_adjusted(arg).kind(), ty::Uint(UintTy::U64)) // We intentionally don't want to evaluate referenced constants, as we don't want to // recommend a literal value over using constants: // // let dur = Duration::from_secs(SIXTY); // ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Duration::from_mins(1)` && let Some(Constant::Int(value)) = ConstEvalCtxt::new(cx).eval_local(arg, expr.span.ctxt()) - && let value = u64::try_from(value).expect("All Duration::from_ constructors take a u64") + && let Ok(value) = u64::try_from(value) // Cannot fail // There is no need to promote e.g. 0 seconds to 0 hours && value != 0 && let Some((promoted_constructor, promoted_value)) = self.promote(cx, func_name.ident.name, value) diff --git a/src/tools/clippy/clippy_lints/src/formatting.rs b/src/tools/clippy/clippy_lints/src/formatting.rs index 3b9c70e23e20d..7a64d3135fa55 100644 --- a/src/tools/clippy/clippy_lints/src/formatting.rs +++ b/src/tools/clippy/clippy_lints/src/formatting.rs @@ -337,7 +337,7 @@ fn check_missing_else(cx: &EarlyContext<'_>, first: &Expr, second: &Expr) { else_span, format!("this looks like {looks_like} but the `else` is missing"), None, - format!("to remove this lint, add the missing `else` or add a new line before {next_thing}",), + format!("to remove this lint, add the missing `else` or add a new line before {next_thing}"), ); } } diff --git a/src/tools/clippy/clippy_lints/src/functions/must_use.rs b/src/tools/clippy/clippy_lints/src/functions/must_use.rs index 68532de0368f8..9ad36f7789041 100644 --- a/src/tools/clippy/clippy_lints/src/functions/must_use.rs +++ b/src/tools/clippy/clippy_lints/src/functions/must_use.rs @@ -13,7 +13,7 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; use clippy_utils::source::snippet_indent; 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 clippy_utils::{is_entrypoint_fn, return_ty, trait_ref_of_method}; use rustc_hir::attrs::AttributeKind; use rustc_hir::find_attr; use rustc_span::Symbol; @@ -211,6 +211,7 @@ fn check_must_use_candidate<'tcx>( || !cx.effective_visibilities.is_exported(item_id.def_id) || is_must_use_ty(cx, return_ty(cx, item_id)) || item_span.from_expansion() + || is_entrypoint_fn(cx, item_id.def_id.to_def_id()) { return; } diff --git a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs index 9dc74a157cbf2..70176c62772b7 100644 --- a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs +++ b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs @@ -92,7 +92,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitHasher { ), ( target.span(), - format!("{}<{}, S>", target.type_name(), target.type_arguments(),), + format!("{}<{}, S>", target.type_name(), target.type_arguments()), ), ]; suggestions.extend(vis.suggestions); @@ -352,7 +352,7 @@ impl<'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'_, '_, 'tcx> { ); self.suggestions.insert( e.span, - format!("{container_name}::with_capacity_and_hasher({arg_snippet}, Default::default())",), + format!("{container_name}::with_capacity_and_hasher({arg_snippet}, Default::default())"), ); }, _ => {}, diff --git a/src/tools/clippy/clippy_lints/src/indexing_slicing.rs b/src/tools/clippy/clippy_lints/src/indexing_slicing.rs index a2fcdb4a54b43..e9ddd3ca8edef 100644 --- a/src/tools/clippy/clippy_lints/src/indexing_slicing.rs +++ b/src/tools/clippy/clippy_lints/src/indexing_slicing.rs @@ -44,7 +44,7 @@ declare_clippy_lint! { /// Checks for usage of indexing or slicing that may panic at runtime. /// /// This lint does not report on indexing or slicing operations - /// that always panic, clippy's `out_of_bound_indexing` already + /// that always panic, [out_of_bounds_indexing](#out_of_bounds_indexing) already /// handles those cases. /// /// ### Why restrict this? diff --git a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs index e7b9b1cd38819..231388e7379a3 100644 --- a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs @@ -6,6 +6,7 @@ use clippy_utils::macros::root_macro_call_first_node; use clippy_utils::source::{snippet, snippet_with_context}; use clippy_utils::visitors::{Descend, for_each_expr_without_closures}; use clippy_utils::{contains_return, sym}; +use rustc_ast::BinOpKind; use rustc_errors::Applicability; use rustc_hir::{ Block, Closure, Destination, Expr, ExprKind, HirId, InlineAsm, InlineAsmOperand, Node, Pat, Stmt, StmtKind, @@ -305,6 +306,9 @@ fn never_loop_expr<'tcx>( } }, ExprKind::Call(e, es) => never_loop_expr_all(cx, once(e).chain(es.iter()), local_labels, main_loop_id), + ExprKind::Binary(op, e1, _) if matches!(op.node, BinOpKind::And | BinOpKind::Or) => { + never_loop_expr(cx, e1, local_labels, main_loop_id) + }, ExprKind::Binary(_, e1, e2) | ExprKind::Assign(e1, e2, _) | ExprKind::AssignOp(_, e1, e2) diff --git a/src/tools/clippy/clippy_lints/src/manual_let_else.rs b/src/tools/clippy/clippy_lints/src/manual_let_else.rs index 38ee4ce104a52..cb3cc999c9361 100644 --- a/src/tools/clippy/clippy_lints/src/manual_let_else.rs +++ b/src/tools/clippy/clippy_lints/src/manual_let_else.rs @@ -301,7 +301,7 @@ fn replace_in_pattern( .collect::>(); let fields_string = fields.join(", "); - let dot_dot_str = if dot_dot.is_some() { " .." } else { "" }; + let dot_dot_str = if dot_dot.is_some() { ", .." } else { "" }; let (sn_pth, _) = snippet_with_context(cx, path.span(), span.ctxt(), "", app); return format!("{sn_pth} {{ {fields_string}{dot_dot_str} }}"); }, diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs b/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs index abbc43d8e9b04..421c6064284d5 100644 --- a/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs +++ b/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs @@ -173,7 +173,7 @@ fn handle( expr.span, format!("this pattern reimplements `{ty_name}::unwrap_or`"), "replace with", - format!("{suggestion}.unwrap_or({reindented_or_body})",), + format!("{suggestion}.unwrap_or({reindented_or_body})"), app, ); } diff --git a/src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs b/src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs index c26b2dbde7fc0..347560b14eeac 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs @@ -187,7 +187,7 @@ pub(super) fn check_match<'tcx>( diag.span_suggestion_verbose( e.span, "use `matches!` directly", - format!("{}matches!({snippet}, {pat_and_guard})", if b0 { "" } else { "!" },), + format!("{}matches!({snippet}, {pat_and_guard})", if b0 { "" } else { "!" }), applicability, ); }, diff --git a/src/tools/clippy/clippy_lints/src/methods/bytes_nth.rs b/src/tools/clippy/clippy_lints/src/methods/bytes_nth.rs index 40d521d61c118..0a3ea3005e72b 100644 --- a/src/tools/clippy/clippy_lints/src/methods/bytes_nth.rs +++ b/src/tools/clippy/clippy_lints/src/methods/bytes_nth.rs @@ -34,7 +34,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, recv: &'tcx E parent.span, format!("called `.bytes().nth().unwrap()` on a `{caller_type}`"), "try", - format!("{receiver}.as_bytes()[{n}]",), + format!("{receiver}.as_bytes()[{n}]"), applicability, ); } else { diff --git a/src/tools/clippy/clippy_lints/src/methods/into_iter_on_ref.rs b/src/tools/clippy/clippy_lints/src/methods/into_iter_on_ref.rs index c4b116af48713..5a062732721e5 100644 --- a/src/tools/clippy/clippy_lints/src/methods/into_iter_on_ref.rs +++ b/src/tools/clippy/clippy_lints/src/methods/into_iter_on_ref.rs @@ -20,7 +20,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_span: Spa cx, INTO_ITER_ON_REF, method_span, - format!("this `.into_iter()` call is equivalent to `.{method_name}()` and will not consume the `{kind}`",), + format!("this `.into_iter()` call is equivalent to `.{method_name}()` and will not consume the `{kind}`"), "call directly", method_name.to_string(), Applicability::MachineApplicable, diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_is_variant_and.rs b/src/tools/clippy/clippy_lints/src/methods/manual_is_variant_and.rs index 5ce9d364cdd84..d877b0a622918 100644 --- a/src/tools/clippy/clippy_lints/src/methods/manual_is_variant_and.rs +++ b/src/tools/clippy/clippy_lints/src/methods/manual_is_variant_and.rs @@ -1,9 +1,9 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::MaybeDef; -use clippy_utils::source::snippet_with_applicability; +use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::Sugg; -use clippy_utils::{get_parent_expr, sym}; +use clippy_utils::{SpanlessEq, get_parent_expr, sym}; use rustc_ast::LitKind; use rustc_errors::Applicability; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; @@ -228,3 +228,65 @@ pub(super) fn check_map(cx: &LateContext<'_>, expr: &Expr<'_>) { } } } + +pub(super) fn check_or<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'tcx>, + lhs: &'tcx Expr<'tcx>, + rhs: &'tcx Expr<'tcx>, + msrv: Msrv, +) { + let (some_recv, some_arg) = if let ( + ExprKind::MethodCall(none_path, none_recv, [], _), + ExprKind::MethodCall(some_path, some_recv, [some_arg], _), + ) + | ( + ExprKind::MethodCall(some_path, some_recv, [some_arg], _), + ExprKind::MethodCall(none_path, none_recv, [], _), + ) = (lhs.kind, rhs.kind) + && none_path.ident.name == sym::is_none + && some_path.ident.name == sym::is_some_and + && cx + .typeck_results() + .expr_ty_adjusted(none_recv) + .peel_refs() + .is_diag_item(cx, sym::Option) + && cx + .typeck_results() + .expr_ty_adjusted(some_recv) + .peel_refs() + .is_diag_item(cx, sym::Option) + && SpanlessEq::new(cx).eq_expr(none_recv, some_recv) + { + (some_recv, some_arg) + } else { + return; + }; + + if !msrv.meets(cx, msrvs::IS_NONE_OR) { + return; + } + + let Ok(map_func) = MapFunc::try_from(some_arg) else { + return; + }; + + span_lint_and_then( + cx, + MANUAL_IS_VARIANT_AND, + expr.span, + "manual implementation of `Option::is_none_or`", + |diag| { + let mut app = Applicability::MachineApplicable; + let (recv_snip, _) = snippet_with_context(cx, some_recv.span, expr.span.ctxt(), "_", &mut app); + let map_func_snip = map_func.sugg(cx, false, &mut app); + + diag.span_suggestion( + expr.span, + "use", + format!("{recv_snip}.is_none_or({map_func_snip})"), + app, + ); + }, + ); +} diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_try_fold.rs b/src/tools/clippy/clippy_lints/src/methods/manual_try_fold.rs index f2e127bedde56..5f5944d5d4230 100644 --- a/src/tools/clippy/clippy_lints/src/methods/manual_try_fold.rs +++ b/src/tools/clippy/clippy_lints/src/methods/manual_try_fold.rs @@ -47,7 +47,7 @@ pub(super) fn check<'tcx>( fold_span, "usage of `Iterator::fold` on a type that implements `Try`", "use `try_fold` instead", - format!("try_fold({init_snip}, {args_snip} ...)",), + format!("try_fold({init_snip}, {args_snip} ...)"), Applicability::HasPlaceholders, ); } diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index 376e93aa7e7d8..264405e6c3fb8 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -4965,6 +4965,16 @@ impl<'tcx> LateLintPass<'tcx> for Methods { io_other_error::check(cx, expr, func, args, self.msrv); swap_with_temporary::check(cx, expr, func, args); ip_constant::check(cx, expr, func, args); + unwrap_expect_used::check_call( + cx, + expr, + func, + args, + self.allow_unwrap_in_tests, + self.allow_expect_in_tests, + self.allow_unwrap_in_consts, + self.allow_expect_in_consts, + ); }, ExprKind::MethodCall(..) => { self.check_methods(cx, expr); @@ -4978,6 +4988,9 @@ impl<'tcx> LateLintPass<'tcx> for Methods { }; lint_binary_expr_with_method_call(cx, &mut info); }, + ExprKind::Binary(op, lhs, rhs) if op.node == hir::BinOpKind::Or => { + manual_is_variant_and::check_or(cx, expr, lhs, rhs, self.msrv); + }, _ => (), } } @@ -5538,7 +5551,7 @@ impl Methods { unnecessary_sort_by::check(cx, expr, call_span, arg, true); }, (sym::split, [arg]) => { - str_split::check(cx, expr, recv, arg); + str_split::check(cx, expr, recv, call_span, arg); }, (sym::splitn | sym::rsplitn, [count_arg, pat_arg]) => { if let Some(Constant::Int(count)) = ConstEvalCtxt::new(cx).eval(count_arg) { diff --git a/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs b/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs index 1239d8927acfe..7c95d65e6b95c 100644 --- a/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs +++ b/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs @@ -58,7 +58,10 @@ pub(super) fn check( .iter() .map(|x| &x.kind) .collect::>() - && let [ty::adjustment::Adjust::Deref(ty::adjustment::DerefAdjustKind::Builtin), ty::adjustment::Adjust::Borrow(_)] = *adj + && let [ + ty::adjustment::Adjust::Deref(ty::adjustment::DerefAdjustKind::Builtin), + ty::adjustment::Adjust::Borrow(_), + ] = *adj && let method_did = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id).unwrap() && let Some(method_name) = cx.tcx.get_diagnostic_name(method_did) { diff --git a/src/tools/clippy/clippy_lints/src/methods/str_split.rs b/src/tools/clippy/clippy_lints/src/methods/str_split.rs index 479064a0671e5..8641f7c0abec3 100644 --- a/src/tools/clippy/clippy_lints/src/methods/str_split.rs +++ b/src/tools/clippy/clippy_lints/src/methods/str_split.rs @@ -1,39 +1,48 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet_with_context; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::sym; use clippy_utils::visitors::is_const_evaluatable; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; +use rustc_span::Span; use super::STR_SPLIT_AT_NEWLINE; -pub(super) fn check<'a>(cx: &LateContext<'a>, expr: &'_ Expr<'_>, split_recv: &'a Expr<'_>, split_arg: &'_ Expr<'_>) { +pub(super) fn check<'a>( + cx: &LateContext<'a>, + expr: &'_ Expr<'_>, + split_recv: &'a Expr<'_>, + split_span: Span, + split_arg: &'_ Expr<'_>, +) { // We're looking for `A.trim().split(B)`, where the adjusted type of `A` is `&str` (e.g. an // expression returning `String`), and `B` is a `Pattern` that hard-codes a newline (either `"\n"` // or `"\r\n"`). There are a lot of ways to specify a pattern, and this lint only checks the most // basic ones: a `'\n'`, `"\n"`, and `"\r\n"`. - if let ExprKind::MethodCall(trim_method_name, trim_recv, [], _) = split_recv.kind + if let ExprKind::MethodCall(trim_method_name, trim_recv, [], trim_span) = split_recv.kind && trim_method_name.ident.name == sym::trim && cx.typeck_results().expr_ty_adjusted(trim_recv).peel_refs().is_str() && !is_const_evaluatable(cx, trim_recv) && let ExprKind::Lit(split_lit) = split_arg.kind - && (matches!(split_lit.node, LitKind::Char('\n')) - || matches!(split_lit.node, LitKind::Str(sym::LF | sym::CRLF, _))) + && matches!( + split_lit.node, + LitKind::Char('\n') | LitKind::Str(sym::LF | sym::CRLF, _) + ) { - let mut app = Applicability::MaybeIncorrect; - span_lint_and_sugg( + span_lint_and_then( cx, STR_SPLIT_AT_NEWLINE, expr.span, "using `str.trim().split()` with hard-coded newlines", - "use `str.lines()` instead", - format!( - "{}.lines()", - snippet_with_context(cx, trim_recv.span, expr.span.ctxt(), "..", &mut app).0 - ), - app, + |diag| { + diag.span_suggestion_verbose( + trim_span.to(split_span), // combine the call spans of the two methods + "use `str.lines()` instead", + "lines()", + Applicability::MaybeIncorrect, + ); + }, ); } } diff --git a/src/tools/clippy/clippy_lints/src/methods/unit_hash.rs b/src/tools/clippy/clippy_lints/src/methods/unit_hash.rs index 9defd5626eb47..fb447a99abdce 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unit_hash.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unit_hash.rs @@ -19,7 +19,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &' diag.span_suggestion( expr.span, "remove the call to `hash` or consider using", - format!("0_u8.hash({})", snippet(cx, arg.span, ".."),), + format!("0_u8.hash({})", snippet(cx, arg.span, "..")), Applicability::MaybeIncorrect, ); diag.note("the implementation of `Hash` for `()` is a no-op"); diff --git a/src/tools/clippy/clippy_lints/src/methods/unwrap_expect_used.rs b/src/tools/clippy/clippy_lints/src/methods/unwrap_expect_used.rs index 30db2a75df577..4effab3a5e63c 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unwrap_expect_used.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unwrap_expect_used.rs @@ -3,6 +3,7 @@ use clippy_utils::res::MaybeDef; use clippy_utils::ty::is_never_like; use clippy_utils::{is_in_test, is_inside_always_const_context, is_lint_allowed}; use rustc_hir::Expr; +use rustc_hir::def::DefKind; use rustc_lint::{LateContext, Lint}; use rustc_middle::ty; use rustc_span::sym; @@ -87,3 +88,70 @@ pub(super) fn check( }, ); } + +#[expect(clippy::too_many_arguments, clippy::fn_params_excessive_bools)] +pub(super) fn check_call( + cx: &LateContext<'_>, + expr: &Expr<'_>, + func: &Expr<'_>, + args: &[Expr<'_>], + allow_unwrap_in_consts: bool, + allow_unwrap_in_tests: bool, + allow_expect_in_consts: bool, + allow_expect_in_tests: bool, +) { + let Some(recv) = args.first() else { + return; + }; + let Some((DefKind::AssocFn, def_id)) = cx.typeck_results().type_dependent_def(func.hir_id) else { + return; + }; + + match cx.tcx.item_name(def_id) { + sym::unwrap => { + check( + cx, + expr, + recv, + false, + allow_unwrap_in_consts, + allow_unwrap_in_tests, + Variant::Unwrap, + ); + }, + sym::expect => { + check( + cx, + expr, + recv, + false, + allow_expect_in_consts, + allow_expect_in_tests, + Variant::Expect, + ); + }, + clippy_utils::sym::unwrap_err => { + check( + cx, + expr, + recv, + true, + allow_unwrap_in_consts, + allow_unwrap_in_tests, + Variant::Unwrap, + ); + }, + clippy_utils::sym::expect_err => { + check( + cx, + expr, + recv, + true, + allow_expect_in_consts, + allow_expect_in_tests, + Variant::Expect, + ); + }, + _ => (), + } +} diff --git a/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs b/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs index 87ee164a1760e..651382fb4bfe2 100644 --- a/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs +++ b/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs @@ -364,15 +364,15 @@ fn report_indexes(cx: &LateContext<'_>, map: UnindexMap> // `v.len() < 5` and `v.len() <= 5` does nothing in terms of bounds checks. // The user probably meant `v.len() > 5` LengthComparison::LengthLessThanInt | LengthComparison::LengthLessThanOrEqualInt => { - Some(format!("assert!({slice_str}.len() > {highest_index})",)) + Some(format!("assert!({slice_str}.len() > {highest_index})")) }, // `5 < v.len()` == `v.len() > 5` LengthComparison::IntLessThanLength if asserted_len < highest_index => { - Some(format!("assert!({slice_str}.len() > {highest_index})",)) + Some(format!("assert!({slice_str}.len() > {highest_index})")) }, // `5 <= v.len() == `v.len() >= 5` LengthComparison::IntLessThanOrEqualLength if asserted_len <= highest_index => { - Some(format!("assert!({slice_str}.len() > {highest_index})",)) + Some(format!("assert!({slice_str}.len() > {highest_index})")) }, // `highest_index` here is rather a length, so we need to add 1 to it LengthComparison::LengthEqualInt if asserted_len < highest_index + 1 => match macro_call { diff --git a/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs b/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs index 5dd38cf059c27..1f9652a2bd384 100644 --- a/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs +++ b/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs @@ -97,7 +97,7 @@ impl LateLintPass<'_> for ImportRename { span_without_semi, "this import should be renamed", "try", - format!("{import} as {name}",), + format!("{import} as {name}"), Applicability::MachineApplicable, ); } diff --git a/src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs b/src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs index 39097833a6c5f..db55772f4e017 100644 --- a/src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs +++ b/src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs @@ -10,10 +10,10 @@ use rustc_span::symbol::sym; use super::CMP_OWNED; -pub(super) fn check(cx: &LateContext<'_>, op: BinOpKind, lhs: &Expr<'_>, rhs: &Expr<'_>) { +pub(super) fn check(cx: &LateContext<'_>, e: &Expr<'_>, op: BinOpKind, lhs: &Expr<'_>, rhs: &Expr<'_>) { if op.is_comparison() { - check_op(cx, lhs, rhs, true); - check_op(cx, rhs, lhs, false); + check_op(cx, e, lhs, rhs, true); + check_op(cx, e, rhs, lhs, false); } } @@ -35,7 +35,11 @@ fn symmetric_partial_eq<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, other: Ty<'t }) } -fn check_op(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) { +fn check_op(cx: &LateContext<'_>, outer: &Expr<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) { + if !outer.span.eq_ctxt(expr.span) { + return; + } + let typeck = cx.typeck_results(); let (arg, arg_span) = match expr.kind { ExprKind::MethodCall(_, arg, [], _) diff --git a/src/tools/clippy/clippy_lints/src/operators/mod.rs b/src/tools/clippy/clippy_lints/src/operators/mod.rs index 53b8e9e5d5ae7..383b135dfa506 100644 --- a/src/tools/clippy/clippy_lints/src/operators/mod.rs +++ b/src/tools/clippy/clippy_lints/src/operators/mod.rs @@ -1038,7 +1038,7 @@ impl<'tcx> LateLintPass<'tcx> for Operators { float_equality_without_abs::check(cx, e, op.node, lhs, rhs); integer_division::check(cx, e, op.node, lhs, rhs); integer_division_remainder_used::check(cx, op.node, lhs, rhs, e.span); - cmp_owned::check(cx, op.node, lhs, rhs); + cmp_owned::check(cx, e, op.node, lhs, rhs); float_cmp::check(cx, e, op.node, lhs, rhs); modulo_one::check(cx, e, op.node, rhs); modulo_arithmetic::check( diff --git a/src/tools/clippy/clippy_lints/src/ptr/cmp_null.rs b/src/tools/clippy/clippy_lints/src/ptr/cmp_null.rs index f2d1c855eddda..5e1c62316b7d3 100644 --- a/src/tools/clippy/clippy_lints/src/ptr/cmp_null.rs +++ b/src/tools/clippy/clippy_lints/src/ptr/cmp_null.rs @@ -32,7 +32,7 @@ pub(super) fn check<'tcx>( expr.span, "comparing with null is better expressed by the `.is_null()` method", "try", - format!("{invert}{non_null_path_snippet}.is_null()",), + format!("{invert}{non_null_path_snippet}.is_null()"), applicability, ); true diff --git a/src/tools/clippy/clippy_lints/src/question_mark.rs b/src/tools/clippy/clippy_lints/src/question_mark.rs index e5fb3c0fa431f..5517b7f260eaa 100644 --- a/src/tools/clippy/clippy_lints/src/question_mark.rs +++ b/src/tools/clippy/clippy_lints/src/question_mark.rs @@ -474,7 +474,6 @@ fn check_if_let_some_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: if_else, .. }) = higher::IfLet::hir(cx, expr) - && !is_else_clause(cx.tcx, expr) && let PatKind::TupleStruct(ref path1, [field], ddpos) = let_pat.kind && ddpos.as_opt_usize().is_none() && let PatKind::Binding(BindingMode(by_ref, _), bind_id, ident, None) = field.kind @@ -509,10 +508,15 @@ fn check_if_let_some_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: ByRef::Yes(_, Mutability::Not) => ".as_ref()", ByRef::No => "", }; - let sugg = format!( + + let mut sugg = format!( "{receiver_str}{method_call_str}?{}", if requires_semi { ";" } else { "" } ); + if is_else_clause(cx.tcx, expr) { + sugg = format!("{{ {sugg} }}"); + } + span_lint_and_sugg( cx, QUESTION_MARK, diff --git a/src/tools/clippy/clippy_lints/src/returns/let_and_return.rs b/src/tools/clippy/clippy_lints/src/returns/let_and_return.rs index 0a00981e15bed..b19935959c4d8 100644 --- a/src/tools/clippy/clippy_lints/src/returns/let_and_return.rs +++ b/src/tools/clippy/clippy_lints/src/returns/let_and_return.rs @@ -27,7 +27,7 @@ pub(super) fn check_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'_>) && !initexpr.span.in_external_macro(cx.sess().source_map()) && !retexpr.span.in_external_macro(cx.sess().source_map()) && !local.span.from_expansion() - && !span_contains_non_whitespace(cx, stmt.span.between(retexpr.span), true) + && !span_contains_non_whitespace(cx, stmt.span.between(retexpr.span), false) { span_lint_hir_and_then( cx, diff --git a/src/tools/clippy/clippy_lints/src/strings.rs b/src/tools/clippy/clippy_lints/src/strings.rs index c0be724bcdeeb..509ad4e4fcb3b 100644 --- a/src/tools/clippy/clippy_lints/src/strings.rs +++ b/src/tools/clippy/clippy_lints/src/strings.rs @@ -5,6 +5,7 @@ use clippy_utils::{ SpanlessEq, get_expr_use_or_unification_node, get_parent_expr, is_lint_allowed, method_calls, peel_blocks, sym, }; use rustc_errors::Applicability; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, LangItem, Node}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -410,6 +411,23 @@ impl<'tcx> LateLintPass<'tcx> for StrToString { diag.span_suggestion(expr.span, "try", format!("{snippet}.to_owned()"), applicability); }, ); + } else if let ExprKind::Path(_) = expr.kind + && let Some(parent) = get_parent_expr(cx, expr) + && let ExprKind::Call(_, args) | ExprKind::MethodCall(_, _, args, _) = &parent.kind + && args.iter().any(|a| a.hir_id == expr.hir_id) + && let Res::Def(DefKind::AssocFn, def_id) = expr.res(cx) + && cx.tcx.is_diagnostic_item(sym::to_string_method, def_id) + { + // Detected `ToString::to_string` passed as an argument (generic: any call or method call) + span_lint_and_sugg( + cx, + STR_TO_STRING, + expr.span, + "`ToString::to_string` used as `&str` to `String` converter", + "try", + "ToOwned::to_owned".to_string(), + Applicability::MachineApplicable, + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/time_subtraction.rs b/src/tools/clippy/clippy_lints/src/time_subtraction.rs index ca8378ba7c6a2..c241935d6c312 100644 --- a/src/tools/clippy/clippy_lints/src/time_subtraction.rs +++ b/src/tools/clippy/clippy_lints/src/time_subtraction.rs @@ -85,9 +85,7 @@ impl LateLintPass<'_> for UncheckedTimeSubtraction { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { let (lhs, rhs) = match expr.kind { ExprKind::Binary(op, lhs, rhs) if matches!(op.node, BinOpKind::Sub,) => (lhs, rhs), - ExprKind::MethodCall(_, lhs, [rhs], _) if cx.ty_based_def(expr).is_diag_item(cx, sym::sub) => { - (lhs, rhs) - }, + ExprKind::MethodCall(_, lhs, [rhs], _) if cx.ty_based_def(expr).is_diag_item(cx, sym::sub) => (lhs, rhs), _ => return, }; let typeck = cx.typeck_results(); diff --git a/src/tools/clippy/clippy_lints/src/toplevel_ref_arg.rs b/src/tools/clippy/clippy_lints/src/toplevel_ref_arg.rs index 250c277ab5e1f..bce2ede6589b6 100644 --- a/src/tools/clippy/clippy_lints/src/toplevel_ref_arg.rs +++ b/src/tools/clippy/clippy_lints/src/toplevel_ref_arg.rs @@ -109,7 +109,7 @@ impl<'tcx> LateLintPass<'tcx> for ToplevelRefArg { diag.span_suggestion( stmt.span, "try", - format!("let {name}{tyopt} = {initref};", name = snippet(cx, name.span, ".."),), + format!("let {name}{tyopt} = {initref};", name = snippet(cx, name.span, "..")), app, ); }, diff --git a/src/tools/clippy/clippy_lints/src/zero_div_zero.rs b/src/tools/clippy/clippy_lints/src/zero_div_zero.rs index bb0cab3a30757..e44c4dc9776eb 100644 --- a/src/tools/clippy/clippy_lints/src/zero_div_zero.rs +++ b/src/tools/clippy/clippy_lints/src/zero_div_zero.rs @@ -56,7 +56,7 @@ impl<'tcx> LateLintPass<'tcx> for ZeroDiv { expr.span, "constant division of `0.0` with `0.0` will always result in NaN", None, - format!("consider using `{float_type}::NAN` if you would like a constant representing NaN",), + format!("consider using `{float_type}::NAN` if you would like a constant representing NaN"), ); } } diff --git a/src/tools/clippy/clippy_lints/src/zero_repeat_side_effects.rs b/src/tools/clippy/clippy_lints/src/zero_repeat_side_effects.rs index 95085161c09c9..cb254cc156296 100644 --- a/src/tools/clippy/clippy_lints/src/zero_repeat_side_effects.rs +++ b/src/tools/clippy/clippy_lints/src/zero_repeat_side_effects.rs @@ -168,7 +168,7 @@ fn assign_expr_suggestion( let indent = snippet_indent(cx, outer_expr.span).unwrap_or_default(); let var_name = snippet(cx, assign_expr_span.source_callsite(), ".."); if needs_curly { - format!("{{\n {indent}{inner_expr};\n {indent}{var_name} = {vec_str}[] as {return_type}\n{indent}}}",) + format!("{{\n {indent}{inner_expr};\n {indent}{var_name} = {vec_str}[] as {return_type}\n{indent}}}") } else { format!("{inner_expr};\n{indent}{var_name} = {vec_str}[] as {return_type}") } diff --git a/src/tools/clippy/clippy_utils/README.md b/src/tools/clippy/clippy_utils/README.md index 204a66a435eeb..1b85dcfd848d5 100644 --- a/src/tools/clippy/clippy_utils/README.md +++ b/src/tools/clippy/clippy_utils/README.md @@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain: ``` -nightly-2026-01-22 +nightly-2026-02-11 ``` diff --git a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs index 3b043f7565ef9..bae3baba12fba 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs @@ -795,14 +795,14 @@ pub fn eq_const_item_rhs(l: &ConstItemRhsKind, r: &ConstItemRhsKind) -> bool { use ConstItemRhsKind::*; match (l, r) { (TypeConst { rhs: Some(l) }, TypeConst { rhs: Some(r) }) => eq_anon_const(l, r), - (TypeConst { rhs: None }, TypeConst { rhs: None }) => true, - (TypeConst { rhs: Some(..) }, TypeConst { rhs: None }) => false, - (TypeConst { rhs: None }, TypeConst { rhs: Some(..) }) => false, + (TypeConst { rhs: None }, TypeConst { rhs: None }) | (Body { rhs: None }, Body { rhs: None }) => true, (Body { rhs: Some(l) }, Body { rhs: Some(r) }) => eq_expr(l, r), - (Body { rhs: None }, Body { rhs: None }) => true, - (Body { rhs: None }, Body { rhs: Some(..) }) => false, - (Body { rhs: Some(..) }, Body { rhs: None }) => false, - (TypeConst {..}, Body { .. }) | ( Body { .. }, TypeConst { .. }) => false, + (TypeConst { rhs: Some(..) }, TypeConst { rhs: None }) + | (TypeConst { rhs: None }, TypeConst { rhs: Some(..) }) + | (Body { rhs: None }, Body { rhs: Some(..) }) + | (Body { rhs: Some(..) }, Body { rhs: None }) + | (TypeConst { .. }, Body { .. }) + | (Body { .. }, TypeConst { .. }) => false, } } diff --git a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs index 7fb8616072a59..def5d968b063f 100644 --- a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs +++ b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs @@ -45,6 +45,8 @@ pub enum Pat { Sym(Symbol), /// Any decimal or hexadecimal digit depending on the location. Num, + /// An attribute. + Attr(Symbol), } /// Checks if the start and the end of the span's text matches the patterns. This will return false @@ -65,12 +67,20 @@ fn span_matches_pat(sess: &Session, span: Span, start_pat: Pat, end_pat: Pat) -> Pat::OwnedMultiStr(texts) => texts.iter().any(|s| start_str.starts_with(s)), Pat::Sym(sym) => start_str.starts_with(sym.as_str()), Pat::Num => start_str.as_bytes().first().is_some_and(u8::is_ascii_digit), + Pat::Attr(sym) => { + let start_str = start_str + .strip_prefix("#[") + .or_else(|| start_str.strip_prefix("#![")) + .unwrap_or(start_str); + start_str.trim_start().starts_with(sym.as_str()) + }, } && match end_pat { Pat::Str(text) => end_str.ends_with(text), Pat::MultiStr(texts) => texts.iter().any(|s| end_str.ends_with(s)), Pat::OwnedMultiStr(texts) => texts.iter().any(|s| end_str.ends_with(s)), Pat::Sym(sym) => end_str.ends_with(sym.as_str()), Pat::Num => end_str.as_bytes().last().is_some_and(u8::is_ascii_hexdigit), + Pat::Attr(_) => false, }) }) } @@ -350,18 +360,7 @@ fn attr_search_pat(attr: &Attribute) -> (Pat, Pat) { AttrKind::Normal(..) => { if let Some(name) = attr.name() { // NOTE: This will likely have false positives, like `allow = 1` - let ident_string = name.to_string(); - if attr.style == AttrStyle::Outer { - ( - Pat::OwnedMultiStr(vec!["#[".to_owned() + &ident_string, ident_string]), - Pat::Str(""), - ) - } else { - ( - Pat::OwnedMultiStr(vec!["#![".to_owned() + &ident_string, ident_string]), - Pat::Str(""), - ) - } + (Pat::Attr(name), Pat::Str("")) } else { (Pat::Str("#"), Pat::Str("]")) } diff --git a/src/tools/clippy/clippy_utils/src/sym.rs b/src/tools/clippy/clippy_utils/src/sym.rs index 5d6f2241c7c0d..e0b03ae4f7b25 100644 --- a/src/tools/clippy/clippy_utils/src/sym.rs +++ b/src/tools/clippy/clippy_utils/src/sym.rs @@ -147,6 +147,7 @@ generate! { exp, expect_err, expn_data, + exported_private_dependencies, extend, filter, filter_map, diff --git a/src/tools/clippy/clippy_utils/src/ty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/mod.rs index 46456528fdf87..639492b75747a 100644 --- a/src/tools/clippy/clippy_utils/src/ty/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ty/mod.rs @@ -31,9 +31,9 @@ use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt; use rustc_trait_selection::traits::{Obligation, ObligationCause}; #[cfg(bootstrap)] use std::assert_matches::debug_assert_matches; +use std::collections::hash_map::Entry; #[cfg(not(bootstrap))] use std::debug_assert_matches; -use std::collections::hash_map::Entry; use std::{iter, mem}; use crate::paths::{PathNS, lookup_path_str}; diff --git a/src/tools/clippy/rust-toolchain.toml b/src/tools/clippy/rust-toolchain.toml index c26289c237255..558f808a7a3ed 100644 --- a/src/tools/clippy/rust-toolchain.toml +++ b/src/tools/clippy/rust-toolchain.toml @@ -1,6 +1,6 @@ [toolchain] # begin autogenerated nightly -channel = "nightly-2026-01-22" +channel = "nightly-2026-02-11" # end autogenerated nightly components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" diff --git a/src/tools/clippy/rustc_tools_util/src/lib.rs b/src/tools/clippy/rustc_tools_util/src/lib.rs index 194ed84d04c28..3b7d2d4085d9c 100644 --- a/src/tools/clippy/rustc_tools_util/src/lib.rs +++ b/src/tools/clippy/rustc_tools_util/src/lib.rs @@ -91,7 +91,7 @@ impl std::fmt::Debug for VersionInfo { self.crate_name, self.major, self.minor, self.patch, )?; if let Some(ref commit_hash) = self.commit_hash { - write!(f, ", commit_hash: \"{}\"", commit_hash.trim(),)?; + write!(f, ", commit_hash: \"{}\"", commit_hash.trim())?; } if let Some(ref commit_date) = self.commit_date { write!(f, ", commit_date: \"{}\"", commit_date.trim())?; diff --git a/src/tools/clippy/src/driver.rs b/src/tools/clippy/src/driver.rs index cf06f43e8d4a3..409eb182fe33c 100644 --- a/src/tools/clippy/src/driver.rs +++ b/src/tools/clippy/src/driver.rs @@ -192,7 +192,7 @@ fn display_help() -> ExitCode { const BUG_REPORT_URL: &str = "https://github.com/rust-lang/rust-clippy/issues/new?template=ice.yml"; -pub fn main() -> ExitCode { +fn main() -> ExitCode { let early_dcx = EarlyDiagCtxt::new(ErrorOutputType::default()); rustc_driver::init_rustc_env_logger(&early_dcx); @@ -257,7 +257,7 @@ pub fn main() -> ExitCode { return match writeln!(&mut anstream::stdout().lock(), "{version_info}") { Ok(()) => ExitCode::SUCCESS, Err(_) => ExitCode::FAILURE, - } + }; } // Setting RUSTC_WRAPPER causes Cargo to pass 'rustc' as the first argument. diff --git a/src/tools/clippy/tests/ui/allow_attributes.fixed b/src/tools/clippy/tests/ui/allow_attributes.fixed index 56a98cca3404c..ecc9f08c19684 100644 --- a/src/tools/clippy/tests/ui/allow_attributes.fixed +++ b/src/tools/clippy/tests/ui/allow_attributes.fixed @@ -63,6 +63,11 @@ fn msrv_1_80() { let x = 1; } +#[rustfmt::skip] +#[ expect ( dead_code ) ] +//~^ allow_attributes +struct Spaced; + #[deny(clippy::allow_attributes)] fn deny_allow_attributes() -> Option { let allow = None; diff --git a/src/tools/clippy/tests/ui/allow_attributes.rs b/src/tools/clippy/tests/ui/allow_attributes.rs index 65a0a6b5a108c..3ab328b25affb 100644 --- a/src/tools/clippy/tests/ui/allow_attributes.rs +++ b/src/tools/clippy/tests/ui/allow_attributes.rs @@ -63,6 +63,11 @@ fn msrv_1_80() { let x = 1; } +#[rustfmt::skip] +#[ allow ( dead_code ) ] +//~^ allow_attributes +struct Spaced; + #[deny(clippy::allow_attributes)] fn deny_allow_attributes() -> Option { let allow = None; diff --git a/src/tools/clippy/tests/ui/allow_attributes.stderr b/src/tools/clippy/tests/ui/allow_attributes.stderr index dd5fb21ffeaf5..67a70aac8a860 100644 --- a/src/tools/clippy/tests/ui/allow_attributes.stderr +++ b/src/tools/clippy/tests/ui/allow_attributes.stderr @@ -19,5 +19,11 @@ error: #[allow] attribute found LL | #[allow(unused)] | ^^^^^ help: replace it with: `expect` -error: aborting due to 3 previous errors +error: #[allow] attribute found + --> tests/ui/allow_attributes.rs:67:4 + | +LL | #[ allow ( dead_code ) ] + | ^^^^^ help: replace it with: `expect` + +error: aborting due to 4 previous errors diff --git a/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.fixed b/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.fixed index 4c3b13b30043d..f65339605e756 100644 --- a/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.fixed +++ b/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.fixed @@ -112,3 +112,35 @@ fn issue16322(item: String) { println!("Ja!"); } } + +fn issue16458() { + macro_rules! partly_comes_from_macro { + ($i:ident: $ty:ty, $def:expr) => { + let _ = { + let res = <$ty>::default() == $def; + let _i: $ty = $def; + res + }; + }; + } + + partly_comes_from_macro! { + required_version: String, env!("HOME").to_string() + } + + macro_rules! all_comes_from_macro { + ($($i:ident: $ty:ty, $def:expr);+ $(;)*) => { + $( + let _ = { + let res = <$ty>::default() == "$def"; + //~^ cmp_owned + let _i: $ty = $def; + res + }; + )+ + }; + } + all_comes_from_macro! { + required_version: String, env!("HOME").to_string(); + } +} diff --git a/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.rs b/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.rs index a9d7509feaaf0..ed2300c80eaa9 100644 --- a/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.rs +++ b/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.rs @@ -112,3 +112,35 @@ fn issue16322(item: String) { println!("Ja!"); } } + +fn issue16458() { + macro_rules! partly_comes_from_macro { + ($i:ident: $ty:ty, $def:expr) => { + let _ = { + let res = <$ty>::default() == $def; + let _i: $ty = $def; + res + }; + }; + } + + partly_comes_from_macro! { + required_version: String, env!("HOME").to_string() + } + + macro_rules! all_comes_from_macro { + ($($i:ident: $ty:ty, $def:expr);+ $(;)*) => { + $( + let _ = { + let res = <$ty>::default() == "$def".to_string(); + //~^ cmp_owned + let _i: $ty = $def; + res + }; + )+ + }; + } + all_comes_from_macro! { + required_version: String, env!("HOME").to_string(); + } +} diff --git a/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.stderr b/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.stderr index 66544ce0c2177..38d124baa4b56 100644 --- a/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.stderr +++ b/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.stderr @@ -55,5 +55,18 @@ error: this creates an owned instance just for comparison LL | if item == t!(frohes_neu_Jahr).to_string() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t!(frohes_neu_Jahr)` -error: aborting due to 9 previous errors +error: this creates an owned instance just for comparison + --> tests/ui/cmp_owned/with_suggestion.rs:135:51 + | +LL | let res = <$ty>::default() == "$def".to_string(); + | ^^^^^^^^^^^^^^^^^^ help: try: `"$def"` +... +LL | / all_comes_from_macro! { +LL | | required_version: String, env!("HOME").to_string(); +LL | | } + | |_____- in this macro invocation + | + = note: this error originates in the macro `all_comes_from_macro` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 10 previous errors diff --git a/src/tools/clippy/tests/ui/doc/doc-fixable.fixed b/src/tools/clippy/tests/ui/doc/doc-fixable.fixed index 46695dc929ab7..58d8b8b33ade0 100644 --- a/src/tools/clippy/tests/ui/doc/doc-fixable.fixed +++ b/src/tools/clippy/tests/ui/doc/doc-fixable.fixed @@ -75,7 +75,7 @@ fn test_units() { /// IPv4 IPv6 /// InfiniBand RoCE /// ClojureScript CoffeeScript JavaScript PostScript PureScript TypeScript -/// PowerPC WebAssembly +/// PowerPC PowerShell WebAssembly /// NaN NaNs /// OAuth GraphQL /// OCaml diff --git a/src/tools/clippy/tests/ui/doc/doc-fixable.rs b/src/tools/clippy/tests/ui/doc/doc-fixable.rs index 4082fa5b56f4b..0b1237f716fa0 100644 --- a/src/tools/clippy/tests/ui/doc/doc-fixable.rs +++ b/src/tools/clippy/tests/ui/doc/doc-fixable.rs @@ -75,7 +75,7 @@ fn test_units() { /// IPv4 IPv6 /// InfiniBand RoCE /// ClojureScript CoffeeScript JavaScript PostScript PureScript TypeScript -/// PowerPC WebAssembly +/// PowerPC PowerShell WebAssembly /// NaN NaNs /// OAuth GraphQL /// OCaml diff --git a/src/tools/clippy/tests/ui/doc/doc_paragraphs_missing_punctuation.fixed b/src/tools/clippy/tests/ui/doc/doc_paragraphs_missing_punctuation.fixed index 95d65039440b5..faabbb381318e 100644 --- a/src/tools/clippy/tests/ui/doc/doc_paragraphs_missing_punctuation.fixed +++ b/src/tools/clippy/tests/ui/doc/doc_paragraphs_missing_punctuation.fixed @@ -46,13 +46,6 @@ enum Exceptions { /// | -------------- | ----- | /// | Markdown table | A-ok | MarkdownTable, - /// Here is a snippet. - //~^ doc_paragraphs_missing_punctuation - /// - /// ``` - /// // Code blocks are no issues. - /// ``` - CodeBlock, } // Check the lint can be expected on a whole enum at once. @@ -130,6 +123,24 @@ enum OrderedLists { Paren, } +/// Some elements do not have to be introduced by an independent clause. +enum NotIndependentClause { + /// Lists are allowed to be introduced by a clause that is not independent: this usually + /// requires that + /// + /// - items end with a comma or a semicolon, which is not enforced; + /// - the last item end with a period, which is also not enforced. + List, + /// For instance, the function + /// + /// ``` + /// fn answer() {} + /// ``` + /// + /// returns the Answer to the Ultimate Question of Life, the Universe, and Everything. + CodeBlock, +} + /// Doc comments with trailing blank lines are supported. //~^ doc_paragraphs_missing_punctuation /// diff --git a/src/tools/clippy/tests/ui/doc/doc_paragraphs_missing_punctuation.rs b/src/tools/clippy/tests/ui/doc/doc_paragraphs_missing_punctuation.rs index 35b74d7d13b9e..9821115601a65 100644 --- a/src/tools/clippy/tests/ui/doc/doc_paragraphs_missing_punctuation.rs +++ b/src/tools/clippy/tests/ui/doc/doc_paragraphs_missing_punctuation.rs @@ -46,13 +46,6 @@ enum Exceptions { /// | -------------- | ----- | /// | Markdown table | A-ok | MarkdownTable, - /// Here is a snippet - //~^ doc_paragraphs_missing_punctuation - /// - /// ``` - /// // Code blocks are no issues. - /// ``` - CodeBlock, } // Check the lint can be expected on a whole enum at once. @@ -130,6 +123,24 @@ enum OrderedLists { Paren, } +/// Some elements do not have to be introduced by an independent clause. +enum NotIndependentClause { + /// Lists are allowed to be introduced by a clause that is not independent: this usually + /// requires that + /// + /// - items end with a comma or a semicolon, which is not enforced; + /// - the last item end with a period, which is also not enforced. + List, + /// For instance, the function + /// + /// ``` + /// fn answer() {} + /// ``` + /// + /// returns the Answer to the Ultimate Question of Life, the Universe, and Everything. + CodeBlock, +} + /// Doc comments with trailing blank lines are supported //~^ doc_paragraphs_missing_punctuation /// diff --git a/src/tools/clippy/tests/ui/doc/doc_paragraphs_missing_punctuation.stderr b/src/tools/clippy/tests/ui/doc/doc_paragraphs_missing_punctuation.stderr index 49aa4e8aeb888..6645e771c6d30 100644 --- a/src/tools/clippy/tests/ui/doc/doc_paragraphs_missing_punctuation.stderr +++ b/src/tools/clippy/tests/ui/doc/doc_paragraphs_missing_punctuation.stderr @@ -32,82 +32,76 @@ LL | /// | ^ help: end the paragraph with some punctuation: `.` error: doc paragraphs should end with a terminal punctuation mark - --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:49:26 - | -LL | /// Here is a snippet - | ^ help: end the paragraph with some punctuation: `.` - -error: doc paragraphs should end with a terminal punctuation mark - --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:72:15 + --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:65:15 | LL | /// U+0001 | ^ help: end the paragraph with some punctuation: `.` error: doc paragraphs should end with a terminal punctuation mark - --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:79:29 + --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:72:29 | LL | //! inner attributes too | ^ help: end the paragraph with some punctuation: `.` error: doc paragraphs should end with a terminal punctuation mark - --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:90:47 + --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:83:47 | LL | /// **But sometimes it is missing a period** | ^ help: end the paragraph with some punctuation: `.` error: doc paragraphs should end with a terminal punctuation mark - --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:95:46 + --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:88:46 | LL | /// _But sometimes it is missing a period_ | ^ help: end the paragraph with some punctuation: `.` error: doc paragraphs should end with a terminal punctuation mark - --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:104:56 + --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:97:56 | LL | /// Doc comments can end with an [inline link](#anchor) | ^ help: end the paragraph with some punctuation: `.` error: doc paragraphs should end with a terminal punctuation mark - --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:108:65 + --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:101:65 | LL | /// Some doc comments contain [link reference definitions][spec] | ^ help: end the paragraph with some punctuation: `.` error: doc paragraphs should end with a terminal punctuation mark - --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:133:57 + --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:144:57 | LL | /// Doc comments with trailing blank lines are supported | ^ help: end the paragraph with some punctuation: `.` error: doc paragraphs should end with a terminal punctuation mark - --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:139:48 + --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:150:48 | LL | /// This first paragraph is missing punctuation | ^ help: end the paragraph with some punctuation: `.` error: doc paragraphs should end with a terminal punctuation mark - --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:143:34 + --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:154:34 | LL | /// And it has multiple sentences | ^ help: end the paragraph with some punctuation: `.` error: doc paragraphs should end with a terminal punctuation mark - --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:146:37 + --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:157:37 | LL | /// Same for this third and last one | ^ help: end the paragraph with some punctuation: `.` error: doc paragraphs should end with a terminal punctuation mark - --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:153:33 + --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:164:33 | LL | /// This ends with a code `span` | ^ help: end the paragraph with some punctuation: `.` error: doc paragraphs should end with a terminal punctuation mark - --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:162:27 + --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:173:27 | LL | * Block doc comments work | ^ help: end the paragraph with some punctuation: `.` -error: aborting due to 18 previous errors +error: aborting due to 17 previous errors diff --git a/src/tools/clippy/tests/ui/duration_suboptimal_units.fixed b/src/tools/clippy/tests/ui/duration_suboptimal_units.fixed index 98c4b6e965ba5..515ec10e572b1 100644 --- a/src/tools/clippy/tests/ui/duration_suboptimal_units.fixed +++ b/src/tools/clippy/tests/ui/duration_suboptimal_units.fixed @@ -89,3 +89,8 @@ mod my_duration { let dur = Duration::from_secs(60); } } + +fn issue16457() { + // Methods taking something else than `u64` are not covered + _ = Duration::from_nanos_u128(1 << 90); +} diff --git a/src/tools/clippy/tests/ui/duration_suboptimal_units.rs b/src/tools/clippy/tests/ui/duration_suboptimal_units.rs index c4f33a9f92e03..357c52cffb35a 100644 --- a/src/tools/clippy/tests/ui/duration_suboptimal_units.rs +++ b/src/tools/clippy/tests/ui/duration_suboptimal_units.rs @@ -89,3 +89,8 @@ mod my_duration { let dur = Duration::from_secs(60); } } + +fn issue16457() { + // Methods taking something else than `u64` are not covered + _ = Duration::from_nanos_u128(1 << 90); +} diff --git a/src/tools/clippy/tests/ui/let_and_return.edition2021.fixed b/src/tools/clippy/tests/ui/let_and_return.edition2021.fixed index e89e4476bf820..6ca0febc2b8d9 100644 --- a/src/tools/clippy/tests/ui/let_and_return.edition2021.fixed +++ b/src/tools/clippy/tests/ui/let_and_return.edition2021.fixed @@ -271,4 +271,12 @@ fn issue15987() -> i32 { r } +fn has_comment() -> Vec { + let v = Vec::new(); + + // TODO: stuff + + v +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/let_and_return.edition2024.fixed b/src/tools/clippy/tests/ui/let_and_return.edition2024.fixed index d2c76673ca03c..0fce22936ae6e 100644 --- a/src/tools/clippy/tests/ui/let_and_return.edition2024.fixed +++ b/src/tools/clippy/tests/ui/let_and_return.edition2024.fixed @@ -271,4 +271,12 @@ fn issue15987() -> i32 { r } +fn has_comment() -> Vec { + let v = Vec::new(); + + // TODO: stuff + + v +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/let_and_return.rs b/src/tools/clippy/tests/ui/let_and_return.rs index 1af5f8ba5c165..301f153ca8b10 100644 --- a/src/tools/clippy/tests/ui/let_and_return.rs +++ b/src/tools/clippy/tests/ui/let_and_return.rs @@ -271,4 +271,12 @@ fn issue15987() -> i32 { r } +fn has_comment() -> Vec { + let v = Vec::new(); + + // TODO: stuff + + v +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/manual_dangling_ptr.fixed b/src/tools/clippy/tests/ui/manual_dangling_ptr.fixed index b6afe7898906c..c6a6379194c96 100644 --- a/src/tools/clippy/tests/ui/manual_dangling_ptr.fixed +++ b/src/tools/clippy/tests/ui/manual_dangling_ptr.fixed @@ -1,3 +1,4 @@ +#![feature(extern_types)] #![warn(clippy::manual_dangling_ptr)] use std::mem; @@ -42,3 +43,14 @@ fn _msrv_1_84() { //~^ manual_dangling_ptr //~| manual_dangling_ptr } + +fn issue16459() { + unsafe extern "C" { + type Extern; + } + let _ = unsafe { &mut *(1 as *mut Extern) }; + + struct Empty; + let _ = unsafe { &mut *std::ptr::dangling_mut::() }; + //~^ manual_dangling_ptr +} diff --git a/src/tools/clippy/tests/ui/manual_dangling_ptr.rs b/src/tools/clippy/tests/ui/manual_dangling_ptr.rs index 581ad50113e28..338003fa41fdb 100644 --- a/src/tools/clippy/tests/ui/manual_dangling_ptr.rs +++ b/src/tools/clippy/tests/ui/manual_dangling_ptr.rs @@ -1,3 +1,4 @@ +#![feature(extern_types)] #![warn(clippy::manual_dangling_ptr)] use std::mem; @@ -42,3 +43,14 @@ fn _msrv_1_84() { //~^ manual_dangling_ptr //~| manual_dangling_ptr } + +fn issue16459() { + unsafe extern "C" { + type Extern; + } + let _ = unsafe { &mut *(1 as *mut Extern) }; + + struct Empty; + let _ = unsafe { &mut *(1 as *mut Empty) }; + //~^ manual_dangling_ptr +} diff --git a/src/tools/clippy/tests/ui/manual_dangling_ptr.stderr b/src/tools/clippy/tests/ui/manual_dangling_ptr.stderr index e3bc9b16b0d93..7e1533f7a30d4 100644 --- a/src/tools/clippy/tests/ui/manual_dangling_ptr.stderr +++ b/src/tools/clippy/tests/ui/manual_dangling_ptr.stderr @@ -1,5 +1,5 @@ error: manual creation of a dangling pointer - --> tests/ui/manual_dangling_ptr.rs:7:24 + --> tests/ui/manual_dangling_ptr.rs:8:24 | LL | let _: *const u8 = 1 as *const _; | ^^^^^^^^^^^^^ help: use: `std::ptr::dangling()` @@ -8,58 +8,64 @@ LL | let _: *const u8 = 1 as *const _; = help: to override `-D warnings` add `#[allow(clippy::manual_dangling_ptr)]` error: manual creation of a dangling pointer - --> tests/ui/manual_dangling_ptr.rs:9:13 + --> tests/ui/manual_dangling_ptr.rs:10:13 | LL | let _ = 2 as *const u32; | ^^^^^^^^^^^^^^^ help: use: `std::ptr::dangling::()` error: manual creation of a dangling pointer - --> tests/ui/manual_dangling_ptr.rs:11:13 + --> tests/ui/manual_dangling_ptr.rs:12:13 | LL | let _ = 4 as *mut f32; | ^^^^^^^^^^^^^ help: use: `std::ptr::dangling_mut::()` error: manual creation of a dangling pointer - --> tests/ui/manual_dangling_ptr.rs:14:13 + --> tests/ui/manual_dangling_ptr.rs:15:13 | LL | let _ = mem::align_of::() as *const u8; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `std::ptr::dangling::()` error: manual creation of a dangling pointer - --> tests/ui/manual_dangling_ptr.rs:16:13 + --> tests/ui/manual_dangling_ptr.rs:17:13 | LL | let _ = mem::align_of::() as *const u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `std::ptr::dangling::()` error: manual creation of a dangling pointer - --> tests/ui/manual_dangling_ptr.rs:18:13 + --> tests/ui/manual_dangling_ptr.rs:19:13 | LL | let _ = mem::align_of::() as *const usize; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `std::ptr::dangling::()` error: manual creation of a dangling pointer - --> tests/ui/manual_dangling_ptr.rs:21:9 + --> tests/ui/manual_dangling_ptr.rs:22:9 | LL | foo(4 as *const _, 4 as *mut _); | ^^^^^^^^^^^^^ help: use: `std::ptr::dangling()` error: manual creation of a dangling pointer - --> tests/ui/manual_dangling_ptr.rs:21:24 + --> tests/ui/manual_dangling_ptr.rs:22:24 | LL | foo(4 as *const _, 4 as *mut _); | ^^^^^^^^^^^ help: use: `std::ptr::dangling_mut()` error: manual creation of a dangling pointer - --> tests/ui/manual_dangling_ptr.rs:41:9 + --> tests/ui/manual_dangling_ptr.rs:42:9 | LL | foo(4 as *const _, 4 as *mut _); | ^^^^^^^^^^^^^ help: use: `std::ptr::dangling()` error: manual creation of a dangling pointer - --> tests/ui/manual_dangling_ptr.rs:41:24 + --> tests/ui/manual_dangling_ptr.rs:42:24 | LL | foo(4 as *const _, 4 as *mut _); | ^^^^^^^^^^^ help: use: `std::ptr::dangling_mut()` -error: aborting due to 10 previous errors +error: manual creation of a dangling pointer + --> tests/ui/manual_dangling_ptr.rs:54:28 + | +LL | let _ = unsafe { &mut *(1 as *mut Empty) }; + | ^^^^^^^^^^^^^^^^^ help: use: `std::ptr::dangling_mut::()` + +error: aborting due to 11 previous errors diff --git a/src/tools/clippy/tests/ui/manual_is_variant_and.fixed b/src/tools/clippy/tests/ui/manual_is_variant_and.fixed index 65a9cfa6e64c2..884bef6af5f90 100644 --- a/src/tools/clippy/tests/ui/manual_is_variant_and.fixed +++ b/src/tools/clippy/tests/ui/manual_is_variant_and.fixed @@ -226,3 +226,22 @@ mod with_func { assert_eq!(a1, a2); } } + +fn issue16419() { + let then_fn = |s: &str| s.len() > 3; + let opt: Option<&str> = Some("test"); + let _ = opt.is_none_or(then_fn); + //~^ manual_is_variant_and + + let _ = opt.is_none_or(then_fn); + //~^ manual_is_variant_and +} + +#[clippy::msrv = "1.75.0"] +fn issue16419_msrv() { + let then_fn = |s: &str| s.len() > 3; + let opt: Option<&str> = Some("test"); + let _ = opt.is_none() || opt.is_some_and(then_fn); + + let _ = opt.is_some_and(then_fn) || opt.is_none(); +} diff --git a/src/tools/clippy/tests/ui/manual_is_variant_and.rs b/src/tools/clippy/tests/ui/manual_is_variant_and.rs index 85b45d654a7d0..53aca94ea3708 100644 --- a/src/tools/clippy/tests/ui/manual_is_variant_and.rs +++ b/src/tools/clippy/tests/ui/manual_is_variant_and.rs @@ -235,3 +235,22 @@ mod with_func { assert_eq!(a1, a2); } } + +fn issue16419() { + let then_fn = |s: &str| s.len() > 3; + let opt: Option<&str> = Some("test"); + let _ = opt.is_none() || opt.is_some_and(then_fn); + //~^ manual_is_variant_and + + let _ = opt.is_some_and(then_fn) || opt.is_none(); + //~^ manual_is_variant_and +} + +#[clippy::msrv = "1.75.0"] +fn issue16419_msrv() { + let then_fn = |s: &str| s.len() > 3; + let opt: Option<&str> = Some("test"); + let _ = opt.is_none() || opt.is_some_and(then_fn); + + let _ = opt.is_some_and(then_fn) || opt.is_none(); +} diff --git a/src/tools/clippy/tests/ui/manual_is_variant_and.stderr b/src/tools/clippy/tests/ui/manual_is_variant_and.stderr index da36b5a07d210..56c3b80c0aa5c 100644 --- a/src/tools/clippy/tests/ui/manual_is_variant_and.stderr +++ b/src/tools/clippy/tests/ui/manual_is_variant_and.stderr @@ -222,5 +222,17 @@ error: called `.map() != Ok()` LL | let a1 = b.map(iad) != Ok(false); | ^^^^^^^^^^^^^^^^^^^^^^^ help: use: `!b.is_ok_and(|x| !iad(x))` -error: aborting due to 31 previous errors +error: manual implementation of `Option::is_none_or` + --> tests/ui/manual_is_variant_and.rs:242:13 + | +LL | let _ = opt.is_none() || opt.is_some_and(then_fn); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `opt.is_none_or(then_fn)` + +error: manual implementation of `Option::is_none_or` + --> tests/ui/manual_is_variant_and.rs:245:13 + | +LL | let _ = opt.is_some_and(then_fn) || opt.is_none(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `opt.is_none_or(then_fn)` + +error: aborting due to 33 previous errors diff --git a/src/tools/clippy/tests/ui/manual_let_else_match.fixed b/src/tools/clippy/tests/ui/manual_let_else_match.fixed index 15f604aec2928..9cdf394e9ee47 100644 --- a/src/tools/clippy/tests/ui/manual_let_else_match.fixed +++ b/src/tools/clippy/tests/ui/manual_let_else_match.fixed @@ -1,4 +1,4 @@ -#![allow(unused_braces, unused_variables, dead_code)] +#![allow(unused_braces, unused_variables, dead_code, irrefutable_let_patterns)] #![allow( clippy::collapsible_else_if, clippy::let_unit_value, @@ -182,3 +182,16 @@ fn issue9939b() { let Some(Issue9939b { earthquake: erosion, hurricane: _x }) = issue else { unreachable!("can't happen") }; assert!(erosion); } + +mod issue16433 { + // https://github.com/rust-lang/rust-clippy/issues/16433 + struct A { + a: u32, + b: u32, + } + + fn foo() { + let a = A { a: 1, b: 1 }; + let A { a: first_arg, .. } = a else { return }; + } +} diff --git a/src/tools/clippy/tests/ui/manual_let_else_match.rs b/src/tools/clippy/tests/ui/manual_let_else_match.rs index 44a044b142bd8..b1a20551e5842 100644 --- a/src/tools/clippy/tests/ui/manual_let_else_match.rs +++ b/src/tools/clippy/tests/ui/manual_let_else_match.rs @@ -1,4 +1,4 @@ -#![allow(unused_braces, unused_variables, dead_code)] +#![allow(unused_braces, unused_variables, dead_code, irrefutable_let_patterns)] #![allow( clippy::collapsible_else_if, clippy::let_unit_value, @@ -250,3 +250,20 @@ fn issue9939b() { }; assert!(erosion); } + +mod issue16433 { + // https://github.com/rust-lang/rust-clippy/issues/16433 + struct A { + a: u32, + b: u32, + } + + fn foo() { + let a = A { a: 1, b: 1 }; + let first_arg = match a { + //~^ manual_let_else + A { a, .. } => a, + _ => return, + }; + } +} diff --git a/src/tools/clippy/tests/ui/manual_let_else_match.stderr b/src/tools/clippy/tests/ui/manual_let_else_match.stderr index ed6117ebffb7d..6bbfb0e84d952 100644 --- a/src/tools/clippy/tests/ui/manual_let_else_match.stderr +++ b/src/tools/clippy/tests/ui/manual_let_else_match.stderr @@ -171,5 +171,15 @@ 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 +error: this could be rewritten as `let...else` + --> tests/ui/manual_let_else_match.rs:263:9 + | +LL | / let first_arg = match a { +LL | | +LL | | A { a, .. } => a, +LL | | _ => return, +LL | | }; + | |__________^ help: consider writing: `let A { a: first_arg, .. } = a else { return };` + +error: aborting due to 18 previous errors diff --git a/src/tools/clippy/tests/ui/must_use_candidates.fixed b/src/tools/clippy/tests/ui/must_use_candidates.fixed index 1e8589cf39d6b..a53e6e9b85bef 100644 --- a/src/tools/clippy/tests/ui/must_use_candidates.fixed +++ b/src/tools/clippy/tests/ui/must_use_candidates.fixed @@ -104,6 +104,7 @@ pub extern "C" fn unmangled(i: bool) -> bool { !i } -fn main() { +pub fn main() -> std::process::ExitCode { assert_eq!(1, pure(1)); + std::process::ExitCode::SUCCESS } diff --git a/src/tools/clippy/tests/ui/must_use_candidates.rs b/src/tools/clippy/tests/ui/must_use_candidates.rs index 71d546718ae79..6593d6c68a134 100644 --- a/src/tools/clippy/tests/ui/must_use_candidates.rs +++ b/src/tools/clippy/tests/ui/must_use_candidates.rs @@ -99,6 +99,7 @@ pub extern "C" fn unmangled(i: bool) -> bool { !i } -fn main() { +pub fn main() -> std::process::ExitCode { assert_eq!(1, pure(1)); + std::process::ExitCode::SUCCESS } diff --git a/src/tools/clippy/tests/ui/never_loop.rs b/src/tools/clippy/tests/ui/never_loop.rs index 9769ee0c3a37d..52470c6ee81bb 100644 --- a/src/tools/clippy/tests/ui/never_loop.rs +++ b/src/tools/clippy/tests/ui/never_loop.rs @@ -546,3 +546,13 @@ fn issue15673() { return; } } + +#[expect(clippy::diverging_sub_expression, clippy::short_circuit_statement)] +fn issue16462() { + let mut n = 10; + loop { + println!("{n}"); + n -= 1; + n >= 0 || break; + } +} diff --git a/src/tools/clippy/tests/ui/question_mark.fixed b/src/tools/clippy/tests/ui/question_mark.fixed index b8072932c4ea7..102517d34c612 100644 --- a/src/tools/clippy/tests/ui/question_mark.fixed +++ b/src/tools/clippy/tests/ui/question_mark.fixed @@ -515,3 +515,12 @@ fn wrongly_unmangled_macros() -> Option { test_expr!(42)?; test_expr!(42) } + +fn issue16429(b: i32) -> Option { + let a = Some(5); + let _ = if b == 1 { + b + } else { a? }; + + Some(0) +} diff --git a/src/tools/clippy/tests/ui/question_mark.rs b/src/tools/clippy/tests/ui/question_mark.rs index b320dcd4b0bca..cfea1277fe767 100644 --- a/src/tools/clippy/tests/ui/question_mark.rs +++ b/src/tools/clippy/tests/ui/question_mark.rs @@ -635,3 +635,17 @@ fn wrongly_unmangled_macros() -> Option { } test_expr!(42) } + +fn issue16429(b: i32) -> Option { + let a = Some(5); + let _ = if b == 1 { + b + } else if let Some(x) = a { + //~^ question_mark + x + } else { + return None; + }; + + Some(0) +} diff --git a/src/tools/clippy/tests/ui/question_mark.stderr b/src/tools/clippy/tests/ui/question_mark.stderr index d645c8830adcf..c243f12de040b 100644 --- a/src/tools/clippy/tests/ui/question_mark.stderr +++ b/src/tools/clippy/tests/ui/question_mark.stderr @@ -350,5 +350,17 @@ LL | | return None; LL | | } | |_____^ help: replace it with: `test_expr!(42)?;` -error: aborting due to 37 previous errors +error: this block may be rewritten with the `?` operator + --> tests/ui/question_mark.rs:643:12 + | +LL | } else if let Some(x) = a { + | ____________^ +LL | | +LL | | x +LL | | } else { +LL | | return None; +LL | | }; + | |_____^ help: replace it with: `{ a? }` + +error: aborting due to 38 previous errors diff --git a/src/tools/clippy/tests/ui/str_split.fixed b/src/tools/clippy/tests/ui/str_split.fixed index 6aca5051c5701..c93d4d4f818d1 100644 --- a/src/tools/clippy/tests/ui/str_split.fixed +++ b/src/tools/clippy/tests/ui/str_split.fixed @@ -1,7 +1,5 @@ #![warn(clippy::str_split_at_newline)] -#![allow(clippy::needless_lifetimes)] -use core::str::Split; use std::ops::Deref; struct NotStr<'a> { diff --git a/src/tools/clippy/tests/ui/str_split.rs b/src/tools/clippy/tests/ui/str_split.rs index 11e9862da14ba..5792ce04ec33c 100644 --- a/src/tools/clippy/tests/ui/str_split.rs +++ b/src/tools/clippy/tests/ui/str_split.rs @@ -1,7 +1,5 @@ #![warn(clippy::str_split_at_newline)] -#![allow(clippy::needless_lifetimes)] -use core::str::Split; use std::ops::Deref; struct NotStr<'a> { diff --git a/src/tools/clippy/tests/ui/str_split.stderr b/src/tools/clippy/tests/ui/str_split.stderr index c4eca81004c58..1ab755008e56b 100644 --- a/src/tools/clippy/tests/ui/str_split.stderr +++ b/src/tools/clippy/tests/ui/str_split.stderr @@ -1,65 +1,124 @@ error: using `str.trim().split()` with hard-coded newlines - --> tests/ui/str_split.rs:60:13 + --> tests/ui/str_split.rs:58:13 | LL | let _ = s1.trim().split('\n'); - | ^^^^^^^^^^^^^^^^^^^^^ help: use `str.lines()` instead: `s1.lines()` + | ^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::str-split-at-newline` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::str_split_at_newline)]` +help: use `str.lines()` instead + | +LL - let _ = s1.trim().split('\n'); +LL + let _ = s1.lines(); + | error: using `str.trim().split()` with hard-coded newlines - --> tests/ui/str_split.rs:63:13 + --> tests/ui/str_split.rs:61:13 | LL | let _ = s1.trim().split("\n"); - | ^^^^^^^^^^^^^^^^^^^^^ help: use `str.lines()` instead: `s1.lines()` + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: use `str.lines()` instead + | +LL - let _ = s1.trim().split("\n"); +LL + let _ = s1.lines(); + | error: using `str.trim().split()` with hard-coded newlines - --> tests/ui/str_split.rs:65:13 + --> tests/ui/str_split.rs:63:13 | LL | let _ = s1.trim().split("\r\n"); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `str.lines()` instead: `s1.lines()` + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `str.lines()` instead + | +LL - let _ = s1.trim().split("\r\n"); +LL + let _ = s1.lines(); + | error: using `str.trim().split()` with hard-coded newlines - --> tests/ui/str_split.rs:69:13 + --> tests/ui/str_split.rs:67:13 | LL | let _ = s2.trim().split('\n'); - | ^^^^^^^^^^^^^^^^^^^^^ help: use `str.lines()` instead: `s2.lines()` + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: use `str.lines()` instead + | +LL - let _ = s2.trim().split('\n'); +LL + let _ = s2.lines(); + | error: using `str.trim().split()` with hard-coded newlines - --> tests/ui/str_split.rs:72:13 + --> tests/ui/str_split.rs:70:13 | LL | let _ = s2.trim().split("\n"); - | ^^^^^^^^^^^^^^^^^^^^^ help: use `str.lines()` instead: `s2.lines()` + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: use `str.lines()` instead + | +LL - let _ = s2.trim().split("\n"); +LL + let _ = s2.lines(); + | error: using `str.trim().split()` with hard-coded newlines - --> tests/ui/str_split.rs:74:13 + --> tests/ui/str_split.rs:72:13 | LL | let _ = s2.trim().split("\r\n"); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `str.lines()` instead: `s2.lines()` + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `str.lines()` instead + | +LL - let _ = s2.trim().split("\r\n"); +LL + let _ = s2.lines(); + | error: using `str.trim().split()` with hard-coded newlines - --> tests/ui/str_split.rs:79:13 + --> tests/ui/str_split.rs:77:13 | LL | let _ = s3.trim().split('\n'); - | ^^^^^^^^^^^^^^^^^^^^^ help: use `str.lines()` instead: `s3.lines()` + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: use `str.lines()` instead + | +LL - let _ = s3.trim().split('\n'); +LL + let _ = s3.lines(); + | error: using `str.trim().split()` with hard-coded newlines - --> tests/ui/str_split.rs:82:13 + --> tests/ui/str_split.rs:80:13 | LL | let _ = s3.trim().split("\n"); - | ^^^^^^^^^^^^^^^^^^^^^ help: use `str.lines()` instead: `s3.lines()` + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: use `str.lines()` instead + | +LL - let _ = s3.trim().split("\n"); +LL + let _ = s3.lines(); + | error: using `str.trim().split()` with hard-coded newlines - --> tests/ui/str_split.rs:84:13 + --> tests/ui/str_split.rs:82:13 | LL | let _ = s3.trim().split("\r\n"); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `str.lines()` instead: `s3.lines()` + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `str.lines()` instead + | +LL - let _ = s3.trim().split("\r\n"); +LL + let _ = s3.lines(); + | error: using `str.trim().split()` with hard-coded newlines - --> tests/ui/str_split.rs:88:13 + --> tests/ui/str_split.rs:86:13 | LL | let _ = make_str!(s1).trim().split('\n'); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `str.lines()` instead: `make_str!(s1).lines()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `str.lines()` instead + | +LL - let _ = make_str!(s1).trim().split('\n'); +LL + let _ = make_str!(s1).lines(); + | error: aborting due to 10 previous errors diff --git a/src/tools/clippy/tests/ui/str_to_string.fixed b/src/tools/clippy/tests/ui/str_to_string.fixed index 8713c4f9bc863..5b76cf78f069d 100644 --- a/src/tools/clippy/tests/ui/str_to_string.fixed +++ b/src/tools/clippy/tests/ui/str_to_string.fixed @@ -22,3 +22,32 @@ fn issue16271(key: &[u8]) { let _value = t!(str::from_utf8(key)).to_owned(); //~^ str_to_string } + +struct GenericWrapper(T); + +impl GenericWrapper { + fn mapper U>(self, f: F) -> U { + f(self.0) + } +} + +fn issue16511(x: Option<&str>) { + let _ = x.map(ToOwned::to_owned); + //~^ str_to_string + + let _ = x.map(ToOwned::to_owned); + //~^ str_to_string + + let _ = ["a", "b"].iter().map(ToOwned::to_owned); + //~^ str_to_string + + fn mapper String>(f: F) -> String { + f("hello") + } + let _ = mapper(ToOwned::to_owned); + //~^ str_to_string + + let w = GenericWrapper("hello"); + let _ = w.mapper(ToOwned::to_owned); + //~^ str_to_string +} diff --git a/src/tools/clippy/tests/ui/str_to_string.rs b/src/tools/clippy/tests/ui/str_to_string.rs index b81759e1037b2..f099eb29b1b5f 100644 --- a/src/tools/clippy/tests/ui/str_to_string.rs +++ b/src/tools/clippy/tests/ui/str_to_string.rs @@ -22,3 +22,32 @@ fn issue16271(key: &[u8]) { let _value = t!(str::from_utf8(key)).to_string(); //~^ str_to_string } + +struct GenericWrapper(T); + +impl GenericWrapper { + fn mapper U>(self, f: F) -> U { + f(self.0) + } +} + +fn issue16511(x: Option<&str>) { + let _ = x.map(ToString::to_string); + //~^ str_to_string + + let _ = x.map(str::to_string); + //~^ str_to_string + + let _ = ["a", "b"].iter().map(ToString::to_string); + //~^ str_to_string + + fn mapper String>(f: F) -> String { + f("hello") + } + let _ = mapper(ToString::to_string); + //~^ str_to_string + + let w = GenericWrapper("hello"); + let _ = w.mapper(ToString::to_string); + //~^ str_to_string +} diff --git a/src/tools/clippy/tests/ui/str_to_string.stderr b/src/tools/clippy/tests/ui/str_to_string.stderr index c0a38c8ebe461..296b8e36f28c8 100644 --- a/src/tools/clippy/tests/ui/str_to_string.stderr +++ b/src/tools/clippy/tests/ui/str_to_string.stderr @@ -19,5 +19,35 @@ error: `to_string()` called on a `&str` LL | let _value = t!(str::from_utf8(key)).to_string(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t!(str::from_utf8(key)).to_owned()` -error: aborting due to 3 previous errors +error: `ToString::to_string` used as `&str` to `String` converter + --> tests/ui/str_to_string.rs:35:19 + | +LL | let _ = x.map(ToString::to_string); + | ^^^^^^^^^^^^^^^^^^^ help: try: `ToOwned::to_owned` + +error: `ToString::to_string` used as `&str` to `String` converter + --> tests/ui/str_to_string.rs:38:19 + | +LL | let _ = x.map(str::to_string); + | ^^^^^^^^^^^^^^ help: try: `ToOwned::to_owned` + +error: `ToString::to_string` used as `&str` to `String` converter + --> tests/ui/str_to_string.rs:41:35 + | +LL | let _ = ["a", "b"].iter().map(ToString::to_string); + | ^^^^^^^^^^^^^^^^^^^ help: try: `ToOwned::to_owned` + +error: `ToString::to_string` used as `&str` to `String` converter + --> tests/ui/str_to_string.rs:47:20 + | +LL | let _ = mapper(ToString::to_string); + | ^^^^^^^^^^^^^^^^^^^ help: try: `ToOwned::to_owned` + +error: `ToString::to_string` used as `&str` to `String` converter + --> tests/ui/str_to_string.rs:51:22 + | +LL | let _ = w.mapper(ToString::to_string); + | ^^^^^^^^^^^^^^^^^^^ help: try: `ToOwned::to_owned` + +error: aborting due to 8 previous errors diff --git a/src/tools/clippy/tests/ui/test_attr_in_doctest.rs b/src/tools/clippy/tests/ui/test_attr_in_doctest.rs index 7d1a09024895b..ee48cebc5e0cf 100644 --- a/src/tools/clippy/tests/ui/test_attr_in_doctest.rs +++ b/src/tools/clippy/tests/ui/test_attr_in_doctest.rs @@ -46,3 +46,11 @@ /// fn not_even_rust() { panic!("Ouch") } /// ``` fn test_attr_in_doctests() {} + +/// ```test_harness +/// #[test] +/// fn foo() { +/// panic!(); +/// } +/// ``` +pub fn issue16447() {} diff --git a/src/tools/clippy/tests/ui/trait_duplication_in_bounds_assoc_const_eq.fixed b/src/tools/clippy/tests/ui/trait_duplication_in_bounds_assoc_const_eq.fixed index 8d63fc44e7f83..96cc654fd090d 100644 --- a/src/tools/clippy/tests/ui/trait_duplication_in_bounds_assoc_const_eq.fixed +++ b/src/tools/clippy/tests/ui/trait_duplication_in_bounds_assoc_const_eq.fixed @@ -3,7 +3,6 @@ #![feature(min_generic_const_args)] trait AssocConstTrait { - type const ASSOC: usize; } fn assoc_const_args() diff --git a/src/tools/clippy/tests/ui/trait_duplication_in_bounds_assoc_const_eq.rs b/src/tools/clippy/tests/ui/trait_duplication_in_bounds_assoc_const_eq.rs index 36a83619c0f94..b81dd673bb694 100644 --- a/src/tools/clippy/tests/ui/trait_duplication_in_bounds_assoc_const_eq.rs +++ b/src/tools/clippy/tests/ui/trait_duplication_in_bounds_assoc_const_eq.rs @@ -3,7 +3,6 @@ #![feature(min_generic_const_args)] trait AssocConstTrait { - type const ASSOC: usize; } fn assoc_const_args() diff --git a/src/tools/clippy/tests/ui/trait_duplication_in_bounds_assoc_const_eq.stderr b/src/tools/clippy/tests/ui/trait_duplication_in_bounds_assoc_const_eq.stderr index accc4d7b5bb8a..4053959aff61b 100644 --- a/src/tools/clippy/tests/ui/trait_duplication_in_bounds_assoc_const_eq.stderr +++ b/src/tools/clippy/tests/ui/trait_duplication_in_bounds_assoc_const_eq.stderr @@ -1,5 +1,5 @@ error: these where clauses contain repeated elements - --> tests/ui/trait_duplication_in_bounds_assoc_const_eq.rs:11:8 + --> tests/ui/trait_duplication_in_bounds_assoc_const_eq.rs:10:8 | LL | T: AssocConstTrait + AssocConstTrait, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `AssocConstTrait` diff --git a/src/tools/clippy/tests/ui/unconditional_recursion.rs b/src/tools/clippy/tests/ui/unconditional_recursion.rs index d9f4c07dc9025..e4dd33a8eeea7 100644 --- a/src/tools/clippy/tests/ui/unconditional_recursion.rs +++ b/src/tools/clippy/tests/ui/unconditional_recursion.rs @@ -334,7 +334,7 @@ mod issue12154 { } // Not necessarily related to the issue but another FP from the http crate that was fixed with it: - // https://docs.rs/http/latest/src/http/header/name.rs.html#1424 + // https://github.com/hyperium/http/blob/5f0c86642f1dc86f156da82b62aceb2f4fab20e1/src/header/name.rs#L1408-L1420 // We used to simply peel refs from the LHS and RHS, so we couldn't differentiate // between `PartialEq for &T` and `PartialEq<&T> for T` impls. #[derive(PartialEq)] diff --git a/src/tools/clippy/tests/ui/unwrap.rs b/src/tools/clippy/tests/ui/unwrap.rs deleted file mode 100644 index 3191b396f99bc..0000000000000 --- a/src/tools/clippy/tests/ui/unwrap.rs +++ /dev/null @@ -1,22 +0,0 @@ -#![warn(clippy::unwrap_used)] -#![allow(clippy::unnecessary_literal_unwrap)] - -fn unwrap_option() { - let opt = Some(0); - let _ = opt.unwrap(); - //~^ unwrap_used -} - -fn unwrap_result() { - let res: Result = Ok(0); - let _ = res.unwrap(); - //~^ unwrap_used - - let _ = res.unwrap_err(); - //~^ unwrap_used -} - -fn main() { - unwrap_option(); - unwrap_result(); -} diff --git a/src/tools/clippy/tests/ui/unwrap.stderr b/src/tools/clippy/tests/ui/unwrap.stderr deleted file mode 100644 index c242541a6bd73..0000000000000 --- a/src/tools/clippy/tests/ui/unwrap.stderr +++ /dev/null @@ -1,31 +0,0 @@ -error: used `unwrap()` on an `Option` value - --> tests/ui/unwrap.rs:6:13 - | -LL | let _ = opt.unwrap(); - | ^^^^^^^^^^^^ - | - = note: if this value is `None`, it will panic - = help: consider using `expect()` to provide a better panic message - = note: `-D clippy::unwrap-used` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::unwrap_used)]` - -error: used `unwrap()` on a `Result` value - --> tests/ui/unwrap.rs:12:13 - | -LL | let _ = res.unwrap(); - | ^^^^^^^^^^^^ - | - = note: if this value is an `Err`, it will panic - = help: consider using `expect()` to provide a better panic message - -error: used `unwrap_err()` on a `Result` value - --> tests/ui/unwrap.rs:15:13 - | -LL | let _ = res.unwrap_err(); - | ^^^^^^^^^^^^^^^^ - | - = note: if this value is an `Ok`, it will panic - = help: consider using `expect_err()` to provide a better panic message - -error: aborting due to 3 previous errors - diff --git a/src/tools/clippy/tests/ui/unwrap_expect_used.rs b/src/tools/clippy/tests/ui/unwrap_expect_used.rs index b429f3a8a0bb9..207d4dd815be3 100644 --- a/src/tools/clippy/tests/ui/unwrap_expect_used.rs +++ b/src/tools/clippy/tests/ui/unwrap_expect_used.rs @@ -83,3 +83,15 @@ mod with_expansion { let _ = open!(file).expect_err("can open"); //~ expect_used } } + +fn issue16484() { + let opt = Some(()); + Option::unwrap(opt); //~ unwrap_used + Option::expect(opt, "error message"); //~ expect_used + + let res: Result<(), i32> = Ok(()); + Result::unwrap(res); //~ unwrap_used + Result::expect(res, "error message"); //~ expect_used + Result::unwrap_err(res); //~ unwrap_used + Result::expect_err(res, "error message"); //~ expect_used +} diff --git a/src/tools/clippy/tests/ui/unwrap_expect_used.stderr b/src/tools/clippy/tests/ui/unwrap_expect_used.stderr index 6fd1b84d81231..b9a2844b284c8 100644 --- a/src/tools/clippy/tests/ui/unwrap_expect_used.stderr +++ b/src/tools/clippy/tests/ui/unwrap_expect_used.stderr @@ -82,5 +82,53 @@ LL | let _ = open!(file).expect_err("can open"); | = note: if this value is an `Ok`, it will panic -error: aborting due to 10 previous errors +error: used `unwrap()` on an `Option` value + --> tests/ui/unwrap_expect_used.rs:89:5 + | +LL | Option::unwrap(opt); + | ^^^^^^^^^^^^^^^^^^^ + | + = note: if this value is `None`, it will panic + +error: used `expect()` on an `Option` value + --> tests/ui/unwrap_expect_used.rs:90:5 + | +LL | Option::expect(opt, "error message"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: if this value is `None`, it will panic + +error: used `unwrap()` on a `Result` value + --> tests/ui/unwrap_expect_used.rs:93:5 + | +LL | Result::unwrap(res); + | ^^^^^^^^^^^^^^^^^^^ + | + = note: if this value is an `Err`, it will panic + +error: used `expect()` on a `Result` value + --> tests/ui/unwrap_expect_used.rs:94:5 + | +LL | Result::expect(res, "error message"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: if this value is an `Err`, it will panic + +error: used `unwrap_err()` on a `Result` value + --> tests/ui/unwrap_expect_used.rs:95:5 + | +LL | Result::unwrap_err(res); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: if this value is an `Ok`, it will panic + +error: used `expect_err()` on a `Result` value + --> tests/ui/unwrap_expect_used.rs:96:5 + | +LL | Result::expect_err(res, "error message"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: if this value is an `Ok`, it will panic + +error: aborting due to 16 previous errors diff --git a/src/tools/clippy/tests/ui/useless_attribute.fixed b/src/tools/clippy/tests/ui/useless_attribute.fixed index e0bc23e0788c6..dbe4b6fb50f3a 100644 --- a/src/tools/clippy/tests/ui/useless_attribute.fixed +++ b/src/tools/clippy/tests/ui/useless_attribute.fixed @@ -42,6 +42,10 @@ mod foo { #[allow(deprecated)] pub use foo::Bar; +// don't lint on exported_private_dependencies for `use` items +#[allow(exported_private_dependencies)] +use {}; + // This should not trigger the lint. There's lint level definitions inside the external derive // that would trigger the useless_attribute lint. #[derive(DeriveSomething)] diff --git a/src/tools/clippy/tests/ui/useless_attribute.rs b/src/tools/clippy/tests/ui/useless_attribute.rs index 30a4c354b238a..44fb6733f054d 100644 --- a/src/tools/clippy/tests/ui/useless_attribute.rs +++ b/src/tools/clippy/tests/ui/useless_attribute.rs @@ -42,6 +42,10 @@ mod foo { #[allow(deprecated)] pub use foo::Bar; +// don't lint on exported_private_dependencies for `use` items +#[allow(exported_private_dependencies)] +use {}; + // This should not trigger the lint. There's lint level definitions inside the external derive // that would trigger the useless_attribute lint. #[derive(DeriveSomething)] diff --git a/src/tools/clippy/triagebot.toml b/src/tools/clippy/triagebot.toml index 09dec7675e7e5..eb8442ab21e2f 100644 --- a/src/tools/clippy/triagebot.toml +++ b/src/tools/clippy/triagebot.toml @@ -55,6 +55,11 @@ labels = ["S-waiting-on-concerns"] # Amend a review to include a link to what was changed since the review [review-changes-since] +# Adds a "View all comments" link on the issue/PR body that shows all the comments of it +# Documentation at: https://forge.rust-lang.org/triagebot/view-all-comments-link.html +[view-all-comments-link] +threshold = 20 + [assign] contributing_url = "https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md" users_on_vacation = [