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
[`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_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_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

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/
### Rust Analyzer
As of [#6869][6869], [`rust-analyzer`][ra_homepage] can understand that Clippy uses compiler-internals
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
For [`rust-analyzer`][ra_homepage] to work correctly make sure that in the `rust-analyzer` configuration you set
```json
{ "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
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/
[6869]: https://github.com/rust-lang/rust-clippy/pull/6869
## 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:
```rust
#![allow(unused)]
#![warn(clippy::foo_functions)]
// 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.
```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;
impl LateLintPass<'_> for MyStructLint {
@ -143,13 +144,6 @@ impl LateLintPass<'_> for MyStructLint {
.map_or(false, |id| implements_trait(cx, ty, id, &[])) {
// `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
```rust
#[macro_use]
extern crate a_crate_with_macros;
use rustc_middle::lint::in_external_macro;
use a_crate_with_macros::foo;
// `foo` is defined in `a_crate_with_macros`
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 {
let mut contents = format!(
indoc! {"
#![allow(unused)]
#![warn(clippy::{})]
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_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
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'))
)
&& !in_external_macro(cx.sess(), span)
{
span_lint_and_then(
cx,

View file

@ -1,9 +1,9 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
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::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::{is_expr_final_block_expr, path_res};
use rustc_errors::Applicability;
use rustc_hir::def::Res;
use rustc_hir::{Expr, ExprKind};
@ -58,6 +58,7 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnResultStates {
return;
}
}
let semicolon = if is_expr_final_block_expr(cx.tcx, e) {";"} else {""};
let mut app = Applicability::MachineApplicable;
match method_segment.ident.as_str() {
"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`",
"replace with",
format!(
"{}.unwrap()",
snippet_with_context(cx, recv.span, condition.span.ctxt(), "..", &mut app).0
"{}.unwrap(){}",
snippet_with_context(cx, recv.span, condition.span.ctxt(), "..", &mut app).0,
semicolon
),
app,
);
@ -82,8 +84,9 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnResultStates {
"called `assert!` with `Result::is_err`",
"replace with",
format!(
"{}.unwrap_err()",
snippet_with_context(cx, recv.span, condition.span.ctxt(), "..", &mut app).0
"{}.unwrap_err(){}",
snippet_with_context(cx, recv.span, condition.span.ctxt(), "..", &mut app).0,
semicolon
),
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 {
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_lint::{LateContext, LateLintPass};
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;
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
&& let Some(then_lit) = int_literal(then)
&& 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 snippet = snippet_block_with_applicability(ctx, check.span, "..", None, &mut applicability);
let snippet_with_braces = {
let need_parens = should_have_parentheses(check);
let (left_paren, right_paren) = if need_parens {("(", ")")} else {("", "")};
format!("{left_paren}{snippet}{right_paren}")
let snippet = {
let mut sugg = Sugg::hir_with_applicability(ctx, check, "..", &mut applicability);
if inverted {
sugg = !sugg;
}
sugg
};
let ty = ctx.typeck_results().expr_ty(then_lit); // then and else must be of same type
let suggestion = {
let wrap_in_curly = is_else_clause(ctx.tcx, expr);
let (left_curly, right_curly) = if wrap_in_curly {("{", "}")} else {("", "")};
format!(
"{left_curly}{ty}::from({snippet}){right_curly}"
)
let mut s = Sugg::NonParen(format!("{ty}::from({snippet})").into());
if wrap_in_curly {
s = s.blockify();
}
s
}; // 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,
BOOL_TO_INT_WITH_IF,
expr.span,
@ -87,7 +102,7 @@ fn check_if_else<'tcx>(ctx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx
suggestion,
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
}
}
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) => {
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);
},
}

View file

@ -297,13 +297,10 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
if !is_lint_allowed(cx, EXPLICIT_DEREF_METHODS, expr.hir_id)
&& position.lint_explicit_deref() =>
{
let ty_changed_count = usize::from(!deref_method_same_type(expr_ty, typeck.expr_ty(sub_expr)));
self.state = Some((
State::DerefMethod {
ty_changed_count: if deref_method_same_type(expr_ty, typeck.expr_ty(sub_expr)) {
0
} else {
1
},
ty_changed_count,
is_final_ufcs: matches!(expr.kind, ExprKind::Call(..)),
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 rustc_errors::Applicability;
use rustc_hir::{
def::{DefKind, Res},
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)),
_ => false,
};
if should_emit {
let path_string = cx.tcx.def_path_str(adt_def.did());
span_lint_and_help(
let struct_span = cx.tcx.def_span(adt_def.did());
span_lint_and_then(
cx,
DERIVABLE_IMPLS,
item.span,
"this `impl` can be derived",
None,
&format!("try annotating `{}` with `#[derive(Default)]`", path_string),
|diag| {
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;
if_chain! {
if format_args.format_string.parts == [kw::Empty];
if arg.format.is_default();
if match cx.typeck_results().expr_ty(value).peel_refs().kind() {
ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(sym::String, adt.did()),
ty::Str => true,
_ => false,
};
if !arg.format.has_string_formatting();
then {
let is_new_string = match value.kind {
ExprKind::Binary(..) => true,

View file

@ -77,7 +77,7 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs {
if let ExpnKind::Macro(_, name) = outermost_expn_data.kind;
then {
for arg in &format_args.args {
if arg.format.has_string_formatting() {
if !arg.format.is_default() {
continue;
}
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::source::snippet;
use if_chain::if_chain;
use rustc_hir::{Expr, ExprKind};
use rustc_hir::{Expr, ExprKind, Item, ItemKind, Node};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::layout::LayoutOf;
use rustc_middle::ty::{self, ConstKind};
@ -39,29 +38,28 @@ impl_lint_pass!(LargeStackArrays => [LARGE_STACK_ARRAYS]);
impl<'tcx> LateLintPass<'tcx> for LargeStackArrays {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
if_chain! {
if let ExprKind::Repeat(_, _) = expr.kind;
if let ty::Array(element_type, cst) = cx.typeck_results().expr_ty(expr).kind();
if let ConstKind::Value(ty::ValTree::Leaf(element_count)) = cst.kind();
if let Ok(element_count) = element_count.try_to_machine_usize(cx.tcx);
if let Ok(element_size) = cx.layout_of(*element_type).map(|l| l.size.bytes());
if self.maximum_allowed_size < element_count * element_size;
then {
span_lint_and_help(
cx,
LARGE_STACK_ARRAYS,
expr.span,
&format!(
"allocating a local array larger than {} bytes",
self.maximum_allowed_size
),
None,
&format!(
"consider allocating on the heap with `vec!{}.into_boxed_slice()`",
snippet(cx, expr.span, "[...]")
),
);
}
}
if let ExprKind::Repeat(_, _) = expr.kind
&& let ty::Array(element_type, cst) = cx.typeck_results().expr_ty(expr).kind()
&& let ConstKind::Value(ty::ValTree::Leaf(element_count)) = cst.kind()
&& 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())
&& !cx.tcx.hir().parent_iter(expr.hir_id)
.any(|(_, node)| matches!(node, Node::Item(Item { kind: ItemKind::Static(..), .. })))
&& self.maximum_allowed_size < element_count * element_size {
span_lint_and_help(
cx,
LARGE_STACK_ARRAYS,
expr.span,
&format!(
"allocating a local array larger than {} bytes",
self.maximum_allowed_size
),
None,
&format!(
"consider allocating on the heap with `vec!{}.into_boxed_slice()`",
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::ITER_CLONED_COLLECT),
LintId::of(methods::ITER_COUNT),
LintId::of(methods::ITER_KV_MAP),
LintId::of(methods::ITER_NEXT_SLICE),
LintId::of(methods::ITER_NTH),
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(vec::USELESS_VEC),
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::PRINT_LITERAL),
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::INSPECT_FOR_EACH),
LintId::of(methods::ITER_COUNT),
LintId::of(methods::ITER_KV_MAP),
LintId::of(methods::MANUAL_FILTER_MAP),
LintId::of(methods::MANUAL_FIND_MAP),
LintId::of(methods::MANUAL_SPLIT_ONCE),

View file

@ -313,6 +313,7 @@ store.register_lints(&[
methods::ITERATOR_STEP_BY_ZERO,
methods::ITER_CLONED_COLLECT,
methods::ITER_COUNT,
methods::ITER_KV_MAP,
methods::ITER_NEXT_SLICE,
methods::ITER_NTH,
methods::ITER_NTH_ZERO,
@ -595,7 +596,6 @@ store.register_lints(&[
vec_init_then_push::VEC_INIT_THEN_PUSH,
wildcard_imports::ENUM_GLOB_USE,
wildcard_imports::WILDCARD_IMPORTS,
write::POSITIONAL_NAMED_FORMAT_PARAMETERS,
write::PRINTLN_EMPTY_STRING,
write::PRINT_LITERAL,
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_OP_ASSIGN_IMPL),
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_mir_dataflow;
extern crate rustc_parse;
extern crate rustc_parse_format;
extern crate rustc_session;
extern crate rustc_span;
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 }));
}
@ -524,7 +522,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
#[cfg(feature = "internal")]
{
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;
}
}
@ -879,6 +877,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
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(empty_structs_with_brackets::EmptyStructsWithBrackets));
store.register_late_pass(|_| Box::new(unnecessary_owned_empty_strings::UnnecessaryOwnedEmptyStrings));

View file

@ -1,6 +1,6 @@
use super::ERR_EXPECT;
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 rustc_errors::Applicability;
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
if let Some(data_type) = get_data_type(cx, result_type);
// 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 {
span_lint_and_sugg(
@ -51,10 +51,3 @@ fn get_data_type<'a>(cx: &LateContext<'_>, ty: Ty<'a>) -> Option<Ty<'a>> {
_ => 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 iter_cloned_collect;
mod iter_count;
mod iter_kv_map;
mod iter_next_slice;
mod iter_nth;
mod iter_nth_zero;
@ -3036,6 +3037,37 @@ declare_clippy_lint! {
"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 {
avoid_breaking_exported_api: bool,
msrv: Option<RustcVersion>,
@ -3159,6 +3191,7 @@ impl_lint_pass!(Methods => [
UNNECESSARY_SORT_BY,
VEC_RESIZE_TO_ZERO,
VERBOSE_FILE_READS,
ITER_KV_MAP,
]);
/// Extracts a method call name, args, and `Span` of the method name.
@ -3498,6 +3531,9 @@ impl Methods {
(name @ ("map" | "map_err"), [m_arg]) => {
if name == "map" {
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 {
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::ty::{implements_trait, is_type_diagnostic_item};
use clippy_utils::ty::{has_debug_impl, is_type_diagnostic_item};
use if_chain::if_chain;
use rustc_hir as hir;
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);
let result_type = cx.typeck_results().expr_ty(recv);
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 {
span_lint_and_help(
@ -37,10 +37,3 @@ fn get_error_type<'a>(cx: &LateContext<'_>, ty: Ty<'a>) -> Option<Ty<'a>> {
_ => 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::{meets_msrv, msrvs};
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_lint::LateContext;
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
// `Target = T`.
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);
then {
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) => {
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();
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)

View file

@ -2,7 +2,7 @@ use rustc_ast::ast;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_lint::{EarlyContext, EarlyLintPass, Level, LintContext};
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::path::{Component, Path};
@ -79,7 +79,7 @@ impl EarlyLintPass for ModStyle {
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
// `[path, to]` but not foo
@ -90,7 +90,7 @@ impl EarlyLintPass for ModStyle {
// `{ foo => path/to/foo.rs, .. }
let mut file_map = FxHashMap::default();
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() {
lp
} 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",
braces: ("[", "]"),
),
macro_matcher!(
name: "matches",
braces: ("(", ")"),
),
]
.into_iter()
.collect::<FxHashMap<_, _>>();

View file

@ -42,27 +42,30 @@ impl ArithmeticSideEffects {
}
}
/// Checks assign operators (+=, -=, *=, /=) of integers in a non-constant environment that
/// won't overflow.
fn has_valid_assign_op(op: &Spanned<hir::BinOpKind>, rhs: &hir::Expr<'_>, rhs_refs: Ty<'_>) -> bool {
if !Self::is_literal_integer(rhs, rhs_refs) {
return false;
/// Assuming that `expr` is a literal integer, checks operators (+=, -=, *, /) in a
/// non-constant environment that won't overflow.
fn has_valid_op(op: &Spanned<hir::BinOpKind>, expr: &hir::Expr<'_>) -> bool {
if let hir::BinOpKind::Add | hir::BinOpKind::Sub = op.node
&& 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
&& let hir::ExprKind::Lit(ref lit) = rhs.kind
&& let ast::LitKind::Int(1, _) = lit.node
if let hir::BinOpKind::Div | hir::BinOpKind::Rem = op.node
&& let hir::ExprKind::Lit(ref lit) = expr.kind
&& !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;
}
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.
fn is_allowed_ty(&self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
self.allowed.contains(
@ -83,7 +86,8 @@ impl ArithmeticSideEffects {
}
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);
}
@ -115,13 +119,18 @@ impl ArithmeticSideEffects {
if self.is_allowed_ty(cx, lhs) || self.is_allowed_ty(cx, rhs) {
return;
}
let lhs_refs = cx.typeck_results().expr_ty(lhs).peel_refs();
let rhs_refs = cx.typeck_results().expr_ty(rhs).peel_refs();
let has_valid_assign_op = Self::has_valid_assign_op(op, rhs, rhs_refs);
if has_valid_assign_op || Self::has_valid_bin_op(lhs, lhs_refs, rhs, rhs_refs) {
return;
let has_valid_op = match (
Self::is_literal_integer(lhs, cx.typeck_results().expr_ty(lhs).peel_refs()),
Self::is_literal_integer(rhs, cx.typeck_results().expr_ty(rhs).peel_refs()),
) {
(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::mem_discriminant_non_enum", "enum_intrinsics_non_enums"),
("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::unknown_clippy_lints", "unknown_lints"),
("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::{Block, Expr, ExprKind, HirId, Local, Node, PatKind, PathSegment, StmtKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::hir::nested_filter::OnlyBodies;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;
@ -109,8 +110,14 @@ impl<'a, 'tcx> PeekableVisitor<'a, 'tcx> {
}
}
impl<'tcx> Visitor<'_> for PeekableVisitor<'_, 'tcx> {
fn visit_expr(&mut self, ex: &'_ Expr<'_>) {
impl<'tcx> Visitor<'tcx> for PeekableVisitor<'_, 'tcx> {
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 {
return;
}
@ -136,12 +143,11 @@ impl<'tcx> Visitor<'_> for PeekableVisitor<'_, 'tcx> {
return;
}
if args.iter().any(|arg| {
matches!(arg.kind, ExprKind::Path(_)) && arg_is_mut_peekable(self.cx, arg)
}) {
if args.iter().any(|arg| arg_is_mut_peekable(self.cx, arg)) {
self.found_peek_call = true;
return;
}
return;
},
// Catch anything taking a Peekable mutably
ExprKind::MethodCall(
@ -190,21 +196,21 @@ impl<'tcx> Visitor<'_> for PeekableVisitor<'_, 'tcx> {
Node::Local(Local { init: Some(init), .. }) => {
if arg_is_mut_peekable(self.cx, init) {
self.found_peek_call = true;
return;
}
break;
return;
},
Node::Stmt(stmt) => match stmt.kind {
StmtKind::Expr(_) | StmtKind::Semi(_) => {},
_ => {
self.found_peek_call = true;
return;
},
Node::Stmt(stmt) => {
match stmt.kind {
StmtKind::Local(_) | StmtKind::Item(_) => self.found_peek_call = true,
StmtKind::Expr(_) | StmtKind::Semi(_) => {},
}
return;
},
Node::Block(_) | Node::ExprField(_) => {},
_ => {
break;
return;
},
}
}

View file

@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
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 rustc_data_structures::fx::FxHashSet;
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";
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(_)) {
// skip over `ItemKind::OpaqueTy` in order to lint `foo() -> impl <..>`
return;
@ -103,6 +103,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
if parameters.as_ref().map_or(true, |params| {
!params.parenthesized && !params.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_)))
});
if !is_from_proc_macro(cx, item); // expensive, should be last check
then {
StackItem::Check {
impl_id: item.def_id,
@ -213,9 +214,6 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
hir_ty_to_ty(cx.tcx, hir_ty)
};
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 {
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);
for row in 0..rows {
write!(msg, "\n").unwrap();
writeln!(msg).unwrap();
for (column, column_width) in column_widths.iter().copied().enumerate() {
let index = column * rows + row;
let field = fields.get(index).copied().unwrap_or_default();

View file

@ -1,20 +1,12 @@
use std::borrow::Cow;
use std::iter;
use std::ops::{Deref, Range};
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability};
use rustc_ast::ast::{Expr, ExprKind, Impl, Item, ItemKind, MacCall, Path, StrLit, StrStyle};
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 clippy_utils::diagnostics::{span_lint, span_lint_and_then};
use clippy_utils::macros::{root_macro_call_first_node, FormatArgsExpn, MacroCall};
use clippy_utils::source::snippet_opt;
use rustc_ast::LitKind;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, HirIdMap, Impl, Item, ItemKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::symbol::{kw, Symbol};
use rustc_span::{sym, BytePos, InnerSpan, Span, DUMMY_SP};
use rustc_span::{sym, BytePos, Span};
declare_clippy_lint! {
/// ### What it does
@ -74,13 +66,7 @@ declare_clippy_lint! {
/// application and might forget to remove those prints afterward.
///
/// ### Known problems
/// * 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
/// Only catches `print!` and `println!` calls.
///
/// ### Example
/// ```rust
@ -102,13 +88,7 @@ declare_clippy_lint! {
/// application and might forget to remove those prints afterward.
///
/// ### Known problems
/// * 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
/// Only catches `eprint!` and `eprintln!` calls.
///
/// ### Example
/// ```rust
@ -149,10 +129,6 @@ declare_clippy_lint! {
/// (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary
/// (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
/// ```rust
/// println!("{}", "foo");
@ -234,10 +210,6 @@ declare_clippy_lint! {
/// (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary
/// (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
/// ```rust
/// # use std::fmt::Write;
@ -257,28 +229,6 @@ declare_clippy_lint! {
"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)]
pub struct Write {
in_debug_impl: bool,
@ -294,537 +244,308 @@ impl_lint_pass!(Write => [
WRITE_WITH_NEWLINE,
WRITELN_EMPTY_STRING,
WRITE_LITERAL,
POSITIONAL_NAMED_FORMAT_PARAMETERS,
]);
impl EarlyLintPass for Write {
fn check_item(&mut self, _: &EarlyContext<'_>, item: &Item) {
if let ItemKind::Impl(box Impl {
of_trait: Some(trait_ref),
..
}) = &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;
}
impl<'tcx> LateLintPass<'tcx> for Write {
fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
if is_debug_impl(cx, item) {
self.in_debug_impl = true;
}
}
fn check_item_post(&mut self, _: &EarlyContext<'_>, _: &Item) {
self.in_debug_impl = false;
fn check_item_post(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
if is_debug_impl(cx, item) {
self.in_debug_impl = false;
}
}
fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &MacCall) {
fn is_build_script(cx: &EarlyContext<'_>) -> bool {
// Cargo sets the crate name for build scripts to `build_script_build`
cx.sess()
.opts
.crate_name
.as_ref()
.map_or(false, |crate_name| crate_name == "build_script_build")
}
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return };
let Some(diag_name) = cx.tcx.get_diagnostic_name(macro_call.def_id) else { return };
let Some(name) = diag_name.as_str().strip_suffix("_macro") else { return };
if mac.path == sym!(print) {
if !is_build_script(cx) {
span_lint(cx, PRINT_STDOUT, mac.span(), "use of `print!`");
}
self.lint_print_with_newline(cx, mac);
} else if mac.path == sym!(println) {
if !is_build_script(cx) {
span_lint(cx, PRINT_STDOUT, mac.span(), "use of `println!`");
}
self.lint_println_empty_string(cx, mac);
} else if mac.path == sym!(eprint) {
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,
);
},
);
let is_build_script = cx
.sess()
.opts
.crate_name
.as_ref()
.map_or(false, |crate_name| crate_name == "build_script_build");
match diag_name {
sym::print_macro | sym::println_macro => {
if !is_build_script {
span_lint(cx, PRINT_STDOUT, macro_call.span, &format!("use of `{name}!`"));
}
}
} else if mac.path == sym!(writeln) {
if let (Some(fmt_str), expr) = self.check_tts(cx, mac.args.inner_tokens(), true) {
if fmt_str.symbol == kw::Empty {
let mut applicability = Applicability::MachineApplicable;
let suggestion = if let Some(e) = expr {
snippet_with_applicability(cx, e.span, "v", &mut applicability)
} else {
applicability = Applicability::HasPlaceholders;
Cow::Borrowed("v")
};
},
sym::eprint_macro | sym::eprintln_macro => {
span_lint(cx, PRINT_STDERR, macro_call.span, &format!("use of `{name}!`"));
},
sym::write_macro | sym::writeln_macro => {},
_ => return,
}
span_lint_and_sugg(
cx,
WRITELN_EMPTY_STRING,
mac.span(),
format!("using `writeln!({}, \"\")`", suggestion).as_str(),
"replace it with",
format!("writeln!({})", suggestion),
applicability,
);
let Some(format_args) = FormatArgsExpn::find_nested(cx, expr, macro_call.expn) else { return };
// ignore `writeln!(w)` and `write!(v, some_macro!())`
if format_args.format_string.span.from_expansion() {
return;
}
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");
}
}
}
}
}
/// Given a format string that ends in a newline and its span, calculates the span of the
/// newline, or the format string itself if the format string consists solely of a newline.
/// Return this and a boolean indicating whether it only consisted of a newline.
fn newline_span(fmtstr: &StrLit) -> (Span, bool) {
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)
fn is_debug_impl(cx: &LateContext<'_>, item: &Item<'_>) -> bool {
if let ItemKind::Impl(Impl { of_trait: Some(trait_ref), .. }) = &item.kind
&& let Some(trait_id) = trait_ref.trait_def_id()
{
cx.tcx.is_diagnostic_item(sym::Debug, trait_id)
} 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
/// empty format string.
#[derive(Default)]
struct SimpleFormatArgs {
unnamed: Vec<Vec<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,
})
}
// ignore trailing arguments: `print!("Issue\n{}", 1265);`
&& format_string_parts.len() > format_args.args.len()
{
let lint = if name == "write" {
format_string_span = expand_past_previous_comma(cx, format_string_span);
fn get_complex_unnamed(&self) -> impl Iterator<Item = &[Span]> {
self.complex_unnamed.iter().map(Vec::as_slice)
}
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,
WRITE_WITH_NEWLINE
} else {
PRINT_WITH_NEWLINE
};
const SIMPLE: FormatSpec<'_> = FormatSpec {
fill: None,
align: AlignUnknown,
flags: 0,
precision: CountImplied,
precision_span: None,
width: CountImplied,
width_span: None,
ty: "",
ty_span: None,
};
span_lint_and_then(
cx,
lint,
macro_call.span,
&format!("using `{name}!()` with a format string that ends in a single newline"),
|diag| {
let name_span = cx.sess().source_map().span_until_char(macro_call.span, '!');
let Some(format_snippet) = snippet_opt(cx, format_string_span) else { return };
match arg.position {
ArgumentIs(n) | ArgumentImplicitlyIs(n) => {
if self.unnamed.len() <= n {
// Use a dummy span to mark all unseen arguments.
self.unnamed.resize_with(n, || vec![DUMMY_SP]);
if arg.format == SIMPLE {
self.unnamed.push(vec![span]);
} else {
self.unnamed.push(Vec::new());
}
} else {
let args = &mut self.unnamed[n];
match (args.as_mut_slice(), arg.format == SIMPLE) {
// A non-empty format string has been seen already.
([], _) => (),
// Replace the dummy span, if it exists.
([dummy @ DUMMY_SP], true) => *dummy = span,
([_, ..], true) => args.push(span),
([_, ..], false) => *args = Vec::new(),
}
if format_string_parts.len() == 1 && last.as_str() == "\n" {
// print!("\n"), write!(f, "\n")
diag.multipart_suggestion(
&format!("use `{name}ln!` instead"),
vec![(name_span, format!("{name}ln")), (format_string_span, String::new())],
Applicability::MachineApplicable,
);
} else if format_snippet.ends_with("\\n\"") {
// print!("...\n"), write!(f, "...\n")
let hi = format_string_span.hi();
let newline_span = format_string_span.with_lo(hi - BytePos(3)).with_hi(hi - BytePos(1));
diag.multipart_suggestion(
&format!("use `{name}ln!` instead"),
vec![(name_span, format!("{name}ln")), (newline_span, String::new())],
Applicability::MachineApplicable,
);
}
},
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 {
/// Parses a format string into a collection of spans for each argument. This only keeps track
/// of empty format arguments. Will also lint usages of debug format strings outside of debug
/// impls.
fn parse_fmt_string(&self, cx: &EarlyContext<'_>, str_lit: &StrLit) -> Option<SimpleFormatArgs> {
use rustc_parse_format::{ParseMode, Parser, Piece};
fn check_empty_string(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, macro_call: &MacroCall, name: &str) {
if let [part] = &format_args.format_string.parts[..]
&& let mut span = format_args.format_string.span
&& part.as_str() == "\n"
{
let lint = if name == "writeln" {
span = expand_past_previous_comma(cx, span);
let str_sym = str_lit.symbol_unescaped.as_str();
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()),
}
WRITELN_EMPTY_STRING
} else {
None
PRINTLN_EMPTY_STRING
};
let fmtstr = match parser.parse_str_lit() {
Ok(fmtstr) => fmtstr,
Err(_) => return (None, expr),
};
let args = match self.parse_fmt_string(cx, &fmtstr) {
Some(args) => args,
None => return (Some(fmtstr), expr),
};
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),
span_lint_and_then(
cx,
lint,
macro_call.span,
&format!("empty string literal in `{name}!`"),
|diag| {
diag.span_suggestion(
span,
"remove the empty string",
String::new(),
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) {
if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) {
if check_newlines(&fmt_str) {
let name = mac.path.segments[0].ident.name;
let suggested = format!("{}ln", name);
span_lint_and_then(
cx,
PRINT_WITH_NEWLINE,
mac.span(),
&format!("using `{}!()` with a format string that ends in a single newline", name),
|err| {
err.multipart_suggestion(
&format!("use `{}!` instead", suggested),
vec![(mac.path.span, suggested), (newline_span(&fmt_str).0, String::new())],
for arg in &format_args.args {
let value = arg.param.value;
if counts[&value.hir_id] == 1
&& arg.format.is_default()
&& let ExprKind::Lit(lit) = &value.kind
&& !value.span.from_expansion()
&& let Some(value_string) = snippet_opt(cx, value.span)
{
let (replacement, replace_raw) = match lit.node {
LitKind::Str(..) => extract_str_literal(&value_string),
LitKind::Char(ch) => (
match ch {
'"' => "\\\"",
'\'' => "'",
_ => &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,
);
},
);
}
}
},
);
}
}
}
/// 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).
fn check_newlines(fmtstr: &StrLit) -> bool {
let mut has_internal_newline = false;
let mut last_was_cr = false;
let mut should_lint = false;
let contents = fmtstr.symbol.as_str();
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;
}
}
/// `r#"a"#` -> (`a`, true)
///
/// `"b"` -> (`b`, false)
fn extract_str_literal(literal: &str) -> (String, bool) {
let (literal, raw) = match literal.strip_prefix('r') {
Some(stripped) => (stripped.trim_matches('#'), true),
None => (literal, false),
};
match fmtstr.style {
StrStyle::Cooked => unescape::unescape_literal(contents, unescape::Mode::Str, &mut cb),
StrStyle::Raw(_) => unescape::unescape_literal(contents, unescape::Mode::RawStr, &mut cb),
(literal[1..literal.len() - 1].to_string(), raw)
}
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::Div if r != 0 => l.checked_div(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::Shl => l.checked_shl(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().ok()?).map(zext),
BinOpKind::BitXor => Some(zext(l ^ r)),
BinOpKind::BitOr => 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::Div => l.checked_div(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::Shl => l.checked_shl(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().ok()?).map(Constant::Int),
BinOpKind::BitXor => Some(Constant::Int(l ^ r)),
BinOpKind::BitOr => 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 itertools::{izip, Either, Itertools};
use rustc_ast::ast::LitKind;
use rustc_hir::intravisit::Visitor;
use rustc_hir::{self as hir, Expr, ExprKind, HirId, Node, QPath};
use rustc_hir::intravisit::{walk_expr, Visitor};
use rustc_hir::{self as hir, Expr, ExprField, ExprKind, HirId, Node, QPath};
use rustc_lexer::unescape::unescape_literal;
use rustc_lexer::{tokenize, unescape, LiteralKind, TokenKind};
use rustc_lint::LateContext;
@ -485,64 +485,49 @@ struct ParamPosition {
precision: Option<usize>,
}
/// 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> {
fn parse_count(expr: &Expr<'_>) -> Option<usize> {
// ::core::fmt::rt::v1::Count::Param(1usize),
if let ExprKind::Call(ctor, [val]) = expr.kind
&& let ExprKind::Path(QPath::Resolved(_, path)) = ctor.kind
&& path.segments.last()?.ident.name == sym::Param
&& let ExprKind::Lit(lit) = &val.kind
&& let LitKind::Int(pos, _) = lit.node
{
Some(pos as usize)
} else {
None
impl<'tcx> Visitor<'tcx> for ParamPosition {
fn visit_expr_field(&mut self, field: &'tcx ExprField<'tcx>) {
fn parse_count(expr: &Expr<'_>) -> Option<usize> {
// ::core::fmt::rt::v1::Count::Param(1usize),
if let ExprKind::Call(ctor, [val]) = expr.kind
&& let ExprKind::Path(QPath::Resolved(_, path)) = ctor.kind
&& path.segments.last()?.ident.name == sym::Param
&& let ExprKind::Lit(lit) = &val.kind
&& let LitKind::Int(pos, _) = lit.node
{
Some(pos as usize)
} else {
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
&& let ExprKind::Array(specs) = array.kind
{
Some(specs.iter().map(|spec| {
let mut position = ParamPosition::default();
// ::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.visit_expr(spec);
position
}))
} else {
@ -711,9 +696,14 @@ impl<'tcx> FormatSpec<'tcx> {
})
}
/// Returns true if this format spec would change the contents of a string when formatted
pub fn has_string_formatting(&self) -> bool {
self.r#trait != sym::Display || !self.width.is_implied() || !self.precision.is_implied()
/// Returns true if this format spec is unchanged from the default. e.g. returns true for `{}`,
/// `{foo}` and `{2}`, but false for `{:?}`, `{foo:5}` and `{3:.5}`
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};
/// A helper type to build suggestion correctly handling parentheses.
#[derive(Clone, PartialEq)]
#[derive(Clone, Debug, PartialEq)]
pub enum Sugg<'a> {
/// An expression that never needs parentheses such as `1337` or `[0; 42]`.
NonParen(Cow<'a, str>),
@ -155,8 +155,8 @@ impl<'a> Sugg<'a> {
| hir::ExprKind::Ret(..)
| hir::ExprKind::Struct(..)
| hir::ExprKind::Tup(..)
| hir::ExprKind::DropTemps(_)
| 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, _) => {
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 {
use rustc_ast::ast::RangeLimits;
let get_whole_snippet = || {
if expr.span.from_expansion() {
snippet_with_macro_callsite(cx, expr.span, default)
let snippet_without_expansion = |cx, span: Span, default| {
if span.from_expansion() {
snippet_with_macro_callsite(cx, span, default)
} else {
snippet(cx, expr.span, default)
snippet(cx, span, default)
}
};
@ -192,7 +192,7 @@ impl<'a> Sugg<'a> {
| ast::ExprKind::If(..)
| ast::ExprKind::Let(..)
| 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::Block(..)
| ast::ExprKind::Break(..)
@ -221,41 +221,45 @@ impl<'a> Sugg<'a> {
| ast::ExprKind::Array(..)
| ast::ExprKind::While(..)
| 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(
AssocOp::DotDot,
lhs.as_ref().map_or("".into(), |lhs| snippet(cx, lhs.span, default)),
rhs.as_ref().map_or("".into(), |rhs| snippet(cx, rhs.span, default)),
lhs.as_ref()
.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(
AssocOp::DotDotEq,
lhs.as_ref().map_or("".into(), |lhs| snippet(cx, lhs.span, default)),
rhs.as_ref().map_or("".into(), |rhs| snippet(cx, rhs.span, default)),
lhs.as_ref()
.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(
AssocOp::Assign,
snippet(cx, lhs.span, default),
snippet(cx, rhs.span, default),
snippet_without_expansion(cx, lhs.span, default),
snippet_without_expansion(cx, rhs.span, default),
),
ast::ExprKind::AssignOp(op, ref lhs, ref rhs) => Sugg::BinOp(
astbinop2assignop(op),
snippet(cx, lhs.span, default),
snippet(cx, rhs.span, default),
snippet_without_expansion(cx, lhs.span, default),
snippet_without_expansion(cx, rhs.span, default),
),
ast::ExprKind::Binary(op, ref lhs, ref rhs) => Sugg::BinOp(
AssocOp::from_ast_binop(op.node),
snippet(cx, lhs.span, default),
snippet(cx, rhs.span, default),
snippet_without_expansion(cx, lhs.span, default),
snippet_without_expansion(cx, rhs.span, default),
),
ast::ExprKind::Cast(ref lhs, ref ty) => Sugg::BinOp(
AssocOp::As,
snippet(cx, lhs.span, default),
snippet(cx, ty.span, default),
snippet_without_expansion(cx, lhs.span, default),
snippet_without_expansion(cx, ty.span, default),
),
ast::ExprKind::Type(ref lhs, ref ty) => Sugg::BinOp(
AssocOp::Colon,
snippet(cx, lhs.span, default),
snippet(cx, ty.span, default),
snippet_without_expansion(cx, lhs.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)
}
/// 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.
pub fn can_partially_move_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
if has_drop(cx, ty) || is_copy(cx, ty) {

View file

@ -221,6 +221,7 @@ docs! {
"items_after_statements",
"iter_cloned_collect",
"iter_count",
"iter_kv_map",
"iter_next_loop",
"iter_next_slice",
"iter_not_returning_iterator",
@ -391,7 +392,6 @@ docs! {
"partialeq_to_none",
"path_buf_push_overwrite",
"pattern_type_mismatch",
"positional_named_format_parameters",
"possible_missing_comma",
"precedence",
"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
(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
```
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.
### Known problems
* 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
Only catches `eprint!` and `eprintln!` calls.
### Example
```

View file

@ -7,13 +7,7 @@ People often print on *stdout* while debugging an
application and might forget to remove those prints afterward.
### Known problems
* 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
Only catches `print!` and `println!` calls.
### 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
(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
```
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() {
let _ = vec! {1, 2, 3};
let _ = format!["ugh {} stop being such a good compiler", "hello"];
let _ = matches!{{}, ()};
let _ = quote!(let x = 1;);
let _ = quote::quote!(match match match);
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"];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: use of irregular braces for `quote!` macro
error: use of irregular braces for `matches!` macro
--> $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;);
| ^^^^^^^^^^^^^^^^^^
|
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;);
| ^^^^^^^^^^^^^^^^^^
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);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
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);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -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)
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![];
| ^^^^^^^^^^^^^^^^
|
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![];
| ^^^^^^^^^^^^^^^^
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");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
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");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 7 previous errors
error: aborting due to 8 previous errors

View file

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

View file

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

View file

@ -1,5 +1,5 @@
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';
| ^^^^^^--^^^
@ -9,7 +9,7 @@ LL | let _ = ('a') ..'z';
= note: `-D clippy::almost-complete-letter-range` implied by `-D warnings`
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');
| ^^^^--^^^^^^
@ -17,7 +17,7 @@ LL | let _ = 'A' .. ('Z');
| help: use an inclusive 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');
| ^^^^^^--^^^^^^
@ -25,7 +25,7 @@ LL | let _ = (b'a')..(b'z');
| help: use an inclusive 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';
| ^^^^--^^^^
@ -33,7 +33,7 @@ LL | let _ = b'A'..b'Z';
| help: use an inclusive 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';
| ^^^^--^^^
@ -41,7 +41,7 @@ LL | let _ = a!()..'z';
| help: use an inclusive 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,
| ^^^^--^^^^
@ -49,7 +49,7 @@ LL | b'a'..b'z' if true => 1,
| help: use an inclusive 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,
| ^^^^--^^^^
@ -57,7 +57,7 @@ LL | b'A'..b'Z' if true => 2,
| help: use an inclusive 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,
| ^^^--^^^
@ -65,7 +65,7 @@ LL | 'a'..'z' if true => 1,
| help: use an inclusive 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,
| ^^^--^^^
@ -73,7 +73,20 @@ LL | 'A'..'Z' if true => 2,
| help: use an inclusive 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,
| ^^^--^^^
@ -81,7 +94,7 @@ LL | 'a'..'z' => 1,
| help: use an inclusive 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';
| ^^^--^^^
@ -89,12 +102,12 @@ LL | let _ = 'a'..'z';
| help: use an inclusive 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,
| ^^^--^^^
| |
| 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)]
#![warn(clippy::arithmetic_side_effects)]
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() {
let _ = 1f32 + 1f32;
let _ = 1f64 + 1f64;
@ -26,7 +66,7 @@ pub fn hard_coded_allowed() {
}
#[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 };
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 };
const _: i32 = 1 + 1;
let _ = 1 + 1;
let _ = const { 1 + 1 };
let mut _a = 1;
_a *= 1;
_a /= 1;
const _: i32 = { let mut n = -1; n = -(-1); n = -n; n };
let _ = const { let mut n = -1; n = -(-1); n = -n; n };
}
#[rustfmt::skip]
pub fn overflowing_ops() {
let mut _a = 1; _a += 1;
pub fn non_overflowing_runtime_ops_or_ops_already_handled_by_the_compiler() {
let mut _n = i32::MAX;
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() {}

View file

@ -1,22 +1,88 @@
error: arithmetic detected
--> $DIR/arithmetic_side_effects.rs:50:21
error: arithmetic operation that can potentially result in unexpected side-effects
--> $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`
error: arithmetic detected
--> $DIR/arithmetic_side_effects.rs:52:26
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:120:5
|
LL | let mut _b = 1; _b = _b + 1;
| ^^^^^^
LL | _n -= 1;
| ^^^^^^^
error: arithmetic detected
--> $DIR/arithmetic_side_effects.rs:54:26
error: arithmetic operation that can potentially result in unexpected side-effects
--> $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);
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);
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());
| ^^^^^^^^^^^^^^^^^^^ 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 {
($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
i32::from(a);
i32::from(!a);
i32::from(!a);
i32::from(a || b);
i32::from(cond(a, b));
i32::from(x + y < 4);
@ -21,7 +22,12 @@ fn main() {
// if else if
if a {
123
} else {i32::from(b)};
} else { i32::from(b) };
// if else if inverted
if a {
123
} else { i32::from(!b) };
// Shouldn't lint

View file

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

View file

@ -14,6 +14,18 @@ LL | | };
error: boolean to int conversion using if
--> $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 | | 1
LL | | } else {
@ -21,10 +33,10 @@ LL | | 0
LL | | };
| |_____^ 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
--> $DIR/bool_to_int_with_if.rs:25:5
--> $DIR/bool_to_int_with_if.rs:30:5
|
LL | / if a || b {
LL | | 1
@ -36,7 +48,7 @@ LL | | };
= note: `(a || b) as i32` or `(a || b).into()` can also be valid options
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 | | 1
@ -48,7 +60,7 @@ LL | | };
= note: `cond(a, b) as i32` or `cond(a, b).into()` can also be valid options
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 | | 1
@ -60,7 +72,7 @@ LL | | };
= note: `(x + y < 4) as i32` or `(x + y < 4).into()` can also be valid options
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 {
| ____________^
@ -68,17 +80,30 @@ LL | | 1
LL | | } else {
LL | | 0
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
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 }
| ^^^^^^^^^^^^^^^^^^^^^ help: replace with from: `u8::from(a)`
|
= 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
if matches!(true, true) && matches!(true, true) {}
// Issue #9375
if matches!(true, true) && truth() && matches!(true, true) {}
if true {
#[cfg(not(teehee))]
if true {

View file

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

View file

@ -126,5 +126,13 @@ LL | | if matches!(true, true) {}
LL | | }
| |_____^ 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;
struct FooDefault<'a> {

View file

@ -1,5 +1,5 @@
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 | | fn default() -> Self {
@ -11,10 +11,14 @@ LL | | }
| |_^
|
= 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
--> $DIR/derivable_impls.rs:39:1
--> $DIR/derivable_impls.rs:43:1
|
LL | / impl std::default::Default for TupleDefault {
LL | | fn default() -> Self {
@ -23,10 +27,14 @@ 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
--> $DIR/derivable_impls.rs:91:1
--> $DIR/derivable_impls.rs:95:1
|
LL | / impl Default for StrDefault<'_> {
LL | | fn default() -> Self {
@ -35,10 +43,14 @@ 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
--> $DIR/derivable_impls.rs:117:1
--> $DIR/derivable_impls.rs:121:1
|
LL | / impl Default for Y {
LL | | fn default() -> Self {
@ -47,10 +59,14 @@ 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
--> $DIR/derivable_impls.rs:156:1
--> $DIR/derivable_impls.rs:160:1
|
LL | / impl Default for WithoutSelfCurly {
LL | | fn default() -> Self {
@ -59,10 +75,14 @@ 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
--> $DIR/derivable_impls.rs:164:1
--> $DIR/derivable_impls.rs:168:1
|
LL | / impl Default for WithoutSelfParan {
LL | | fn default() -> Self {
@ -71,10 +91,14 @@ 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
--> $DIR/derivable_impls.rs:214:1
--> $DIR/derivable_impls.rs:218:1
|
LL | / impl Default for RepeatDefault1 {
LL | | fn default() -> Self {
@ -83,7 +107,11 @@ 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

View file

@ -45,5 +45,13 @@ fn main() {
eprint!("\r\n");
eprint!("foo\r\n");
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
|
LL ~ eprintln!(
LL ~ ""
LL ~
|
error: using `eprint!()` with a format string that ends in a single newline
@ -98,7 +98,7 @@ LL | | );
help: use `eprintln!` instead
|
LL ~ eprintln!(
LL ~ r""
LL ~
|
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
|
error: using `eprint!()` with a format string that ends in a single newline
--> $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
error: aborting due to 9 previous errors

View file

@ -36,6 +36,8 @@ fn main() {
eprintln!("with {} {}", 2, value);
eprintln!("with {value}");
eprintln!("macro arg {}", one!());
let width = 2;
eprintln!("{:w$}", value, w = width);
}
// 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 {value}").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
{

View file

@ -72,5 +72,11 @@ error: use of `writeln!(stderr(), ...).unwrap()`
LL | writeln!(std::io::stderr(), "macro arg {}", one!()).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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!("{:8}", "foo");
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!("{} bar", "foo");
@ -38,8 +36,6 @@ fn main() {
format!("{:?}", arg); // Don't warn about debug.
format!("{:8}", arg);
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!("{} bar", arg);

View file

@ -30,8 +30,6 @@ fn main() {
format!("{:?}", "foo"); // Don't warn about `Debug`.
format!("{:8}", "foo");
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!("{} bar", "foo");
@ -40,8 +38,6 @@ fn main() {
format!("{:?}", arg); // Don't warn about debug.
format!("{:8}", arg);
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!("{} bar", arg);

View file

@ -46,82 +46,58 @@ LL | format!("{}", "foo");
| ^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()`
error: useless use of `format!`
--> $DIR/format.rs:33: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
--> $DIR/format.rs:37:5
|
LL | format!("{}", arg);
| ^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `arg.to_string()`
error: useless use of `format!`
--> $DIR/format.rs:43: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
--> $DIR/format.rs:67:5
|
LL | format!("{}", 42.to_string());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `42.to_string()`
error: useless use of `format!`
--> $DIR/format.rs:73:5
--> $DIR/format.rs:69:5
|
LL | format!("{}", x.display().to_string());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.display().to_string()`
error: useless use of `format!`
--> $DIR/format.rs:77:18
--> $DIR/format.rs:73:18
|
LL | let _ = Some(format!("{}", a + "bar"));
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `a + "bar"`
error: useless use of `format!`
--> $DIR/format.rs:81:22
--> $DIR/format.rs:77:22
|
LL | let _s: String = format!("{}", &*v.join("/n"));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `(&*v.join("/n")).to_string()`
error: useless use of `format!`
--> $DIR/format.rs:87:13
--> $DIR/format.rs:83:13
|
LL | let _ = format!("{x}");
| ^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.to_string()`
error: useless use of `format!`
--> $DIR/format.rs:89:13
--> $DIR/format.rs:85:13
|
LL | let _ = format!("{y}", y = x);
| ^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.to_string()`
error: useless use of `format!`
--> $DIR/format.rs:93:13
--> $DIR/format.rs:89:13
|
LL | let _ = format!("{abc}");
| ^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `abc.to_string()`
error: useless use of `format!`
--> $DIR/format.rs:95:13
--> $DIR/format.rs:91:13
|
LL | let _ = format!("{xx}");
| ^^^^^^^^^^^^^^^ 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)]
enum CopyableLargeEnum {
A(bool),
B([u128; 4000]),
B([u64; 8000]),
}
enum ManuallyCopyLargeEnum {
A(bool),
B([u128; 4000]),
B([u64; 8000]),
}
impl Clone for ManuallyCopyLargeEnum {

View file

@ -167,8 +167,8 @@ error: large size difference between variants
LL | / enum CopyableLargeEnum {
LL | | A(bool),
| | ------- the second-largest variant contains at least 1 bytes
LL | | B([u128; 4000]),
| | --------------- the largest variant contains at least 64000 bytes
LL | | B([u64; 8000]),
| | -------------- the largest variant contains at least 64000 bytes
LL | | }
| |_^ 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
--> $DIR/large_enum_variant.rs:104:5
|
LL | B([u128; 4000]),
| ^^^^^^^^^^^^^^^
LL | B([u64; 8000]),
| ^^^^^^^^^^^^^^
error: large size difference between variants
--> $DIR/large_enum_variant.rs:107:1
@ -189,8 +189,8 @@ error: large size difference between variants
LL | / enum ManuallyCopyLargeEnum {
LL | | A(bool),
| | ------- the second-largest variant contains at least 1 bytes
LL | | B([u128; 4000]),
| | --------------- the largest variant contains at least 64000 bytes
LL | | B([u64; 8000]),
| | -------------- the largest variant contains at least 64000 bytes
LL | | }
| |_^ 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
--> $DIR/large_enum_variant.rs:109:5
|
LL | B([u128; 4000]),
| ^^^^^^^^^^^^^^^
LL | B([u64; 8000]),
| ^^^^^^^^^^^^^^
error: large size difference between variants
--> $DIR/large_enum_variant.rs:120:1

View file

@ -12,6 +12,12 @@ enum E {
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() {
let bad = (
[0u32; 20_000_000],

View file

@ -1,5 +1,5 @@
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],
| ^^^^^^^^^^^^^^^^^^
@ -8,7 +8,7 @@ LL | [0u32; 20_000_000],
= help: consider allocating on the heap with `vec![0u32; 20_000_000].into_boxed_slice()`
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],
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -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()`
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],
| ^^^^^^^^^^^^^^^^^^^^^^
@ -24,7 +24,7 @@ LL | [Some(""); 20_000_000],
= help: consider allocating on the heap with `vec![Some(""); 20_000_000].into_boxed_slice()`
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],
| ^^^^^^^^^^^^^^^

View file

@ -274,7 +274,7 @@ impl AsyncLen {
}
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 {

View file

@ -57,3 +57,9 @@ fn check_expect() {
#[expect(clippy::nonminimal_bool)]
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;
| ~~~~~~~~~~~~~~~~
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!("10 / 4 is {}", 2.5);
println!("2 + 1 = {}", 3);
println!("From expansion {}", stringify!(not a string literal));
// these should throw warnings
print!("Hello {}", "world");
println!("Hello {} {}", world, "world");
println!("Hello {}", "world");
println!("{} {:.4}", "a literal", 5);
// positional args don't change the fact
// that we're using a literal -- this should

View file

@ -1,5 +1,5 @@
error: literal with an empty format string
--> $DIR/print_literal.rs:25:24
--> $DIR/print_literal.rs:26:24
|
LL | print!("Hello {}", "world");
| ^^^^^^^
@ -12,7 +12,7 @@ LL + print!("Hello world");
|
error: literal with an empty format string
--> $DIR/print_literal.rs:26:36
--> $DIR/print_literal.rs:27:36
|
LL | println!("Hello {} {}", world, "world");
| ^^^^^^^
@ -24,7 +24,7 @@ LL + println!("Hello {} world", world);
|
error: literal with an empty format string
--> $DIR/print_literal.rs:27:26
--> $DIR/print_literal.rs:28:26
|
LL | println!("Hello {}", "world");
| ^^^^^^^
@ -36,7 +36,19 @@ LL + println!("Hello world");
|
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");
| ^^^^^^^
@ -48,7 +60,7 @@ LL + println!("hello {1}", "world");
|
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");
| ^^^^^^^
@ -60,19 +72,7 @@ LL + println!("{0} world", "hello");
|
error: literal with an empty format string
--> $DIR/print_literal.rs:33: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:33:34
--> $DIR/print_literal.rs:35:34
|
LL | println!("{1} {0}", "hello", "world");
| ^^^^^^^
@ -84,10 +84,22 @@ LL + println!("world {0}", "hello");
|
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");
| ^^^^^^^^^^^^^
| ^^^^^^^
|
help: try this
|
@ -96,10 +108,10 @@ LL + println!("hello {bar}", bar = "world");
|
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");
| ^^^^^^^^^^^^^
| ^^^^^^^
|
help: try this
|
@ -108,22 +120,10 @@ LL + println!("{foo} world", foo = "hello");
|
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");
| ^^^^^^^^^^^^^
|
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
|
@ -131,5 +131,17 @@ LL - println!("{bar} {foo}", foo = "hello", bar = "world");
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!("foo\r\n");
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
|
LL ~ println!(
LL ~ ""
LL ~
|
error: using `print!()` with a format string that ends in a single newline
@ -98,7 +98,7 @@ LL | | );
help: use `println!` instead
|
LL ~ println!(
LL ~ r""
LL ~
|
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
|
error: using `print!()` with a format string that ends in a single newline
--> $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
error: aborting due to 9 previous errors

View file

@ -1,28 +1,36 @@
error: using `println!("")`
error: empty string literal in `println!`
--> $DIR/println_empty_string.rs:6:5
|
LL | println!("");
| ^^^^^^^^^^^^ help: replace it with: `println!()`
| ^^^^^^^^^--^
| |
| help: remove the empty string
|
= 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
|
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
|
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
|
LL | _ => eprintln!(""),
| ^^^^^^^^^^^^^ help: replace it with: `eprintln!()`
| ^^^^^^^^^^--^
| |
| help: remove the empty string
error: aborting due to 4 previous errors

View file

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

View file

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

View file

@ -1,5 +1,5 @@
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)]
| ^^^^^^^^^^^^^^^^^^^^^^^^ 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`
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)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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`
--> $DIR/rename.rs:40:9
--> $DIR/rename.rs:41:9
|
LL | #![warn(clippy::block_in_if_condition_stmt)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions`
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)]
| ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection`
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)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes`
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)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity`
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)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods`
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)]
| ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types`
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)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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`
--> $DIR/rename.rs:47:9
--> $DIR/rename.rs:48:9
|
LL | #![warn(clippy::for_loop_over_option)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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`
--> $DIR/rename.rs:48:9
--> $DIR/rename.rs:49:9
|
LL | #![warn(clippy::for_loop_over_result)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::for_loops_over_fallibles`
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)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion`
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)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok`
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)]
| ^^^^^^^^^^^^^^^^^ 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`
--> $DIR/rename.rs:52:9
--> $DIR/rename.rs:53:9
|
LL | #![warn(clippy::new_without_default_derive)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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`
--> $DIR/rename.rs:53:9
--> $DIR/rename.rs:54:9
|
LL | #![warn(clippy::option_and_then_some)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map`
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)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used`
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)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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`
--> $DIR/rename.rs:56:9
--> $DIR/rename.rs:57:9
|
LL | #![warn(clippy::option_map_unwrap_or_else)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
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)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used`
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)]
| ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow`
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)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used`
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)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
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)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used`
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)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str`
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)]
| ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions`
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)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl`
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)]
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters`
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)]
| ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds`
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)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter`
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)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering`
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)]
| ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value`
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)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums`
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)]
| ^^^^^^^^^^^^^^^^^^^^ 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`
--> $DIR/rename.rs:72:9
--> $DIR/rename.rs:74:9
|
LL | #![warn(clippy::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`
--> $DIR/rename.rs:73:9
--> $DIR/rename.rs:75:9
|
LL | #![warn(clippy::unknown_clippy_lints)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints`
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)]
| ^^^^^^^^^^^^^^^^^^^^ 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()));
}
}
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