[`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
[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
Update the file with some examples to get started:
```rust
+#![allow(unused)]
#![warn(clippy::foo_functions)]
// Impl methods
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 {
.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
- }
}
}
```
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");
fn get_test_file_contents(lint_name: &str, header_commands: Option<&str>) -> String {
let mut contents = format!(
indoc! {"
+ #![allow(unused)]
#![warn(clippy::{})]
fn main() {{
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;
(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,
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};
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)) => {
"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,
);
"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,
);
}
}
-/// 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()
}
-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! {
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,
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"));
});
};
}
false
}
}
-
-fn should_have_parentheses<'tcx>(check: &'tcx rustc_hir::Expr<'tcx>) -> bool {
- check.precedence().order() < ExprPrecedence::Cast.order()
-}
}
},
&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);
},
}
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,
},
-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,
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
+ );
+ }
);
}
}
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,
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) {
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};
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, "[...]")
+ ),
+ );
+ }
}
}
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),
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),
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),
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,
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,
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),
])
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;
})
});
- store.register_pre_expansion_pass(|| Box::new(write::Write::default()));
store.register_pre_expansion_pass(move || Box::new(attrs::EarlyAttributes { msrv }));
}
#[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;
}
}
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));
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;
// 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(
_ => 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, &[]))
-}
--- /dev/null
+#![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,
+ }
+}
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;
"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>,
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.
(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);
}
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;
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(
_ => 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, &[]))
-}
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;
// 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(
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)
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};
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
// `{ 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) {
name: "vec",
braces: ("[", "]"),
),
+ macro_matcher!(
+ name: "matches",
+ braces: ("(", ")"),
+ ),
]
.into_iter()
.collect::<FxHashMap<_, _>>();
}
}
- /// 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::Rem = op.node
+ && let hir::ExprKind::Lit(ref lit) = expr.kind
+ && !matches!(lit.node, ast::LitKind::Int(0, _))
+ {
+ 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::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(
}
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);
}
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);
}
}
("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"),
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;
}
}
-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;
}
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(
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;
},
}
}
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;
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;
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,
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);
}
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();
-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
/// 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
/// 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
/// (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");
/// (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;
"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,
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,
- );
- },
- );
- }
- }
- } 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")
- };
-
- span_lint_and_sugg(
- cx,
- WRITELN_EMPTY_STRING,
- mac.span(),
- format!("using `writeln!({}, \"\")`", suggestion).as_str(),
- "replace it with",
- format!("writeln!({})", suggestion),
- applicability,
- );
+ 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}!`"));
}
- }
+ },
+ 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,
}
- }
-}
-/// 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();
+ let Some(format_args) = FormatArgsExpn::find_nested(cx, expr, macro_call.expn) else { return };
- 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()),
- };
+ // ignore `writeln!(w)` and `write!(v, some_macro!())`
+ if format_args.format_string.span.from_expansion() {
+ return;
+ }
- let newline_sp_len = if contents.ends_with('\n') {
- BytePos(1)
- } else if contents.ends_with(r"\n") {
- BytePos(2)
- } else {
- panic!("expected format string to contain a newline");
- };
+ match diag_name {
+ sym::print_macro | sym::eprint_macro | sym::write_macro => {
+ check_newline(cx, &format_args, ¯o_call, name);
+ },
+ sym::println_macro | sym::eprintln_macro | sym::writeln_macro => {
+ check_empty_string(cx, &format_args, ¯o_call, name);
+ },
+ _ => {},
+ }
- (sp.with_lo(newline_sp_hi - newline_sp_len).with_hi(newline_sp_hi), false)
-}
+ check_literal(cx, &format_args, name);
-/// 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>)>,
+ 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");
+ }
+ }
+ }
+ }
}
-impl SimpleFormatArgs {
- fn get_unnamed(&self) -> impl Iterator<Item = &[Span]> {
- self.unnamed.iter().map(|x| match x.as_slice() {
- // Ignore the dummy span added from out of order format arguments.
- [DUMMY_SP] => &[],
- x => x,
- })
+fn 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 {
+ false
}
+}
- fn get_complex_unnamed(&self) -> impl Iterator<Item = &[Span]> {
- self.complex_unnamed.iter().map(Vec::as_slice)
- }
+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;
- fn get_named(&self, n: &Path) -> &[Span] {
- self.named.iter().find(|x| *n == x.0).map_or(&[], |x| x.1.as_slice())
- }
+ let Some(last) = format_string_parts.last() else { return };
- fn push(&mut self, arg: rustc_parse_format::Argument<'_>, span: Span) {
- use rustc_parse_format::{
- AlignUnknown, ArgumentImplicitlyIs, ArgumentIs, ArgumentNamed, CountImplied, FormatSpec,
- };
+ let count_vertical_whitespace = || {
+ format_string_parts
+ .iter()
+ .flat_map(|part| part.as_str().chars())
+ .filter(|ch| matches!(ch, '\r' | '\n'))
+ .count()
+ };
- const SIMPLE: FormatSpec<'_> = FormatSpec {
- fill: None,
- align: AlignUnknown,
- flags: 0,
- precision: CountImplied,
- precision_span: None,
- width: CountImplied,
- width_span: None,
- ty: "",
- ty_span: None,
- };
+ if last.as_str().ends_with('\n')
+ // ignore format strings with other internal vertical whitespace
+ && count_vertical_whitespace() == 1
- 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(),
- }
- }
- },
- 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()));
- }
- },
- };
- }
+ // 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 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]);
+ WRITE_WITH_NEWLINE
} 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);
+ PRINT_WITH_NEWLINE
};
- 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
- };
+ 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 };
+
+ 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 span = str_lit_span.from_inner(InnerSpan {
- start: span.start + 1,
- end: span.end - hack,
- });
- self.push_to_complex(span, n);
- };
+ let hi = format_string_span.hi();
+ let newline_span = format_string_span.with_lo(hi - BytePos(3)).with_hi(hi - BytePos(1));
- 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);
- };
+ diag.multipart_suggestion(
+ &format!("use `{name}ln!` instead"),
+ vec![(name_span, format!("{name}ln")), (newline_span, String::new())],
+ Applicability::MachineApplicable,
+ );
+ }
+ },
+ );
}
}
-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};
-
- 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();
+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);
- 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),
- };
+ 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,
+ );
+ },
+ );
+ }
+}
- let args = match self.parse_fmt_string(cx, &fmtstr) {
- Some(args) => args,
- None => return (Some(fmtstr), expr),
- };
+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;
+ }
- 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);
- }
+ for arg in &format_args.args {
+ let value = arg.param.value;
- 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,
+ 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],
}
- },
- _ => {
- unnamed_args.next();
- continue;
- },
+ .to_string(),
+ false,
+ ),
+ LitKind::Bool(b) => (b.to_string(), false),
+ _ => continue,
+ };
+
+ let lint = if name.starts_with("write") {
+ WRITE_LITERAL
+ } else {
+ PRINT_LITERAL
};
- 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('}', "}}")
+ 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,
},
- LitKind::Str | LitKind::ByteStr if matches!(fmtstr.style, StrStyle::Cooked) => {
- lit.token_lit.symbol.as_str().replace('{', "{{").replace('}', "}}")
+ (true, true) => {
+ if replacement.contains(['#', '"']) {
+ None
+ } else {
+ Some(replacement)
+ }
},
- 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| {
+ 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",
- iter::once((comma_span.to(token_expr.span), String::new()))
- .chain(fmt_spans.iter().copied().zip(iter::repeat(replacement)))
- .collect(),
+ vec![(arg.span, replacement), (value_span, String::new())],
Applicability::MachineApplicable,
);
- },
- );
- }
- }
- }
-
- fn report_positional_named_param(cx: &EarlyContext<'_>, span: Span, lhs: &P<Expr>, _rhs: &P<Expr>) {
- if let ExprKind::Path(_, _p) = &lhs.kind {
- let mut applicability = Applicability::MachineApplicable;
- let name = snippet_with_applicability(cx, lhs.span, "name", &mut applicability);
- // We need to do this hack as precision spans should be converted from .* to .foo$
- let hack = snippet(cx, span, "").contains('*');
-
- span_lint_and_sugg(
- cx,
- POSITIONAL_NAMED_FORMAT_PARAMETERS,
- span,
- &format!("named parameter {} is used as a positional parameter", name),
- "replace it with",
- if hack {
- format!("{}$", name)
- } else {
- format!("{}", name)
+ }
},
- applicability,
);
- };
- }
-
- fn lint_println_empty_string(&self, cx: &EarlyContext<'_>, mac: &MacCall) {
- if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) {
- if fmt_str.symbol == kw::Empty {
- let name = mac.path.segments[0].ident.name;
- span_lint_and_sugg(
- cx,
- PRINTLN_EMPTY_STRING,
- mac.span(),
- &format!("using `{}!(\"\")`", name),
- "replace it with",
- format!("{}!()", name),
- Applicability::MachineApplicable,
- );
- }
- }
- }
-
- 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())],
- 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))
}
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)),
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)),
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;
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 {
})
}
- /// 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
}
}
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>),
| 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))
},
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)
}
};
| 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(..)
| 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),
),
}
}
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) {
"items_after_statements",
"iter_cloned_collect",
"iter_count",
+ "iter_kv_map",
"iter_next_loop",
"iter_next_slice",
"iter_not_returning_iterator",
"partialeq_to_none",
"path_buf_push_overwrite",
"pattern_type_mismatch",
- "positional_named_format_parameters",
"possible_missing_comma",
"precedence",
"print_in_format_impl",
--- /dev/null
+### 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<_>>();
+```
\ No newline at end of file
+++ /dev/null
-### 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);
-```
\ No newline at end of file
(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");
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
```
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
```
(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");
--- /dev/null
+[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]
--- /dev/null
+pub mod inner;
--- /dev/null
+// compile-flags: --remap-path-prefix {{src-base}}=/remapped
+
+#![warn(clippy::self_named_module_files)]
+
+mod bad;
+
+fn main() {}
--- /dev/null
+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
+
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
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);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= 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
// run-rustfix
// edition:2018
+// aux-build:macro_rules.rs
#![feature(custom_inner_attributes)]
#![feature(exclusive_range_pattern)]
#![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]
{
'B'..'Z' => 4,
_ => 5,
};
+
+ almost_complete_letter_range!();
+ b!();
}
fn _under_msrv() {
// run-rustfix
// edition:2018
+// aux-build:macro_rules.rs
#![feature(custom_inner_attributes)]
#![feature(exclusive_range_pattern)]
#![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]
{
'B'..'Z' => 4,
_ => 5,
};
+
+ almost_complete_letter_range!();
+ b!();
}
fn _under_msrv() {
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';
| ^^^^^^--^^^
= 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');
| ^^^^--^^^^^^
| 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');
| ^^^^^^--^^^^^^
| 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';
| ^^^^--^^^^
| 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';
| ^^^^--^^^
| 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,
| ^^^^--^^^^
| 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,
| ^^^^--^^^^
| 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,
| ^^^--^^^
| 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,
| ^^^--^^^
| 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,
| ^^^--^^^
| 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';
| ^^^--^^^
| 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
-#![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;
}
#[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 };
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;
+
+ // Assign
+ _n += 0;
+ _n -= 0;
+ _n /= 99;
+ _n %= 99;
+ _n *= 0;
+ _n *= 1;
+
+ // 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;
- let mut _b = 1; _b = _b + 1;
+ // 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;
- let mut _c = 1; _c = 1 + _c;
+ // Unary
+ _n = -_n;
}
fn main() {}
-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
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();
+}
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())
+}
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
macro_rules! equatable_if_let {
($a:ident) => {{ if let 2 = $a {} }};
}
+
+#[macro_export]
+macro_rules! almost_complete_letter_range {
+ () => {
+ let _ = 'a'..'z';
+ };
+}
// precedence
i32::from(a);
i32::from(!a);
+ i32::from(!a);
i32::from(a || b);
i32::from(cond(a, b));
i32::from(x + y < 4);
// 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
} else {
0
};
+ if a {
+ 0
+ } else {
+ 1
+ };
if !a {
1
} else {
0
};
+ // if else if inverted
+ if a {
+ 123
+ } else if b {
+ 0
+ } else {
+ 1
+ };
+
// Shouldn't lint
if a {
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 {
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
= 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
= 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
= 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 {
| ____________^
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
// 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 {
if matches!(true, true) {}
}
+ // Issue #9375
+ if matches!(true, true) && truth() {
+ if matches!(true, true) {}
+ }
+
if true {
#[cfg(not(teehee))]
if 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
--- /dev/null
+#![deny(arithmetic_overflow, const_err)]
+fn main() {
+ let _x = -1_i32 >> -1;
+ let _y = 1u32 >> 10000000000000u32;
+}
--- /dev/null
+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
+
--- /dev/null
+// 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() {}
+// run-rustfix
+
+#![allow(dead_code)]
+
use std::collections::HashMap;
struct FooDefault<'a> {
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 {
| |_^
|
= 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 {
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 {
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 {
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 {
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 {
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 {
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
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!());
}
help: use `eprintln!` instead
|
LL ~ eprintln!(
-LL ~ ""
+LL ~
|
error: using `eprint!()` with a format string that ends in a single newline
help: use `eprintln!` instead
|
LL ~ eprintln!(
-LL ~ r""
+LL ~
|
error: using `eprint!()` with a format string that ends in a single newline
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
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
{
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
{
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
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");
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);
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");
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);
| ^^^^^^^^^^^^^^^^^^^^ 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
--- /dev/null
+// 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();
+}
--- /dev/null
+// 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();
+}
--- /dev/null
+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
+
#[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 {
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
|
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
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
|
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
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],
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],
| ^^^^^^^^^^^^^^^^^^
= 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],
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
= 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],
| ^^^^^^^^^^^^^^^^^^^^^^
= 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],
| ^^^^^^^^^^^^^^^
}
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 {
#[expect(clippy::nonminimal_bool)]
let _ = !!a;
}
+
+fn issue9428() {
+ if matches!(true, true) && true {
+ println!("foo");
+ }
+}
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
+++ /dev/null
-// 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);
-}
+++ /dev/null
-// 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);
-}
+++ /dev/null
-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
-
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
error: literal with an empty format string
- --> $DIR/print_literal.rs:25:24
+ --> $DIR/print_literal.rs:26:24
|
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");
| ^^^^^^^
|
error: literal with an empty format string
- --> $DIR/print_literal.rs:27:26
+ --> $DIR/print_literal.rs:28:26
|
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");
| ^^^^^^^
|
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");
| ^^^^^^^
|
error: literal with an empty format string
- --> $DIR/print_literal.rs:33:25
+ --> $DIR/print_literal.rs:35:34
|
LL | println!("{1} {0}", "hello", "world");
- | ^^^^^^^
+ | ^^^^^^^
|
help: try this
|
LL - println!("{1} {0}", "hello", "world");
-LL + println!("{1} hello", "world");
+LL + println!("world {0}", "hello");
|
error: literal with an empty format string
- --> $DIR/print_literal.rs:33:34
+ --> $DIR/print_literal.rs:35:25
|
LL | println!("{1} {0}", "hello", "world");
- | ^^^^^^^
+ | ^^^^^^^
|
help: try this
|
LL - println!("{1} {0}", "hello", "world");
-LL + println!("world {0}", "hello");
+LL + println!("{1} hello", "world");
|
error: literal with an empty format string
- --> $DIR/print_literal.rs:36:29
+ --> $DIR/print_literal.rs:38:35
|
LL | println!("{foo} {bar}", foo = "hello", bar = "world");
- | ^^^^^^^^^^^^^
+ | ^^^^^^^
|
help: try this
|
|
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
|
|
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");
+LL + println!("world {foo}", foo = "hello");
|
error: literal with an empty format string
- --> $DIR/print_literal.rs:37:44
+ --> $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!("world {foo}", foo = "hello");
+LL + println!("{bar} hello", bar = "world");
|
-error: aborting due to 11 previous errors
+error: aborting due to 12 previous errors
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!());
}
help: use `println!` instead
|
LL ~ println!(
-LL ~ ""
+LL ~
|
error: using `print!()` with a format string that ends in a single newline
help: use `println!` instead
|
LL ~ println!(
-LL ~ r""
+LL ~
|
error: using `print!()` with a format string that ends in a single newline
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
-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
#![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)]
#![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)]
#![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)]
#![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)]
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`
= 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
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;
+ }
+}
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;
+ }
+}
impl PeekableConsumer {
fn consume(&self, _: Peekable<Empty<u32>>) {}
fn consume_mut_ref(&self, _: &mut Peekable<Empty<u32>>) {}
+ fn consume_assoc(_: Peekable<Empty<u32>>) {}
+ fn consume_assoc_mut_ref(_: &mut Peekable<Empty<u32>>) {}
}
-
let peekable_consumer = PeekableConsumer;
- let mut passed_along_to_method = std::iter::empty::<u32>().peekable();
- peekable_consumer.consume_mut_ref(&mut passed_along_to_method);
- peekable_consumer.consume(passed_along_to_method);
+
+ let peekable = std::iter::empty::<u32>().peekable();
+ peekable_consumer.consume(peekable);
+
+ let mut peekable = std::iter::empty::<u32>().peekable();
+ peekable_consumer.consume_mut_ref(&mut peekable);
+
+ let peekable = std::iter::empty::<u32>().peekable();
+ PeekableConsumer::consume_assoc(peekable);
+
+ let mut peekable = std::iter::empty::<u32>().peekable();
+ PeekableConsumer::consume_assoc_mut_ref(&mut peekable);
// `peek` called in another block
let mut peekable_in_block = std::iter::empty::<u32>().peekable();
{
peekable_last_expr.peek();
}
+
+ let mut peek_in_closure = std::iter::empty::<u32>().peekable();
+ let _ = || {
+ let _ = peek_in_closure.peek();
+ };
+
+ trait PeekTrait {}
+ impl<I> PeekTrait for Peekable<I> where I: Iterator {}
+
+ let mut peekable = std::iter::empty::<u32>().peekable();
+ let _dyn = &mut peekable as &mut dyn PeekTrait;
+
+ fn takes_dyn(_: &mut dyn PeekTrait) {}
+ let mut peekable = std::iter::empty::<u32>().peekable();
+ takes_dyn(&mut peekable);
}
}
}
}
+
+mod issue6902 {
+ use serde::Serialize;
+
+ #[derive(Serialize)]
+ pub enum Foo {
+ Bar = 1,
+ }
+}
}
}
}
+
+mod issue6902 {
+ use serde::Serialize;
+
+ #[derive(Serialize)]
+ pub enum Foo {
+ Bar = 1,
+ }
+}
writeln!(v, "{} of {:b} people know binary, the other half doesn't", 1, 2);
writeln!(v, "10 / 4 is {}", 2.5);
writeln!(v, "2 + 1 = {}", 3);
+ writeln!(v, "From expansion {}", stringify!(not a string literal));
// these should throw warnings
write!(v, "Hello {}", "world");
writeln!(v, "Hello {} {}", world, "world");
writeln!(v, "Hello {}", "world");
+ writeln!(v, "{} {:.4}", "a literal", 5);
// positional args don't change the fact
// that we're using a literal -- this should
error: literal with an empty format string
- --> $DIR/write_literal.rs:30:27
+ --> $DIR/write_literal.rs:31:27
|
LL | write!(v, "Hello {}", "world");
| ^^^^^^^
|
error: literal with an empty format string
- --> $DIR/write_literal.rs:31:39
+ --> $DIR/write_literal.rs:32:39
|
LL | writeln!(v, "Hello {} {}", world, "world");
| ^^^^^^^
|
error: literal with an empty format string
- --> $DIR/write_literal.rs:32:29
+ --> $DIR/write_literal.rs:33:29
|
LL | writeln!(v, "Hello {}", "world");
| ^^^^^^^
|
error: literal with an empty format string
- --> $DIR/write_literal.rs:37:28
+ --> $DIR/write_literal.rs:34:29
+ |
+LL | writeln!(v, "{} {:.4}", "a literal", 5);
+ | ^^^^^^^^^^^
+ |
+help: try this
+ |
+LL - writeln!(v, "{} {:.4}", "a literal", 5);
+LL + writeln!(v, "a literal {:.4}", 5);
+ |
+
+error: literal with an empty format string
+ --> $DIR/write_literal.rs:39:28
|
LL | writeln!(v, "{0} {1}", "hello", "world");
| ^^^^^^^
|
error: literal with an empty format string
- --> $DIR/write_literal.rs:37:37
+ --> $DIR/write_literal.rs:39:37
|
LL | writeln!(v, "{0} {1}", "hello", "world");
| ^^^^^^^
|
error: literal with an empty format string
- --> $DIR/write_literal.rs:38:28
+ --> $DIR/write_literal.rs:40:37
|
LL | writeln!(v, "{1} {0}", "hello", "world");
- | ^^^^^^^
+ | ^^^^^^^
|
help: try this
|
LL - writeln!(v, "{1} {0}", "hello", "world");
-LL + writeln!(v, "{1} hello", "world");
+LL + writeln!(v, "world {0}", "hello");
|
error: literal with an empty format string
- --> $DIR/write_literal.rs:38:37
+ --> $DIR/write_literal.rs:40:28
|
LL | writeln!(v, "{1} {0}", "hello", "world");
- | ^^^^^^^
+ | ^^^^^^^
|
help: try this
|
LL - writeln!(v, "{1} {0}", "hello", "world");
-LL + writeln!(v, "world {0}", "hello");
+LL + writeln!(v, "{1} hello", "world");
|
error: literal with an empty format string
- --> $DIR/write_literal.rs:41:32
+ --> $DIR/write_literal.rs:43:38
|
LL | writeln!(v, "{foo} {bar}", foo = "hello", bar = "world");
- | ^^^^^^^^^^^^^
+ | ^^^^^^^
|
help: try this
|
|
error: literal with an empty format string
- --> $DIR/write_literal.rs:41:47
+ --> $DIR/write_literal.rs:43:53
|
LL | writeln!(v, "{foo} {bar}", foo = "hello", bar = "world");
- | ^^^^^^^^^^^^^
+ | ^^^^^^^
|
help: try this
|
|
error: literal with an empty format string
- --> $DIR/write_literal.rs:42:32
+ --> $DIR/write_literal.rs:44:53
|
LL | writeln!(v, "{bar} {foo}", foo = "hello", bar = "world");
- | ^^^^^^^^^^^^^
+ | ^^^^^^^
|
help: try this
|
LL - writeln!(v, "{bar} {foo}", foo = "hello", bar = "world");
-LL + writeln!(v, "{bar} hello", bar = "world");
+LL + writeln!(v, "world {foo}", foo = "hello");
|
error: literal with an empty format string
- --> $DIR/write_literal.rs:42:47
+ --> $DIR/write_literal.rs:44:38
|
LL | writeln!(v, "{bar} {foo}", foo = "hello", bar = "world");
- | ^^^^^^^^^^^^^
+ | ^^^^^^^
|
help: try this
|
LL - writeln!(v, "{bar} {foo}", foo = "hello", bar = "world");
-LL + writeln!(v, "world {foo}", foo = "hello");
+LL + writeln!(v, "{bar} hello", bar = "world");
|
-error: aborting due to 11 previous errors
+error: aborting due to 12 previous errors
writeln!(v, r"{}", r"{hello}");
writeln!(v, "{}", '\'');
writeln!(v, "{}", '"');
- writeln!(v, r"{}", '"'); // don't lint
+ writeln!(v, r"{}", '"');
writeln!(v, r"{}", '\'');
writeln!(
v,
{} \\ {}",
"1", "2", "3",
);
+ writeln!(v, "{}", "\\");
+ writeln!(v, r"{}", "\\");
+ writeln!(v, r#"{}"#, "\\");
+ writeln!(v, "{}", r"\");
+ writeln!(v, "{}", "\r");
+ writeln!(v, r#"{}{}"#, '#', '"'); // hard mode
+ writeln!(v, r"{}", "\r"); // should not lint
}
LL + writeln!(v, "/"");
|
+error: literal with an empty format string
+ --> $DIR/write_literal_2.rs:13:24
+ |
+LL | writeln!(v, r"{}", '"');
+ | ^^^
+
error: literal with an empty format string
--> $DIR/write_literal_2.rs:14:24
|
LL ~ "1", "2",
|
-error: aborting due to 9 previous errors
+error: literal with an empty format string
+ --> $DIR/write_literal_2.rs:27:23
+ |
+LL | writeln!(v, "{}", "/");
+ | ^^^^
+ |
+help: try this
+ |
+LL - writeln!(v, "{}", "/");
+LL + writeln!(v, "/");
+ |
+
+error: literal with an empty format string
+ --> $DIR/write_literal_2.rs:28:24
+ |
+LL | writeln!(v, r"{}", "/");
+ | ^^^^
+ |
+help: try this
+ |
+LL - writeln!(v, r"{}", "/");
+LL + writeln!(v, r"/");
+ |
+
+error: literal with an empty format string
+ --> $DIR/write_literal_2.rs:29:26
+ |
+LL | writeln!(v, r#"{}"#, "/");
+ | ^^^^
+ |
+help: try this
+ |
+LL - writeln!(v, r#"{}"#, "/");
+LL + writeln!(v, r#"/"#);
+ |
+
+error: literal with an empty format string
+ --> $DIR/write_literal_2.rs:30:23
+ |
+LL | writeln!(v, "{}", r"/");
+ | ^^^^
+ |
+help: try this
+ |
+LL - writeln!(v, "{}", r"/");
+LL + writeln!(v, "/");
+ |
+
+error: literal with an empty format string
+ --> $DIR/write_literal_2.rs:31:23
+ |
+LL | writeln!(v, "{}", "/r");
+ | ^^^^
+ |
+help: try this
+ |
+LL - writeln!(v, "{}", "/r");
+LL + writeln!(v, "/r");
+ |
+
+error: literal with an empty format string
+ --> $DIR/write_literal_2.rs:32:28
+ |
+LL | writeln!(v, r#"{}{}"#, '#', '"'); // hard mode
+ | ^^^
+
+error: literal with an empty format string
+ --> $DIR/write_literal_2.rs:32:33
+ |
+LL | writeln!(v, r#"{}{}"#, '#', '"'); // hard mode
+ | ^^^
+
+error: aborting due to 17 previous errors
write!(v, "foo\r\n");
write!(v, "\\r\n"); //~ ERROR
write!(v, "foo\rbar\n");
+
+ // Ignore expanded format strings
+ macro_rules! newline {
+ () => {
+ "\n"
+ };
+ }
+ write!(v, newline!());
}
| ^^^^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::write-with-newline` implied by `-D warnings`
-help: use `writeln!()` instead
+help: use `writeln!` instead
|
LL - write!(v, "Hello/n");
LL + writeln!(v, "Hello");
LL | write!(v, "Hello {}/n", "world");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
-help: use `writeln!()` instead
+help: use `writeln!` instead
|
LL - write!(v, "Hello {}/n", "world");
LL + writeln!(v, "Hello {}", "world");
LL | write!(v, "Hello {} {}/n", "world", "#2");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
-help: use `writeln!()` instead
+help: use `writeln!` instead
|
LL - write!(v, "Hello {} {}/n", "world", "#2");
LL + writeln!(v, "Hello {} {}", "world", "#2");
LL | write!(v, "{}/n", 1265);
| ^^^^^^^^^^^^^^^^^^^^^^^
|
-help: use `writeln!()` instead
+help: use `writeln!` instead
|
LL - write!(v, "{}/n", 1265);
LL + writeln!(v, "{}", 1265);
LL | write!(v, "/n");
| ^^^^^^^^^^^^^^^
|
-help: use `writeln!()` instead
+help: use `writeln!` instead
|
LL - write!(v, "/n");
LL + writeln!(v);
LL | write!(v, "//n"); // should fail
| ^^^^^^^^^^^^^^^^^
|
-help: use `writeln!()` instead
+help: use `writeln!` instead
|
LL - write!(v, "//n"); // should fail
LL + writeln!(v, "/"); // should fail
LL | | );
| |_____^
|
-help: use `writeln!()` instead
+help: use `writeln!` instead
|
LL ~ writeln!(
-LL | v,
-LL ~ ""
+LL ~ v
|
error: using `write!()` with a format string that ends in a single newline
LL | | );
| |_____^
|
-help: use `writeln!()` instead
+help: use `writeln!` instead
|
LL ~ writeln!(
-LL | v,
-LL ~ r""
+LL ~ v
|
error: using `write!()` with a format string that ends in a single newline
LL | write!(v, "/r/n"); //~ ERROR
| ^^^^^^^^^^^^^^^^^^
|
-help: use `writeln!()` instead
+help: use `writeln!` instead
|
LL - write!(v, "/r/n"); //~ ERROR
LL + writeln!(v, "/r"); //~ ERROR
|
-error: using `write!()` with a format string that ends in a single newline
- --> $DIR/write_with_newline.rs:58:5
- |
-LL | write!(v, "foo/rbar/n");
- | ^^^^^^^^^^^^^^^^^^^^^^^
- |
-help: use `writeln!()` instead
- |
-LL - write!(v, "foo/rbar/n");
-LL + writeln!(v, "foo/rbar");
- |
-
-error: aborting due to 10 previous errors
+error: aborting due to 9 previous errors
-error: using `writeln!(v, "")`
+error: empty string literal in `writeln!`
--> $DIR/writeln_empty_string.rs:11:5
|
LL | writeln!(v, "");
- | ^^^^^^^^^^^^^^^ help: replace it with: `writeln!(v)`
+ | ^^^^^^^^^^----^
+ | |
+ | help: remove the empty string
|
= note: `-D clippy::writeln-empty-string` implied by `-D warnings`
-error: using `writeln!(suggestion, "")`
+error: empty string literal in `writeln!`
--> $DIR/writeln_empty_string.rs:14:5
|
LL | writeln!(suggestion, "");
- | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `writeln!(suggestion)`
+ | ^^^^^^^^^^^^^^^^^^^----^
+ | |
+ | help: remove the empty string
error: aborting due to 2 previous errors