Merge commit 'a62c6af53676bb15a40488ce2d632de558f001de' into clippy-subtree-update

This commit is contained in:
Philipp Krones
2026-02-12 13:43:03 +01:00
parent 99e47220fc
commit c8fd55b457
101 changed files with 980 additions and 293 deletions
+1 -1
View File
@@ -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"
+2 -2
View File
@@ -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
+3 -3
View File
@@ -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
+3 -3
View File
@@ -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:**
+3 -3
View File
@@ -35,7 +35,7 @@
"IPv4", "IPv6",
"InfiniBand", "RoCE",
"ClojureScript", "CoffeeScript", "JavaScript", "PostScript", "PureScript", "TypeScript",
"PowerPC", "WebAssembly",
"PowerPC", "PowerShell", "WebAssembly",
"NaN", "NaNs",
"OAuth", "GraphQL",
"OCaml",
@@ -423,7 +423,7 @@ fn span_from_toml_range(file: &SourceFile, span: Range<usize>) -> Span {
#[lints(multiple_crate_versions)]
allowed_duplicate_crates: Vec<String> = 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<String> =
@@ -620,7 +620,7 @@ fn span_from_toml_range(file: &SourceFile, span: Range<usize>) -> Span {
#[lints(disallowed_types)]
disallowed_types: Vec<DisallowedPath> = 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.
+2 -2
View File
@@ -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);
@@ -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
@@ -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,
);
},
@@ -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() => {
@@ -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",
+51 -43
View File
@@ -75,45 +75,47 @@ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
"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<Macr
}
/// Extracts all value expressions from the `match`-tree generated by `dbg!`.
///
///
/// E.g. from
/// ```rust, ignore
/// match 1 {
@@ -181,14 +183,20 @@ fn first_dbg_macro_in_expansion(cx: &LateContext<'_>, span: Span) -> Option<Macr
fn collect_vals<'hir>(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"),
}
}
@@ -53,21 +53,26 @@ fn is_missing_punctuation(doc_string: &str) -> Vec<MissingPunctuation> {
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<MissingPunctuation> {
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)
@@ -91,7 +91,7 @@ pub fn check(cx: &LateContext<'_>, doc: &str, range: Range<usize>, fragments: &F
diag.span_suggestion_verbose(
this_fragment.span.shrink_to_hi(),
"add footnote definition",
format!("\n\n{label}: <!-- description -->", label = &doc[start..end],),
format!("\n\n{label}: <!-- description -->", label = &doc[start..end]),
Applicability::HasPlaceholders,
);
} else {
+8 -2
View File
@@ -1016,6 +1016,7 @@ struct CodeTags {
no_run: bool,
ignore: bool,
compile_fail: bool,
test_harness: bool,
rust: bool,
}
@@ -1026,6 +1027,7 @@ fn default() -> Self {
no_run: false,
ignore: false,
compile_fail: false,
test_harness: false,
rust: true,
}
@@ -1059,7 +1061,11 @@ fn parse(lang: &str) -> Self {
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<Item = (pulldown_cmark::Event<'a>, Range<usize
if tags.rust && !tags.compile_fail && !tags.ignore {
needless_doctest_main::check(cx, &text, range.start, fragments);
if !tags.no_run {
if !tags.no_run && !tags.test_harness {
test_attr_in_doctest::check(cx, &text, range.start, fragments);
}
}
@@ -9,7 +9,7 @@
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, QPath, RustcVersion};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::ty::TyCtxt;
use rustc_middle::ty::{self, TyCtxt, UintTy};
use rustc_session::impl_lint_pass;
use rustc_span::Symbol;
@@ -76,13 +76,14 @@ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) {
.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_<time-unit> 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)
+1 -1
View File
@@ -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}"),
);
}
}
+2 -1
View File
@@ -13,7 +13,7 @@
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;
}
+2 -2
View File
@@ -92,7 +92,7 @@ fn suggestion(
),
(
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 @@ fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
);
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())"),
);
},
_ => {},
+1 -1
View File
@@ -44,7 +44,7 @@
/// 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?
+4
View File
@@ -6,6 +6,7 @@
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)
+1 -1
View File
@@ -301,7 +301,7 @@ fn replace_in_pattern(
.collect::<Vec<_>>();
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} }}");
},
+1 -1
View File
@@ -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,
);
}
@@ -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,
);
},
+1 -1
View File
@@ -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 {
+1 -1
View File
@@ -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,
@@ -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,
);
},
);
}
+1 -1
View File
@@ -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,
);
}
+14 -1
View File
@@ -4965,6 +4965,16 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
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 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
};
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 @@ fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
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) {
@@ -58,7 +58,10 @@ pub(super) fn check(
.iter()
.map(|x| &x.kind)
.collect::<Box<[_]>>()
&& 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)
{
+23 -14
View File
@@ -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,
);
},
);
}
}
+1 -1
View File
@@ -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");
@@ -3,6 +3,7 @@
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,
);
},
_ => (),
}
}
@@ -364,15 +364,15 @@ fn report_indexes(cx: &LateContext<'_>, map: UnindexMap<u64, Vec<IndexEntry<'_>>
// `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 {
@@ -97,7 +97,7 @@ fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
span_without_semi,
"this import should be renamed",
"try",
format!("{import} as {name}",),
format!("{import} as {name}"),
Applicability::MachineApplicable,
);
}
+8 -4
View File
@@ -10,10 +10,10 @@
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, [], _)
+1 -1
View File
@@ -1038,7 +1038,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
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(
+1 -1
View File
@@ -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
+6 -2
View File
@@ -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,
+1 -1
View File
@@ -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,
+18
View File
@@ -5,6 +5,7 @@
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 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) {
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,
);
}
}
}
+1 -3
View File
@@ -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();
+1 -1
View File
@@ -109,7 +109,7 @@ fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
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,
);
},
+1 -1
View File
@@ -56,7 +56,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
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"),
);
}
}
+1 -1
View File
@@ -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}")
}
+1 -1
View File
@@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain:
<!-- begin autogenerated nightly -->
```
nightly-2026-01-22
nightly-2026-02-11
```
<!-- end autogenerated nightly -->
+7 -7
View File
@@ -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,
}
}
+11 -12
View File
@@ -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("]"))
}
+1
View File
@@ -147,6 +147,7 @@ macro_rules! generate {
exp,
expect_err,
expn_data,
exported_private_dependencies,
extend,
filter,
filter_map,
+1 -1
View File
@@ -31,9 +31,9 @@
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};
+1 -1
View File
@@ -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"
+1 -1
View File
@@ -91,7 +91,7 @@ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
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())?;
+2 -2
View File
@@ -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.
+5
View File
@@ -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<u8> {
let allow = None;
+5
View File
@@ -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<u8> {
let allow = None;
+7 -1
View File
@@ -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
+32
View File
@@ -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();
}
}
+32
View File
@@ -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();
}
}
+14 -1
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
@@ -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
///
@@ -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
///
@@ -32,82 +32,76 @@ LL | /// <https://spec.commonmark.org/0.31.2/#autolinks>
| ^ 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
+5
View File
@@ -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);
}
+5
View File
@@ -89,3 +89,8 @@ fn test() {
let dur = Duration::from_secs(60);
}
}
fn issue16457() {
// Methods taking something else than `u64` are not covered
_ = Duration::from_nanos_u128(1 << 90);
}
@@ -271,4 +271,12 @@ fn issue15987() -> i32 {
r
}
fn has_comment() -> Vec<usize> {
let v = Vec::new();
// TODO: stuff
v
}
fn main() {}
@@ -271,4 +271,12 @@ fn issue15987() -> i32 {
r
}
fn has_comment() -> Vec<usize> {
let v = Vec::new();
// TODO: stuff
v
}
fn main() {}
+8
View File
@@ -271,4 +271,12 @@ macro_rules! sample {
r
}
fn has_comment() -> Vec<usize> {
let v = Vec::new();
// TODO: stuff
v
}
fn main() {}
+12
View File
@@ -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::<Empty>() };
//~^ manual_dangling_ptr
}
+12
View File
@@ -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
}
+17 -11
View File
@@ -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::<u32>()`
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::<f32>()`
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::<u8>() as *const u8;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `std::ptr::dangling::<u8>()`
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::<u32>() as *const u32;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `std::ptr::dangling::<u32>()`
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::<usize>() as *const usize;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `std::ptr::dangling::<usize>()`
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::<Empty>()`
error: aborting due to 11 previous errors
+19
View File
@@ -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();
}
+19
View File
@@ -235,3 +235,22 @@ fn check_result(b: Result<u8, ()>) {
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();
}
+13 -1
View File
@@ -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
+14 -1
View File
@@ -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 };
}
}
+18 -1
View File
@@ -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,
};
}
}
+11 -1
View File
@@ -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
+2 -1
View File
@@ -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
}
+2 -1
View File
@@ -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
}
+10
View File
@@ -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;
}
}
+9
View File
@@ -515,3 +515,12 @@ fn wrongly_unmangled_macros() -> Option<i32> {
test_expr!(42)?;
test_expr!(42)
}
fn issue16429(b: i32) -> Option<i32> {
let a = Some(5);
let _ = if b == 1 {
b
} else { a? };
Some(0)
}
+14
View File
@@ -635,3 +635,17 @@ macro_rules! test_expr {
}
test_expr!(42)
}
fn issue16429(b: i32) -> Option<i32> {
let a = Some(5);
let _ = if b == 1 {
b
} else if let Some(x) = a {
//~^ question_mark
x
} else {
return None;
};
Some(0)
}
+13 -1
View File
@@ -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
-2
View File
@@ -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> {
-2
View File
@@ -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> {
+88 -29
View File
@@ -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:61:13
|
LL | let _ = s1.trim().split("\n");
| ^^^^^^^^^^^^^^^^^^^^^
|
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
|
LL | let _ = s1.trim().split("\n");
| ^^^^^^^^^^^^^^^^^^^^^ help: use `str.lines()` instead: `s1.lines()`
error: using `str.trim().split()` with hard-coded newlines
--> tests/ui/str_split.rs:65: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:70:13
|
LL | let _ = s2.trim().split("\n");
| ^^^^^^^^^^^^^^^^^^^^^
|
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
|
LL | let _ = s2.trim().split("\n");
| ^^^^^^^^^^^^^^^^^^^^^ help: use `str.lines()` instead: `s2.lines()`
error: using `str.trim().split()` with hard-coded newlines
--> tests/ui/str_split.rs:74: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:80:13
|
LL | let _ = s3.trim().split("\n");
| ^^^^^^^^^^^^^^^^^^^^^
|
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
|
LL | let _ = s3.trim().split("\n");
| ^^^^^^^^^^^^^^^^^^^^^ help: use `str.lines()` instead: `s3.lines()`
error: using `str.trim().split()` with hard-coded newlines
--> tests/ui/str_split.rs:84: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
+29
View File
@@ -22,3 +22,32 @@ fn issue16271(key: &[u8]) {
let _value = t!(str::from_utf8(key)).to_owned();
//~^ str_to_string
}
struct GenericWrapper<T>(T);
impl<T> GenericWrapper<T> {
fn mapper<U, F: FnOnce(T) -> 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<F: Fn(&str) -> 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
}
+29
View File
@@ -22,3 +22,32 @@ macro_rules! t {
let _value = t!(str::from_utf8(key)).to_string();
//~^ str_to_string
}
struct GenericWrapper<T>(T);
impl<T> GenericWrapper<T> {
fn mapper<U, F: FnOnce(T) -> 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<F: Fn(&str) -> 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
}
+31 -1
View File
@@ -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
+8
View File
@@ -46,3 +46,11 @@
/// fn not_even_rust() { panic!("Ouch") }
/// ```
fn test_attr_in_doctests() {}
/// ```test_harness
/// #[test]
/// fn foo() {
/// panic!();
/// }
/// ```
pub fn issue16447() {}
@@ -3,7 +3,6 @@
#![feature(min_generic_const_args)]
trait AssocConstTrait {
type const ASSOC: usize;
}
fn assoc_const_args<T>()
@@ -3,7 +3,6 @@
#![feature(min_generic_const_args)]
trait AssocConstTrait {
type const ASSOC: usize;
}
fn assoc_const_args<T>()
@@ -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<ASSOC = 0> + AssocConstTrait<ASSOC = 0>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `AssocConstTrait<ASSOC = 0>`
+1 -1
View File
@@ -334,7 +334,7 @@ fn eq(&self, other: &Self) -> bool {
}
// 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<T> for &T` and `PartialEq<&T> for T` impls.
#[derive(PartialEq)]
-22
View File
@@ -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<u8, u8> = Ok(0);
let _ = res.unwrap();
//~^ unwrap_used
let _ = res.unwrap_err();
//~^ unwrap_used
}
fn main() {
unwrap_option();
unwrap_result();
}
-31
View File
@@ -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
+12
View File
@@ -83,3 +83,15 @@ fn test(file: &str) {
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
}
+49 -1
View File
@@ -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
+4
View File
@@ -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)]
+4
View File
@@ -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)]

Some files were not shown because too many files have changed in this diff Show More