Merge commit '7248d06384c6a90de58c04c1f46be88821278d8b' into sync-from-clippy

This commit is contained in:
David Koloski
2022-09-21 13:02:37 -04:00
parent 0dc24ca376
commit 4d015293d1
111 changed files with 2040 additions and 1674 deletions

View File

@@ -3800,6 +3800,7 @@ Released 2018-09-13
[`items_after_statements`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_statements [`items_after_statements`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_statements
[`iter_cloned_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_cloned_collect [`iter_cloned_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_cloned_collect
[`iter_count`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_count [`iter_count`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_count
[`iter_kv_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_kv_map
[`iter_next_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_next_loop [`iter_next_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_next_loop
[`iter_next_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_next_slice [`iter_next_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_next_slice
[`iter_not_returning_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_not_returning_iterator [`iter_not_returning_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_not_returning_iterator

View File

@@ -110,23 +110,28 @@ Just make sure to remove the dependencies again before finally making a pull req
[IntelliJ_rust_homepage]: https://intellij-rust.github.io/ [IntelliJ_rust_homepage]: https://intellij-rust.github.io/
### Rust Analyzer ### Rust Analyzer
As of [#6869][6869], [`rust-analyzer`][ra_homepage] can understand that Clippy uses compiler-internals For [`rust-analyzer`][ra_homepage] to work correctly make sure that in the `rust-analyzer` configuration you set
using `extern crate` when `package.metadata.rust-analyzer.rustc_private` is set to `true` in Clippy's `Cargo.toml.`
You will require a `nightly` toolchain with the `rustc-dev` component installed.
Make sure that in the `rust-analyzer` configuration, you set
```json ```json
{ "rust-analyzer.rustc.source": "discover" } { "rust-analyzer.rustc.source": "discover" }
``` ```
and
```json
{ "rust-analyzer.updates.channel": "nightly" }
```
You should be able to see information on things like `Expr` or `EarlyContext` now if you hover them, also You should be able to see information on things like `Expr` or `EarlyContext` now if you hover them, also
a lot more type hints. a lot more type hints.
This will work with `rust-analyzer 2021-03-15` shipped in nightly `1.52.0-nightly (107896c32 2021-03-15)` or later.
To have `rust-analyzer` also work in the `clippy_dev` and `lintcheck` crates, add the following configuration
```json
{
"rust-analyzer.linkedProjects": [
"./Cargo.toml",
"clippy_dev/Cargo.toml",
"lintcheck/Cargo.toml",
]
}
```
[ra_homepage]: https://rust-analyzer.github.io/ [ra_homepage]: https://rust-analyzer.github.io/
[6869]: https://github.com/rust-lang/rust-clippy/pull/6869
## How Clippy works ## How Clippy works

View File

@@ -90,6 +90,7 @@ We start by opening the test file created at `tests/ui/foo_functions.rs`.
Update the file with some examples to get started: Update the file with some examples to get started:
```rust ```rust
#![allow(unused)]
#![warn(clippy::foo_functions)] #![warn(clippy::foo_functions)]
// Impl methods // Impl methods

View File

@@ -123,7 +123,8 @@ There are three ways to do this, depending on if the target trait has a
diagnostic item, lang item or neither. diagnostic item, lang item or neither.
```rust ```rust
use clippy_utils::{implements_trait, is_trait_method, match_trait_method, paths}; use clippy_utils::ty::implements_trait;
use clippy_utils::is_trait_method;
use rustc_span::symbol::sym; use rustc_span::symbol::sym;
impl LateLintPass<'_> for MyStructLint { impl LateLintPass<'_> for MyStructLint {
@@ -143,13 +144,6 @@ impl LateLintPass<'_> for MyStructLint {
.map_or(false, |id| implements_trait(cx, ty, id, &[])) { .map_or(false, |id| implements_trait(cx, ty, id, &[])) {
// `expr` implements `Drop` trait // `expr` implements `Drop` trait
} }
// 3. Using the type path with the expression
// we use `match_trait_method` function from Clippy's utils
// (This method should be avoided if possible)
if match_trait_method(cx, expr, &paths::INTO) {
// `expr` implements `Into` trait
}
} }
} }
``` ```
@@ -233,8 +227,9 @@ functions to deal with macros:
crates crates
```rust ```rust
#[macro_use] use rustc_middle::lint::in_external_macro;
extern crate a_crate_with_macros;
use a_crate_with_macros::foo;
// `foo` is defined in `a_crate_with_macros` // `foo` is defined in `a_crate_with_macros`
foo!("bar"); foo!("bar");

View File

@@ -188,6 +188,7 @@ pub(crate) fn get_stabilization_version() -> String {
fn get_test_file_contents(lint_name: &str, header_commands: Option<&str>) -> String { fn get_test_file_contents(lint_name: &str, header_commands: Option<&str>) -> String {
let mut contents = format!( let mut contents = format!(
indoc! {" indoc! {"
#![allow(unused)]
#![warn(clippy::{})] #![warn(clippy::{})]
fn main() {{ fn main() {{

View File

@@ -4,6 +4,7 @@ use clippy_utils::{meets_msrv, msrvs};
use rustc_ast::ast::{Expr, ExprKind, LitKind, Pat, PatKind, RangeEnd, RangeLimits}; use rustc_ast::ast::{Expr, ExprKind, LitKind, Pat, PatKind, RangeEnd, RangeLimits};
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_semver::RustcVersion; use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::Span; use rustc_span::Span;
@@ -79,6 +80,7 @@ fn check_range(cx: &EarlyContext<'_>, span: Span, start: &Expr, end: &Expr, sugg
(LitKind::Byte(b'a') | LitKind::Char('a'), LitKind::Byte(b'z') | LitKind::Char('z')) (LitKind::Byte(b'a') | LitKind::Char('a'), LitKind::Byte(b'z') | LitKind::Char('z'))
| (LitKind::Byte(b'A') | LitKind::Char('A'), LitKind::Byte(b'Z') | LitKind::Char('Z')) | (LitKind::Byte(b'A') | LitKind::Char('A'), LitKind::Byte(b'Z') | LitKind::Char('Z'))
) )
&& !in_external_macro(cx.sess(), span)
{ {
span_lint_and_then( span_lint_and_then(
cx, cx,

View File

@@ -1,9 +1,9 @@
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::macros::{find_assert_args, root_macro_call_first_node, PanicExpn}; use clippy_utils::macros::{find_assert_args, root_macro_call_first_node, PanicExpn};
use clippy_utils::path_res;
use clippy_utils::source::snippet_with_context; use clippy_utils::source::snippet_with_context;
use clippy_utils::ty::{implements_trait, is_copy, is_type_diagnostic_item}; use clippy_utils::ty::{has_debug_impl, is_copy, is_type_diagnostic_item};
use clippy_utils::usage::local_used_after_expr; use clippy_utils::usage::local_used_after_expr;
use clippy_utils::{is_expr_final_block_expr, path_res};
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::def::Res; use rustc_hir::def::Res;
use rustc_hir::{Expr, ExprKind}; use rustc_hir::{Expr, ExprKind};
@@ -58,6 +58,7 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnResultStates {
return; return;
} }
} }
let semicolon = if is_expr_final_block_expr(cx.tcx, e) {";"} else {""};
let mut app = Applicability::MachineApplicable; let mut app = Applicability::MachineApplicable;
match method_segment.ident.as_str() { match method_segment.ident.as_str() {
"is_ok" if type_suitable_to_unwrap(cx, substs.type_at(1)) => { "is_ok" if type_suitable_to_unwrap(cx, substs.type_at(1)) => {
@@ -68,8 +69,9 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnResultStates {
"called `assert!` with `Result::is_ok`", "called `assert!` with `Result::is_ok`",
"replace with", "replace with",
format!( format!(
"{}.unwrap()", "{}.unwrap(){}",
snippet_with_context(cx, recv.span, condition.span.ctxt(), "..", &mut app).0 snippet_with_context(cx, recv.span, condition.span.ctxt(), "..", &mut app).0,
semicolon
), ),
app, app,
); );
@@ -82,8 +84,9 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnResultStates {
"called `assert!` with `Result::is_err`", "called `assert!` with `Result::is_err`",
"replace with", "replace with",
format!( format!(
"{}.unwrap_err()", "{}.unwrap_err(){}",
snippet_with_context(cx, recv.span, condition.span.ctxt(), "..", &mut app).0 snippet_with_context(cx, recv.span, condition.span.ctxt(), "..", &mut app).0,
semicolon
), ),
app, app,
); );
@@ -94,13 +97,6 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnResultStates {
} }
} }
/// This checks whether a given type is known to implement Debug.
fn has_debug_impl<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
cx.tcx
.get_diagnostic_item(sym::Debug)
.map_or(false, |debug| implements_trait(cx, ty, debug, &[]))
}
fn type_suitable_to_unwrap<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { fn type_suitable_to_unwrap<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
has_debug_impl(cx, ty) && !ty.is_unit() && !ty.is_never() has_debug_impl(cx, ty) && !ty.is_unit() && !ty.is_never()
} }

View File

@@ -1,9 +1,9 @@
use rustc_ast::{ExprPrecedence, LitKind}; use rustc_ast::LitKind;
use rustc_hir::{Block, ExprKind}; use rustc_hir::{Block, ExprKind};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use clippy_utils::{diagnostics::span_lint_and_then, is_else_clause, source::snippet_block_with_applicability}; use clippy_utils::{diagnostics::span_lint_and_then, is_else_clause, sugg::Sugg};
use rustc_errors::Applicability; use rustc_errors::Applicability;
declare_clippy_lint! { declare_clippy_lint! {
@@ -55,27 +55,42 @@ fn check_if_else<'tcx>(ctx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx
if let ExprKind::If(check, then, Some(else_)) = expr.kind if let ExprKind::If(check, then, Some(else_)) = expr.kind
&& let Some(then_lit) = int_literal(then) && let Some(then_lit) = int_literal(then)
&& let Some(else_lit) = int_literal(else_) && let Some(else_lit) = int_literal(else_)
&& check_int_literal_equals_val(then_lit, 1)
&& check_int_literal_equals_val(else_lit, 0)
{ {
let inverted = if
check_int_literal_equals_val(then_lit, 1)
&& check_int_literal_equals_val(else_lit, 0) {
false
} else if
check_int_literal_equals_val(then_lit, 0)
&& check_int_literal_equals_val(else_lit, 1) {
true
} else {
// Expression isn't boolean, exit
return;
};
let mut applicability = Applicability::MachineApplicable; let mut applicability = Applicability::MachineApplicable;
let snippet = snippet_block_with_applicability(ctx, check.span, "..", None, &mut applicability); let snippet = {
let snippet_with_braces = { let mut sugg = Sugg::hir_with_applicability(ctx, check, "..", &mut applicability);
let need_parens = should_have_parentheses(check); if inverted {
let (left_paren, right_paren) = if need_parens {("(", ")")} else {("", "")}; sugg = !sugg;
format!("{left_paren}{snippet}{right_paren}") }
sugg
}; };
let ty = ctx.typeck_results().expr_ty(then_lit); // then and else must be of same type let ty = ctx.typeck_results().expr_ty(then_lit); // then and else must be of same type
let suggestion = { let suggestion = {
let wrap_in_curly = is_else_clause(ctx.tcx, expr); let wrap_in_curly = is_else_clause(ctx.tcx, expr);
let (left_curly, right_curly) = if wrap_in_curly {("{", "}")} else {("", "")}; let mut s = Sugg::NonParen(format!("{ty}::from({snippet})").into());
format!( if wrap_in_curly {
"{left_curly}{ty}::from({snippet}){right_curly}" s = s.blockify();
) }
s
}; // when used in else clause if statement should be wrapped in curly braces }; // when used in else clause if statement should be wrapped in curly braces
let into_snippet = snippet.clone().maybe_par();
let as_snippet = snippet.as_ty(ty);
span_lint_and_then(ctx, span_lint_and_then(ctx,
BOOL_TO_INT_WITH_IF, BOOL_TO_INT_WITH_IF,
expr.span, expr.span,
@@ -87,7 +102,7 @@ fn check_if_else<'tcx>(ctx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx
suggestion, suggestion,
applicability, applicability,
); );
diag.note(format!("`{snippet_with_braces} as {ty}` or `{snippet_with_braces}.into()` can also be valid options")); diag.note(format!("`{as_snippet}` or `{into_snippet}.into()` can also be valid options"));
}); });
}; };
} }
@@ -119,7 +134,3 @@ fn check_int_literal_equals_val<'tcx>(expr: &'tcx rustc_hir::Expr<'tcx>, expecte
false false
} }
} }
fn should_have_parentheses<'tcx>(check: &'tcx rustc_hir::Expr<'tcx>) -> bool {
check.precedence().order() < ExprPrecedence::Cast.order()
}

View File

@@ -237,7 +237,7 @@ impl<'a, 'tcx, 'v> SuggestContext<'a, 'tcx, 'v> {
} }
}, },
&Term(n) => { &Term(n) => {
let snip = snippet_opt(self.cx, self.terminals[n as usize].span)?; let snip = snippet_opt(self.cx, self.terminals[n as usize].span.source_callsite())?;
self.output.push_str(&snip); self.output.push_str(&snip);
}, },
} }

View File

@@ -297,13 +297,10 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
if !is_lint_allowed(cx, EXPLICIT_DEREF_METHODS, expr.hir_id) if !is_lint_allowed(cx, EXPLICIT_DEREF_METHODS, expr.hir_id)
&& position.lint_explicit_deref() => && position.lint_explicit_deref() =>
{ {
let ty_changed_count = usize::from(!deref_method_same_type(expr_ty, typeck.expr_ty(sub_expr)));
self.state = Some(( self.state = Some((
State::DerefMethod { State::DerefMethod {
ty_changed_count: if deref_method_same_type(expr_ty, typeck.expr_ty(sub_expr)) { ty_changed_count,
0
} else {
1
},
is_final_ufcs: matches!(expr.kind, ExprKind::Call(..)), is_final_ufcs: matches!(expr.kind, ExprKind::Call(..)),
target_mut, target_mut,
}, },

View File

@@ -1,5 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::{is_default_equivalent, peel_blocks}; use clippy_utils::{is_default_equivalent, peel_blocks};
use rustc_errors::Applicability;
use rustc_hir::{ use rustc_hir::{
def::{DefKind, Res}, def::{DefKind, Res},
Body, Expr, ExprKind, GenericArg, Impl, ImplItemKind, Item, ItemKind, Node, PathSegment, QPath, TyKind, Body, Expr, ExprKind, GenericArg, Impl, ImplItemKind, Item, ItemKind, Node, PathSegment, QPath, TyKind,
@@ -100,15 +101,28 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls {
ExprKind::Struct(_, fields, _) => fields.iter().all(|ef| is_default_equivalent(cx, ef.expr)), ExprKind::Struct(_, fields, _) => fields.iter().all(|ef| is_default_equivalent(cx, ef.expr)),
_ => false, _ => false,
}; };
if should_emit { if should_emit {
let path_string = cx.tcx.def_path_str(adt_def.did()); let struct_span = cx.tcx.def_span(adt_def.did());
span_lint_and_help( span_lint_and_then(
cx, cx,
DERIVABLE_IMPLS, DERIVABLE_IMPLS,
item.span, item.span,
"this `impl` can be derived", "this `impl` can be derived",
None, |diag| {
&format!("try annotating `{}` with `#[derive(Default)]`", path_string), diag.span_suggestion_hidden(
item.span,
"remove the manual implementation...",
String::new(),
Applicability::MachineApplicable
);
diag.span_suggestion(
struct_span.shrink_to_lo(),
"...and instead derive it",
"#[derive(Default)]\n".to_string(),
Applicability::MachineApplicable
);
}
); );
} }
} }

View File

@@ -71,12 +71,12 @@ impl<'tcx> LateLintPass<'tcx> for UselessFormat {
let value = arg.param.value; let value = arg.param.value;
if_chain! { if_chain! {
if format_args.format_string.parts == [kw::Empty]; if format_args.format_string.parts == [kw::Empty];
if arg.format.is_default();
if match cx.typeck_results().expr_ty(value).peel_refs().kind() { if match cx.typeck_results().expr_ty(value).peel_refs().kind() {
ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(sym::String, adt.did()), ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(sym::String, adt.did()),
ty::Str => true, ty::Str => true,
_ => false, _ => false,
}; };
if !arg.format.has_string_formatting();
then { then {
let is_new_string = match value.kind { let is_new_string = match value.kind {
ExprKind::Binary(..) => true, ExprKind::Binary(..) => true,

View File

@@ -77,7 +77,7 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs {
if let ExpnKind::Macro(_, name) = outermost_expn_data.kind; if let ExpnKind::Macro(_, name) = outermost_expn_data.kind;
then { then {
for arg in &format_args.args { for arg in &format_args.args {
if arg.format.has_string_formatting() { if !arg.format.is_default() {
continue; continue;
} }
if is_aliased(&format_args, arg.param.value.hir_id) { if is_aliased(&format_args, arg.param.value.hir_id) {

View File

@@ -1,7 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::source::snippet; use clippy_utils::source::snippet;
use if_chain::if_chain; use rustc_hir::{Expr, ExprKind, Item, ItemKind, Node};
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::layout::LayoutOf;
use rustc_middle::ty::{self, ConstKind}; use rustc_middle::ty::{self, ConstKind};
@@ -39,29 +38,28 @@ impl_lint_pass!(LargeStackArrays => [LARGE_STACK_ARRAYS]);
impl<'tcx> LateLintPass<'tcx> for LargeStackArrays { impl<'tcx> LateLintPass<'tcx> for LargeStackArrays {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
if_chain! { if let ExprKind::Repeat(_, _) = expr.kind
if let ExprKind::Repeat(_, _) = expr.kind; && let ty::Array(element_type, cst) = cx.typeck_results().expr_ty(expr).kind()
if let ty::Array(element_type, cst) = cx.typeck_results().expr_ty(expr).kind(); && let ConstKind::Value(ty::ValTree::Leaf(element_count)) = cst.kind()
if let ConstKind::Value(ty::ValTree::Leaf(element_count)) = cst.kind(); && let Ok(element_count) = element_count.try_to_machine_usize(cx.tcx)
if let Ok(element_count) = element_count.try_to_machine_usize(cx.tcx); && let Ok(element_size) = cx.layout_of(*element_type).map(|l| l.size.bytes())
if let Ok(element_size) = cx.layout_of(*element_type).map(|l| l.size.bytes()); && !cx.tcx.hir().parent_iter(expr.hir_id)
if self.maximum_allowed_size < element_count * element_size; .any(|(_, node)| matches!(node, Node::Item(Item { kind: ItemKind::Static(..), .. })))
then { && self.maximum_allowed_size < element_count * element_size {
span_lint_and_help( span_lint_and_help(
cx, cx,
LARGE_STACK_ARRAYS, LARGE_STACK_ARRAYS,
expr.span, expr.span,
&format!( &format!(
"allocating a local array larger than {} bytes", "allocating a local array larger than {} bytes",
self.maximum_allowed_size self.maximum_allowed_size
), ),
None, None,
&format!( &format!(
"consider allocating on the heap with `vec!{}.into_boxed_slice()`", "consider allocating on the heap with `vec!{}.into_boxed_slice()`",
snippet(cx, expr.span, "[...]") snippet(cx, expr.span, "[...]")
), ),
); );
} }
}
} }
} }

View File

@@ -171,6 +171,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
LintId::of(methods::ITERATOR_STEP_BY_ZERO), LintId::of(methods::ITERATOR_STEP_BY_ZERO),
LintId::of(methods::ITER_CLONED_COLLECT), LintId::of(methods::ITER_CLONED_COLLECT),
LintId::of(methods::ITER_COUNT), LintId::of(methods::ITER_COUNT),
LintId::of(methods::ITER_KV_MAP),
LintId::of(methods::ITER_NEXT_SLICE), LintId::of(methods::ITER_NEXT_SLICE),
LintId::of(methods::ITER_NTH), LintId::of(methods::ITER_NTH),
LintId::of(methods::ITER_NTH_ZERO), LintId::of(methods::ITER_NTH_ZERO),
@@ -351,7 +352,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
LintId::of(useless_conversion::USELESS_CONVERSION), LintId::of(useless_conversion::USELESS_CONVERSION),
LintId::of(vec::USELESS_VEC), LintId::of(vec::USELESS_VEC),
LintId::of(vec_init_then_push::VEC_INIT_THEN_PUSH), LintId::of(vec_init_then_push::VEC_INIT_THEN_PUSH),
LintId::of(write::POSITIONAL_NAMED_FORMAT_PARAMETERS),
LintId::of(write::PRINTLN_EMPTY_STRING), LintId::of(write::PRINTLN_EMPTY_STRING),
LintId::of(write::PRINT_LITERAL), LintId::of(write::PRINT_LITERAL),
LintId::of(write::PRINT_WITH_NEWLINE), LintId::of(write::PRINT_WITH_NEWLINE),

View File

@@ -40,6 +40,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec!
LintId::of(methods::GET_LAST_WITH_LEN), LintId::of(methods::GET_LAST_WITH_LEN),
LintId::of(methods::INSPECT_FOR_EACH), LintId::of(methods::INSPECT_FOR_EACH),
LintId::of(methods::ITER_COUNT), LintId::of(methods::ITER_COUNT),
LintId::of(methods::ITER_KV_MAP),
LintId::of(methods::MANUAL_FILTER_MAP), LintId::of(methods::MANUAL_FILTER_MAP),
LintId::of(methods::MANUAL_FIND_MAP), LintId::of(methods::MANUAL_FIND_MAP),
LintId::of(methods::MANUAL_SPLIT_ONCE), LintId::of(methods::MANUAL_SPLIT_ONCE),

View File

@@ -313,6 +313,7 @@ store.register_lints(&[
methods::ITERATOR_STEP_BY_ZERO, methods::ITERATOR_STEP_BY_ZERO,
methods::ITER_CLONED_COLLECT, methods::ITER_CLONED_COLLECT,
methods::ITER_COUNT, methods::ITER_COUNT,
methods::ITER_KV_MAP,
methods::ITER_NEXT_SLICE, methods::ITER_NEXT_SLICE,
methods::ITER_NTH, methods::ITER_NTH,
methods::ITER_NTH_ZERO, methods::ITER_NTH_ZERO,
@@ -595,7 +596,6 @@ store.register_lints(&[
vec_init_then_push::VEC_INIT_THEN_PUSH, vec_init_then_push::VEC_INIT_THEN_PUSH,
wildcard_imports::ENUM_GLOB_USE, wildcard_imports::ENUM_GLOB_USE,
wildcard_imports::WILDCARD_IMPORTS, wildcard_imports::WILDCARD_IMPORTS,
write::POSITIONAL_NAMED_FORMAT_PARAMETERS,
write::PRINTLN_EMPTY_STRING, write::PRINTLN_EMPTY_STRING,
write::PRINT_LITERAL, write::PRINT_LITERAL,
write::PRINT_STDERR, write::PRINT_STDERR,

View File

@@ -35,5 +35,4 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec!
LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
LintId::of(swap_ptr_to_ref::SWAP_PTR_TO_REF), LintId::of(swap_ptr_to_ref::SWAP_PTR_TO_REF),
LintId::of(write::POSITIONAL_NAMED_FORMAT_PARAMETERS),
]) ])

View File

@@ -40,7 +40,6 @@ extern crate rustc_lint;
extern crate rustc_middle; extern crate rustc_middle;
extern crate rustc_mir_dataflow; extern crate rustc_mir_dataflow;
extern crate rustc_parse; extern crate rustc_parse;
extern crate rustc_parse_format;
extern crate rustc_session; extern crate rustc_session;
extern crate rustc_span; extern crate rustc_span;
extern crate rustc_target; extern crate rustc_target;
@@ -425,7 +424,6 @@ pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore, sess: &Se
}) })
}); });
store.register_pre_expansion_pass(|| Box::new(write::Write::default()));
store.register_pre_expansion_pass(move || Box::new(attrs::EarlyAttributes { msrv })); store.register_pre_expansion_pass(move || Box::new(attrs::EarlyAttributes { msrv }));
} }
@@ -524,7 +522,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
#[cfg(feature = "internal")] #[cfg(feature = "internal")]
{ {
if std::env::var("ENABLE_METADATA_COLLECTION").eq(&Ok("1".to_string())) { if std::env::var("ENABLE_METADATA_COLLECTION").eq(&Ok("1".to_string())) {
store.register_late_pass(|| Box::new(utils::internal_lints::metadata_collector::MetadataCollector::new())); store.register_late_pass(|_| Box::new(utils::internal_lints::metadata_collector::MetadataCollector::new()));
return; return;
} }
} }
@@ -879,6 +877,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
ignore_publish: cargo_ignore_publish, ignore_publish: cargo_ignore_publish,
}) })
}); });
store.register_late_pass(|_| Box::new(write::Write::default()));
store.register_early_pass(|| Box::new(crate_in_macro_def::CrateInMacroDef)); store.register_early_pass(|| Box::new(crate_in_macro_def::CrateInMacroDef));
store.register_early_pass(|| Box::new(empty_structs_with_brackets::EmptyStructsWithBrackets)); store.register_early_pass(|| Box::new(empty_structs_with_brackets::EmptyStructsWithBrackets));
store.register_late_pass(|_| Box::new(unnecessary_owned_empty_strings::UnnecessaryOwnedEmptyStrings)); store.register_late_pass(|_| Box::new(unnecessary_owned_empty_strings::UnnecessaryOwnedEmptyStrings));

View File

@@ -1,6 +1,6 @@
use super::ERR_EXPECT; use super::ERR_EXPECT;
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::ty::implements_trait; use clippy_utils::ty::has_debug_impl;
use clippy_utils::{meets_msrv, msrvs, ty::is_type_diagnostic_item}; use clippy_utils::{meets_msrv, msrvs, ty::is_type_diagnostic_item};
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_lint::LateContext; use rustc_lint::LateContext;
@@ -28,7 +28,7 @@ pub(super) fn check(
// Tests if the T type in a `Result<T, E>` is not None // Tests if the T type in a `Result<T, E>` is not None
if let Some(data_type) = get_data_type(cx, result_type); if let Some(data_type) = get_data_type(cx, result_type);
// Tests if the T type in a `Result<T, E>` implements debug // Tests if the T type in a `Result<T, E>` implements debug
if has_debug_impl(data_type, cx); if has_debug_impl(cx, data_type);
then { then {
span_lint_and_sugg( span_lint_and_sugg(
@@ -51,10 +51,3 @@ fn get_data_type<'a>(cx: &LateContext<'_>, ty: Ty<'a>) -> Option<Ty<'a>> {
_ => None, _ => None,
} }
} }
/// Given a type, very if the Debug trait has been impl'd
fn has_debug_impl<'tcx>(ty: Ty<'tcx>, cx: &LateContext<'tcx>) -> bool {
cx.tcx
.get_diagnostic_item(sym::Debug)
.map_or(false, |debug| implements_trait(cx, ty, debug, &[]))
}

View File

@@ -0,0 +1,87 @@
#![allow(unused_imports)]
use super::ITER_KV_MAP;
use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_sugg, span_lint_and_then};
use clippy_utils::source::{snippet, snippet_with_applicability};
use clippy_utils::sugg;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::visitors::is_local_used;
use rustc_hir::{BindingAnnotation, Body, BorrowKind, Expr, ExprKind, Mutability, Pat, PatKind};
use rustc_lint::{LateContext, LintContext};
use rustc_middle::ty;
use rustc_span::sym;
use rustc_span::Span;
/// lint use of:
/// - `hashmap.iter().map(|(_, v)| v)`
/// - `hashmap.into_iter().map(|(_, v)| v)`
/// on `HashMaps` and `BTreeMaps` in std
pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
map_type: &'tcx str, // iter / into_iter
expr: &'tcx Expr<'tcx>, // .iter().map(|(_, v_| v))
recv: &'tcx Expr<'tcx>, // hashmap
m_arg: &'tcx Expr<'tcx>, // |(_, v)| v
) {
if_chain! {
if !expr.span.from_expansion();
if let ExprKind::Closure(c) = m_arg.kind;
if let Body {params: [p], value: body_expr, generator_kind: _ } = cx.tcx.hir().body(c.body);
if let PatKind::Tuple([key_pat, val_pat], _) = p.pat.kind;
let (replacement_kind, binded_ident) = match (&key_pat.kind, &val_pat.kind) {
(key, PatKind::Binding(_, _, value, _)) if pat_is_wild(cx, key, m_arg) => ("value", value),
(PatKind::Binding(_, _, key, _), value) if pat_is_wild(cx, value, m_arg) => ("key", key),
_ => return,
};
let ty = cx.typeck_results().expr_ty(recv);
if is_type_diagnostic_item(cx, ty, sym::HashMap) || is_type_diagnostic_item(cx, ty, sym::BTreeMap);
then {
let mut applicability = rustc_errors::Applicability::MachineApplicable;
let recv_snippet = snippet_with_applicability(cx, recv.span, "map", &mut applicability);
let into_prefix = if map_type == "into_iter" {"into_"} else {""};
if_chain! {
if let ExprKind::Path(rustc_hir::QPath::Resolved(_, path)) = body_expr.kind;
if let [local_ident] = path.segments;
if local_ident.ident.as_str() == binded_ident.as_str();
then {
span_lint_and_sugg(
cx,
ITER_KV_MAP,
expr.span,
&format!("iterating on a map's {}s", replacement_kind),
"try",
format!("{}.{}{}s()", recv_snippet, into_prefix, replacement_kind),
applicability,
);
} else {
span_lint_and_sugg(
cx,
ITER_KV_MAP,
expr.span,
&format!("iterating on a map's {}s", replacement_kind),
"try",
format!("{}.{}{}s().map(|{}| {})", recv_snippet, into_prefix, replacement_kind, binded_ident,
snippet_with_applicability(cx, body_expr.span, "/* body */", &mut applicability)),
applicability,
);
}
}
}
}
}
/// Returns `true` if the pattern is a `PatWild`, or is an ident prefixed with `_`
/// that is not locally used.
fn pat_is_wild<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx PatKind<'_>, body: &'tcx Expr<'_>) -> bool {
match *pat {
PatKind::Wild => true,
PatKind::Binding(_, id, ident, None) if ident.as_str().starts_with('_') => !is_local_used(cx, body, id),
_ => false,
}
}

View File

@@ -35,6 +35,7 @@ mod into_iter_on_ref;
mod is_digit_ascii_radix; mod is_digit_ascii_radix;
mod iter_cloned_collect; mod iter_cloned_collect;
mod iter_count; mod iter_count;
mod iter_kv_map;
mod iter_next_slice; mod iter_next_slice;
mod iter_nth; mod iter_nth;
mod iter_nth_zero; mod iter_nth_zero;
@@ -3036,6 +3037,37 @@ declare_clippy_lint! {
"use of `File::read_to_end` or `File::read_to_string`" "use of `File::read_to_end` or `File::read_to_string`"
} }
declare_clippy_lint! {
/// ### What it does
///
/// Checks for iterating a map (`HashMap` or `BTreeMap`) and
/// ignoring either the keys or values.
///
/// ### Why is this bad?
///
/// Readability. There are `keys` and `values` methods that
/// can be used to express that we only need the keys or the values.
///
/// ### Example
///
/// ```
/// # use std::collections::HashMap;
/// let map: HashMap<u32, u32> = HashMap::new();
/// let values = map.iter().map(|(_, value)| value).collect::<Vec<_>>();
/// ```
///
/// Use instead:
/// ```
/// # use std::collections::HashMap;
/// let map: HashMap<u32, u32> = HashMap::new();
/// let values = map.values().collect::<Vec<_>>();
/// ```
#[clippy::version = "1.65.0"]
pub ITER_KV_MAP,
complexity,
"iterating on map using `iter` when `keys` or `values` would do"
}
pub struct Methods { pub struct Methods {
avoid_breaking_exported_api: bool, avoid_breaking_exported_api: bool,
msrv: Option<RustcVersion>, msrv: Option<RustcVersion>,
@@ -3159,6 +3191,7 @@ impl_lint_pass!(Methods => [
UNNECESSARY_SORT_BY, UNNECESSARY_SORT_BY,
VEC_RESIZE_TO_ZERO, VEC_RESIZE_TO_ZERO,
VERBOSE_FILE_READS, VERBOSE_FILE_READS,
ITER_KV_MAP,
]); ]);
/// Extracts a method call name, args, and `Span` of the method name. /// Extracts a method call name, args, and `Span` of the method name.
@@ -3498,6 +3531,9 @@ impl Methods {
(name @ ("map" | "map_err"), [m_arg]) => { (name @ ("map" | "map_err"), [m_arg]) => {
if name == "map" { if name == "map" {
map_clone::check(cx, expr, recv, m_arg, self.msrv); map_clone::check(cx, expr, recv, m_arg, self.msrv);
if let Some((map_name @ ("iter" | "into_iter"), recv2, _, _)) = method_call(recv) {
iter_kv_map::check(cx, map_name, expr, recv2, m_arg);
}
} else { } else {
map_err_ignore::check(cx, expr, m_arg); map_err_ignore::check(cx, expr, m_arg);
} }

View File

@@ -1,5 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use clippy_utils::ty::{has_debug_impl, is_type_diagnostic_item};
use if_chain::if_chain; use if_chain::if_chain;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_lint::LateContext; use rustc_lint::LateContext;
@@ -15,7 +15,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result); if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result);
let result_type = cx.typeck_results().expr_ty(recv); let result_type = cx.typeck_results().expr_ty(recv);
if let Some(error_type) = get_error_type(cx, result_type); if let Some(error_type) = get_error_type(cx, result_type);
if has_debug_impl(error_type, cx); if has_debug_impl(cx, error_type);
then { then {
span_lint_and_help( span_lint_and_help(
@@ -37,10 +37,3 @@ fn get_error_type<'a>(cx: &LateContext<'_>, ty: Ty<'a>) -> Option<Ty<'a>> {
_ => None, _ => None,
} }
} }
/// This checks whether a given type is known to implement Debug.
fn has_debug_impl<'tcx>(ty: Ty<'tcx>, cx: &LateContext<'tcx>) -> bool {
cx.tcx
.get_diagnostic_item(sym::Debug)
.map_or(false, |debug| implements_trait(cx, ty, debug, &[]))
}

View File

@@ -7,7 +7,7 @@ use clippy_utils::visitors::find_all_ret_expressions;
use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, return_ty}; use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, return_ty};
use clippy_utils::{meets_msrv, msrvs}; use clippy_utils::{meets_msrv, msrvs};
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind, ItemKind, Node}; use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind, ItemKind, LangItem, Node};
use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::LateContext; use rustc_lint::LateContext;
use rustc_middle::mir::Mutability; use rustc_middle::mir::Mutability;
@@ -268,7 +268,7 @@ fn check_other_call_arg<'tcx>(
// We can't add an `&` when the trait is `Deref` because `Target = &T` won't match // We can't add an `&` when the trait is `Deref` because `Target = &T` won't match
// `Target = T`. // `Target = T`.
if n_refs > 0 || is_copy(cx, receiver_ty) || trait_predicate.def_id() != deref_trait_id; if n_refs > 0 || is_copy(cx, receiver_ty) || trait_predicate.def_id() != deref_trait_id;
let n_refs = max(n_refs, if is_copy(cx, receiver_ty) { 0 } else { 1 }); let n_refs = max(n_refs, usize::from(!is_copy(cx, receiver_ty)));
if let Some(receiver_snippet) = snippet_opt(cx, receiver.span); if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
then { then {
span_lint_and_sugg( span_lint_and_sugg(
@@ -379,6 +379,10 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty<
Node::Expr(parent_expr) => { Node::Expr(parent_expr) => {
if let Some((callee_def_id, call_substs, recv, call_args)) = get_callee_substs_and_args(cx, parent_expr) if let Some((callee_def_id, call_substs, recv, call_args)) = get_callee_substs_and_args(cx, parent_expr)
{ {
if cx.tcx.lang_items().require(LangItem::IntoFutureIntoFuture) == Ok(callee_def_id) {
return false;
}
let fn_sig = cx.tcx.fn_sig(callee_def_id).skip_binder(); let fn_sig = cx.tcx.fn_sig(callee_def_id).skip_binder();
if let Some(arg_index) = recv.into_iter().chain(call_args).position(|arg| arg.hir_id == expr.hir_id) if let Some(arg_index) = recv.into_iter().chain(call_args).position(|arg| arg.hir_id == expr.hir_id)
&& let Some(param_ty) = fn_sig.inputs().get(arg_index) && let Some(param_ty) = fn_sig.inputs().get(arg_index)

View File

@@ -2,7 +2,7 @@ use rustc_ast::ast;
use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_lint::{EarlyContext, EarlyLintPass, Level, LintContext}; use rustc_lint::{EarlyContext, EarlyLintPass, Level, LintContext};
use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::{FileName, RealFileName, SourceFile, Span, SyntaxContext}; use rustc_span::{FileName, SourceFile, Span, SyntaxContext};
use std::ffi::OsStr; use std::ffi::OsStr;
use std::path::{Component, Path}; use std::path::{Component, Path};
@@ -79,7 +79,7 @@ impl EarlyLintPass for ModStyle {
let files = cx.sess().source_map().files(); let files = cx.sess().source_map().files();
let RealFileName::LocalPath(trim_to_src) = &cx.sess().opts.working_dir else { return }; let Some(trim_to_src) = cx.sess().opts.working_dir.local_path() else { return };
// `folder_segments` is all unique folder path segments `path/to/foo.rs` gives // `folder_segments` is all unique folder path segments `path/to/foo.rs` gives
// `[path, to]` but not foo // `[path, to]` but not foo
@@ -90,7 +90,7 @@ impl EarlyLintPass for ModStyle {
// `{ foo => path/to/foo.rs, .. } // `{ foo => path/to/foo.rs, .. }
let mut file_map = FxHashMap::default(); let mut file_map = FxHashMap::default();
for file in files.iter() { for file in files.iter() {
if let FileName::Real(RealFileName::LocalPath(lp)) = &file.name { if let FileName::Real(name) = &file.name && let Some(lp) = name.local_path() {
let path = if lp.is_relative() { let path = if lp.is_relative() {
lp lp
} else if let Ok(relative) = lp.strip_prefix(trim_to_src) { } else if let Ok(relative) = lp.strip_prefix(trim_to_src) {

View File

@@ -184,6 +184,10 @@ fn macro_braces(conf: FxHashSet<MacroMatcher>) -> FxHashMap<String, (String, Str
name: "vec", name: "vec",
braces: ("[", "]"), braces: ("[", "]"),
), ),
macro_matcher!(
name: "matches",
braces: ("(", ")"),
),
] ]
.into_iter() .into_iter()
.collect::<FxHashMap<_, _>>(); .collect::<FxHashMap<_, _>>();

View File

@@ -42,27 +42,30 @@ impl ArithmeticSideEffects {
} }
} }
/// Checks assign operators (+=, -=, *=, /=) of integers in a non-constant environment that /// Assuming that `expr` is a literal integer, checks operators (+=, -=, *, /) in a
/// won't overflow. /// non-constant environment that won't overflow.
fn has_valid_assign_op(op: &Spanned<hir::BinOpKind>, rhs: &hir::Expr<'_>, rhs_refs: Ty<'_>) -> bool { fn has_valid_op(op: &Spanned<hir::BinOpKind>, expr: &hir::Expr<'_>) -> bool {
if !Self::is_literal_integer(rhs, rhs_refs) { if let hir::BinOpKind::Add | hir::BinOpKind::Sub = op.node
return false; && let hir::ExprKind::Lit(ref lit) = expr.kind
&& let ast::LitKind::Int(0, _) = lit.node
{
return true;
} }
if let hir::BinOpKind::Div | hir::BinOpKind::Mul = op.node if let hir::BinOpKind::Div | hir::BinOpKind::Rem = op.node
&& let hir::ExprKind::Lit(ref lit) = rhs.kind && let hir::ExprKind::Lit(ref lit) = expr.kind
&& let ast::LitKind::Int(1, _) = lit.node && !matches!(lit.node, ast::LitKind::Int(0, _))
{
return true;
}
if let hir::BinOpKind::Mul = op.node
&& let hir::ExprKind::Lit(ref lit) = expr.kind
&& let ast::LitKind::Int(0 | 1, _) = lit.node
{ {
return true; return true;
} }
false false
} }
/// Checks "raw" binary operators (+, -, *, /) of integers in a non-constant environment
/// already handled by the CTFE.
fn has_valid_bin_op(lhs: &hir::Expr<'_>, lhs_refs: Ty<'_>, rhs: &hir::Expr<'_>, rhs_refs: Ty<'_>) -> bool {
Self::is_literal_integer(lhs, lhs_refs) && Self::is_literal_integer(rhs, rhs_refs)
}
/// Checks if the given `expr` has any of the inner `allowed` elements. /// Checks if the given `expr` has any of the inner `allowed` elements.
fn is_allowed_ty(&self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { fn is_allowed_ty(&self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
self.allowed.contains( self.allowed.contains(
@@ -83,7 +86,8 @@ impl ArithmeticSideEffects {
} }
fn issue_lint(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) { fn issue_lint(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) {
span_lint(cx, ARITHMETIC_SIDE_EFFECTS, expr.span, "arithmetic detected"); let msg = "arithmetic operation that can potentially result in unexpected side-effects";
span_lint(cx, ARITHMETIC_SIDE_EFFECTS, expr.span, msg);
self.expr_span = Some(expr.span); self.expr_span = Some(expr.span);
} }
@@ -115,13 +119,18 @@ impl ArithmeticSideEffects {
if self.is_allowed_ty(cx, lhs) || self.is_allowed_ty(cx, rhs) { if self.is_allowed_ty(cx, lhs) || self.is_allowed_ty(cx, rhs) {
return; return;
} }
let lhs_refs = cx.typeck_results().expr_ty(lhs).peel_refs(); let has_valid_op = match (
let rhs_refs = cx.typeck_results().expr_ty(rhs).peel_refs(); Self::is_literal_integer(lhs, cx.typeck_results().expr_ty(lhs).peel_refs()),
let has_valid_assign_op = Self::has_valid_assign_op(op, rhs, rhs_refs); Self::is_literal_integer(rhs, cx.typeck_results().expr_ty(rhs).peel_refs()),
if has_valid_assign_op || Self::has_valid_bin_op(lhs, lhs_refs, rhs, rhs_refs) { ) {
return; (true, true) => true,
(true, false) => Self::has_valid_op(op, lhs),
(false, true) => Self::has_valid_op(op, rhs),
(false, false) => false,
};
if !has_valid_op {
self.issue_lint(cx, expr);
} }
self.issue_lint(cx, expr);
} }
} }

View File

@@ -36,6 +36,7 @@ pub static RENAMED_LINTS: &[(&str, &str)] = &[
("clippy::invalid_ref", "invalid_value"), ("clippy::invalid_ref", "invalid_value"),
("clippy::mem_discriminant_non_enum", "enum_intrinsics_non_enums"), ("clippy::mem_discriminant_non_enum", "enum_intrinsics_non_enums"),
("clippy::panic_params", "non_fmt_panics"), ("clippy::panic_params", "non_fmt_panics"),
("clippy::positional_named_format_parameters", "named_arguments_used_positionally"),
("clippy::temporary_cstring_as_ptr", "temporary_cstring_as_ptr"), ("clippy::temporary_cstring_as_ptr", "temporary_cstring_as_ptr"),
("clippy::unknown_clippy_lints", "unknown_lints"), ("clippy::unknown_clippy_lints", "unknown_lints"),
("clippy::unused_label", "unused_labels"), ("clippy::unused_label", "unused_labels"),

View File

@@ -6,6 +6,7 @@ use rustc_hir::intravisit::{walk_expr, Visitor};
use rustc_hir::lang_items::LangItem; use rustc_hir::lang_items::LangItem;
use rustc_hir::{Block, Expr, ExprKind, HirId, Local, Node, PatKind, PathSegment, StmtKind}; use rustc_hir::{Block, Expr, ExprKind, HirId, Local, Node, PatKind, PathSegment, StmtKind};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::hir::nested_filter::OnlyBodies;
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym; use rustc_span::sym;
@@ -109,8 +110,14 @@ impl<'a, 'tcx> PeekableVisitor<'a, 'tcx> {
} }
} }
impl<'tcx> Visitor<'_> for PeekableVisitor<'_, 'tcx> { impl<'tcx> Visitor<'tcx> for PeekableVisitor<'_, 'tcx> {
fn visit_expr(&mut self, ex: &'_ Expr<'_>) { type NestedFilter = OnlyBodies;
fn nested_visit_map(&mut self) -> Self::Map {
self.cx.tcx.hir()
}
fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
if self.found_peek_call { if self.found_peek_call {
return; return;
} }
@@ -136,12 +143,11 @@ impl<'tcx> Visitor<'_> for PeekableVisitor<'_, 'tcx> {
return; return;
} }
if args.iter().any(|arg| { if args.iter().any(|arg| arg_is_mut_peekable(self.cx, arg)) {
matches!(arg.kind, ExprKind::Path(_)) && arg_is_mut_peekable(self.cx, arg)
}) {
self.found_peek_call = true; self.found_peek_call = true;
return;
} }
return;
}, },
// Catch anything taking a Peekable mutably // Catch anything taking a Peekable mutably
ExprKind::MethodCall( ExprKind::MethodCall(
@@ -190,21 +196,21 @@ impl<'tcx> Visitor<'_> for PeekableVisitor<'_, 'tcx> {
Node::Local(Local { init: Some(init), .. }) => { Node::Local(Local { init: Some(init), .. }) => {
if arg_is_mut_peekable(self.cx, init) { if arg_is_mut_peekable(self.cx, init) {
self.found_peek_call = true; self.found_peek_call = true;
return;
} }
break; return;
}, },
Node::Stmt(stmt) => match stmt.kind { Node::Stmt(stmt) => {
StmtKind::Expr(_) | StmtKind::Semi(_) => {}, match stmt.kind {
_ => { StmtKind::Local(_) | StmtKind::Item(_) => self.found_peek_call = true,
self.found_peek_call = true; StmtKind::Expr(_) | StmtKind::Semi(_) => {},
return; }
},
return;
}, },
Node::Block(_) | Node::ExprField(_) => {}, Node::Block(_) | Node::ExprField(_) => {},
_ => { _ => {
break; return;
}, },
} }
} }

View File

@@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::ty::same_type_and_consts; use clippy_utils::ty::same_type_and_consts;
use clippy_utils::{meets_msrv, msrvs}; use clippy_utils::{is_from_proc_macro, meets_msrv, msrvs};
use if_chain::if_chain; use if_chain::if_chain;
use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Applicability; use rustc_errors::Applicability;
@@ -87,7 +87,7 @@ impl_lint_pass!(UseSelf => [USE_SELF]);
const SEGMENTS_MSG: &str = "segments should be composed of at least 1 element"; const SEGMENTS_MSG: &str = "segments should be composed of at least 1 element";
impl<'tcx> LateLintPass<'tcx> for UseSelf { impl<'tcx> LateLintPass<'tcx> for UseSelf {
fn check_item(&mut self, _cx: &LateContext<'_>, item: &Item<'_>) { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &Item<'tcx>) {
if matches!(item.kind, ItemKind::OpaqueTy(_)) { if matches!(item.kind, ItemKind::OpaqueTy(_)) {
// skip over `ItemKind::OpaqueTy` in order to lint `foo() -> impl <..>` // skip over `ItemKind::OpaqueTy` in order to lint `foo() -> impl <..>`
return; return;
@@ -103,6 +103,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
if parameters.as_ref().map_or(true, |params| { if parameters.as_ref().map_or(true, |params| {
!params.parenthesized && !params.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_))) !params.parenthesized && !params.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_)))
}); });
if !is_from_proc_macro(cx, item); // expensive, should be last check
then { then {
StackItem::Check { StackItem::Check {
impl_id: item.def_id, impl_id: item.def_id,
@@ -213,9 +214,6 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
hir_ty_to_ty(cx.tcx, hir_ty) hir_ty_to_ty(cx.tcx, hir_ty)
}; };
if same_type_and_consts(ty, cx.tcx.type_of(impl_id)); if same_type_and_consts(ty, cx.tcx.type_of(impl_id));
let hir = cx.tcx.hir();
// prevents false positive on `#[derive(serde::Deserialize)]`
if !hir.span(hir.get_parent_node(hir_ty.hir_id)).in_derive_expansion();
then { then {
span_lint(cx, hir_ty.span); span_lint(cx, hir_ty.span);
} }

View File

@@ -476,7 +476,7 @@ pub fn format_error(error: Box<dyn Error>) -> String {
let mut msg = String::from(prefix); let mut msg = String::from(prefix);
for row in 0..rows { for row in 0..rows {
write!(msg, "\n").unwrap(); writeln!(msg).unwrap();
for (column, column_width) in column_widths.iter().copied().enumerate() { for (column, column_width) in column_widths.iter().copied().enumerate() {
let index = column * rows + row; let index = column * rows + row;
let field = fields.get(index).copied().unwrap_or_default(); let field = fields.get(index).copied().unwrap_or_default();

View File

@@ -1,20 +1,12 @@
use std::borrow::Cow; use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
use std::iter; use clippy_utils::macros::{root_macro_call_first_node, FormatArgsExpn, MacroCall};
use std::ops::{Deref, Range}; use clippy_utils::source::snippet_opt;
use rustc_ast::LitKind;
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; use rustc_errors::Applicability;
use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability}; use rustc_hir::{Expr, ExprKind, HirIdMap, Impl, Item, ItemKind};
use rustc_ast::ast::{Expr, ExprKind, Impl, Item, ItemKind, MacCall, Path, StrLit, StrStyle}; use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_ast::ptr::P;
use rustc_ast::token::{self, LitKind};
use rustc_ast::tokenstream::TokenStream;
use rustc_errors::{Applicability, DiagnosticBuilder};
use rustc_lexer::unescape::{self, EscapeError};
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
use rustc_parse::parser;
use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::symbol::{kw, Symbol}; use rustc_span::{sym, BytePos, Span};
use rustc_span::{sym, BytePos, InnerSpan, Span, DUMMY_SP};
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### What it does
@@ -74,13 +66,7 @@ declare_clippy_lint! {
/// application and might forget to remove those prints afterward. /// application and might forget to remove those prints afterward.
/// ///
/// ### Known problems /// ### Known problems
/// * Only catches `print!` and `println!` calls. /// Only catches `print!` and `println!` calls.
/// * The lint level is unaffected by crate attributes. The level can still
/// be set for functions, modules and other items. To change the level for
/// the entire crate, please use command line flags. More information and a
/// configuration example can be found in [clippy#6610].
///
/// [clippy#6610]: https://github.com/rust-lang/rust-clippy/issues/6610#issuecomment-977120558
/// ///
/// ### Example /// ### Example
/// ```rust /// ```rust
@@ -102,13 +88,7 @@ declare_clippy_lint! {
/// application and might forget to remove those prints afterward. /// application and might forget to remove those prints afterward.
/// ///
/// ### Known problems /// ### Known problems
/// * Only catches `eprint!` and `eprintln!` calls. /// Only catches `eprint!` and `eprintln!` calls.
/// * The lint level is unaffected by crate attributes. The level can still
/// be set for functions, modules and other items. To change the level for
/// the entire crate, please use command line flags. More information and a
/// configuration example can be found in [clippy#6610].
///
/// [clippy#6610]: https://github.com/rust-lang/rust-clippy/issues/6610#issuecomment-977120558
/// ///
/// ### Example /// ### Example
/// ```rust /// ```rust
@@ -149,10 +129,6 @@ declare_clippy_lint! {
/// (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary /// (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary
/// (i.e., just put the literal in the format string) /// (i.e., just put the literal in the format string)
/// ///
/// ### Known problems
/// Will also warn with macro calls as arguments that expand to literals
/// -- e.g., `println!("{}", env!("FOO"))`.
///
/// ### Example /// ### Example
/// ```rust /// ```rust
/// println!("{}", "foo"); /// println!("{}", "foo");
@@ -234,10 +210,6 @@ declare_clippy_lint! {
/// (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary /// (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary
/// (i.e., just put the literal in the format string) /// (i.e., just put the literal in the format string)
/// ///
/// ### Known problems
/// Will also warn with macro calls as arguments that expand to literals
/// -- e.g., `writeln!(buf, "{}", env!("FOO"))`.
///
/// ### Example /// ### Example
/// ```rust /// ```rust
/// # use std::fmt::Write; /// # use std::fmt::Write;
@@ -257,28 +229,6 @@ declare_clippy_lint! {
"writing a literal with a format string" "writing a literal with a format string"
} }
declare_clippy_lint! {
/// ### What it does
/// This lint warns when a named parameter in a format string is used as a positional one.
///
/// ### Why is this bad?
/// It may be confused for an assignment and obfuscates which parameter is being used.
///
/// ### Example
/// ```rust
/// println!("{}", x = 10);
/// ```
///
/// Use instead:
/// ```rust
/// println!("{x}", x = 10);
/// ```
#[clippy::version = "1.63.0"]
pub POSITIONAL_NAMED_FORMAT_PARAMETERS,
suspicious,
"named parameter in a format string is used positionally"
}
#[derive(Default)] #[derive(Default)]
pub struct Write { pub struct Write {
in_debug_impl: bool, in_debug_impl: bool,
@@ -294,537 +244,308 @@ impl_lint_pass!(Write => [
WRITE_WITH_NEWLINE, WRITE_WITH_NEWLINE,
WRITELN_EMPTY_STRING, WRITELN_EMPTY_STRING,
WRITE_LITERAL, WRITE_LITERAL,
POSITIONAL_NAMED_FORMAT_PARAMETERS,
]); ]);
impl EarlyLintPass for Write { impl<'tcx> LateLintPass<'tcx> for Write {
fn check_item(&mut self, _: &EarlyContext<'_>, item: &Item) { fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
if let ItemKind::Impl(box Impl { if is_debug_impl(cx, item) {
of_trait: Some(trait_ref), self.in_debug_impl = true;
..
}) = &item.kind
{
let trait_name = trait_ref
.path
.segments
.iter()
.last()
.expect("path has at least one segment")
.ident
.name;
if trait_name == sym::Debug {
self.in_debug_impl = true;
}
} }
} }
fn check_item_post(&mut self, _: &EarlyContext<'_>, _: &Item) { fn check_item_post(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
self.in_debug_impl = false; if is_debug_impl(cx, item) {
self.in_debug_impl = false;
}
} }
fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &MacCall) { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
fn is_build_script(cx: &EarlyContext<'_>) -> bool { let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return };
// Cargo sets the crate name for build scripts to `build_script_build` let Some(diag_name) = cx.tcx.get_diagnostic_name(macro_call.def_id) else { return };
cx.sess() let Some(name) = diag_name.as_str().strip_suffix("_macro") else { return };
.opts
.crate_name
.as_ref()
.map_or(false, |crate_name| crate_name == "build_script_build")
}
if mac.path == sym!(print) { let is_build_script = cx
if !is_build_script(cx) { .sess()
span_lint(cx, PRINT_STDOUT, mac.span(), "use of `print!`"); .opts
} .crate_name
self.lint_print_with_newline(cx, mac); .as_ref()
} else if mac.path == sym!(println) { .map_or(false, |crate_name| crate_name == "build_script_build");
if !is_build_script(cx) {
span_lint(cx, PRINT_STDOUT, mac.span(), "use of `println!`"); match diag_name {
} sym::print_macro | sym::println_macro => {
self.lint_println_empty_string(cx, mac); if !is_build_script {
} else if mac.path == sym!(eprint) { span_lint(cx, PRINT_STDOUT, macro_call.span, &format!("use of `{name}!`"));
span_lint(cx, PRINT_STDERR, mac.span(), "use of `eprint!`");
self.lint_print_with_newline(cx, mac);
} else if mac.path == sym!(eprintln) {
span_lint(cx, PRINT_STDERR, mac.span(), "use of `eprintln!`");
self.lint_println_empty_string(cx, mac);
} else if mac.path == sym!(write) {
if let (Some(fmt_str), dest) = self.check_tts(cx, mac.args.inner_tokens(), true) {
if check_newlines(&fmt_str) {
let (nl_span, only_nl) = newline_span(&fmt_str);
let nl_span = match (dest, only_nl) {
// Special case of `write!(buf, "\n")`: Mark everything from the end of
// `buf` for removal so no trailing comma [`writeln!(buf, )`] remains.
(Some(dest_expr), true) => nl_span.with_lo(dest_expr.span.hi()),
_ => nl_span,
};
span_lint_and_then(
cx,
WRITE_WITH_NEWLINE,
mac.span(),
"using `write!()` with a format string that ends in a single newline",
|err| {
err.multipart_suggestion(
"use `writeln!()` instead",
vec![(mac.path.span, String::from("writeln")), (nl_span, String::new())],
Applicability::MachineApplicable,
);
},
);
} }
} },
} else if mac.path == sym!(writeln) { sym::eprint_macro | sym::eprintln_macro => {
if let (Some(fmt_str), expr) = self.check_tts(cx, mac.args.inner_tokens(), true) { span_lint(cx, PRINT_STDERR, macro_call.span, &format!("use of `{name}!`"));
if fmt_str.symbol == kw::Empty { },
let mut applicability = Applicability::MachineApplicable; sym::write_macro | sym::writeln_macro => {},
let suggestion = if let Some(e) = expr { _ => return,
snippet_with_applicability(cx, e.span, "v", &mut applicability) }
} else {
applicability = Applicability::HasPlaceholders;
Cow::Borrowed("v")
};
span_lint_and_sugg( let Some(format_args) = FormatArgsExpn::find_nested(cx, expr, macro_call.expn) else { return };
cx,
WRITELN_EMPTY_STRING, // ignore `writeln!(w)` and `write!(v, some_macro!())`
mac.span(), if format_args.format_string.span.from_expansion() {
format!("using `writeln!({}, \"\")`", suggestion).as_str(), return;
"replace it with", }
format!("writeln!({})", suggestion),
applicability, match diag_name {
); sym::print_macro | sym::eprint_macro | sym::write_macro => {
check_newline(cx, &format_args, &macro_call, name);
},
sym::println_macro | sym::eprintln_macro | sym::writeln_macro => {
check_empty_string(cx, &format_args, &macro_call, name);
},
_ => {},
}
check_literal(cx, &format_args, name);
if !self.in_debug_impl {
for arg in &format_args.args {
if arg.format.r#trait == sym::Debug {
span_lint(cx, USE_DEBUG, arg.span, "use of `Debug`-based formatting");
} }
} }
} }
} }
} }
fn is_debug_impl(cx: &LateContext<'_>, item: &Item<'_>) -> bool {
/// Given a format string that ends in a newline and its span, calculates the span of the if let ItemKind::Impl(Impl { of_trait: Some(trait_ref), .. }) = &item.kind
/// newline, or the format string itself if the format string consists solely of a newline. && let Some(trait_id) = trait_ref.trait_def_id()
/// Return this and a boolean indicating whether it only consisted of a newline. {
fn newline_span(fmtstr: &StrLit) -> (Span, bool) { cx.tcx.is_diagnostic_item(sym::Debug, trait_id)
let sp = fmtstr.span;
let contents = fmtstr.symbol.as_str();
if contents == r"\n" {
return (sp, true);
}
let newline_sp_hi = sp.hi()
- match fmtstr.style {
StrStyle::Cooked => BytePos(1),
StrStyle::Raw(hashes) => BytePos((1 + hashes).into()),
};
let newline_sp_len = if contents.ends_with('\n') {
BytePos(1)
} else if contents.ends_with(r"\n") {
BytePos(2)
} else { } else {
panic!("expected format string to contain a newline"); false
}
}
fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, macro_call: &MacroCall, name: &str) {
let format_string_parts = &format_args.format_string.parts;
let mut format_string_span = format_args.format_string.span;
let Some(last) = format_string_parts.last() else { return };
let count_vertical_whitespace = || {
format_string_parts
.iter()
.flat_map(|part| part.as_str().chars())
.filter(|ch| matches!(ch, '\r' | '\n'))
.count()
}; };
(sp.with_lo(newline_sp_hi - newline_sp_len).with_hi(newline_sp_hi), false) if last.as_str().ends_with('\n')
} // ignore format strings with other internal vertical whitespace
&& count_vertical_whitespace() == 1
/// Stores a list of replacement spans for each argument, but only if all the replacements used an // ignore trailing arguments: `print!("Issue\n{}", 1265);`
/// empty format string. && format_string_parts.len() > format_args.args.len()
#[derive(Default)] {
struct SimpleFormatArgs { let lint = if name == "write" {
unnamed: Vec<Vec<Span>>, format_string_span = expand_past_previous_comma(cx, format_string_span);
complex_unnamed: Vec<Vec<Span>>,
named: Vec<(Symbol, Vec<Span>)>,
}
impl SimpleFormatArgs {
fn get_unnamed(&self) -> impl Iterator<Item = &[Span]> {
self.unnamed.iter().map(|x| match x.as_slice() {
// Ignore the dummy span added from out of order format arguments.
[DUMMY_SP] => &[],
x => x,
})
}
fn get_complex_unnamed(&self) -> impl Iterator<Item = &[Span]> { WRITE_WITH_NEWLINE
self.complex_unnamed.iter().map(Vec::as_slice) } else {
} PRINT_WITH_NEWLINE
fn get_named(&self, n: &Path) -> &[Span] {
self.named.iter().find(|x| *n == x.0).map_or(&[], |x| x.1.as_slice())
}
fn push(&mut self, arg: rustc_parse_format::Argument<'_>, span: Span) {
use rustc_parse_format::{
AlignUnknown, ArgumentImplicitlyIs, ArgumentIs, ArgumentNamed, CountImplied, FormatSpec,
}; };
const SIMPLE: FormatSpec<'_> = FormatSpec { span_lint_and_then(
fill: None, cx,
align: AlignUnknown, lint,
flags: 0, macro_call.span,
precision: CountImplied, &format!("using `{name}!()` with a format string that ends in a single newline"),
precision_span: None, |diag| {
width: CountImplied, let name_span = cx.sess().source_map().span_until_char(macro_call.span, '!');
width_span: None, let Some(format_snippet) = snippet_opt(cx, format_string_span) else { return };
ty: "",
ty_span: None,
};
match arg.position { if format_string_parts.len() == 1 && last.as_str() == "\n" {
ArgumentIs(n) | ArgumentImplicitlyIs(n) => { // print!("\n"), write!(f, "\n")
if self.unnamed.len() <= n {
// Use a dummy span to mark all unseen arguments. diag.multipart_suggestion(
self.unnamed.resize_with(n, || vec![DUMMY_SP]); &format!("use `{name}ln!` instead"),
if arg.format == SIMPLE { vec![(name_span, format!("{name}ln")), (format_string_span, String::new())],
self.unnamed.push(vec![span]); Applicability::MachineApplicable,
} else { );
self.unnamed.push(Vec::new()); } else if format_snippet.ends_with("\\n\"") {
} // print!("...\n"), write!(f, "...\n")
} else {
let args = &mut self.unnamed[n]; let hi = format_string_span.hi();
match (args.as_mut_slice(), arg.format == SIMPLE) { let newline_span = format_string_span.with_lo(hi - BytePos(3)).with_hi(hi - BytePos(1));
// A non-empty format string has been seen already.
([], _) => (), diag.multipart_suggestion(
// Replace the dummy span, if it exists. &format!("use `{name}ln!` instead"),
([dummy @ DUMMY_SP], true) => *dummy = span, vec![(name_span, format!("{name}ln")), (newline_span, String::new())],
([_, ..], true) => args.push(span), Applicability::MachineApplicable,
([_, ..], false) => *args = Vec::new(), );
}
} }
}, },
ArgumentNamed(n) => { );
let n = Symbol::intern(n);
if let Some(x) = self.named.iter_mut().find(|x| x.0 == n) {
match x.1.as_slice() {
// A non-empty format string has been seen already.
[] => (),
[_, ..] if arg.format == SIMPLE => x.1.push(span),
[_, ..] => x.1 = Vec::new(),
}
} else if arg.format == SIMPLE {
self.named.push((n, vec![span]));
} else {
self.named.push((n, Vec::new()));
}
},
};
}
fn push_to_complex(&mut self, span: Span, position: usize) {
if self.complex_unnamed.len() <= position {
self.complex_unnamed.resize_with(position, Vec::new);
self.complex_unnamed.push(vec![span]);
} else {
let args: &mut Vec<Span> = &mut self.complex_unnamed[position];
args.push(span);
}
}
fn push_complex(
&mut self,
cx: &EarlyContext<'_>,
arg: rustc_parse_format::Argument<'_>,
str_lit_span: Span,
fmt_span: Span,
) {
use rustc_parse_format::{ArgumentImplicitlyIs, ArgumentIs, CountIsParam, CountIsStar};
let snippet = snippet_opt(cx, fmt_span);
let end = snippet
.as_ref()
.and_then(|s| s.find(':'))
.or_else(|| fmt_span.hi().0.checked_sub(fmt_span.lo().0 + 1).map(|u| u as usize));
if let (ArgumentIs(n) | ArgumentImplicitlyIs(n), Some(end)) = (arg.position, end) {
let span = fmt_span.from_inner(InnerSpan::new(1, end));
self.push_to_complex(span, n);
};
if let (CountIsParam(n) | CountIsStar(n), Some(span)) = (arg.format.precision, arg.format.precision_span) {
// We need to do this hack as precision spans should be converted from .* to .foo$
let hack = if snippet.as_ref().and_then(|s| s.find('*')).is_some() {
0
} else {
1
};
let span = str_lit_span.from_inner(InnerSpan {
start: span.start + 1,
end: span.end - hack,
});
self.push_to_complex(span, n);
};
if let (CountIsParam(n), Some(span)) = (arg.format.width, arg.format.width_span) {
let span = str_lit_span.from_inner(InnerSpan {
start: span.start,
end: span.end - 1,
});
self.push_to_complex(span, n);
};
} }
} }
impl Write { fn check_empty_string(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, macro_call: &MacroCall, name: &str) {
/// Parses a format string into a collection of spans for each argument. This only keeps track if let [part] = &format_args.format_string.parts[..]
/// of empty format arguments. Will also lint usages of debug format strings outside of debug && let mut span = format_args.format_string.span
/// impls. && part.as_str() == "\n"
fn parse_fmt_string(&self, cx: &EarlyContext<'_>, str_lit: &StrLit) -> Option<SimpleFormatArgs> { {
use rustc_parse_format::{ParseMode, Parser, Piece}; let lint = if name == "writeln" {
span = expand_past_previous_comma(cx, span);
let str_sym = str_lit.symbol_unescaped.as_str(); WRITELN_EMPTY_STRING
let style = match str_lit.style {
StrStyle::Cooked => None,
StrStyle::Raw(n) => Some(n as usize),
};
let mut parser = Parser::new(str_sym, style, snippet_opt(cx, str_lit.span), false, ParseMode::Format);
let mut args = SimpleFormatArgs::default();
while let Some(arg) = parser.next() {
let arg = match arg {
Piece::String(_) => continue,
Piece::NextArgument(arg) => arg,
};
let span = parser
.arg_places
.last()
.map_or(DUMMY_SP, |&x| str_lit.span.from_inner(InnerSpan::new(x.start, x.end)));
if !self.in_debug_impl && arg.format.ty == "?" {
// FIXME: modify rustc's fmt string parser to give us the current span
span_lint(cx, USE_DEBUG, span, "use of `Debug`-based formatting");
}
args.push(arg, span);
args.push_complex(cx, arg, str_lit.span, span);
}
parser.errors.is_empty().then_some(args)
}
/// Checks the arguments of `print[ln]!` and `write[ln]!` calls. It will return a tuple of two
/// `Option`s. The first `Option` of the tuple is the macro's format string. It includes
/// the contents of the string, whether it's a raw string, and the span of the literal in the
/// source. The second `Option` in the tuple is, in the `write[ln]!` case, the expression the
/// `format_str` should be written to.
///
/// Example:
///
/// Calling this function on
/// ```rust
/// # use std::fmt::Write;
/// # let mut buf = String::new();
/// # let something = "something";
/// writeln!(buf, "string to write: {}", something);
/// ```
/// will return
/// ```rust,ignore
/// (Some("string to write: {}"), Some(buf))
/// ```
fn check_tts<'a>(&self, cx: &EarlyContext<'a>, tts: TokenStream, is_write: bool) -> (Option<StrLit>, Option<Expr>) {
let mut parser = parser::Parser::new(&cx.sess().parse_sess, tts, false, None);
let expr = if is_write {
match parser
.parse_expr()
.map(rustc_ast::ptr::P::into_inner)
.map_err(DiagnosticBuilder::cancel)
{
// write!(e, ...)
Ok(p) if parser.eat(&token::Comma) => Some(p),
// write!(e) or error
e => return (None, e.ok()),
}
} else { } else {
None PRINTLN_EMPTY_STRING
}; };
let fmtstr = match parser.parse_str_lit() { span_lint_and_then(
Ok(fmtstr) => fmtstr, cx,
Err(_) => return (None, expr), lint,
}; macro_call.span,
&format!("empty string literal in `{name}!`"),
let args = match self.parse_fmt_string(cx, &fmtstr) { |diag| {
Some(args) => args, diag.span_suggestion(
None => return (Some(fmtstr), expr), span,
}; "remove the empty string",
String::new(),
let lint = if is_write { WRITE_LITERAL } else { PRINT_LITERAL };
let mut unnamed_args = args.get_unnamed();
let mut complex_unnamed_args = args.get_complex_unnamed();
loop {
if !parser.eat(&token::Comma) {
return (Some(fmtstr), expr);
}
let comma_span = parser.prev_token.span;
let token_expr = if let Ok(expr) = parser.parse_expr().map_err(DiagnosticBuilder::cancel) {
expr
} else {
return (Some(fmtstr), None);
};
let complex_unnamed_arg = complex_unnamed_args.next();
let (fmt_spans, lit) = match &token_expr.kind {
ExprKind::Lit(lit) => (unnamed_args.next().unwrap_or(&[]), lit),
ExprKind::Assign(lhs, rhs, _) => {
if let Some(span) = complex_unnamed_arg {
for x in span {
Self::report_positional_named_param(cx, *x, lhs, rhs);
}
}
match (&lhs.kind, &rhs.kind) {
(ExprKind::Path(_, p), ExprKind::Lit(lit)) => (args.get_named(p), lit),
_ => continue,
}
},
_ => {
unnamed_args.next();
continue;
},
};
let replacement: String = match lit.token_lit.kind {
LitKind::StrRaw(_) | LitKind::ByteStrRaw(_) if matches!(fmtstr.style, StrStyle::Raw(_)) => {
lit.token_lit.symbol.as_str().replace('{', "{{").replace('}', "}}")
},
LitKind::Str | LitKind::ByteStr if matches!(fmtstr.style, StrStyle::Cooked) => {
lit.token_lit.symbol.as_str().replace('{', "{{").replace('}', "}}")
},
LitKind::StrRaw(_)
| LitKind::Str
| LitKind::ByteStrRaw(_)
| LitKind::ByteStr
| LitKind::Integer
| LitKind::Float
| LitKind::Err => continue,
LitKind::Byte | LitKind::Char => match lit.token_lit.symbol.as_str() {
"\"" if matches!(fmtstr.style, StrStyle::Cooked) => "\\\"",
"\"" if matches!(fmtstr.style, StrStyle::Raw(0)) => continue,
"\\\\" if matches!(fmtstr.style, StrStyle::Raw(_)) => "\\",
"\\'" => "'",
"{" => "{{",
"}" => "}}",
x if matches!(fmtstr.style, StrStyle::Raw(_)) && x.starts_with('\\') => continue,
x => x,
}
.into(),
LitKind::Bool => lit.token_lit.symbol.as_str().deref().into(),
};
if !fmt_spans.is_empty() {
span_lint_and_then(
cx,
lint,
token_expr.span,
"literal with an empty format string",
|diag| {
diag.multipart_suggestion(
"try this",
iter::once((comma_span.to(token_expr.span), String::new()))
.chain(fmt_spans.iter().copied().zip(iter::repeat(replacement)))
.collect(),
Applicability::MachineApplicable,
);
},
);
}
}
}
fn report_positional_named_param(cx: &EarlyContext<'_>, span: Span, lhs: &P<Expr>, _rhs: &P<Expr>) {
if let ExprKind::Path(_, _p) = &lhs.kind {
let mut applicability = Applicability::MachineApplicable;
let name = snippet_with_applicability(cx, lhs.span, "name", &mut applicability);
// We need to do this hack as precision spans should be converted from .* to .foo$
let hack = snippet(cx, span, "").contains('*');
span_lint_and_sugg(
cx,
POSITIONAL_NAMED_FORMAT_PARAMETERS,
span,
&format!("named parameter {} is used as a positional parameter", name),
"replace it with",
if hack {
format!("{}$", name)
} else {
format!("{}", name)
},
applicability,
);
};
}
fn lint_println_empty_string(&self, cx: &EarlyContext<'_>, mac: &MacCall) {
if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) {
if fmt_str.symbol == kw::Empty {
let name = mac.path.segments[0].ident.name;
span_lint_and_sugg(
cx,
PRINTLN_EMPTY_STRING,
mac.span(),
&format!("using `{}!(\"\")`", name),
"replace it with",
format!("{}!()", name),
Applicability::MachineApplicable, Applicability::MachineApplicable,
); );
} },
} );
}
}
fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, name: &str) {
let mut counts = HirIdMap::<usize>::default();
for param in format_args.params() {
*counts.entry(param.value.hir_id).or_default() += 1;
} }
fn lint_print_with_newline(&self, cx: &EarlyContext<'_>, mac: &MacCall) { for arg in &format_args.args {
if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) { let value = arg.param.value;
if check_newlines(&fmt_str) {
let name = mac.path.segments[0].ident.name; if counts[&value.hir_id] == 1
let suggested = format!("{}ln", name); && arg.format.is_default()
span_lint_and_then( && let ExprKind::Lit(lit) = &value.kind
cx, && !value.span.from_expansion()
PRINT_WITH_NEWLINE, && let Some(value_string) = snippet_opt(cx, value.span)
mac.span(), {
&format!("using `{}!()` with a format string that ends in a single newline", name), let (replacement, replace_raw) = match lit.node {
|err| { LitKind::Str(..) => extract_str_literal(&value_string),
err.multipart_suggestion( LitKind::Char(ch) => (
&format!("use `{}!` instead", suggested), match ch {
vec![(mac.path.span, suggested), (newline_span(&fmt_str).0, String::new())], '"' => "\\\"",
'\'' => "'",
_ => &value_string[1..value_string.len() - 1],
}
.to_string(),
false,
),
LitKind::Bool(b) => (b.to_string(), false),
_ => continue,
};
let lint = if name.starts_with("write") {
WRITE_LITERAL
} else {
PRINT_LITERAL
};
let format_string_is_raw = format_args.format_string.style.is_some();
let replacement = match (format_string_is_raw, replace_raw) {
(false, false) => Some(replacement),
(false, true) => Some(replacement.replace('"', "\\\"").replace('\\', "\\\\")),
(true, false) => match conservative_unescape(&replacement) {
Ok(unescaped) => Some(unescaped),
Err(UnescapeErr::Lint) => None,
Err(UnescapeErr::Ignore) => continue,
},
(true, true) => {
if replacement.contains(['#', '"']) {
None
} else {
Some(replacement)
}
},
};
span_lint_and_then(
cx,
lint,
value.span,
"literal with an empty format string",
|diag| {
if let Some(replacement) = replacement {
// `format!("{}", "a")`, `format!("{named}", named = "b")
// ~~~~~ ~~~~~~~~~~~~~
let value_span = expand_past_previous_comma(cx, value.span);
let replacement = replacement.replace('{', "{{").replace('}', "}}");
diag.multipart_suggestion(
"try this",
vec![(arg.span, replacement), (value_span, String::new())],
Applicability::MachineApplicable, Applicability::MachineApplicable,
); );
}, }
); },
} );
} }
} }
} }
/// Checks if the format string contains a single newline that terminates it. /// Removes the raw marker, `#`s and quotes from a str, and returns if the literal is raw
/// ///
/// Literal and escaped newlines are both checked (only literal for raw strings). /// `r#"a"#` -> (`a`, true)
fn check_newlines(fmtstr: &StrLit) -> bool { ///
let mut has_internal_newline = false; /// `"b"` -> (`b`, false)
let mut last_was_cr = false; fn extract_str_literal(literal: &str) -> (String, bool) {
let mut should_lint = false; let (literal, raw) = match literal.strip_prefix('r') {
Some(stripped) => (stripped.trim_matches('#'), true),
let contents = fmtstr.symbol.as_str(); None => (literal, false),
let mut cb = |r: Range<usize>, c: Result<char, EscapeError>| {
let c = match c {
Ok(c) => c,
Err(e) if !e.is_fatal() => return,
Err(e) => panic!("{:?}", e),
};
if r.end == contents.len() && c == '\n' && !last_was_cr && !has_internal_newline {
should_lint = true;
} else {
last_was_cr = c == '\r';
if c == '\n' {
has_internal_newline = true;
}
}
}; };
match fmtstr.style { (literal[1..literal.len() - 1].to_string(), raw)
StrStyle::Cooked => unescape::unescape_literal(contents, unescape::Mode::Str, &mut cb), }
StrStyle::Raw(_) => unescape::unescape_literal(contents, unescape::Mode::RawStr, &mut cb),
enum UnescapeErr {
/// Should still be linted, can be manually resolved by author, e.g.
///
/// ```ignore
/// print!(r"{}", '"');
/// ```
Lint,
/// Should not be linted, e.g.
///
/// ```ignore
/// print!(r"{}", '\r');
/// ```
Ignore,
}
/// Unescape a normal string into a raw string
fn conservative_unescape(literal: &str) -> Result<String, UnescapeErr> {
let mut unescaped = String::with_capacity(literal.len());
let mut chars = literal.chars();
let mut err = false;
while let Some(ch) = chars.next() {
match ch {
'#' => err = true,
'\\' => match chars.next() {
Some('\\') => unescaped.push('\\'),
Some('"') => err = true,
_ => return Err(UnescapeErr::Ignore),
},
_ => unescaped.push(ch),
}
} }
should_lint if err { Err(UnescapeErr::Lint) } else { Ok(unescaped) }
}
// Expand from `writeln!(o, "")` to `writeln!(o, "")`
// ^^ ^^^^
fn expand_past_previous_comma(cx: &LateContext<'_>, span: Span) -> Span {
let extended = cx.sess().source_map().span_extend_to_prev_char(span, ',', true);
extended.with_lo(extended.lo() - BytePos(1))
} }

View File

@@ -501,8 +501,8 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
BinOpKind::Mul => l.checked_mul(r).map(zext), BinOpKind::Mul => l.checked_mul(r).map(zext),
BinOpKind::Div if r != 0 => l.checked_div(r).map(zext), BinOpKind::Div if r != 0 => l.checked_div(r).map(zext),
BinOpKind::Rem if r != 0 => l.checked_rem(r).map(zext), BinOpKind::Rem if r != 0 => l.checked_rem(r).map(zext),
BinOpKind::Shr => l.checked_shr(r.try_into().expect("invalid shift")).map(zext), BinOpKind::Shr => l.checked_shr(r.try_into().ok()?).map(zext),
BinOpKind::Shl => l.checked_shl(r.try_into().expect("invalid shift")).map(zext), BinOpKind::Shl => l.checked_shl(r.try_into().ok()?).map(zext),
BinOpKind::BitXor => Some(zext(l ^ r)), BinOpKind::BitXor => Some(zext(l ^ r)),
BinOpKind::BitOr => Some(zext(l | r)), BinOpKind::BitOr => Some(zext(l | r)),
BinOpKind::BitAnd => Some(zext(l & r)), BinOpKind::BitAnd => Some(zext(l & r)),
@@ -521,8 +521,8 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
BinOpKind::Mul => l.checked_mul(r).map(Constant::Int), BinOpKind::Mul => l.checked_mul(r).map(Constant::Int),
BinOpKind::Div => l.checked_div(r).map(Constant::Int), BinOpKind::Div => l.checked_div(r).map(Constant::Int),
BinOpKind::Rem => l.checked_rem(r).map(Constant::Int), BinOpKind::Rem => l.checked_rem(r).map(Constant::Int),
BinOpKind::Shr => l.checked_shr(r.try_into().expect("shift too large")).map(Constant::Int), BinOpKind::Shr => l.checked_shr(r.try_into().ok()?).map(Constant::Int),
BinOpKind::Shl => l.checked_shl(r.try_into().expect("shift too large")).map(Constant::Int), BinOpKind::Shl => l.checked_shl(r.try_into().ok()?).map(Constant::Int),
BinOpKind::BitXor => Some(Constant::Int(l ^ r)), BinOpKind::BitXor => Some(Constant::Int(l ^ r)),
BinOpKind::BitOr => Some(Constant::Int(l | r)), BinOpKind::BitOr => Some(Constant::Int(l | r)),
BinOpKind::BitAnd => Some(Constant::Int(l & r)), BinOpKind::BitAnd => Some(Constant::Int(l & r)),

View File

@@ -7,8 +7,8 @@ use crate::visitors::expr_visitor_no_bodies;
use arrayvec::ArrayVec; use arrayvec::ArrayVec;
use itertools::{izip, Either, Itertools}; use itertools::{izip, Either, Itertools};
use rustc_ast::ast::LitKind; use rustc_ast::ast::LitKind;
use rustc_hir::intravisit::Visitor; use rustc_hir::intravisit::{walk_expr, Visitor};
use rustc_hir::{self as hir, Expr, ExprKind, HirId, Node, QPath}; use rustc_hir::{self as hir, Expr, ExprField, ExprKind, HirId, Node, QPath};
use rustc_lexer::unescape::unescape_literal; use rustc_lexer::unescape::unescape_literal;
use rustc_lexer::{tokenize, unescape, LiteralKind, TokenKind}; use rustc_lexer::{tokenize, unescape, LiteralKind, TokenKind};
use rustc_lint::LateContext; use rustc_lint::LateContext;
@@ -485,64 +485,49 @@ struct ParamPosition {
precision: Option<usize>, precision: Option<usize>,
} }
/// Parses the `fmt` arg of `Arguments::new_v1_formatted(pieces, args, fmt, _)` impl<'tcx> Visitor<'tcx> for ParamPosition {
fn parse_rt_fmt<'tcx>(fmt_arg: &'tcx Expr<'tcx>) -> Option<impl Iterator<Item = ParamPosition> + 'tcx> { fn visit_expr_field(&mut self, field: &'tcx ExprField<'tcx>) {
fn parse_count(expr: &Expr<'_>) -> Option<usize> { fn parse_count(expr: &Expr<'_>) -> Option<usize> {
// ::core::fmt::rt::v1::Count::Param(1usize), // ::core::fmt::rt::v1::Count::Param(1usize),
if let ExprKind::Call(ctor, [val]) = expr.kind if let ExprKind::Call(ctor, [val]) = expr.kind
&& let ExprKind::Path(QPath::Resolved(_, path)) = ctor.kind && let ExprKind::Path(QPath::Resolved(_, path)) = ctor.kind
&& path.segments.last()?.ident.name == sym::Param && path.segments.last()?.ident.name == sym::Param
&& let ExprKind::Lit(lit) = &val.kind && let ExprKind::Lit(lit) = &val.kind
&& let LitKind::Int(pos, _) = lit.node && let LitKind::Int(pos, _) = lit.node
{ {
Some(pos as usize) Some(pos as usize)
} else { } else {
None None
}
}
match field.ident.name {
sym::position => {
if let ExprKind::Lit(lit) = &field.expr.kind
&& let LitKind::Int(pos, _) = lit.node
{
self.value = pos as usize;
}
},
sym::precision => {
self.precision = parse_count(field.expr);
},
sym::width => {
self.width = parse_count(field.expr);
},
_ => walk_expr(self, field.expr),
} }
} }
}
/// Parses the `fmt` arg of `Arguments::new_v1_formatted(pieces, args, fmt, _)`
fn parse_rt_fmt<'tcx>(fmt_arg: &'tcx Expr<'tcx>) -> Option<impl Iterator<Item = ParamPosition> + 'tcx> {
if let ExprKind::AddrOf(.., array) = fmt_arg.kind if let ExprKind::AddrOf(.., array) = fmt_arg.kind
&& let ExprKind::Array(specs) = array.kind && let ExprKind::Array(specs) = array.kind
{ {
Some(specs.iter().map(|spec| { Some(specs.iter().map(|spec| {
let mut position = ParamPosition::default(); let mut position = ParamPosition::default();
position.visit_expr(spec);
// ::core::fmt::rt::v1::Argument {
// position: 0usize,
// format: ::core::fmt::rt::v1::FormatSpec {
// ..
// precision: ::core::fmt::rt::v1::Count::Implied,
// width: ::core::fmt::rt::v1::Count::Implied,
// },
// }
// TODO: this can be made much nicer next sync with `Visitor::visit_expr_field`
if let ExprKind::Struct(_, fields, _) = spec.kind {
for field in fields {
match (field.ident.name, &field.expr.kind) {
(sym::position, ExprKind::Lit(lit)) => {
if let LitKind::Int(pos, _) = lit.node {
position.value = pos as usize;
}
},
(sym::format, &ExprKind::Struct(_, spec_fields, _)) => {
for spec_field in spec_fields {
match spec_field.ident.name {
sym::precision => {
position.precision = parse_count(spec_field.expr);
},
sym::width => {
position.width = parse_count(spec_field.expr);
},
_ => {},
}
}
},
_ => {},
}
}
}
position position
})) }))
} else { } else {
@@ -711,9 +696,14 @@ impl<'tcx> FormatSpec<'tcx> {
}) })
} }
/// Returns true if this format spec would change the contents of a string when formatted /// Returns true if this format spec is unchanged from the default. e.g. returns true for `{}`,
pub fn has_string_formatting(&self) -> bool { /// `{foo}` and `{2}`, but false for `{:?}`, `{foo:5}` and `{3:.5}`
self.r#trait != sym::Display || !self.width.is_implied() || !self.precision.is_implied() pub fn is_default(&self) -> bool {
self.r#trait == sym::Display
&& self.width.is_implied()
&& self.precision.is_implied()
&& self.align == Alignment::AlignUnknown
&& self.flags == 0
} }
} }

View File

@@ -22,7 +22,7 @@ use std::fmt::{Display, Write as _};
use std::ops::{Add, Neg, Not, Sub}; use std::ops::{Add, Neg, Not, Sub};
/// A helper type to build suggestion correctly handling parentheses. /// A helper type to build suggestion correctly handling parentheses.
#[derive(Clone, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum Sugg<'a> { pub enum Sugg<'a> {
/// An expression that never needs parentheses such as `1337` or `[0; 42]`. /// An expression that never needs parentheses such as `1337` or `[0; 42]`.
NonParen(Cow<'a, str>), NonParen(Cow<'a, str>),
@@ -155,8 +155,8 @@ impl<'a> Sugg<'a> {
| hir::ExprKind::Ret(..) | hir::ExprKind::Ret(..)
| hir::ExprKind::Struct(..) | hir::ExprKind::Struct(..)
| hir::ExprKind::Tup(..) | hir::ExprKind::Tup(..)
| hir::ExprKind::DropTemps(_)
| hir::ExprKind::Err => Sugg::NonParen(get_snippet(expr.span)), | hir::ExprKind::Err => Sugg::NonParen(get_snippet(expr.span)),
hir::ExprKind::DropTemps(inner) => Self::hir_from_snippet(inner, get_snippet),
hir::ExprKind::Assign(lhs, rhs, _) => { hir::ExprKind::Assign(lhs, rhs, _) => {
Sugg::BinOp(AssocOp::Assign, get_snippet(lhs.span), get_snippet(rhs.span)) Sugg::BinOp(AssocOp::Assign, get_snippet(lhs.span), get_snippet(rhs.span))
}, },
@@ -177,11 +177,11 @@ impl<'a> Sugg<'a> {
pub fn ast(cx: &EarlyContext<'_>, expr: &ast::Expr, default: &'a str) -> Self { pub fn ast(cx: &EarlyContext<'_>, expr: &ast::Expr, default: &'a str) -> Self {
use rustc_ast::ast::RangeLimits; use rustc_ast::ast::RangeLimits;
let get_whole_snippet = || { let snippet_without_expansion = |cx, span: Span, default| {
if expr.span.from_expansion() { if span.from_expansion() {
snippet_with_macro_callsite(cx, expr.span, default) snippet_with_macro_callsite(cx, span, default)
} else { } else {
snippet(cx, expr.span, default) snippet(cx, span, default)
} }
}; };
@@ -192,7 +192,7 @@ impl<'a> Sugg<'a> {
| ast::ExprKind::If(..) | ast::ExprKind::If(..)
| ast::ExprKind::Let(..) | ast::ExprKind::Let(..)
| ast::ExprKind::Unary(..) | ast::ExprKind::Unary(..)
| ast::ExprKind::Match(..) => Sugg::MaybeParen(get_whole_snippet()), | ast::ExprKind::Match(..) => Sugg::MaybeParen(snippet_without_expansion(cx, expr.span, default)),
ast::ExprKind::Async(..) ast::ExprKind::Async(..)
| ast::ExprKind::Block(..) | ast::ExprKind::Block(..)
| ast::ExprKind::Break(..) | ast::ExprKind::Break(..)
@@ -221,41 +221,45 @@ impl<'a> Sugg<'a> {
| ast::ExprKind::Array(..) | ast::ExprKind::Array(..)
| ast::ExprKind::While(..) | ast::ExprKind::While(..)
| ast::ExprKind::Await(..) | ast::ExprKind::Await(..)
| ast::ExprKind::Err => Sugg::NonParen(get_whole_snippet()), | ast::ExprKind::Err => Sugg::NonParen(snippet_without_expansion(cx, expr.span, default)),
ast::ExprKind::Range(ref lhs, ref rhs, RangeLimits::HalfOpen) => Sugg::BinOp( ast::ExprKind::Range(ref lhs, ref rhs, RangeLimits::HalfOpen) => Sugg::BinOp(
AssocOp::DotDot, AssocOp::DotDot,
lhs.as_ref().map_or("".into(), |lhs| snippet(cx, lhs.span, default)), lhs.as_ref()
rhs.as_ref().map_or("".into(), |rhs| snippet(cx, rhs.span, default)), .map_or("".into(), |lhs| snippet_without_expansion(cx, lhs.span, default)),
rhs.as_ref()
.map_or("".into(), |rhs| snippet_without_expansion(cx, rhs.span, default)),
), ),
ast::ExprKind::Range(ref lhs, ref rhs, RangeLimits::Closed) => Sugg::BinOp( ast::ExprKind::Range(ref lhs, ref rhs, RangeLimits::Closed) => Sugg::BinOp(
AssocOp::DotDotEq, AssocOp::DotDotEq,
lhs.as_ref().map_or("".into(), |lhs| snippet(cx, lhs.span, default)), lhs.as_ref()
rhs.as_ref().map_or("".into(), |rhs| snippet(cx, rhs.span, default)), .map_or("".into(), |lhs| snippet_without_expansion(cx, lhs.span, default)),
rhs.as_ref()
.map_or("".into(), |rhs| snippet_without_expansion(cx, rhs.span, default)),
), ),
ast::ExprKind::Assign(ref lhs, ref rhs, _) => Sugg::BinOp( ast::ExprKind::Assign(ref lhs, ref rhs, _) => Sugg::BinOp(
AssocOp::Assign, AssocOp::Assign,
snippet(cx, lhs.span, default), snippet_without_expansion(cx, lhs.span, default),
snippet(cx, rhs.span, default), snippet_without_expansion(cx, rhs.span, default),
), ),
ast::ExprKind::AssignOp(op, ref lhs, ref rhs) => Sugg::BinOp( ast::ExprKind::AssignOp(op, ref lhs, ref rhs) => Sugg::BinOp(
astbinop2assignop(op), astbinop2assignop(op),
snippet(cx, lhs.span, default), snippet_without_expansion(cx, lhs.span, default),
snippet(cx, rhs.span, default), snippet_without_expansion(cx, rhs.span, default),
), ),
ast::ExprKind::Binary(op, ref lhs, ref rhs) => Sugg::BinOp( ast::ExprKind::Binary(op, ref lhs, ref rhs) => Sugg::BinOp(
AssocOp::from_ast_binop(op.node), AssocOp::from_ast_binop(op.node),
snippet(cx, lhs.span, default), snippet_without_expansion(cx, lhs.span, default),
snippet(cx, rhs.span, default), snippet_without_expansion(cx, rhs.span, default),
), ),
ast::ExprKind::Cast(ref lhs, ref ty) => Sugg::BinOp( ast::ExprKind::Cast(ref lhs, ref ty) => Sugg::BinOp(
AssocOp::As, AssocOp::As,
snippet(cx, lhs.span, default), snippet_without_expansion(cx, lhs.span, default),
snippet(cx, ty.span, default), snippet_without_expansion(cx, ty.span, default),
), ),
ast::ExprKind::Type(ref lhs, ref ty) => Sugg::BinOp( ast::ExprKind::Type(ref lhs, ref ty) => Sugg::BinOp(
AssocOp::Colon, AssocOp::Colon,
snippet(cx, lhs.span, default), snippet_without_expansion(cx, lhs.span, default),
snippet(cx, ty.span, default), snippet_without_expansion(cx, ty.span, default),
), ),
} }
} }

View File

@@ -31,6 +31,13 @@ pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
ty.is_copy_modulo_regions(cx.tcx.at(DUMMY_SP), cx.param_env) ty.is_copy_modulo_regions(cx.tcx.at(DUMMY_SP), cx.param_env)
} }
/// This checks whether a given type is known to implement Debug.
pub fn has_debug_impl<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
cx.tcx
.get_diagnostic_item(sym::Debug)
.map_or(false, |debug| implements_trait(cx, ty, debug, &[]))
}
/// Checks whether a type can be partially moved. /// Checks whether a type can be partially moved.
pub fn can_partially_move_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { pub fn can_partially_move_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
if has_drop(cx, ty) || is_copy(cx, ty) { if has_drop(cx, ty) || is_copy(cx, ty) {

View File

@@ -221,6 +221,7 @@ docs! {
"items_after_statements", "items_after_statements",
"iter_cloned_collect", "iter_cloned_collect",
"iter_count", "iter_count",
"iter_kv_map",
"iter_next_loop", "iter_next_loop",
"iter_next_slice", "iter_next_slice",
"iter_not_returning_iterator", "iter_not_returning_iterator",
@@ -391,7 +392,6 @@ docs! {
"partialeq_to_none", "partialeq_to_none",
"path_buf_push_overwrite", "path_buf_push_overwrite",
"pattern_type_mismatch", "pattern_type_mismatch",
"positional_named_format_parameters",
"possible_missing_comma", "possible_missing_comma",
"precedence", "precedence",
"print_in_format_impl", "print_in_format_impl",

22
src/docs/iter_kv_map.txt Normal file
View File

@@ -0,0 +1,22 @@
### What it does
Checks for iterating a map (`HashMap` or `BTreeMap`) and
ignoring either the keys or values.
### Why is this bad?
Readability. There are `keys` and `values` methods that
can be used to express that we only need the keys or the values.
### Example
```
let map: HashMap<u32, u32> = HashMap::new();
let values = map.iter().map(|(_, value)| value).collect::<Vec<_>>();
```
Use instead:
```
let map: HashMap<u32, u32> = HashMap::new();
let values = map.values().collect::<Vec<_>>();
```

View File

@@ -1,15 +0,0 @@
### What it does
This lint warns when a named parameter in a format string is used as a positional one.
### Why is this bad?
It may be confused for an assignment and obfuscates which parameter is being used.
### Example
```
println!("{}", x = 10);
```
Use instead:
```
println!("{x}", x = 10);
```

View File

@@ -6,10 +6,6 @@ Using literals as `println!` args is inefficient
(c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary
(i.e., just put the literal in the format string) (i.e., just put the literal in the format string)
### Known problems
Will also warn with macro calls as arguments that expand to literals
-- e.g., `println!("{}", env!("FOO"))`.
### Example ### Example
``` ```
println!("{}", "foo"); println!("{}", "foo");

View File

@@ -7,13 +7,7 @@ People often print on *stderr* while debugging an
application and might forget to remove those prints afterward. application and might forget to remove those prints afterward.
### Known problems ### Known problems
* Only catches `eprint!` and `eprintln!` calls. Only catches `eprint!` and `eprintln!` calls.
* The lint level is unaffected by crate attributes. The level can still
be set for functions, modules and other items. To change the level for
the entire crate, please use command line flags. More information and a
configuration example can be found in [clippy#6610].
[clippy#6610]: https://github.com/rust-lang/rust-clippy/issues/6610#issuecomment-977120558
### Example ### Example
``` ```

View File

@@ -7,13 +7,7 @@ People often print on *stdout* while debugging an
application and might forget to remove those prints afterward. application and might forget to remove those prints afterward.
### Known problems ### Known problems
* Only catches `print!` and `println!` calls. Only catches `print!` and `println!` calls.
* The lint level is unaffected by crate attributes. The level can still
be set for functions, modules and other items. To change the level for
the entire crate, please use command line flags. More information and a
configuration example can be found in [clippy#6610].
[clippy#6610]: https://github.com/rust-lang/rust-clippy/issues/6610#issuecomment-977120558
### Example ### Example
``` ```

View File

@@ -6,10 +6,6 @@ Using literals as `writeln!` args is inefficient
(c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary
(i.e., just put the literal in the format string) (i.e., just put the literal in the format string)
### Known problems
Will also warn with macro calls as arguments that expand to literals
-- e.g., `writeln!(buf, "{}", env!("FOO"))`.
### Example ### Example
``` ```
writeln!(buf, "{}", "foo"); writeln!(buf, "{}", "foo");

View File

@@ -0,0 +1,9 @@
[package]
name = "fail-mod-remap"
version = "0.1.0"
edition = "2018"
publish = false
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

View File

@@ -0,0 +1 @@
pub mod inner;

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1,7 @@
// compile-flags: --remap-path-prefix {{src-base}}=/remapped
#![warn(clippy::self_named_module_files)]
mod bad;
fn main() {}

View File

@@ -0,0 +1,11 @@
error: `mod.rs` files are required, found `bad.rs`
--> /remapped/module_style/fail_mod_remap/src/bad.rs:1:1
|
LL | pub mod inner;
| ^
|
= note: `-D clippy::self-named-module-files` implied by `-D warnings`
= help: move `bad.rs` to `bad/mod.rs`
error: aborting due to previous error

View File

@@ -42,6 +42,7 @@ macro_rules! printlnfoo {
fn main() { fn main() {
let _ = vec! {1, 2, 3}; let _ = vec! {1, 2, 3};
let _ = format!["ugh {} stop being such a good compiler", "hello"]; let _ = format!["ugh {} stop being such a good compiler", "hello"];
let _ = matches!{{}, ()};
let _ = quote!(let x = 1;); let _ = quote!(let x = 1;);
let _ = quote::quote!(match match match); let _ = quote::quote!(match match match);
let _ = test!(); // trigger when macro def is inside our own crate let _ = test!(); // trigger when macro def is inside our own crate

View File

@@ -23,26 +23,38 @@ help: consider writing `format!("ugh () stop being such a good compiler", "hello
LL | let _ = format!["ugh {} stop being such a good compiler", "hello"]; LL | let _ = format!["ugh {} stop being such a good compiler", "hello"];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: use of irregular braces for `quote!` macro error: use of irregular braces for `matches!` macro
--> $DIR/conf_nonstandard_macro_braces.rs:45:13 --> $DIR/conf_nonstandard_macro_braces.rs:45:13
| |
LL | let _ = matches!{{}, ()};
| ^^^^^^^^^^^^^^^^
|
help: consider writing `matches!((), ())`
--> $DIR/conf_nonstandard_macro_braces.rs:45:13
|
LL | let _ = matches!{{}, ()};
| ^^^^^^^^^^^^^^^^
error: use of irregular braces for `quote!` macro
--> $DIR/conf_nonstandard_macro_braces.rs:46:13
|
LL | let _ = quote!(let x = 1;); LL | let _ = quote!(let x = 1;);
| ^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^
| |
help: consider writing `quote! {let x = 1;}` help: consider writing `quote! {let x = 1;}`
--> $DIR/conf_nonstandard_macro_braces.rs:45:13 --> $DIR/conf_nonstandard_macro_braces.rs:46:13
| |
LL | let _ = quote!(let x = 1;); LL | let _ = quote!(let x = 1;);
| ^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^
error: use of irregular braces for `quote::quote!` macro error: use of irregular braces for `quote::quote!` macro
--> $DIR/conf_nonstandard_macro_braces.rs:46:13 --> $DIR/conf_nonstandard_macro_braces.rs:47:13
| |
LL | let _ = quote::quote!(match match match); LL | let _ = quote::quote!(match match match);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
help: consider writing `quote::quote! {match match match}` help: consider writing `quote::quote! {match match match}`
--> $DIR/conf_nonstandard_macro_braces.rs:46:13 --> $DIR/conf_nonstandard_macro_braces.rs:47:13
| |
LL | let _ = quote::quote!(match match match); LL | let _ = quote::quote!(match match match);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -67,28 +79,28 @@ LL | let _ = test!(); // trigger when macro def is inside our own crate
= note: this error originates in the macro `test` (in Nightly builds, run with -Z macro-backtrace for more info) = note: this error originates in the macro `test` (in Nightly builds, run with -Z macro-backtrace for more info)
error: use of irregular braces for `type_pos!` macro error: use of irregular braces for `type_pos!` macro
--> $DIR/conf_nonstandard_macro_braces.rs:55:12 --> $DIR/conf_nonstandard_macro_braces.rs:56:12
| |
LL | let _: type_pos!(usize) = vec![]; LL | let _: type_pos!(usize) = vec![];
| ^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^
| |
help: consider writing `type_pos![usize]` help: consider writing `type_pos![usize]`
--> $DIR/conf_nonstandard_macro_braces.rs:55:12 --> $DIR/conf_nonstandard_macro_braces.rs:56:12
| |
LL | let _: type_pos!(usize) = vec![]; LL | let _: type_pos!(usize) = vec![];
| ^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^
error: use of irregular braces for `eprint!` macro error: use of irregular braces for `eprint!` macro
--> $DIR/conf_nonstandard_macro_braces.rs:57:5 --> $DIR/conf_nonstandard_macro_braces.rs:58:5
| |
LL | eprint!("test if user config overrides defaults"); LL | eprint!("test if user config overrides defaults");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
help: consider writing `eprint!["test if user config overrides defaults"]` help: consider writing `eprint!["test if user config overrides defaults"]`
--> $DIR/conf_nonstandard_macro_braces.rs:57:5 --> $DIR/conf_nonstandard_macro_braces.rs:58:5
| |
LL | eprint!("test if user config overrides defaults"); LL | eprint!("test if user config overrides defaults");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 7 previous errors error: aborting due to 8 previous errors

View File

@@ -1,5 +1,6 @@
// run-rustfix // run-rustfix
// edition:2018 // edition:2018
// aux-build:macro_rules.rs
#![feature(custom_inner_attributes)] #![feature(custom_inner_attributes)]
#![feature(exclusive_range_pattern)] #![feature(exclusive_range_pattern)]
@@ -8,12 +9,21 @@
#![allow(ellipsis_inclusive_range_patterns)] #![allow(ellipsis_inclusive_range_patterns)]
#![allow(clippy::needless_parens_on_range_literals)] #![allow(clippy::needless_parens_on_range_literals)]
#[macro_use]
extern crate macro_rules;
macro_rules! a { macro_rules! a {
() => { () => {
'a' 'a'
}; };
} }
macro_rules! b {
() => {
let _ = 'a'..='z';
};
}
fn main() { fn main() {
#[rustfmt::skip] #[rustfmt::skip]
{ {
@@ -47,6 +57,9 @@ fn main() {
'B'..'Z' => 4, 'B'..'Z' => 4,
_ => 5, _ => 5,
}; };
almost_complete_letter_range!();
b!();
} }
fn _under_msrv() { fn _under_msrv() {

View File

@@ -1,5 +1,6 @@
// run-rustfix // run-rustfix
// edition:2018 // edition:2018
// aux-build:macro_rules.rs
#![feature(custom_inner_attributes)] #![feature(custom_inner_attributes)]
#![feature(exclusive_range_pattern)] #![feature(exclusive_range_pattern)]
@@ -8,12 +9,21 @@
#![allow(ellipsis_inclusive_range_patterns)] #![allow(ellipsis_inclusive_range_patterns)]
#![allow(clippy::needless_parens_on_range_literals)] #![allow(clippy::needless_parens_on_range_literals)]
#[macro_use]
extern crate macro_rules;
macro_rules! a { macro_rules! a {
() => { () => {
'a' 'a'
}; };
} }
macro_rules! b {
() => {
let _ = 'a'..'z';
};
}
fn main() { fn main() {
#[rustfmt::skip] #[rustfmt::skip]
{ {
@@ -47,6 +57,9 @@ fn main() {
'B'..'Z' => 4, 'B'..'Z' => 4,
_ => 5, _ => 5,
}; };
almost_complete_letter_range!();
b!();
} }
fn _under_msrv() { fn _under_msrv() {

View File

@@ -1,5 +1,5 @@
error: almost complete ascii letter range error: almost complete ascii letter range
--> $DIR/almost_complete_letter_range.rs:20:17 --> $DIR/almost_complete_letter_range.rs:30:17
| |
LL | let _ = ('a') ..'z'; LL | let _ = ('a') ..'z';
| ^^^^^^--^^^ | ^^^^^^--^^^
@@ -9,7 +9,7 @@ LL | let _ = ('a') ..'z';
= note: `-D clippy::almost-complete-letter-range` implied by `-D warnings` = note: `-D clippy::almost-complete-letter-range` implied by `-D warnings`
error: almost complete ascii letter range error: almost complete ascii letter range
--> $DIR/almost_complete_letter_range.rs:21:17 --> $DIR/almost_complete_letter_range.rs:31:17
| |
LL | let _ = 'A' .. ('Z'); LL | let _ = 'A' .. ('Z');
| ^^^^--^^^^^^ | ^^^^--^^^^^^
@@ -17,7 +17,7 @@ LL | let _ = 'A' .. ('Z');
| help: use an inclusive range: `..=` | help: use an inclusive range: `..=`
error: almost complete ascii letter range error: almost complete ascii letter range
--> $DIR/almost_complete_letter_range.rs:27:13 --> $DIR/almost_complete_letter_range.rs:37:13
| |
LL | let _ = (b'a')..(b'z'); LL | let _ = (b'a')..(b'z');
| ^^^^^^--^^^^^^ | ^^^^^^--^^^^^^
@@ -25,7 +25,7 @@ LL | let _ = (b'a')..(b'z');
| help: use an inclusive range: `..=` | help: use an inclusive range: `..=`
error: almost complete ascii letter range error: almost complete ascii letter range
--> $DIR/almost_complete_letter_range.rs:28:13 --> $DIR/almost_complete_letter_range.rs:38:13
| |
LL | let _ = b'A'..b'Z'; LL | let _ = b'A'..b'Z';
| ^^^^--^^^^ | ^^^^--^^^^
@@ -33,7 +33,7 @@ LL | let _ = b'A'..b'Z';
| help: use an inclusive range: `..=` | help: use an inclusive range: `..=`
error: almost complete ascii letter range error: almost complete ascii letter range
--> $DIR/almost_complete_letter_range.rs:33:13 --> $DIR/almost_complete_letter_range.rs:43:13
| |
LL | let _ = a!()..'z'; LL | let _ = a!()..'z';
| ^^^^--^^^ | ^^^^--^^^
@@ -41,7 +41,7 @@ LL | let _ = a!()..'z';
| help: use an inclusive range: `..=` | help: use an inclusive range: `..=`
error: almost complete ascii letter range error: almost complete ascii letter range
--> $DIR/almost_complete_letter_range.rs:36:9 --> $DIR/almost_complete_letter_range.rs:46:9
| |
LL | b'a'..b'z' if true => 1, LL | b'a'..b'z' if true => 1,
| ^^^^--^^^^ | ^^^^--^^^^
@@ -49,7 +49,7 @@ LL | b'a'..b'z' if true => 1,
| help: use an inclusive range: `..=` | help: use an inclusive range: `..=`
error: almost complete ascii letter range error: almost complete ascii letter range
--> $DIR/almost_complete_letter_range.rs:37:9 --> $DIR/almost_complete_letter_range.rs:47:9
| |
LL | b'A'..b'Z' if true => 2, LL | b'A'..b'Z' if true => 2,
| ^^^^--^^^^ | ^^^^--^^^^
@@ -57,7 +57,7 @@ LL | b'A'..b'Z' if true => 2,
| help: use an inclusive range: `..=` | help: use an inclusive range: `..=`
error: almost complete ascii letter range error: almost complete ascii letter range
--> $DIR/almost_complete_letter_range.rs:44:9 --> $DIR/almost_complete_letter_range.rs:54:9
| |
LL | 'a'..'z' if true => 1, LL | 'a'..'z' if true => 1,
| ^^^--^^^ | ^^^--^^^
@@ -65,7 +65,7 @@ LL | 'a'..'z' if true => 1,
| help: use an inclusive range: `..=` | help: use an inclusive range: `..=`
error: almost complete ascii letter range error: almost complete ascii letter range
--> $DIR/almost_complete_letter_range.rs:45:9 --> $DIR/almost_complete_letter_range.rs:55:9
| |
LL | 'A'..'Z' if true => 2, LL | 'A'..'Z' if true => 2,
| ^^^--^^^ | ^^^--^^^
@@ -73,7 +73,20 @@ LL | 'A'..'Z' if true => 2,
| help: use an inclusive range: `..=` | help: use an inclusive range: `..=`
error: almost complete ascii letter range error: almost complete ascii letter range
--> $DIR/almost_complete_letter_range.rs:55:9 --> $DIR/almost_complete_letter_range.rs:23:17
|
LL | let _ = 'a'..'z';
| ^^^--^^^
| |
| help: use an inclusive range: `..=`
...
LL | b!();
| ---- in this macro invocation
|
= note: this error originates in the macro `b` (in Nightly builds, run with -Z macro-backtrace for more info)
error: almost complete ascii letter range
--> $DIR/almost_complete_letter_range.rs:68:9
| |
LL | 'a'..'z' => 1, LL | 'a'..'z' => 1,
| ^^^--^^^ | ^^^--^^^
@@ -81,7 +94,7 @@ LL | 'a'..'z' => 1,
| help: use an inclusive range: `...` | help: use an inclusive range: `...`
error: almost complete ascii letter range error: almost complete ascii letter range
--> $DIR/almost_complete_letter_range.rs:62:13 --> $DIR/almost_complete_letter_range.rs:75:13
| |
LL | let _ = 'a'..'z'; LL | let _ = 'a'..'z';
| ^^^--^^^ | ^^^--^^^
@@ -89,12 +102,12 @@ LL | let _ = 'a'..'z';
| help: use an inclusive range: `..=` | help: use an inclusive range: `..=`
error: almost complete ascii letter range error: almost complete ascii letter range
--> $DIR/almost_complete_letter_range.rs:64:9 --> $DIR/almost_complete_letter_range.rs:77:9
| |
LL | 'a'..'z' => 1, LL | 'a'..'z' => 1,
| ^^^--^^^ | ^^^--^^^
| | | |
| help: use an inclusive range: `..=` | help: use an inclusive range: `..=`
error: aborting due to 12 previous errors error: aborting due to 13 previous errors

View File

@@ -1,9 +1,49 @@
#![allow(clippy::assign_op_pattern, clippy::unnecessary_owned_empty_strings)] #![allow(
clippy::assign_op_pattern,
clippy::erasing_op,
clippy::identity_op,
clippy::unnecessary_owned_empty_strings,
arithmetic_overflow,
unconditional_panic
)]
#![feature(inline_const, saturating_int_impl)] #![feature(inline_const, saturating_int_impl)]
#![warn(clippy::arithmetic_side_effects)] #![warn(clippy::arithmetic_side_effects)]
use core::num::{Saturating, Wrapping}; use core::num::{Saturating, Wrapping};
pub fn association_with_structures_should_not_trigger_the_lint() {
enum Foo {
Bar = -2,
}
impl Trait for Foo {
const ASSOC: i32 = {
let _: [i32; 1 + 1];
fn foo() {}
1 + 1
};
}
struct Baz([i32; 1 + 1]);
trait Trait {
const ASSOC: i32 = 1 + 1;
}
type Alias = [i32; 1 + 1];
union Qux {
field: [i32; 1 + 1],
}
let _: [i32; 1 + 1] = [0, 0];
let _: [i32; 1 + 1] = {
let a: [i32; 1 + 1] = [0, 0];
a
};
}
pub fn hard_coded_allowed() { pub fn hard_coded_allowed() {
let _ = 1f32 + 1f32; let _ = 1f32 + 1f32;
let _ = 1f64 + 1f64; let _ = 1f64 + 1f64;
@@ -26,7 +66,7 @@ pub fn hard_coded_allowed() {
} }
#[rustfmt::skip] #[rustfmt::skip]
pub fn non_overflowing_ops() { pub fn const_ops_should_not_trigger_the_lint() {
const _: i32 = { let mut n = 1; n += 1; n }; const _: i32 = { let mut n = 1; n += 1; n };
let _ = const { let mut n = 1; n += 1; n }; let _ = const { let mut n = 1; n += 1; n };
@@ -37,21 +77,63 @@ pub fn non_overflowing_ops() {
let _ = const { let mut n = 1; n = 1 + n; n }; let _ = const { let mut n = 1; n = 1 + n; n };
const _: i32 = 1 + 1; const _: i32 = 1 + 1;
let _ = 1 + 1;
let _ = const { 1 + 1 }; let _ = const { 1 + 1 };
let mut _a = 1; const _: i32 = { let mut n = -1; n = -(-1); n = -n; n };
_a *= 1; let _ = const { let mut n = -1; n = -(-1); n = -n; n };
_a /= 1;
} }
#[rustfmt::skip] pub fn non_overflowing_runtime_ops_or_ops_already_handled_by_the_compiler() {
pub fn overflowing_ops() { let mut _n = i32::MAX;
let mut _a = 1; _a += 1;
let mut _b = 1; _b = _b + 1; // Assign
_n += 0;
_n -= 0;
_n /= 99;
_n %= 99;
_n *= 0;
_n *= 1;
let mut _c = 1; _c = 1 + _c; // Binary
_n = _n + 0;
_n = 0 + _n;
_n = _n - 0;
_n = 0 - _n;
_n = _n / 99;
_n = _n % 99;
_n = _n * 0;
_n = 0 * _n;
_n = _n * 1;
_n = 1 * _n;
_n = 23 + 85;
// Unary
_n = -1;
_n = -(-1);
}
pub fn overflowing_runtime_ops() {
let mut _n = i32::MAX;
// Assign
_n += 1;
_n -= 1;
_n /= 0;
_n %= 0;
_n *= 2;
// Binary
_n = _n + 1;
_n = 1 + _n;
_n = _n - 1;
_n = 1 - _n;
_n = _n / 0;
_n = _n % 0;
_n = _n * 2;
_n = 2 * _n;
// Unary
_n = -_n;
} }
fn main() {} fn main() {}

View File

@@ -1,22 +1,88 @@
error: arithmetic detected error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:50:21 --> $DIR/arithmetic_side_effects.rs:119:5
| |
LL | let mut _a = 1; _a += 1; LL | _n += 1;
| ^^^^^^^ | ^^^^^^^
| |
= note: `-D clippy::arithmetic-side-effects` implied by `-D warnings` = note: `-D clippy::arithmetic-side-effects` implied by `-D warnings`
error: arithmetic detected error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:52:26 --> $DIR/arithmetic_side_effects.rs:120:5
| |
LL | let mut _b = 1; _b = _b + 1; LL | _n -= 1;
| ^^^^^^ | ^^^^^^^
error: arithmetic detected error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:54:26 --> $DIR/arithmetic_side_effects.rs:121:5
| |
LL | let mut _c = 1; _c = 1 + _c; LL | _n /= 0;
| ^^^^^^ | ^^^^^^^
error: aborting due to 3 previous errors error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:122:5
|
LL | _n %= 0;
| ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:123:5
|
LL | _n *= 2;
| ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:126:10
|
LL | _n = _n + 1;
| ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:127:10
|
LL | _n = 1 + _n;
| ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:128:10
|
LL | _n = _n - 1;
| ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:129:10
|
LL | _n = 1 - _n;
| ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:130:10
|
LL | _n = _n / 0;
| ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:131:10
|
LL | _n = _n % 0;
| ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:132:10
|
LL | _n = _n * 2;
| ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:133:10
|
LL | _n = 2 * _n;
| ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:136:10
|
LL | _n = -_n;
| ^^^
error: aborting due to 14 previous errors

View File

@@ -75,3 +75,9 @@ fn main() {
let r: Result<Foo, Foo> = Err(Foo); let r: Result<Foo, Foo> = Err(Foo);
assert!(r.is_err()); assert!(r.is_err());
} }
#[allow(dead_code)]
fn issue9450() {
let res: Result<i32, i32> = Ok(1);
res.unwrap_err();
}

View File

@@ -75,3 +75,9 @@ fn main() {
let r: Result<Foo, Foo> = Err(Foo); let r: Result<Foo, Foo> = Err(Foo);
assert!(r.is_err()); assert!(r.is_err());
} }
#[allow(dead_code)]
fn issue9450() {
let res: Result<i32, i32> = Ok(1);
assert!(res.is_err())
}

View File

@@ -36,5 +36,11 @@ error: called `assert!` with `Result::is_err`
LL | assert!(r.is_err()); LL | assert!(r.is_err());
| ^^^^^^^^^^^^^^^^^^^ help: replace with: `r.unwrap_err()` | ^^^^^^^^^^^^^^^^^^^ help: replace with: `r.unwrap_err()`
error: aborting due to 6 previous errors error: called `assert!` with `Result::is_err`
--> $DIR/assertions_on_result_states.rs:82:5
|
LL | assert!(res.is_err())
| ^^^^^^^^^^^^^^^^^^^^^ help: replace with: `res.unwrap_err();`
error: aborting due to 7 previous errors

View File

@@ -140,3 +140,10 @@ macro_rules! manual_rem_euclid {
macro_rules! equatable_if_let { macro_rules! equatable_if_let {
($a:ident) => {{ if let 2 = $a {} }}; ($a:ident) => {{ if let 2 = $a {} }};
} }
#[macro_export]
macro_rules! almost_complete_letter_range {
() => {
let _ = 'a'..'z';
};
}

View File

@@ -14,6 +14,7 @@ fn main() {
// precedence // precedence
i32::from(a); i32::from(a);
i32::from(!a); i32::from(!a);
i32::from(!a);
i32::from(a || b); i32::from(a || b);
i32::from(cond(a, b)); i32::from(cond(a, b));
i32::from(x + y < 4); i32::from(x + y < 4);
@@ -21,7 +22,12 @@ fn main() {
// if else if // if else if
if a { if a {
123 123
} else {i32::from(b)}; } else { i32::from(b) };
// if else if inverted
if a {
123
} else { i32::from(!b) };
// Shouldn't lint // Shouldn't lint

View File

@@ -17,6 +17,11 @@ fn main() {
} else { } else {
0 0
}; };
if a {
0
} else {
1
};
if !a { if !a {
1 1
} else { } else {
@@ -47,6 +52,15 @@ fn main() {
0 0
}; };
// if else if inverted
if a {
123
} else if b {
0
} else {
1
};
// Shouldn't lint // Shouldn't lint
if a { if a {

View File

@@ -14,6 +14,18 @@ LL | | };
error: boolean to int conversion using if error: boolean to int conversion using if
--> $DIR/bool_to_int_with_if.rs:20:5 --> $DIR/bool_to_int_with_if.rs:20:5
| |
LL | / if a {
LL | | 0
LL | | } else {
LL | | 1
LL | | };
| |_____^ help: replace with from: `i32::from(!a)`
|
= note: `!a as i32` or `(!a).into()` can also be valid options
error: boolean to int conversion using if
--> $DIR/bool_to_int_with_if.rs:25:5
|
LL | / if !a { LL | / if !a {
LL | | 1 LL | | 1
LL | | } else { LL | | } else {
@@ -21,10 +33,10 @@ LL | | 0
LL | | }; LL | | };
| |_____^ help: replace with from: `i32::from(!a)` | |_____^ help: replace with from: `i32::from(!a)`
| |
= note: `!a as i32` or `!a.into()` can also be valid options = note: `!a as i32` or `(!a).into()` can also be valid options
error: boolean to int conversion using if error: boolean to int conversion using if
--> $DIR/bool_to_int_with_if.rs:25:5 --> $DIR/bool_to_int_with_if.rs:30:5
| |
LL | / if a || b { LL | / if a || b {
LL | | 1 LL | | 1
@@ -36,7 +48,7 @@ LL | | };
= note: `(a || b) as i32` or `(a || b).into()` can also be valid options = note: `(a || b) as i32` or `(a || b).into()` can also be valid options
error: boolean to int conversion using if error: boolean to int conversion using if
--> $DIR/bool_to_int_with_if.rs:30:5 --> $DIR/bool_to_int_with_if.rs:35:5
| |
LL | / if cond(a, b) { LL | / if cond(a, b) {
LL | | 1 LL | | 1
@@ -48,7 +60,7 @@ LL | | };
= note: `cond(a, b) as i32` or `cond(a, b).into()` can also be valid options = note: `cond(a, b) as i32` or `cond(a, b).into()` can also be valid options
error: boolean to int conversion using if error: boolean to int conversion using if
--> $DIR/bool_to_int_with_if.rs:35:5 --> $DIR/bool_to_int_with_if.rs:40:5
| |
LL | / if x + y < 4 { LL | / if x + y < 4 {
LL | | 1 LL | | 1
@@ -60,7 +72,7 @@ LL | | };
= note: `(x + y < 4) as i32` or `(x + y < 4).into()` can also be valid options = note: `(x + y < 4) as i32` or `(x + y < 4).into()` can also be valid options
error: boolean to int conversion using if error: boolean to int conversion using if
--> $DIR/bool_to_int_with_if.rs:44:12 --> $DIR/bool_to_int_with_if.rs:49:12
| |
LL | } else if b { LL | } else if b {
| ____________^ | ____________^
@@ -68,17 +80,30 @@ LL | | 1
LL | | } else { LL | | } else {
LL | | 0 LL | | 0
LL | | }; LL | | };
| |_____^ help: replace with from: `{i32::from(b)}` | |_____^ help: replace with from: `{ i32::from(b) }`
| |
= note: `b as i32` or `b.into()` can also be valid options = note: `b as i32` or `b.into()` can also be valid options
error: boolean to int conversion using if error: boolean to int conversion using if
--> $DIR/bool_to_int_with_if.rs:102:5 --> $DIR/bool_to_int_with_if.rs:58:12
|
LL | } else if b {
| ____________^
LL | | 0
LL | | } else {
LL | | 1
LL | | };
| |_____^ help: replace with from: `{ i32::from(!b) }`
|
= note: `!b as i32` or `(!b).into()` can also be valid options
error: boolean to int conversion using if
--> $DIR/bool_to_int_with_if.rs:116:5
| |
LL | if a { 1 } else { 0 } LL | if a { 1 } else { 0 }
| ^^^^^^^^^^^^^^^^^^^^^ help: replace with from: `u8::from(a)` | ^^^^^^^^^^^^^^^^^^^^^ help: replace with from: `u8::from(a)`
| |
= note: `a as u8` or `a.into()` can also be valid options = note: `a as u8` or `a.into()` can also be valid options
error: aborting due to 7 previous errors error: aborting due to 9 previous errors

View File

@@ -139,6 +139,9 @@ fn main() {
// Fix #5962 // Fix #5962
if matches!(true, true) && matches!(true, true) {} if matches!(true, true) && matches!(true, true) {}
// Issue #9375
if matches!(true, true) && truth() && matches!(true, true) {}
if true { if true {
#[cfg(not(teehee))] #[cfg(not(teehee))]
if true { if true {

View File

@@ -155,6 +155,11 @@ fn main() {
if matches!(true, true) {} if matches!(true, true) {}
} }
// Issue #9375
if matches!(true, true) && truth() {
if matches!(true, true) {}
}
if true { if true {
#[cfg(not(teehee))] #[cfg(not(teehee))]
if true { if true {

View File

@@ -126,5 +126,13 @@ LL | | if matches!(true, true) {}
LL | | } LL | | }
| |_____^ help: collapse nested if block: `if matches!(true, true) && matches!(true, true) {}` | |_____^ help: collapse nested if block: `if matches!(true, true) && matches!(true, true) {}`
error: aborting due to 8 previous errors error: this `if` statement can be collapsed
--> $DIR/collapsible_if.rs:159:5
|
LL | / if matches!(true, true) && truth() {
LL | | if matches!(true, true) {}
LL | | }
| |_____^ help: collapse nested if block: `if matches!(true, true) && truth() && matches!(true, true) {}`
error: aborting due to 9 previous errors

View File

@@ -0,0 +1,5 @@
#![deny(arithmetic_overflow, const_err)]
fn main() {
let _x = -1_i32 >> -1;
let _y = 1u32 >> 10000000000000u32;
}

View File

@@ -0,0 +1,29 @@
error: this arithmetic operation will overflow
--> $DIR/ice-9463.rs:3:14
|
LL | let _x = -1_i32 >> -1;
| ^^^^^^^^^^^^ attempt to shift right by `-1_i32`, which would overflow
|
note: the lint level is defined here
--> $DIR/ice-9463.rs:1:9
|
LL | #![deny(arithmetic_overflow, const_err)]
| ^^^^^^^^^^^^^^^^^^^
error: this arithmetic operation will overflow
--> $DIR/ice-9463.rs:4:14
|
LL | let _y = 1u32 >> 10000000000000u32;
| ^^^^^^^^^^^^^^^^^^^^^^^^^ attempt to shift right by `1316134912_u32`, which would overflow
error: literal out of range for `u32`
--> $DIR/ice-9463.rs:4:22
|
LL | let _y = 1u32 >> 10000000000000u32;
| ^^^^^^^^^^^^^^^^^
|
= note: `#[deny(overflowing_literals)]` on by default
= note: the literal `10000000000000u32` does not fit into the type `u32` whose range is `0..=4294967295`
error: aborting due to 3 previous errors

View File

@@ -0,0 +1,213 @@
// run-rustfix
#![allow(dead_code)]
use std::collections::HashMap;
#[derive(Default)]
struct FooDefault<'a> {
a: bool,
b: i32,
c: u64,
d: Vec<i32>,
e: FooND1,
f: FooND2,
g: HashMap<i32, i32>,
h: (i32, Vec<i32>),
i: [Vec<i32>; 3],
j: [i32; 5],
k: Option<i32>,
l: &'a [i32],
}
#[derive(Default)]
struct TupleDefault(bool, i32, u64);
struct FooND1 {
a: bool,
}
impl std::default::Default for FooND1 {
fn default() -> Self {
Self { a: true }
}
}
struct FooND2 {
a: i32,
}
impl std::default::Default for FooND2 {
fn default() -> Self {
Self { a: 5 }
}
}
struct FooNDNew {
a: bool,
}
impl FooNDNew {
fn new() -> Self {
Self { a: true }
}
}
impl Default for FooNDNew {
fn default() -> Self {
Self::new()
}
}
struct FooNDVec(Vec<i32>);
impl Default for FooNDVec {
fn default() -> Self {
Self(vec![5, 12])
}
}
#[derive(Default)]
struct StrDefault<'a>(&'a str);
#[derive(Default)]
struct AlreadyDerived(i32, bool);
macro_rules! mac {
() => {
0
};
($e:expr) => {
struct X(u32);
impl Default for X {
fn default() -> Self {
Self($e)
}
}
};
}
mac!(0);
#[derive(Default)]
struct Y(u32);
struct RustIssue26925<T> {
a: Option<T>,
}
// We should watch out for cases where a manual impl is needed because a
// derive adds different type bounds (https://github.com/rust-lang/rust/issues/26925).
// For example, a struct with Option<T> does not require T: Default, but a derive adds
// that type bound anyways. So until #26925 get fixed we should disable lint
// for the following case
impl<T> Default for RustIssue26925<T> {
fn default() -> Self {
Self { a: None }
}
}
struct SpecializedImpl<A, B> {
a: A,
b: B,
}
impl<T: Default> Default for SpecializedImpl<T, T> {
fn default() -> Self {
Self {
a: T::default(),
b: T::default(),
}
}
}
#[derive(Default)]
struct WithoutSelfCurly {
a: bool,
}
#[derive(Default)]
struct WithoutSelfParan(bool);
// https://github.com/rust-lang/rust-clippy/issues/7655
pub struct SpecializedImpl2<T> {
v: Vec<T>,
}
impl Default for SpecializedImpl2<String> {
fn default() -> Self {
Self { v: Vec::new() }
}
}
// https://github.com/rust-lang/rust-clippy/issues/7654
pub struct Color {
pub r: u8,
pub g: u8,
pub b: u8,
}
/// `#000000`
impl Default for Color {
fn default() -> Self {
Color { r: 0, g: 0, b: 0 }
}
}
pub struct Color2 {
pub r: u8,
pub g: u8,
pub b: u8,
}
impl Default for Color2 {
/// `#000000`
fn default() -> Self {
Self { r: 0, g: 0, b: 0 }
}
}
#[derive(Default)]
pub struct RepeatDefault1 {
a: [i8; 32],
}
pub struct RepeatDefault2 {
a: [i8; 33],
}
impl Default for RepeatDefault2 {
fn default() -> Self {
RepeatDefault2 { a: [0; 33] }
}
}
// https://github.com/rust-lang/rust-clippy/issues/7753
pub enum IntOrString {
Int(i32),
String(String),
}
impl Default for IntOrString {
fn default() -> Self {
IntOrString::Int(0)
}
}
fn main() {}

View File

@@ -1,3 +1,7 @@
// run-rustfix
#![allow(dead_code)]
use std::collections::HashMap; use std::collections::HashMap;
struct FooDefault<'a> { struct FooDefault<'a> {

View File

@@ -1,5 +1,5 @@
error: this `impl` can be derived error: this `impl` can be derived
--> $DIR/derivable_impls.rs:18:1 --> $DIR/derivable_impls.rs:22:1
| |
LL | / impl std::default::Default for FooDefault<'_> { LL | / impl std::default::Default for FooDefault<'_> {
LL | | fn default() -> Self { LL | | fn default() -> Self {
@@ -11,10 +11,14 @@ LL | | }
| |_^ | |_^
| |
= note: `-D clippy::derivable-impls` implied by `-D warnings` = note: `-D clippy::derivable-impls` implied by `-D warnings`
= help: try annotating `FooDefault` with `#[derive(Default)]` = help: remove the manual implementation...
help: ...and instead derive it
|
LL | #[derive(Default)]
|
error: this `impl` can be derived error: this `impl` can be derived
--> $DIR/derivable_impls.rs:39:1 --> $DIR/derivable_impls.rs:43:1
| |
LL | / impl std::default::Default for TupleDefault { LL | / impl std::default::Default for TupleDefault {
LL | | fn default() -> Self { LL | | fn default() -> Self {
@@ -23,10 +27,14 @@ LL | | }
LL | | } LL | | }
| |_^ | |_^
| |
= help: try annotating `TupleDefault` with `#[derive(Default)]` = help: remove the manual implementation...
help: ...and instead derive it
|
LL | #[derive(Default)]
|
error: this `impl` can be derived error: this `impl` can be derived
--> $DIR/derivable_impls.rs:91:1 --> $DIR/derivable_impls.rs:95:1
| |
LL | / impl Default for StrDefault<'_> { LL | / impl Default for StrDefault<'_> {
LL | | fn default() -> Self { LL | | fn default() -> Self {
@@ -35,10 +43,14 @@ LL | | }
LL | | } LL | | }
| |_^ | |_^
| |
= help: try annotating `StrDefault` with `#[derive(Default)]` = help: remove the manual implementation...
help: ...and instead derive it
|
LL | #[derive(Default)]
|
error: this `impl` can be derived error: this `impl` can be derived
--> $DIR/derivable_impls.rs:117:1 --> $DIR/derivable_impls.rs:121:1
| |
LL | / impl Default for Y { LL | / impl Default for Y {
LL | | fn default() -> Self { LL | | fn default() -> Self {
@@ -47,10 +59,14 @@ LL | | }
LL | | } LL | | }
| |_^ | |_^
| |
= help: try annotating `Y` with `#[derive(Default)]` = help: remove the manual implementation...
help: ...and instead derive it
|
LL | #[derive(Default)]
|
error: this `impl` can be derived error: this `impl` can be derived
--> $DIR/derivable_impls.rs:156:1 --> $DIR/derivable_impls.rs:160:1
| |
LL | / impl Default for WithoutSelfCurly { LL | / impl Default for WithoutSelfCurly {
LL | | fn default() -> Self { LL | | fn default() -> Self {
@@ -59,10 +75,14 @@ LL | | }
LL | | } LL | | }
| |_^ | |_^
| |
= help: try annotating `WithoutSelfCurly` with `#[derive(Default)]` = help: remove the manual implementation...
help: ...and instead derive it
|
LL | #[derive(Default)]
|
error: this `impl` can be derived error: this `impl` can be derived
--> $DIR/derivable_impls.rs:164:1 --> $DIR/derivable_impls.rs:168:1
| |
LL | / impl Default for WithoutSelfParan { LL | / impl Default for WithoutSelfParan {
LL | | fn default() -> Self { LL | | fn default() -> Self {
@@ -71,10 +91,14 @@ LL | | }
LL | | } LL | | }
| |_^ | |_^
| |
= help: try annotating `WithoutSelfParan` with `#[derive(Default)]` = help: remove the manual implementation...
help: ...and instead derive it
|
LL | #[derive(Default)]
|
error: this `impl` can be derived error: this `impl` can be derived
--> $DIR/derivable_impls.rs:214:1 --> $DIR/derivable_impls.rs:218:1
| |
LL | / impl Default for RepeatDefault1 { LL | / impl Default for RepeatDefault1 {
LL | | fn default() -> Self { LL | | fn default() -> Self {
@@ -83,7 +107,11 @@ LL | | }
LL | | } LL | | }
| |_^ | |_^
| |
= help: try annotating `RepeatDefault1` with `#[derive(Default)]` = help: remove the manual implementation...
help: ...and instead derive it
|
LL | #[derive(Default)]
|
error: aborting due to 7 previous errors error: aborting due to 7 previous errors

View File

@@ -45,5 +45,13 @@ fn main() {
eprint!("\r\n"); eprint!("\r\n");
eprint!("foo\r\n"); eprint!("foo\r\n");
eprint!("\\r\n"); //~ ERROR eprint!("\\r\n"); //~ ERROR
eprint!("foo\rbar\n") // ~ ERROR eprint!("foo\rbar\n");
// Ignore expanded format strings
macro_rules! newline {
() => {
"\n"
};
}
eprint!(newline!());
} }

View File

@@ -83,7 +83,7 @@ LL | | );
help: use `eprintln!` instead help: use `eprintln!` instead
| |
LL ~ eprintln!( LL ~ eprintln!(
LL ~ "" LL ~
| |
error: using `eprint!()` with a format string that ends in a single newline error: using `eprint!()` with a format string that ends in a single newline
@@ -98,7 +98,7 @@ LL | | );
help: use `eprintln!` instead help: use `eprintln!` instead
| |
LL ~ eprintln!( LL ~ eprintln!(
LL ~ r"" LL ~
| |
error: using `eprint!()` with a format string that ends in a single newline error: using `eprint!()` with a format string that ends in a single newline
@@ -113,17 +113,5 @@ LL - eprint!("/r/n"); //~ ERROR
LL + eprintln!("/r"); //~ ERROR LL + eprintln!("/r"); //~ ERROR
| |
error: using `eprint!()` with a format string that ends in a single newline error: aborting due to 9 previous errors
--> $DIR/eprint_with_newline.rs:48:5
|
LL | eprint!("foo/rbar/n") // ~ ERROR
| ^^^^^^^^^^^^^^^^^^^^^
|
help: use `eprintln!` instead
|
LL - eprint!("foo/rbar/n") // ~ ERROR
LL + eprintln!("foo/rbar") // ~ ERROR
|
error: aborting due to 10 previous errors

View File

@@ -36,6 +36,8 @@ fn main() {
eprintln!("with {} {}", 2, value); eprintln!("with {} {}", 2, value);
eprintln!("with {value}"); eprintln!("with {value}");
eprintln!("macro arg {}", one!()); eprintln!("macro arg {}", one!());
let width = 2;
eprintln!("{:w$}", value, w = width);
} }
// these should not warn, different destination // these should not warn, different destination
{ {

View File

@@ -36,6 +36,8 @@ fn main() {
writeln!(std::io::stderr(), "with {} {}", 2, value).unwrap(); writeln!(std::io::stderr(), "with {} {}", 2, value).unwrap();
writeln!(std::io::stderr(), "with {value}").unwrap(); writeln!(std::io::stderr(), "with {value}").unwrap();
writeln!(std::io::stderr(), "macro arg {}", one!()).unwrap(); writeln!(std::io::stderr(), "macro arg {}", one!()).unwrap();
let width = 2;
writeln!(std::io::stderr(), "{:w$}", value, w = width).unwrap();
} }
// these should not warn, different destination // these should not warn, different destination
{ {

View File

@@ -72,5 +72,11 @@ error: use of `writeln!(stderr(), ...).unwrap()`
LL | writeln!(std::io::stderr(), "macro arg {}", one!()).unwrap(); LL | writeln!(std::io::stderr(), "macro arg {}", one!()).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("macro arg {}", one!())` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("macro arg {}", one!())`
error: aborting due to 12 previous errors error: use of `writeln!(stderr(), ...).unwrap()`
--> $DIR/explicit_write.rs:40:9
|
LL | writeln!(std::io::stderr(), "{:w$}", value, w = width).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("{:w$}", value, w = width)`
error: aborting due to 13 previous errors

View File

@@ -28,8 +28,6 @@ fn main() {
format!("{:?}", "foo"); // Don't warn about `Debug`. format!("{:?}", "foo"); // Don't warn about `Debug`.
format!("{:8}", "foo"); format!("{:8}", "foo");
format!("{:width$}", "foo", width = 8); format!("{:width$}", "foo", width = 8);
"foo".to_string(); // Warn when the format makes no difference.
"foo".to_string(); // Warn when the format makes no difference.
format!("foo {}", "bar"); format!("foo {}", "bar");
format!("{} bar", "foo"); format!("{} bar", "foo");
@@ -38,8 +36,6 @@ fn main() {
format!("{:?}", arg); // Don't warn about debug. format!("{:?}", arg); // Don't warn about debug.
format!("{:8}", arg); format!("{:8}", arg);
format!("{:width$}", arg, width = 8); format!("{:width$}", arg, width = 8);
arg.to_string(); // Warn when the format makes no difference.
arg.to_string(); // Warn when the format makes no difference.
format!("foo {}", arg); format!("foo {}", arg);
format!("{} bar", arg); format!("{} bar", arg);

View File

@@ -30,8 +30,6 @@ fn main() {
format!("{:?}", "foo"); // Don't warn about `Debug`. format!("{:?}", "foo"); // Don't warn about `Debug`.
format!("{:8}", "foo"); format!("{:8}", "foo");
format!("{:width$}", "foo", width = 8); format!("{:width$}", "foo", width = 8);
format!("{:+}", "foo"); // Warn when the format makes no difference.
format!("{:<}", "foo"); // Warn when the format makes no difference.
format!("foo {}", "bar"); format!("foo {}", "bar");
format!("{} bar", "foo"); format!("{} bar", "foo");
@@ -40,8 +38,6 @@ fn main() {
format!("{:?}", arg); // Don't warn about debug. format!("{:?}", arg); // Don't warn about debug.
format!("{:8}", arg); format!("{:8}", arg);
format!("{:width$}", arg, width = 8); format!("{:width$}", arg, width = 8);
format!("{:+}", arg); // Warn when the format makes no difference.
format!("{:<}", arg); // Warn when the format makes no difference.
format!("foo {}", arg); format!("foo {}", arg);
format!("{} bar", arg); format!("{} bar", arg);

View File

@@ -46,82 +46,58 @@ LL | format!("{}", "foo");
| ^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()` | ^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()`
error: useless use of `format!` error: useless use of `format!`
--> $DIR/format.rs:33:5 --> $DIR/format.rs:37:5
|
LL | format!("{:+}", "foo"); // Warn when the format makes no difference.
| ^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()`
error: useless use of `format!`
--> $DIR/format.rs:34:5
|
LL | format!("{:<}", "foo"); // Warn when the format makes no difference.
| ^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()`
error: useless use of `format!`
--> $DIR/format.rs:39:5
| |
LL | format!("{}", arg); LL | format!("{}", arg);
| ^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `arg.to_string()` | ^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `arg.to_string()`
error: useless use of `format!` error: useless use of `format!`
--> $DIR/format.rs:43:5 --> $DIR/format.rs:67:5
|
LL | format!("{:+}", arg); // Warn when the format makes no difference.
| ^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `arg.to_string()`
error: useless use of `format!`
--> $DIR/format.rs:44:5
|
LL | format!("{:<}", arg); // Warn when the format makes no difference.
| ^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `arg.to_string()`
error: useless use of `format!`
--> $DIR/format.rs:71:5
| |
LL | format!("{}", 42.to_string()); LL | format!("{}", 42.to_string());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `42.to_string()` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `42.to_string()`
error: useless use of `format!` error: useless use of `format!`
--> $DIR/format.rs:73:5 --> $DIR/format.rs:69:5
| |
LL | format!("{}", x.display().to_string()); LL | format!("{}", x.display().to_string());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.display().to_string()` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.display().to_string()`
error: useless use of `format!` error: useless use of `format!`
--> $DIR/format.rs:77:18 --> $DIR/format.rs:73:18
| |
LL | let _ = Some(format!("{}", a + "bar")); LL | let _ = Some(format!("{}", a + "bar"));
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `a + "bar"` | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `a + "bar"`
error: useless use of `format!` error: useless use of `format!`
--> $DIR/format.rs:81:22 --> $DIR/format.rs:77:22
| |
LL | let _s: String = format!("{}", &*v.join("/n")); LL | let _s: String = format!("{}", &*v.join("/n"));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `(&*v.join("/n")).to_string()` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `(&*v.join("/n")).to_string()`
error: useless use of `format!` error: useless use of `format!`
--> $DIR/format.rs:87:13 --> $DIR/format.rs:83:13
| |
LL | let _ = format!("{x}"); LL | let _ = format!("{x}");
| ^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.to_string()` | ^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.to_string()`
error: useless use of `format!` error: useless use of `format!`
--> $DIR/format.rs:89:13 --> $DIR/format.rs:85:13
| |
LL | let _ = format!("{y}", y = x); LL | let _ = format!("{y}", y = x);
| ^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.to_string()` | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.to_string()`
error: useless use of `format!` error: useless use of `format!`
--> $DIR/format.rs:93:13 --> $DIR/format.rs:89:13
| |
LL | let _ = format!("{abc}"); LL | let _ = format!("{abc}");
| ^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `abc.to_string()` | ^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `abc.to_string()`
error: useless use of `format!` error: useless use of `format!`
--> $DIR/format.rs:95:13 --> $DIR/format.rs:91:13
| |
LL | let _ = format!("{xx}"); LL | let _ = format!("{xx}");
| ^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `xx.to_string()` | ^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `xx.to_string()`
error: aborting due to 19 previous errors error: aborting due to 15 previous errors

View File

@@ -0,0 +1,64 @@
// run-rustfix
#![warn(clippy::iter_kv_map)]
#![allow(clippy::redundant_clone)]
#![allow(clippy::suspicious_map)]
#![allow(clippy::map_identity)]
use std::collections::{BTreeMap, HashMap};
fn main() {
let get_key = |(key, _val)| key;
let map: HashMap<u32, u32> = HashMap::new();
let _ = map.keys().collect::<Vec<_>>();
let _ = map.values().collect::<Vec<_>>();
let _ = map.values().map(|v| v + 2).collect::<Vec<_>>();
let _ = map.clone().into_keys().collect::<Vec<_>>();
let _ = map.clone().into_keys().map(|key| key + 2).collect::<Vec<_>>();
let _ = map.clone().into_values().collect::<Vec<_>>();
let _ = map.clone().into_values().map(|val| val + 2).collect::<Vec<_>>();
let _ = map.clone().values().collect::<Vec<_>>();
let _ = map.keys().filter(|x| *x % 2 == 0).count();
// Don't lint
let _ = map.iter().filter(|(_, val)| *val % 2 == 0).map(|(key, _)| key).count();
let _ = map.iter().map(get_key).collect::<Vec<_>>();
// Linting the following could be an improvement to the lint
// map.iter().filter_map(|(_, val)| (val % 2 == 0).then(val * 17)).count();
// Lint
let _ = map.keys().map(|key| key * 9).count();
let _ = map.values().map(|value| value * 17).count();
let map: BTreeMap<u32, u32> = BTreeMap::new();
let _ = map.keys().collect::<Vec<_>>();
let _ = map.values().collect::<Vec<_>>();
let _ = map.values().map(|v| v + 2).collect::<Vec<_>>();
let _ = map.clone().into_keys().collect::<Vec<_>>();
let _ = map.clone().into_keys().map(|key| key + 2).collect::<Vec<_>>();
let _ = map.clone().into_values().collect::<Vec<_>>();
let _ = map.clone().into_values().map(|val| val + 2).collect::<Vec<_>>();
let _ = map.clone().values().collect::<Vec<_>>();
let _ = map.keys().filter(|x| *x % 2 == 0).count();
// Don't lint
let _ = map.iter().filter(|(_, val)| *val % 2 == 0).map(|(key, _)| key).count();
let _ = map.iter().map(get_key).collect::<Vec<_>>();
// Linting the following could be an improvement to the lint
// map.iter().filter_map(|(_, val)| (val % 2 == 0).then(val * 17)).count();
// Lint
let _ = map.keys().map(|key| key * 9).count();
let _ = map.values().map(|value| value * 17).count();
}

64
tests/ui/iter_kv_map.rs Normal file
View File

@@ -0,0 +1,64 @@
// run-rustfix
#![warn(clippy::iter_kv_map)]
#![allow(clippy::redundant_clone)]
#![allow(clippy::suspicious_map)]
#![allow(clippy::map_identity)]
use std::collections::{BTreeMap, HashMap};
fn main() {
let get_key = |(key, _val)| key;
let map: HashMap<u32, u32> = HashMap::new();
let _ = map.iter().map(|(key, _)| key).collect::<Vec<_>>();
let _ = map.iter().map(|(_, value)| value).collect::<Vec<_>>();
let _ = map.iter().map(|(_, v)| v + 2).collect::<Vec<_>>();
let _ = map.clone().into_iter().map(|(key, _)| key).collect::<Vec<_>>();
let _ = map.clone().into_iter().map(|(key, _)| key + 2).collect::<Vec<_>>();
let _ = map.clone().into_iter().map(|(_, val)| val).collect::<Vec<_>>();
let _ = map.clone().into_iter().map(|(_, val)| val + 2).collect::<Vec<_>>();
let _ = map.clone().iter().map(|(_, val)| val).collect::<Vec<_>>();
let _ = map.iter().map(|(key, _)| key).filter(|x| *x % 2 == 0).count();
// Don't lint
let _ = map.iter().filter(|(_, val)| *val % 2 == 0).map(|(key, _)| key).count();
let _ = map.iter().map(get_key).collect::<Vec<_>>();
// Linting the following could be an improvement to the lint
// map.iter().filter_map(|(_, val)| (val % 2 == 0).then(val * 17)).count();
// Lint
let _ = map.iter().map(|(key, _value)| key * 9).count();
let _ = map.iter().map(|(_key, value)| value * 17).count();
let map: BTreeMap<u32, u32> = BTreeMap::new();
let _ = map.iter().map(|(key, _)| key).collect::<Vec<_>>();
let _ = map.iter().map(|(_, value)| value).collect::<Vec<_>>();
let _ = map.iter().map(|(_, v)| v + 2).collect::<Vec<_>>();
let _ = map.clone().into_iter().map(|(key, _)| key).collect::<Vec<_>>();
let _ = map.clone().into_iter().map(|(key, _)| key + 2).collect::<Vec<_>>();
let _ = map.clone().into_iter().map(|(_, val)| val).collect::<Vec<_>>();
let _ = map.clone().into_iter().map(|(_, val)| val + 2).collect::<Vec<_>>();
let _ = map.clone().iter().map(|(_, val)| val).collect::<Vec<_>>();
let _ = map.iter().map(|(key, _)| key).filter(|x| *x % 2 == 0).count();
// Don't lint
let _ = map.iter().filter(|(_, val)| *val % 2 == 0).map(|(key, _)| key).count();
let _ = map.iter().map(get_key).collect::<Vec<_>>();
// Linting the following could be an improvement to the lint
// map.iter().filter_map(|(_, val)| (val % 2 == 0).then(val * 17)).count();
// Lint
let _ = map.iter().map(|(key, _value)| key * 9).count();
let _ = map.iter().map(|(_key, value)| value * 17).count();
}

136
tests/ui/iter_kv_map.stderr Normal file
View File

@@ -0,0 +1,136 @@
error: iterating on a map's keys
--> $DIR/iter_kv_map.rs:15:13
|
LL | let _ = map.iter().map(|(key, _)| key).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()`
|
= note: `-D clippy::iter-kv-map` implied by `-D warnings`
error: iterating on a map's values
--> $DIR/iter_kv_map.rs:16:13
|
LL | let _ = map.iter().map(|(_, value)| value).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values()`
error: iterating on a map's values
--> $DIR/iter_kv_map.rs:17:13
|
LL | let _ = map.iter().map(|(_, v)| v + 2).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|v| v + 2)`
error: iterating on a map's keys
--> $DIR/iter_kv_map.rs:19:13
|
LL | let _ = map.clone().into_iter().map(|(key, _)| key).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys()`
error: iterating on a map's keys
--> $DIR/iter_kv_map.rs:20:13
|
LL | let _ = map.clone().into_iter().map(|(key, _)| key + 2).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys().map(|key| key + 2)`
error: iterating on a map's values
--> $DIR/iter_kv_map.rs:22:13
|
LL | let _ = map.clone().into_iter().map(|(_, val)| val).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values()`
error: iterating on a map's values
--> $DIR/iter_kv_map.rs:23:13
|
LL | let _ = map.clone().into_iter().map(|(_, val)| val + 2).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values().map(|val| val + 2)`
error: iterating on a map's values
--> $DIR/iter_kv_map.rs:25:13
|
LL | let _ = map.clone().iter().map(|(_, val)| val).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().values()`
error: iterating on a map's keys
--> $DIR/iter_kv_map.rs:26:13
|
LL | let _ = map.iter().map(|(key, _)| key).filter(|x| *x % 2 == 0).count();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()`
error: iterating on a map's keys
--> $DIR/iter_kv_map.rs:36:13
|
LL | let _ = map.iter().map(|(key, _value)| key * 9).count();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys().map(|key| key * 9)`
error: iterating on a map's values
--> $DIR/iter_kv_map.rs:37:13
|
LL | let _ = map.iter().map(|(_key, value)| value * 17).count();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|value| value * 17)`
error: iterating on a map's keys
--> $DIR/iter_kv_map.rs:41:13
|
LL | let _ = map.iter().map(|(key, _)| key).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()`
error: iterating on a map's values
--> $DIR/iter_kv_map.rs:42:13
|
LL | let _ = map.iter().map(|(_, value)| value).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values()`
error: iterating on a map's values
--> $DIR/iter_kv_map.rs:43:13
|
LL | let _ = map.iter().map(|(_, v)| v + 2).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|v| v + 2)`
error: iterating on a map's keys
--> $DIR/iter_kv_map.rs:45:13
|
LL | let _ = map.clone().into_iter().map(|(key, _)| key).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys()`
error: iterating on a map's keys
--> $DIR/iter_kv_map.rs:46:13
|
LL | let _ = map.clone().into_iter().map(|(key, _)| key + 2).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys().map(|key| key + 2)`
error: iterating on a map's values
--> $DIR/iter_kv_map.rs:48:13
|
LL | let _ = map.clone().into_iter().map(|(_, val)| val).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values()`
error: iterating on a map's values
--> $DIR/iter_kv_map.rs:49:13
|
LL | let _ = map.clone().into_iter().map(|(_, val)| val + 2).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values().map(|val| val + 2)`
error: iterating on a map's values
--> $DIR/iter_kv_map.rs:51:13
|
LL | let _ = map.clone().iter().map(|(_, val)| val).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().values()`
error: iterating on a map's keys
--> $DIR/iter_kv_map.rs:52:13
|
LL | let _ = map.iter().map(|(key, _)| key).filter(|x| *x % 2 == 0).count();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()`
error: iterating on a map's keys
--> $DIR/iter_kv_map.rs:62:13
|
LL | let _ = map.iter().map(|(key, _value)| key * 9).count();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys().map(|key| key * 9)`
error: iterating on a map's values
--> $DIR/iter_kv_map.rs:63:13
|
LL | let _ = map.iter().map(|(_key, value)| value * 17).count();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|value| value * 17)`
error: aborting due to 22 previous errors

View File

@@ -101,12 +101,12 @@ struct Struct2 {
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
enum CopyableLargeEnum { enum CopyableLargeEnum {
A(bool), A(bool),
B([u128; 4000]), B([u64; 8000]),
} }
enum ManuallyCopyLargeEnum { enum ManuallyCopyLargeEnum {
A(bool), A(bool),
B([u128; 4000]), B([u64; 8000]),
} }
impl Clone for ManuallyCopyLargeEnum { impl Clone for ManuallyCopyLargeEnum {

View File

@@ -167,8 +167,8 @@ error: large size difference between variants
LL | / enum CopyableLargeEnum { LL | / enum CopyableLargeEnum {
LL | | A(bool), LL | | A(bool),
| | ------- the second-largest variant contains at least 1 bytes | | ------- the second-largest variant contains at least 1 bytes
LL | | B([u128; 4000]), LL | | B([u64; 8000]),
| | --------------- the largest variant contains at least 64000 bytes | | -------------- the largest variant contains at least 64000 bytes
LL | | } LL | | }
| |_^ the entire enum is at least 64008 bytes | |_^ the entire enum is at least 64008 bytes
| |
@@ -180,8 +180,8 @@ LL | enum CopyableLargeEnum {
help: consider boxing the large fields to reduce the total size of the enum help: consider boxing the large fields to reduce the total size of the enum
--> $DIR/large_enum_variant.rs:104:5 --> $DIR/large_enum_variant.rs:104:5
| |
LL | B([u128; 4000]), LL | B([u64; 8000]),
| ^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^
error: large size difference between variants error: large size difference between variants
--> $DIR/large_enum_variant.rs:107:1 --> $DIR/large_enum_variant.rs:107:1
@@ -189,8 +189,8 @@ error: large size difference between variants
LL | / enum ManuallyCopyLargeEnum { LL | / enum ManuallyCopyLargeEnum {
LL | | A(bool), LL | | A(bool),
| | ------- the second-largest variant contains at least 1 bytes | | ------- the second-largest variant contains at least 1 bytes
LL | | B([u128; 4000]), LL | | B([u64; 8000]),
| | --------------- the largest variant contains at least 64000 bytes | | -------------- the largest variant contains at least 64000 bytes
LL | | } LL | | }
| |_^ the entire enum is at least 64008 bytes | |_^ the entire enum is at least 64008 bytes
| |
@@ -202,8 +202,8 @@ LL | enum ManuallyCopyLargeEnum {
help: consider boxing the large fields to reduce the total size of the enum help: consider boxing the large fields to reduce the total size of the enum
--> $DIR/large_enum_variant.rs:109:5 --> $DIR/large_enum_variant.rs:109:5
| |
LL | B([u128; 4000]), LL | B([u64; 8000]),
| ^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^
error: large size difference between variants error: large size difference between variants
--> $DIR/large_enum_variant.rs:120:1 --> $DIR/large_enum_variant.rs:120:1

View File

@@ -12,6 +12,12 @@ enum E {
T(u32), T(u32),
} }
pub static DOESNOTLINT: [u8; 512_001] = [0; 512_001];
pub static DOESNOTLINT2: [u8; 512_001] = {
let x = 0;
[x; 512_001]
};
fn main() { fn main() {
let bad = ( let bad = (
[0u32; 20_000_000], [0u32; 20_000_000],

View File

@@ -1,5 +1,5 @@
error: allocating a local array larger than 512000 bytes error: allocating a local array larger than 512000 bytes
--> $DIR/large_stack_arrays.rs:17:9 --> $DIR/large_stack_arrays.rs:23:9
| |
LL | [0u32; 20_000_000], LL | [0u32; 20_000_000],
| ^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^
@@ -8,7 +8,7 @@ LL | [0u32; 20_000_000],
= help: consider allocating on the heap with `vec![0u32; 20_000_000].into_boxed_slice()` = help: consider allocating on the heap with `vec![0u32; 20_000_000].into_boxed_slice()`
error: allocating a local array larger than 512000 bytes error: allocating a local array larger than 512000 bytes
--> $DIR/large_stack_arrays.rs:18:9 --> $DIR/large_stack_arrays.rs:24:9
| |
LL | [S { data: [0; 32] }; 5000], LL | [S { data: [0; 32] }; 5000],
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -16,7 +16,7 @@ LL | [S { data: [0; 32] }; 5000],
= help: consider allocating on the heap with `vec![S { data: [0; 32] }; 5000].into_boxed_slice()` = help: consider allocating on the heap with `vec![S { data: [0; 32] }; 5000].into_boxed_slice()`
error: allocating a local array larger than 512000 bytes error: allocating a local array larger than 512000 bytes
--> $DIR/large_stack_arrays.rs:19:9 --> $DIR/large_stack_arrays.rs:25:9
| |
LL | [Some(""); 20_000_000], LL | [Some(""); 20_000_000],
| ^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^
@@ -24,7 +24,7 @@ LL | [Some(""); 20_000_000],
= help: consider allocating on the heap with `vec![Some(""); 20_000_000].into_boxed_slice()` = help: consider allocating on the heap with `vec![Some(""); 20_000_000].into_boxed_slice()`
error: allocating a local array larger than 512000 bytes error: allocating a local array larger than 512000 bytes
--> $DIR/large_stack_arrays.rs:20:9 --> $DIR/large_stack_arrays.rs:26:9
| |
LL | [E::T(0); 5000], LL | [E::T(0); 5000],
| ^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^

View File

@@ -274,7 +274,7 @@ impl AsyncLen {
} }
pub async fn len(&self) -> usize { pub async fn len(&self) -> usize {
if self.async_task().await { 0 } else { 1 } usize::from(!self.async_task().await)
} }
pub async fn is_empty(&self) -> bool { pub async fn is_empty(&self) -> bool {

View File

@@ -57,3 +57,9 @@ fn check_expect() {
#[expect(clippy::nonminimal_bool)] #[expect(clippy::nonminimal_bool)]
let _ = !!a; let _ = !!a;
} }
fn issue9428() {
if matches!(true, true) && true {
println!("foo");
}
}

View File

@@ -107,5 +107,11 @@ LL | let _ = !(a == b || c == d);
LL | let _ = a != b && c != d; LL | let _ = a != b && c != d;
| ~~~~~~~~~~~~~~~~ | ~~~~~~~~~~~~~~~~
error: aborting due to 12 previous errors error: this boolean expression can be simplified
--> $DIR/nonminimal_bool.rs:62:8
|
LL | if matches!(true, true) && true {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(true, true)`
error: aborting due to 13 previous errors

View File

@@ -1,56 +0,0 @@
// run-rustfix
#![allow(unused_must_use)]
#![allow(named_arguments_used_positionally)] // Unstable at time of writing.
#![warn(clippy::positional_named_format_parameters)]
use std::io::Write;
fn main() {
let mut v = Vec::new();
let hello = "Hello";
println!("{hello:.foo$}", foo = 2);
writeln!(v, "{hello:.foo$}", foo = 2);
// Warnings
println!("{zero} {one:?}", zero = 0, one = 1);
println!("This is a test {zero} {one:?}", zero = 0, one = 1);
println!("Hello {one} is {two:.zero$}", zero = 5, one = hello, two = 0.01);
println!("Hello {one:zero$}!", zero = 5, one = 1);
println!("Hello {zero:one$}!", zero = 4, one = 1);
println!("Hello {zero:0one$}!", zero = 4, one = 1);
println!("Hello is {one:.zero$}", zero = 5, one = 0.01);
println!("Hello is {one:<6.zero$}", zero = 5, one = 0.01);
println!("{zero}, `{two:>8.one$}` has 3", zero = hello, one = 3, two = hello);
println!("Hello {one} is {two:.zero$}", zero = 5, one = hello, two = 0.01);
println!("Hello {world} {world}!", world = 5);
writeln!(v, "{zero} {one:?}", zero = 0, one = 1);
writeln!(v, "This is a test {zero} {one:?}", zero = 0, one = 1);
writeln!(v, "Hello {one} is {two:.zero$}", zero = 5, one = hello, two = 0.01);
writeln!(v, "Hello {one:zero$}!", zero = 4, one = 1);
writeln!(v, "Hello {zero:one$}!", zero = 4, one = 1);
writeln!(v, "Hello {zero:0one$}!", zero = 4, one = 1);
writeln!(v, "Hello is {one:.zero$}", zero = 3, one = 0.01);
writeln!(v, "Hello is {one:<6.zero$}", zero = 2, one = 0.01);
writeln!(v, "{zero}, `{two:>8.one$}` has 3", zero = hello, one = 3, two = hello);
writeln!(v, "Hello {one} is {two:.zero$}", zero = 1, one = hello, two = 0.01);
writeln!(v, "Hello {world} {world}!", world = 0);
// Tests from other files
println!("{w:w$}", w = 1);
println!("{p:.p$}", p = 1);
println!("{v}", v = 1);
println!("{v:v$}", v = 1);
println!("{v:v$}", v = 1);
println!("{v:v$.v$}", v = 1);
println!("{v:v$.v$}", v = 1);
println!("{v:v$.v$}", v = 1);
println!("{v:v$.v$}", v = 1);
println!("{v:v$.v$}", v = 1);
println!("{v:v$.v$}", v = 1);
println!("{v:v$.v$}", v = 1);
println!("{w:w$}", w = 1);
println!("{p:.p$}", p = 1);
println!("{:p$.w$}", 1, w = 1, p = 1);
}

View File

@@ -1,56 +0,0 @@
// run-rustfix
#![allow(unused_must_use)]
#![allow(named_arguments_used_positionally)] // Unstable at time of writing.
#![warn(clippy::positional_named_format_parameters)]
use std::io::Write;
fn main() {
let mut v = Vec::new();
let hello = "Hello";
println!("{hello:.foo$}", foo = 2);
writeln!(v, "{hello:.foo$}", foo = 2);
// Warnings
println!("{} {1:?}", zero = 0, one = 1);
println!("This is a test { } {000001:?}", zero = 0, one = 1);
println!("Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01);
println!("Hello {1:0$}!", zero = 5, one = 1);
println!("Hello {0:1$}!", zero = 4, one = 1);
println!("Hello {0:01$}!", zero = 4, one = 1);
println!("Hello is {1:.*}", zero = 5, one = 0.01);
println!("Hello is {:<6.*}", zero = 5, one = 0.01);
println!("{}, `{two:>8.*}` has 3", zero = hello, one = 3, two = hello);
println!("Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01);
println!("Hello {world} {}!", world = 5);
writeln!(v, "{} {1:?}", zero = 0, one = 1);
writeln!(v, "This is a test { } {000001:?}", zero = 0, one = 1);
writeln!(v, "Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01);
writeln!(v, "Hello {1:0$}!", zero = 4, one = 1);
writeln!(v, "Hello {0:1$}!", zero = 4, one = 1);
writeln!(v, "Hello {0:01$}!", zero = 4, one = 1);
writeln!(v, "Hello is {1:.*}", zero = 3, one = 0.01);
writeln!(v, "Hello is {:<6.*}", zero = 2, one = 0.01);
writeln!(v, "{}, `{two:>8.*}` has 3", zero = hello, one = 3, two = hello);
writeln!(v, "Hello {1} is {2:.0$}", zero = 1, one = hello, two = 0.01);
writeln!(v, "Hello {world} {}!", world = 0);
// Tests from other files
println!("{:w$}", w = 1);
println!("{:.p$}", p = 1);
println!("{}", v = 1);
println!("{:0$}", v = 1);
println!("{0:0$}", v = 1);
println!("{:0$.0$}", v = 1);
println!("{0:0$.0$}", v = 1);
println!("{0:0$.v$}", v = 1);
println!("{0:v$.0$}", v = 1);
println!("{v:0$.0$}", v = 1);
println!("{v:v$.0$}", v = 1);
println!("{v:0$.v$}", v = 1);
println!("{:w$}", w = 1);
println!("{:.p$}", p = 1);
println!("{:p$.w$}", 1, w = 1, p = 1);
}

View File

@@ -1,418 +0,0 @@
error: named parameter zero is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:16:16
|
LL | println!("{} {1:?}", zero = 0, one = 1);
| ^ help: replace it with: `zero`
|
= note: `-D clippy::positional-named-format-parameters` implied by `-D warnings`
error: named parameter one is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:16:19
|
LL | println!("{} {1:?}", zero = 0, one = 1);
| ^ help: replace it with: `one`
error: named parameter zero is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:17:31
|
LL | println!("This is a test { } {000001:?}", zero = 0, one = 1);
| ^ help: replace it with: `zero`
error: named parameter one is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:17:35
|
LL | println!("This is a test { } {000001:?}", zero = 0, one = 1);
| ^^^^^^ help: replace it with: `one`
error: named parameter zero is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:18:32
|
LL | println!("Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01);
| ^ help: replace it with: `zero`
error: named parameter one is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:18:22
|
LL | println!("Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01);
| ^ help: replace it with: `one`
error: named parameter two is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:18:29
|
LL | println!("Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01);
| ^ help: replace it with: `two`
error: named parameter zero is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:19:24
|
LL | println!("Hello {1:0$}!", zero = 5, one = 1);
| ^ help: replace it with: `zero`
error: named parameter one is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:19:22
|
LL | println!("Hello {1:0$}!", zero = 5, one = 1);
| ^ help: replace it with: `one`
error: named parameter zero is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:20:22
|
LL | println!("Hello {0:1$}!", zero = 4, one = 1);
| ^ help: replace it with: `zero`
error: named parameter one is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:20:24
|
LL | println!("Hello {0:1$}!", zero = 4, one = 1);
| ^ help: replace it with: `one`
error: named parameter zero is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:21:22
|
LL | println!("Hello {0:01$}!", zero = 4, one = 1);
| ^ help: replace it with: `zero`
error: named parameter one is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:21:25
|
LL | println!("Hello {0:01$}!", zero = 4, one = 1);
| ^ help: replace it with: `one`
error: named parameter zero is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:22:28
|
LL | println!("Hello is {1:.*}", zero = 5, one = 0.01);
| ^ help: replace it with: `zero$`
error: named parameter one is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:22:25
|
LL | println!("Hello is {1:.*}", zero = 5, one = 0.01);
| ^ help: replace it with: `one`
error: named parameter zero is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:23:29
|
LL | println!("Hello is {:<6.*}", zero = 5, one = 0.01);
| ^ help: replace it with: `zero$`
error: named parameter one is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:23:25
|
LL | println!("Hello is {:<6.*}", zero = 5, one = 0.01);
| ^ help: replace it with: `one`
error: named parameter zero is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:24:16
|
LL | println!("{}, `{two:>8.*}` has 3", zero = hello, one = 3, two = hello);
| ^ help: replace it with: `zero`
error: named parameter one is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:24:28
|
LL | println!("{}, `{two:>8.*}` has 3", zero = hello, one = 3, two = hello);
| ^ help: replace it with: `one$`
error: named parameter zero is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:25:32
|
LL | println!("Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01);
| ^ help: replace it with: `zero`
error: named parameter one is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:25:22
|
LL | println!("Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01);
| ^ help: replace it with: `one`
error: named parameter two is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:25:29
|
LL | println!("Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01);
| ^ help: replace it with: `two`
error: named parameter world is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:26:30
|
LL | println!("Hello {world} {}!", world = 5);
| ^ help: replace it with: `world`
error: named parameter zero is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:28:19
|
LL | writeln!(v, "{} {1:?}", zero = 0, one = 1);
| ^ help: replace it with: `zero`
error: named parameter one is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:28:22
|
LL | writeln!(v, "{} {1:?}", zero = 0, one = 1);
| ^ help: replace it with: `one`
error: named parameter zero is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:29:34
|
LL | writeln!(v, "This is a test { } {000001:?}", zero = 0, one = 1);
| ^ help: replace it with: `zero`
error: named parameter one is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:29:38
|
LL | writeln!(v, "This is a test { } {000001:?}", zero = 0, one = 1);
| ^^^^^^ help: replace it with: `one`
error: named parameter zero is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:30:35
|
LL | writeln!(v, "Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01);
| ^ help: replace it with: `zero`
error: named parameter one is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:30:25
|
LL | writeln!(v, "Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01);
| ^ help: replace it with: `one`
error: named parameter two is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:30:32
|
LL | writeln!(v, "Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01);
| ^ help: replace it with: `two`
error: named parameter zero is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:31:27
|
LL | writeln!(v, "Hello {1:0$}!", zero = 4, one = 1);
| ^ help: replace it with: `zero`
error: named parameter one is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:31:25
|
LL | writeln!(v, "Hello {1:0$}!", zero = 4, one = 1);
| ^ help: replace it with: `one`
error: named parameter zero is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:32:25
|
LL | writeln!(v, "Hello {0:1$}!", zero = 4, one = 1);
| ^ help: replace it with: `zero`
error: named parameter one is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:32:27
|
LL | writeln!(v, "Hello {0:1$}!", zero = 4, one = 1);
| ^ help: replace it with: `one`
error: named parameter zero is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:33:25
|
LL | writeln!(v, "Hello {0:01$}!", zero = 4, one = 1);
| ^ help: replace it with: `zero`
error: named parameter one is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:33:28
|
LL | writeln!(v, "Hello {0:01$}!", zero = 4, one = 1);
| ^ help: replace it with: `one`
error: named parameter zero is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:34:31
|
LL | writeln!(v, "Hello is {1:.*}", zero = 3, one = 0.01);
| ^ help: replace it with: `zero$`
error: named parameter one is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:34:28
|
LL | writeln!(v, "Hello is {1:.*}", zero = 3, one = 0.01);
| ^ help: replace it with: `one`
error: named parameter zero is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:35:32
|
LL | writeln!(v, "Hello is {:<6.*}", zero = 2, one = 0.01);
| ^ help: replace it with: `zero$`
error: named parameter one is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:35:28
|
LL | writeln!(v, "Hello is {:<6.*}", zero = 2, one = 0.01);
| ^ help: replace it with: `one`
error: named parameter zero is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:36:19
|
LL | writeln!(v, "{}, `{two:>8.*}` has 3", zero = hello, one = 3, two = hello);
| ^ help: replace it with: `zero`
error: named parameter one is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:36:31
|
LL | writeln!(v, "{}, `{two:>8.*}` has 3", zero = hello, one = 3, two = hello);
| ^ help: replace it with: `one$`
error: named parameter zero is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:37:35
|
LL | writeln!(v, "Hello {1} is {2:.0$}", zero = 1, one = hello, two = 0.01);
| ^ help: replace it with: `zero`
error: named parameter one is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:37:25
|
LL | writeln!(v, "Hello {1} is {2:.0$}", zero = 1, one = hello, two = 0.01);
| ^ help: replace it with: `one`
error: named parameter two is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:37:32
|
LL | writeln!(v, "Hello {1} is {2:.0$}", zero = 1, one = hello, two = 0.01);
| ^ help: replace it with: `two`
error: named parameter world is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:38:33
|
LL | writeln!(v, "Hello {world} {}!", world = 0);
| ^ help: replace it with: `world`
error: named parameter w is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:41:16
|
LL | println!("{:w$}", w = 1);
| ^ help: replace it with: `w`
error: named parameter p is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:42:16
|
LL | println!("{:.p$}", p = 1);
| ^ help: replace it with: `p`
error: named parameter v is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:43:16
|
LL | println!("{}", v = 1);
| ^ help: replace it with: `v`
error: named parameter v is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:44:16
|
LL | println!("{:0$}", v = 1);
| ^ help: replace it with: `v`
error: named parameter v is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:44:17
|
LL | println!("{:0$}", v = 1);
| ^ help: replace it with: `v`
error: named parameter v is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:45:16
|
LL | println!("{0:0$}", v = 1);
| ^ help: replace it with: `v`
error: named parameter v is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:45:18
|
LL | println!("{0:0$}", v = 1);
| ^ help: replace it with: `v`
error: named parameter v is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:46:16
|
LL | println!("{:0$.0$}", v = 1);
| ^ help: replace it with: `v`
error: named parameter v is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:46:20
|
LL | println!("{:0$.0$}", v = 1);
| ^ help: replace it with: `v`
error: named parameter v is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:46:17
|
LL | println!("{:0$.0$}", v = 1);
| ^ help: replace it with: `v`
error: named parameter v is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:47:16
|
LL | println!("{0:0$.0$}", v = 1);
| ^ help: replace it with: `v`
error: named parameter v is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:47:21
|
LL | println!("{0:0$.0$}", v = 1);
| ^ help: replace it with: `v`
error: named parameter v is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:47:18
|
LL | println!("{0:0$.0$}", v = 1);
| ^ help: replace it with: `v`
error: named parameter v is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:48:16
|
LL | println!("{0:0$.v$}", v = 1);
| ^ help: replace it with: `v`
error: named parameter v is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:48:18
|
LL | println!("{0:0$.v$}", v = 1);
| ^ help: replace it with: `v`
error: named parameter v is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:49:16
|
LL | println!("{0:v$.0$}", v = 1);
| ^ help: replace it with: `v`
error: named parameter v is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:49:21
|
LL | println!("{0:v$.0$}", v = 1);
| ^ help: replace it with: `v`
error: named parameter v is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:50:21
|
LL | println!("{v:0$.0$}", v = 1);
| ^ help: replace it with: `v`
error: named parameter v is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:50:18
|
LL | println!("{v:0$.0$}", v = 1);
| ^ help: replace it with: `v`
error: named parameter v is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:51:21
|
LL | println!("{v:v$.0$}", v = 1);
| ^ help: replace it with: `v`
error: named parameter v is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:52:18
|
LL | println!("{v:0$.v$}", v = 1);
| ^ help: replace it with: `v`
error: named parameter w is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:53:16
|
LL | println!("{:w$}", w = 1);
| ^ help: replace it with: `w`
error: named parameter p is used as a positional parameter
--> $DIR/positional_named_format_parameters.rs:54:16
|
LL | println!("{:.p$}", p = 1);
| ^ help: replace it with: `p`
error: aborting due to 69 previous errors

View File

@@ -20,11 +20,13 @@ fn main() {
println!("{} of {:b} people know binary, the other half doesn't", 1, 2); println!("{} of {:b} people know binary, the other half doesn't", 1, 2);
println!("10 / 4 is {}", 2.5); println!("10 / 4 is {}", 2.5);
println!("2 + 1 = {}", 3); println!("2 + 1 = {}", 3);
println!("From expansion {}", stringify!(not a string literal));
// these should throw warnings // these should throw warnings
print!("Hello {}", "world"); print!("Hello {}", "world");
println!("Hello {} {}", world, "world"); println!("Hello {} {}", world, "world");
println!("Hello {}", "world"); println!("Hello {}", "world");
println!("{} {:.4}", "a literal", 5);
// positional args don't change the fact // positional args don't change the fact
// that we're using a literal -- this should // that we're using a literal -- this should

View File

@@ -1,5 +1,5 @@
error: literal with an empty format string error: literal with an empty format string
--> $DIR/print_literal.rs:25:24 --> $DIR/print_literal.rs:26:24
| |
LL | print!("Hello {}", "world"); LL | print!("Hello {}", "world");
| ^^^^^^^ | ^^^^^^^
@@ -12,7 +12,7 @@ LL + print!("Hello world");
| |
error: literal with an empty format string error: literal with an empty format string
--> $DIR/print_literal.rs:26:36 --> $DIR/print_literal.rs:27:36
| |
LL | println!("Hello {} {}", world, "world"); LL | println!("Hello {} {}", world, "world");
| ^^^^^^^ | ^^^^^^^
@@ -24,7 +24,7 @@ LL + println!("Hello {} world", world);
| |
error: literal with an empty format string error: literal with an empty format string
--> $DIR/print_literal.rs:27:26 --> $DIR/print_literal.rs:28:26
| |
LL | println!("Hello {}", "world"); LL | println!("Hello {}", "world");
| ^^^^^^^ | ^^^^^^^
@@ -36,7 +36,19 @@ LL + println!("Hello world");
| |
error: literal with an empty format string error: literal with an empty format string
--> $DIR/print_literal.rs:32:25 --> $DIR/print_literal.rs:29:26
|
LL | println!("{} {:.4}", "a literal", 5);
| ^^^^^^^^^^^
|
help: try this
|
LL - println!("{} {:.4}", "a literal", 5);
LL + println!("a literal {:.4}", 5);
|
error: literal with an empty format string
--> $DIR/print_literal.rs:34:25
| |
LL | println!("{0} {1}", "hello", "world"); LL | println!("{0} {1}", "hello", "world");
| ^^^^^^^ | ^^^^^^^
@@ -48,7 +60,7 @@ LL + println!("hello {1}", "world");
| |
error: literal with an empty format string error: literal with an empty format string
--> $DIR/print_literal.rs:32:34 --> $DIR/print_literal.rs:34:34
| |
LL | println!("{0} {1}", "hello", "world"); LL | println!("{0} {1}", "hello", "world");
| ^^^^^^^ | ^^^^^^^
@@ -60,19 +72,7 @@ LL + println!("{0} world", "hello");
| |
error: literal with an empty format string error: literal with an empty format string
--> $DIR/print_literal.rs:33:25 --> $DIR/print_literal.rs:35:34
|
LL | println!("{1} {0}", "hello", "world");
| ^^^^^^^
|
help: try this
|
LL - println!("{1} {0}", "hello", "world");
LL + println!("{1} hello", "world");
|
error: literal with an empty format string
--> $DIR/print_literal.rs:33:34
| |
LL | println!("{1} {0}", "hello", "world"); LL | println!("{1} {0}", "hello", "world");
| ^^^^^^^ | ^^^^^^^
@@ -84,10 +84,22 @@ LL + println!("world {0}", "hello");
| |
error: literal with an empty format string error: literal with an empty format string
--> $DIR/print_literal.rs:36:29 --> $DIR/print_literal.rs:35:25
|
LL | println!("{1} {0}", "hello", "world");
| ^^^^^^^
|
help: try this
|
LL - println!("{1} {0}", "hello", "world");
LL + println!("{1} hello", "world");
|
error: literal with an empty format string
--> $DIR/print_literal.rs:38:35
| |
LL | println!("{foo} {bar}", foo = "hello", bar = "world"); LL | println!("{foo} {bar}", foo = "hello", bar = "world");
| ^^^^^^^^^^^^^ | ^^^^^^^
| |
help: try this help: try this
| |
@@ -96,10 +108,10 @@ LL + println!("hello {bar}", bar = "world");
| |
error: literal with an empty format string error: literal with an empty format string
--> $DIR/print_literal.rs:36:44 --> $DIR/print_literal.rs:38:50
| |
LL | println!("{foo} {bar}", foo = "hello", bar = "world"); LL | println!("{foo} {bar}", foo = "hello", bar = "world");
| ^^^^^^^^^^^^^ | ^^^^^^^
| |
help: try this help: try this
| |
@@ -108,22 +120,10 @@ LL + println!("{foo} world", foo = "hello");
| |
error: literal with an empty format string error: literal with an empty format string
--> $DIR/print_literal.rs:37:29 --> $DIR/print_literal.rs:39:50
| |
LL | println!("{bar} {foo}", foo = "hello", bar = "world"); LL | println!("{bar} {foo}", foo = "hello", bar = "world");
| ^^^^^^^^^^^^^ | ^^^^^^^
|
help: try this
|
LL - println!("{bar} {foo}", foo = "hello", bar = "world");
LL + println!("{bar} hello", bar = "world");
|
error: literal with an empty format string
--> $DIR/print_literal.rs:37:44
|
LL | println!("{bar} {foo}", foo = "hello", bar = "world");
| ^^^^^^^^^^^^^
| |
help: try this help: try this
| |
@@ -131,5 +131,17 @@ LL - println!("{bar} {foo}", foo = "hello", bar = "world");
LL + println!("world {foo}", foo = "hello"); LL + println!("world {foo}", foo = "hello");
| |
error: aborting due to 11 previous errors error: literal with an empty format string
--> $DIR/print_literal.rs:39:35
|
LL | println!("{bar} {foo}", foo = "hello", bar = "world");
| ^^^^^^^
|
help: try this
|
LL - println!("{bar} {foo}", foo = "hello", bar = "world");
LL + println!("{bar} hello", bar = "world");
|
error: aborting due to 12 previous errors

View File

@@ -48,5 +48,13 @@ fn main() {
print!("\r\n"); print!("\r\n");
print!("foo\r\n"); print!("foo\r\n");
print!("\\r\n"); //~ ERROR print!("\\r\n"); //~ ERROR
print!("foo\rbar\n") // ~ ERROR print!("foo\rbar\n");
// Ignore expanded format strings
macro_rules! newline {
() => {
"\n"
};
}
print!(newline!());
} }

View File

@@ -83,7 +83,7 @@ LL | | );
help: use `println!` instead help: use `println!` instead
| |
LL ~ println!( LL ~ println!(
LL ~ "" LL ~
| |
error: using `print!()` with a format string that ends in a single newline error: using `print!()` with a format string that ends in a single newline
@@ -98,7 +98,7 @@ LL | | );
help: use `println!` instead help: use `println!` instead
| |
LL ~ println!( LL ~ println!(
LL ~ r"" LL ~
| |
error: using `print!()` with a format string that ends in a single newline error: using `print!()` with a format string that ends in a single newline
@@ -113,17 +113,5 @@ LL - print!("/r/n"); //~ ERROR
LL + println!("/r"); //~ ERROR LL + println!("/r"); //~ ERROR
| |
error: using `print!()` with a format string that ends in a single newline error: aborting due to 9 previous errors
--> $DIR/print_with_newline.rs:51:5
|
LL | print!("foo/rbar/n") // ~ ERROR
| ^^^^^^^^^^^^^^^^^^^^
|
help: use `println!` instead
|
LL - print!("foo/rbar/n") // ~ ERROR
LL + println!("foo/rbar") // ~ ERROR
|
error: aborting due to 10 previous errors

View File

@@ -1,28 +1,36 @@
error: using `println!("")` error: empty string literal in `println!`
--> $DIR/println_empty_string.rs:6:5 --> $DIR/println_empty_string.rs:6:5
| |
LL | println!(""); LL | println!("");
| ^^^^^^^^^^^^ help: replace it with: `println!()` | ^^^^^^^^^--^
| |
| help: remove the empty string
| |
= note: `-D clippy::println-empty-string` implied by `-D warnings` = note: `-D clippy::println-empty-string` implied by `-D warnings`
error: using `println!("")` error: empty string literal in `println!`
--> $DIR/println_empty_string.rs:9:14 --> $DIR/println_empty_string.rs:9:14
| |
LL | _ => println!(""), LL | _ => println!(""),
| ^^^^^^^^^^^^ help: replace it with: `println!()` | ^^^^^^^^^--^
| |
| help: remove the empty string
error: using `eprintln!("")` error: empty string literal in `eprintln!`
--> $DIR/println_empty_string.rs:13:5 --> $DIR/println_empty_string.rs:13:5
| |
LL | eprintln!(""); LL | eprintln!("");
| ^^^^^^^^^^^^^ help: replace it with: `eprintln!()` | ^^^^^^^^^^--^
| |
| help: remove the empty string
error: using `eprintln!("")` error: empty string literal in `eprintln!`
--> $DIR/println_empty_string.rs:16:14 --> $DIR/println_empty_string.rs:16:14
| |
LL | _ => eprintln!(""), LL | _ => eprintln!(""),
| ^^^^^^^^^^^^^ help: replace it with: `eprintln!()` | ^^^^^^^^^^--^
| |
| help: remove the empty string
error: aborting due to 4 previous errors error: aborting due to 4 previous errors

View File

@@ -32,6 +32,7 @@
#![allow(invalid_value)] #![allow(invalid_value)]
#![allow(enum_intrinsics_non_enums)] #![allow(enum_intrinsics_non_enums)]
#![allow(non_fmt_panics)] #![allow(non_fmt_panics)]
#![allow(named_arguments_used_positionally)]
#![allow(temporary_cstring_as_ptr)] #![allow(temporary_cstring_as_ptr)]
#![allow(unknown_lints)] #![allow(unknown_lints)]
#![allow(unused_labels)] #![allow(unused_labels)]
@@ -69,6 +70,7 @@
#![warn(invalid_value)] #![warn(invalid_value)]
#![warn(enum_intrinsics_non_enums)] #![warn(enum_intrinsics_non_enums)]
#![warn(non_fmt_panics)] #![warn(non_fmt_panics)]
#![warn(named_arguments_used_positionally)]
#![warn(temporary_cstring_as_ptr)] #![warn(temporary_cstring_as_ptr)]
#![warn(unknown_lints)] #![warn(unknown_lints)]
#![warn(unused_labels)] #![warn(unused_labels)]

View File

@@ -32,6 +32,7 @@
#![allow(invalid_value)] #![allow(invalid_value)]
#![allow(enum_intrinsics_non_enums)] #![allow(enum_intrinsics_non_enums)]
#![allow(non_fmt_panics)] #![allow(non_fmt_panics)]
#![allow(named_arguments_used_positionally)]
#![allow(temporary_cstring_as_ptr)] #![allow(temporary_cstring_as_ptr)]
#![allow(unknown_lints)] #![allow(unknown_lints)]
#![allow(unused_labels)] #![allow(unused_labels)]
@@ -69,6 +70,7 @@
#![warn(clippy::invalid_ref)] #![warn(clippy::invalid_ref)]
#![warn(clippy::mem_discriminant_non_enum)] #![warn(clippy::mem_discriminant_non_enum)]
#![warn(clippy::panic_params)] #![warn(clippy::panic_params)]
#![warn(clippy::positional_named_format_parameters)]
#![warn(clippy::temporary_cstring_as_ptr)] #![warn(clippy::temporary_cstring_as_ptr)]
#![warn(clippy::unknown_clippy_lints)] #![warn(clippy::unknown_clippy_lints)]
#![warn(clippy::unused_label)] #![warn(clippy::unused_label)]

View File

@@ -1,5 +1,5 @@
error: lint `clippy::blacklisted_name` has been renamed to `clippy::disallowed_names` error: lint `clippy::blacklisted_name` has been renamed to `clippy::disallowed_names`
--> $DIR/rename.rs:38:9 --> $DIR/rename.rs:39:9
| |
LL | #![warn(clippy::blacklisted_name)] LL | #![warn(clippy::blacklisted_name)]
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_names` | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_names`
@@ -7,220 +7,226 @@ LL | #![warn(clippy::blacklisted_name)]
= note: `-D renamed-and-removed-lints` implied by `-D warnings` = note: `-D renamed-and-removed-lints` implied by `-D warnings`
error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_if_conditions` error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_if_conditions`
--> $DIR/rename.rs:39:9 --> $DIR/rename.rs:40:9
| |
LL | #![warn(clippy::block_in_if_condition_expr)] LL | #![warn(clippy::block_in_if_condition_expr)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions`
error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_if_conditions` error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_if_conditions`
--> $DIR/rename.rs:40:9 --> $DIR/rename.rs:41:9
| |
LL | #![warn(clippy::block_in_if_condition_stmt)] LL | #![warn(clippy::block_in_if_condition_stmt)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions`
error: lint `clippy::box_vec` has been renamed to `clippy::box_collection` error: lint `clippy::box_vec` has been renamed to `clippy::box_collection`
--> $DIR/rename.rs:41:9 --> $DIR/rename.rs:42:9
| |
LL | #![warn(clippy::box_vec)] LL | #![warn(clippy::box_vec)]
| ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection` | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection`
error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes` error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes`
--> $DIR/rename.rs:42:9 --> $DIR/rename.rs:43:9
| |
LL | #![warn(clippy::const_static_lifetime)] LL | #![warn(clippy::const_static_lifetime)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes`
error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity` error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity`
--> $DIR/rename.rs:43:9 --> $DIR/rename.rs:44:9
| |
LL | #![warn(clippy::cyclomatic_complexity)] LL | #![warn(clippy::cyclomatic_complexity)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity`
error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods` error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods`
--> $DIR/rename.rs:44:9 --> $DIR/rename.rs:45:9
| |
LL | #![warn(clippy::disallowed_method)] LL | #![warn(clippy::disallowed_method)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods` | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods`
error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types` error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types`
--> $DIR/rename.rs:45:9 --> $DIR/rename.rs:46:9
| |
LL | #![warn(clippy::disallowed_type)] LL | #![warn(clippy::disallowed_type)]
| ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types` | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types`
error: lint `clippy::eval_order_dependence` has been renamed to `clippy::mixed_read_write_in_expression` error: lint `clippy::eval_order_dependence` has been renamed to `clippy::mixed_read_write_in_expression`
--> $DIR/rename.rs:46:9 --> $DIR/rename.rs:47:9
| |
LL | #![warn(clippy::eval_order_dependence)] LL | #![warn(clippy::eval_order_dependence)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression`
error: lint `clippy::for_loop_over_option` has been renamed to `clippy::for_loops_over_fallibles` error: lint `clippy::for_loop_over_option` has been renamed to `clippy::for_loops_over_fallibles`
--> $DIR/rename.rs:47:9 --> $DIR/rename.rs:48:9
| |
LL | #![warn(clippy::for_loop_over_option)] LL | #![warn(clippy::for_loop_over_option)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::for_loops_over_fallibles` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::for_loops_over_fallibles`
error: lint `clippy::for_loop_over_result` has been renamed to `clippy::for_loops_over_fallibles` error: lint `clippy::for_loop_over_result` has been renamed to `clippy::for_loops_over_fallibles`
--> $DIR/rename.rs:48:9 --> $DIR/rename.rs:49:9
| |
LL | #![warn(clippy::for_loop_over_result)] LL | #![warn(clippy::for_loop_over_result)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::for_loops_over_fallibles` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::for_loops_over_fallibles`
error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion` error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion`
--> $DIR/rename.rs:49:9 --> $DIR/rename.rs:50:9
| |
LL | #![warn(clippy::identity_conversion)] LL | #![warn(clippy::identity_conversion)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion`
error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok` error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok`
--> $DIR/rename.rs:50:9 --> $DIR/rename.rs:51:9
| |
LL | #![warn(clippy::if_let_some_result)] LL | #![warn(clippy::if_let_some_result)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok` | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok`
error: lint `clippy::logic_bug` has been renamed to `clippy::overly_complex_bool_expr` error: lint `clippy::logic_bug` has been renamed to `clippy::overly_complex_bool_expr`
--> $DIR/rename.rs:51:9 --> $DIR/rename.rs:52:9
| |
LL | #![warn(clippy::logic_bug)] LL | #![warn(clippy::logic_bug)]
| ^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::overly_complex_bool_expr` | ^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::overly_complex_bool_expr`
error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default` error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default`
--> $DIR/rename.rs:52:9 --> $DIR/rename.rs:53:9
| |
LL | #![warn(clippy::new_without_default_derive)] LL | #![warn(clippy::new_without_default_derive)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default`
error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map` error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map`
--> $DIR/rename.rs:53:9 --> $DIR/rename.rs:54:9
| |
LL | #![warn(clippy::option_and_then_some)] LL | #![warn(clippy::option_and_then_some)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map`
error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used` error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used`
--> $DIR/rename.rs:54:9 --> $DIR/rename.rs:55:9
| |
LL | #![warn(clippy::option_expect_used)] LL | #![warn(clippy::option_expect_used)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used`
error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or` error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or`
--> $DIR/rename.rs:55:9 --> $DIR/rename.rs:56:9
| |
LL | #![warn(clippy::option_map_unwrap_or)] LL | #![warn(clippy::option_map_unwrap_or)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or`
--> $DIR/rename.rs:56:9 --> $DIR/rename.rs:57:9
| |
LL | #![warn(clippy::option_map_unwrap_or_else)] LL | #![warn(clippy::option_map_unwrap_or_else)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used` error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used`
--> $DIR/rename.rs:57:9 --> $DIR/rename.rs:58:9
| |
LL | #![warn(clippy::option_unwrap_used)] LL | #![warn(clippy::option_unwrap_used)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used`
error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow` error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow`
--> $DIR/rename.rs:58:9 --> $DIR/rename.rs:59:9
| |
LL | #![warn(clippy::ref_in_deref)] LL | #![warn(clippy::ref_in_deref)]
| ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow` | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow`
error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used` error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used`
--> $DIR/rename.rs:59:9 --> $DIR/rename.rs:60:9
| |
LL | #![warn(clippy::result_expect_used)] LL | #![warn(clippy::result_expect_used)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used`
error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or`
--> $DIR/rename.rs:60:9 --> $DIR/rename.rs:61:9
| |
LL | #![warn(clippy::result_map_unwrap_or_else)] LL | #![warn(clippy::result_map_unwrap_or_else)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used` error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used`
--> $DIR/rename.rs:61:9 --> $DIR/rename.rs:62:9
| |
LL | #![warn(clippy::result_unwrap_used)] LL | #![warn(clippy::result_unwrap_used)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used`
error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str` error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str`
--> $DIR/rename.rs:62:9 --> $DIR/rename.rs:63:9
| |
LL | #![warn(clippy::single_char_push_str)] LL | #![warn(clippy::single_char_push_str)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str`
error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions` error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions`
--> $DIR/rename.rs:63:9 --> $DIR/rename.rs:64:9
| |
LL | #![warn(clippy::stutter)] LL | #![warn(clippy::stutter)]
| ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions` | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions`
error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl` error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl`
--> $DIR/rename.rs:64:9 --> $DIR/rename.rs:65:9
| |
LL | #![warn(clippy::to_string_in_display)] LL | #![warn(clippy::to_string_in_display)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl`
error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters` error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters`
--> $DIR/rename.rs:65:9 --> $DIR/rename.rs:66:9
| |
LL | #![warn(clippy::zero_width_space)] LL | #![warn(clippy::zero_width_space)]
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters` | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters`
error: lint `clippy::drop_bounds` has been renamed to `drop_bounds` error: lint `clippy::drop_bounds` has been renamed to `drop_bounds`
--> $DIR/rename.rs:66:9 --> $DIR/rename.rs:67:9
| |
LL | #![warn(clippy::drop_bounds)] LL | #![warn(clippy::drop_bounds)]
| ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds` | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds`
error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter`
--> $DIR/rename.rs:67:9 --> $DIR/rename.rs:68:9
| |
LL | #![warn(clippy::into_iter_on_array)] LL | #![warn(clippy::into_iter_on_array)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter` | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter`
error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering` error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering`
--> $DIR/rename.rs:68:9 --> $DIR/rename.rs:69:9
| |
LL | #![warn(clippy::invalid_atomic_ordering)] LL | #![warn(clippy::invalid_atomic_ordering)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering`
error: lint `clippy::invalid_ref` has been renamed to `invalid_value` error: lint `clippy::invalid_ref` has been renamed to `invalid_value`
--> $DIR/rename.rs:69:9 --> $DIR/rename.rs:70:9
| |
LL | #![warn(clippy::invalid_ref)] LL | #![warn(clippy::invalid_ref)]
| ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value` | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value`
error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums` error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums`
--> $DIR/rename.rs:70:9 --> $DIR/rename.rs:71:9
| |
LL | #![warn(clippy::mem_discriminant_non_enum)] LL | #![warn(clippy::mem_discriminant_non_enum)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums`
error: lint `clippy::panic_params` has been renamed to `non_fmt_panics` error: lint `clippy::panic_params` has been renamed to `non_fmt_panics`
--> $DIR/rename.rs:71:9 --> $DIR/rename.rs:72:9
| |
LL | #![warn(clippy::panic_params)] LL | #![warn(clippy::panic_params)]
| ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics` | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics`
error: lint `clippy::positional_named_format_parameters` has been renamed to `named_arguments_used_positionally`
--> $DIR/rename.rs:73:9
|
LL | #![warn(clippy::positional_named_format_parameters)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `named_arguments_used_positionally`
error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr` error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr`
--> $DIR/rename.rs:72:9 --> $DIR/rename.rs:74:9
| |
LL | #![warn(clippy::temporary_cstring_as_ptr)] LL | #![warn(clippy::temporary_cstring_as_ptr)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr`
error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints` error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints`
--> $DIR/rename.rs:73:9 --> $DIR/rename.rs:75:9
| |
LL | #![warn(clippy::unknown_clippy_lints)] LL | #![warn(clippy::unknown_clippy_lints)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints`
error: lint `clippy::unused_label` has been renamed to `unused_labels` error: lint `clippy::unused_label` has been renamed to `unused_labels`
--> $DIR/rename.rs:74:9 --> $DIR/rename.rs:76:9
| |
LL | #![warn(clippy::unused_label)] LL | #![warn(clippy::unused_label)]
| ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels` | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels`
error: aborting due to 37 previous errors error: aborting due to 38 previous errors

View File

@@ -417,3 +417,12 @@ mod issue_9351 {
predicates_are_satisfied(id("abc".to_string())); predicates_are_satisfied(id("abc".to_string()));
} }
} }
mod issue_9504 {
#![allow(dead_code)]
async fn foo<S: AsRef<str>>(_: S) {}
async fn bar() {
foo(std::path::PathBuf::new().to_string_lossy().to_string()).await;
}
}

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