* Previously deprecated [`str_to_string`] and [`string_to_string`] have been un-deprecated
as `restriction` lints [#6333](https://github.com/rust-lang/rust-clippy/pull/6333)
-* Deprecate `panic_params` lint. This is now available in rustc as `non_fmt_panic`
+* Deprecate `panic_params` lint. This is now available in rustc as `non_fmt_panics`
[#6351](https://github.com/rust-lang/rust-clippy/pull/6351)
* Move [`map_err_ignore`] to `restriction`
[#6416](https://github.com/rust-lang/rust-clippy/pull/6416)
///
/// **Why is this bad?** Duplicate code is less maintainable.
///
- /// **Known problems:** Hopefully none.
+ /// **Known problems:**
+ /// * The lint doesn't check if the moved expressions modify values that are beeing used in
+ /// the if condition. The suggestion can in that case modify the behavior of the program.
+ /// See [rust-clippy#7452](https://github.com/rust-lang/rust-clippy/issues/7452)
///
/// **Example:**
/// ```ignore
expr_eq &= block_expr_eq;
}
- let has_expr = blocks[0].expr.is_some();
- if has_expr && !expr_eq {
+ if !expr_eq {
end_eq = 0;
}
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet;
+use clippy_utils::numeric_literal;
+use clippy_utils::source::snippet_opt;
use if_chain::if_chain;
use rustc_ast::ast::{LitFloatType, LitIntType, LitKind};
use rustc_errors::Applicability;
if let Some(ty_bound) = self.ty_bounds.last();
if matches!(lit.node,
LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed));
- if !ty_bound.is_integral();
+ if !ty_bound.is_numeric();
then {
- let suffix = match lit_ty.kind() {
- ty::Int(IntTy::I32) => "i32",
- ty::Float(FloatTy::F64) => "f64",
+ let (suffix, is_float) = match lit_ty.kind() {
+ ty::Int(IntTy::I32) => ("i32", false),
+ ty::Float(FloatTy::F64) => ("f64", true),
// Default numeric fallback never results in other types.
_ => return,
};
- let sugg = format!("{}_{}", snippet(self.cx, lit.span, ""), suffix);
+ let src = if let Some(src) = snippet_opt(self.cx, lit.span) {
+ src
+ } else {
+ match lit.node {
+ LitKind::Int(src, _) => format!("{}", src),
+ LitKind::Float(src, _) => format!("{}", src),
+ _ => return,
+ }
+ };
+ let sugg = numeric_literal::format(&src, Some(suffix), is_float);
span_lint_and_sugg(
self.cx,
DEFAULT_NUMERIC_FALLBACK,
}
impl<'tcx> TyBound<'tcx> {
- fn is_integral(self) -> bool {
+ fn is_numeric(self) -> bool {
match self {
TyBound::Any => true,
- TyBound::Ty(t) => t.is_integral(),
+ TyBound::Ty(t) => t.is_numeric(),
TyBound::Nothing => false,
}
}
use rustc_span::{sym, FileName, Pos};
use std::io;
use std::ops::Range;
+use std::thread;
use url::Url;
declare_clippy_lint! {
}
fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) {
- fn has_needless_main(code: &str, edition: Edition) -> bool {
+ fn has_needless_main(code: String, edition: Edition) -> bool {
rustc_driver::catch_fatal_errors(|| {
- rustc_span::with_session_globals(edition, || {
- let filename = FileName::anon_source_code(code);
+ rustc_span::create_session_globals_then(edition, || {
+ let filename = FileName::anon_source_code(&code);
let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
let emitter = EmitterWriter::new(box io::sink(), None, false, false, false, None, false);
let handler = Handler::with_emitter(false, None, box emitter);
let sess = ParseSess::with_span_handler(handler, sm);
- let mut parser = match maybe_new_parser_from_source_str(&sess, filename, code.into()) {
+ let mut parser = match maybe_new_parser_from_source_str(&sess, filename, code) {
Ok(p) => p,
Err(errs) => {
for mut err in errs {
.unwrap_or_default()
}
- if has_needless_main(text, edition) {
+ // Because of the global session, we need to create a new session in a different thread with
+ // the edition we need.
+ let text = text.to_owned();
+ if thread::spawn(move || has_needless_main(text, edition))
+ .join()
+ .expect("thread::spawn failed")
+ {
span_lint(cx, NEEDLESS_DOCTEST_MAIN, span, "needless `fn main` in doctest");
}
}
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
-use clippy_utils::higher;
use clippy_utils::higher::VecArgs;
use clippy_utils::source::snippet_opt;
use clippy_utils::ty::{implements_trait, type_is_unsafe_function};
+use clippy_utils::usage::UsedAfterExprVisitor;
+use clippy_utils::{get_enclosing_loop_or_closure, higher};
use clippy_utils::{is_adjusted, iter_input_pats};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{def_id, Expr, ExprKind, Param, PatKind, QPath};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
-use rustc_middle::ty::{self, Ty};
+use rustc_middle::ty::{self, ClosureKind, Ty};
use rustc_session::{declare_lint_pass, declare_tool_lint};
declare_clippy_lint! {
}
}
-fn check_closure(cx: &LateContext<'_>, expr: &Expr<'_>) {
+fn check_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if let ExprKind::Closure(_, decl, eid, _, _) = expr.kind {
let body = cx.tcx.hir().body(eid);
let ex = &body.value;
then {
span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| {
- if let Some(snippet) = snippet_opt(cx, caller.span) {
+ if let Some(mut snippet) = snippet_opt(cx, caller.span) {
+ if_chain! {
+ if let ty::Closure(_, substs) = fn_ty.kind();
+ if let ClosureKind::FnMut = substs.as_closure().kind();
+ if UsedAfterExprVisitor::is_found(cx, caller)
+ || get_enclosing_loop_or_closure(cx.tcx, expr).is_some();
+
+ then {
+ // Mutable closure is used after current expr; we cannot consume it.
+ snippet = format!("&mut {}", snippet);
+ }
+ }
diag.span_suggestion(
expr.span,
"replace the closure with the function itself",
-use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
+use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
+use clippy_utils::higher::FormatArgsExpn;
use clippy_utils::{is_expn_of, match_function_call, paths};
use if_chain::if_chain;
-use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
-use rustc_hir::{BorrowKind, Expr, ExprKind};
+use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if_chain! {
// match call to unwrap
- if let ExprKind::MethodCall(unwrap_fun, _, unwrap_args, _) = expr.kind;
+ if let ExprKind::MethodCall(unwrap_fun, _, [write_call], _) = expr.kind;
if unwrap_fun.ident.name == sym::unwrap;
// match call to write_fmt
- if !unwrap_args.is_empty();
- if let ExprKind::MethodCall(write_fun, _, write_args, _) =
- unwrap_args[0].kind;
+ if let ExprKind::MethodCall(write_fun, _, [write_recv, write_arg], _) = write_call.kind;
if write_fun.ident.name == sym!(write_fmt);
// match calls to std::io::stdout() / std::io::stderr ()
- if !write_args.is_empty();
- if let Some(dest_name) = if match_function_call(cx, &write_args[0], &paths::STDOUT).is_some() {
+ if let Some(dest_name) = if match_function_call(cx, write_recv, &paths::STDOUT).is_some() {
Some("stdout")
- } else if match_function_call(cx, &write_args[0], &paths::STDERR).is_some() {
+ } else if match_function_call(cx, write_recv, &paths::STDERR).is_some() {
Some("stderr")
} else {
None
};
+ if let Some(format_args) = FormatArgsExpn::parse(write_arg);
then {
- let write_span = unwrap_args[0].span;
let calling_macro =
// ordering is important here, since `writeln!` uses `write!` internally
- if is_expn_of(write_span, "writeln").is_some() {
+ if is_expn_of(write_call.span, "writeln").is_some() {
Some("writeln")
- } else if is_expn_of(write_span, "write").is_some() {
+ } else if is_expn_of(write_call.span, "write").is_some() {
Some("write")
} else {
None
// We need to remove the last trailing newline from the string because the
// underlying `fmt::write` function doesn't know whether `println!` or `print!` was
// used.
- if let Some(mut write_output) = write_output_string(write_args) {
+ let (used, sugg_mac) = if let Some(macro_name) = calling_macro {
+ (
+ format!("{}!({}(), ...)", macro_name, dest_name),
+ macro_name.replace("write", "print"),
+ )
+ } else {
+ (
+ format!("{}().write_fmt(...)", dest_name),
+ "print".into(),
+ )
+ };
+ let msg = format!("use of `{}.unwrap()`", used);
+ if let [write_output] = *format_args.format_string_symbols {
+ let mut write_output = write_output.to_string();
if write_output.ends_with('\n') {
write_output.pop();
}
- if let Some(macro_name) = calling_macro {
- span_lint_and_sugg(
- cx,
- EXPLICIT_WRITE,
- expr.span,
- &format!(
- "use of `{}!({}(), ...).unwrap()`",
- macro_name,
- dest_name
- ),
- "try this",
- format!("{}{}!(\"{}\")", prefix, macro_name.replace("write", "print"), write_output.escape_default()),
- Applicability::MachineApplicable
- );
- } else {
- span_lint_and_sugg(
- cx,
- EXPLICIT_WRITE,
- expr.span,
- &format!("use of `{}().write_fmt(...).unwrap()`", dest_name),
- "try this",
- format!("{}print!(\"{}\")", prefix, write_output.escape_default()),
- Applicability::MachineApplicable
- );
- }
+ let sugg = format!("{}{}!(\"{}\")", prefix, sugg_mac, write_output.escape_default());
+ span_lint_and_sugg(
+ cx,
+ EXPLICIT_WRITE,
+ expr.span,
+ &msg,
+ "try this",
+ sugg,
+ Applicability::MachineApplicable
+ );
} else {
// We don't have a proper suggestion
- if let Some(macro_name) = calling_macro {
- span_lint(
- cx,
- EXPLICIT_WRITE,
- expr.span,
- &format!(
- "use of `{}!({}(), ...).unwrap()`. Consider using `{}{}!` instead",
- macro_name,
- dest_name,
- prefix,
- macro_name.replace("write", "print")
- )
- );
- } else {
- span_lint(
- cx,
- EXPLICIT_WRITE,
- expr.span,
- &format!("use of `{}().write_fmt(...).unwrap()`. Consider using `{}print!` instead", dest_name, prefix),
- );
- }
+ let help = format!("consider using `{}{}!` instead", prefix, sugg_mac);
+ span_lint_and_help(cx, EXPLICIT_WRITE, expr.span, &msg, None, &help);
}
-
}
}
}
}
-
-// Extract the output string from the given `write_args`.
-fn write_output_string(write_args: &[Expr<'_>]) -> Option<String> {
- if_chain! {
- // Obtain the string that should be printed
- if write_args.len() > 1;
- if let ExprKind::Call(_, output_args) = write_args[1].kind;
- if !output_args.is_empty();
- if let ExprKind::AddrOf(BorrowKind::Ref, _, output_string_expr) = output_args[0].kind;
- if let ExprKind::Array(string_exprs) = output_string_expr.kind;
- // we only want to provide an automatic suggestion for simple (non-format) strings
- if string_exprs.len() == 1;
- if let ExprKind::Lit(ref lit) = string_exprs[0].kind;
- if let LitKind::Str(ref write_output, _) = lit.node;
- then {
- return Some(write_output.to_string())
- }
- }
- None
-}
-use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::paths;
-use clippy_utils::source::{snippet, snippet_opt};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::higher::FormatExpn;
+use clippy_utils::last_path_segment;
+use clippy_utils::source::{snippet_opt, snippet_with_applicability};
use clippy_utils::sugg::Sugg;
-use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{is_expn_of, last_path_segment, match_def_path, match_function_call};
use if_chain::if_chain;
-use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
-use rustc_hir::{Arm, BorrowKind, Expr, ExprKind, MatchSource, PatKind};
-use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_hir::{BorrowKind, Expr, ExprKind, QPath};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::source_map::Span;
-use rustc_span::sym;
+use rustc_span::symbol::kw;
+use rustc_span::{sym, Span};
declare_clippy_lint! {
/// **What it does:** Checks for the use of `format!("string literal with no
impl<'tcx> LateLintPass<'tcx> for UselessFormat {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- let span = match is_expn_of(expr.span, "format") {
- Some(s) if !s.from_expansion() => s,
+ let FormatExpn { call_site, format_args } = match FormatExpn::parse(expr) {
+ Some(e) if !e.call_site.from_expansion() => e,
_ => return,
};
- // Operate on the only argument of `alloc::fmt::format`.
- if let Some(sugg) = on_new_v1(cx, expr) {
- span_useless_format(cx, span, "consider using `.to_string()`", sugg);
- } else if let Some(sugg) = on_new_v1_fmt(cx, expr) {
- span_useless_format(cx, span, "consider using `.to_string()`", sugg);
- }
- }
-}
-
-fn span_useless_format<T: LintContext>(cx: &T, span: Span, help: &str, mut sugg: String) {
- let to_replace = span.source_callsite();
-
- // The callsite span contains the statement semicolon for some reason.
- let snippet = snippet(cx, to_replace, "..");
- if snippet.ends_with(';') {
- sugg.push(';');
- }
-
- span_lint_and_then(cx, USELESS_FORMAT, span, "useless use of `format!`", |diag| {
- diag.span_suggestion(
- to_replace,
- help,
- sugg,
- Applicability::MachineApplicable, // snippet
- );
- });
-}
-
-fn on_argumentv1_new<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>]) -> Option<String> {
- if_chain! {
- if let ExprKind::AddrOf(BorrowKind::Ref, _, format_args) = expr.kind;
- if let ExprKind::Array(elems) = arms[0].body.kind;
- if elems.len() == 1;
- if let Some(args) = match_function_call(cx, &elems[0], &paths::FMT_ARGUMENTV1_NEW);
- // matches `core::fmt::Display::fmt`
- if args.len() == 2;
- if let ExprKind::Path(ref qpath) = args[1].kind;
- if let Some(did) = cx.qpath_res(qpath, args[1].hir_id).opt_def_id();
- if match_def_path(cx, did, &paths::DISPLAY_FMT_METHOD);
- // check `(arg0,)` in match block
- if let PatKind::Tuple(pats, None) = arms[0].pat.kind;
- if pats.len() == 1;
- then {
- let ty = cx.typeck_results().pat_ty(pats[0]).peel_refs();
- if *ty.kind() != rustc_middle::ty::Str && !is_type_diagnostic_item(cx, ty, sym::string_type) {
- return None;
- }
- if let ExprKind::Lit(ref lit) = format_args.kind {
- if let LitKind::Str(ref s, _) = lit.node {
- return Some(format!("{:?}.to_string()", s.as_str()));
+ let mut applicability = Applicability::MachineApplicable;
+ if format_args.value_args.is_empty() {
+ if_chain! {
+ if let [e] = &*format_args.format_string_parts;
+ if let ExprKind::Lit(lit) = &e.kind;
+ if let Some(s_src) = snippet_opt(cx, lit.span);
+ then {
+ // Simulate macro expansion, converting {{ and }} to { and }.
+ let s_expand = s_src.replace("{{", "{").replace("}}", "}");
+ let sugg = format!("{}.to_string()", s_expand);
+ span_useless_format(cx, call_site, sugg, applicability);
}
- } else {
- let sugg = Sugg::hir(cx, format_args, "<arg>");
- if let ExprKind::MethodCall(path, _, _, _) = format_args.kind {
- if path.ident.name == sym!(to_string) {
- return Some(format!("{}", sugg));
- }
- } else if let ExprKind::Binary(..) = format_args.kind {
- return Some(format!("{}", sugg));
+ }
+ } else if let [value] = *format_args.value_args {
+ if_chain! {
+ if format_args.format_string_symbols == [kw::Empty];
+ if match cx.typeck_results().expr_ty(value).peel_refs().kind() {
+ ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(sym::string_type, adt.did),
+ ty::Str => true,
+ _ => false,
+ };
+ if format_args.args.iter().all(|e| is_display_arg(e));
+ if format_args.fmt_expr.map_or(true, |e| check_unformatted(e));
+ then {
+ let is_new_string = match value.kind {
+ ExprKind::Binary(..) => true,
+ ExprKind::MethodCall(path, ..) => path.ident.name.as_str() == "to_string",
+ _ => false,
+ };
+ let sugg = if is_new_string {
+ snippet_with_applicability(cx, value.span, "..", &mut applicability).into_owned()
+ } else {
+ let sugg = Sugg::hir_with_applicability(cx, value, "<arg>", &mut applicability);
+ format!("{}.to_string()", sugg.maybe_par())
+ };
+ span_useless_format(cx, call_site, sugg, applicability);
}
- return Some(format!("{}.to_string()", sugg.maybe_par()));
}
- }
+ };
}
- None
}
-fn on_new_v1<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<String> {
- if_chain! {
- if let Some(args) = match_function_call(cx, expr, &paths::FMT_ARGUMENTS_NEW_V1);
- if args.len() == 2;
- // Argument 1 in `new_v1()`
- if let ExprKind::AddrOf(BorrowKind::Ref, _, arr) = args[0].kind;
- if let ExprKind::Array(pieces) = arr.kind;
- if pieces.len() == 1;
- if let ExprKind::Lit(ref lit) = pieces[0].kind;
- if let LitKind::Str(ref s, _) = lit.node;
- // Argument 2 in `new_v1()`
- if let ExprKind::AddrOf(BorrowKind::Ref, _, arg1) = args[1].kind;
- if let ExprKind::Match(matchee, arms, MatchSource::Normal) = arg1.kind;
- if arms.len() == 1;
- if let ExprKind::Tup(tup) = matchee.kind;
- then {
- // `format!("foo")` expansion contains `match () { () => [], }`
- if tup.is_empty() {
- if let Some(s_src) = snippet_opt(cx, lit.span) {
- // Simulate macro expansion, converting {{ and }} to { and }.
- let s_expand = s_src.replace("{{", "{").replace("}}", "}");
- return Some(format!("{}.to_string()", s_expand));
- }
- } else if s.as_str().is_empty() {
- return on_argumentv1_new(cx, &tup[0], arms);
- }
- }
+fn span_useless_format(cx: &LateContext<'_>, span: Span, mut sugg: String, mut applicability: Applicability) {
+ // The callsite span contains the statement semicolon for some reason.
+ if snippet_with_applicability(cx, span, "..", &mut applicability).ends_with(';') {
+ sugg.push(';');
}
- None
+
+ span_lint_and_sugg(
+ cx,
+ USELESS_FORMAT,
+ span,
+ "useless use of `format!`",
+ "consider using `.to_string()`",
+ sugg,
+ applicability,
+ );
}
-fn on_new_v1_fmt<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<String> {
+fn is_display_arg(expr: &Expr<'_>) -> bool {
if_chain! {
- if let Some(args) = match_function_call(cx, expr, &paths::FMT_ARGUMENTS_NEW_V1_FORMATTED);
- if args.len() == 3;
- if check_unformatted(&args[2]);
- // Argument 1 in `new_v1_formatted()`
- if let ExprKind::AddrOf(BorrowKind::Ref, _, arr) = args[0].kind;
- if let ExprKind::Array(pieces) = arr.kind;
- if pieces.len() == 1;
- if let ExprKind::Lit(ref lit) = pieces[0].kind;
- if let LitKind::Str(..) = lit.node;
- // Argument 2 in `new_v1_formatted()`
- if let ExprKind::AddrOf(BorrowKind::Ref, _, arg1) = args[1].kind;
- if let ExprKind::Match(matchee, arms, MatchSource::Normal) = arg1.kind;
- if arms.len() == 1;
- if let ExprKind::Tup(tup) = matchee.kind;
- then {
- return on_argumentv1_new(cx, &tup[0], arms);
- }
+ if let ExprKind::Call(_, [_, fmt]) = expr.kind;
+ if let ExprKind::Path(QPath::Resolved(_, path)) = fmt.kind;
+ if let [.., t, _] = path.segments;
+ if t.ident.name.as_str() == "Display";
+ then { true } else { false }
}
- None
}
/// Checks if the expression matches
fn check_unformatted(expr: &Expr<'_>) -> bool {
if_chain! {
if let ExprKind::AddrOf(BorrowKind::Ref, _, expr) = expr.kind;
- if let ExprKind::Array(exprs) = expr.kind;
- if exprs.len() == 1;
+ if let ExprKind::Array([expr]) = expr.kind;
// struct `core::fmt::rt::v1::Argument`
- if let ExprKind::Struct(_, fields, _) = exprs[0].kind;
+ if let ExprKind::Struct(_, fields, _) = expr.kind;
if let Some(format_field) = fields.iter().find(|f| f.ident.name == sym::format);
// struct `core::fmt::rt::v1::FormatSpec`
if let ExprKind::Struct(_, fields, _) = format_field.expr.kind;
-#![allow(rustc::default_hash_types)]
-
use std::borrow::Cow;
use std::collections::BTreeMap;
ls.register_renamed("clippy::unused_label", "unused_labels");
ls.register_renamed("clippy::drop_bounds", "drop_bounds");
ls.register_renamed("clippy::temporary_cstring_as_ptr", "temporary_cstring_as_ptr");
- ls.register_renamed("clippy::panic_params", "non_fmt_panic");
+ ls.register_renamed("clippy::panic_params", "non_fmt_panics");
ls.register_renamed("clippy::unknown_clippy_lints", "unknown_lints");
}
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::is_expn_of;
-use clippy_utils::source::{snippet, snippet_with_applicability};
+use clippy_utils::higher::FormatExpn;
+use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::is_type_diagnostic_item;
-use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
}
}
- fn generate_format_arg_snippet(
- cx: &LateContext<'_>,
- a: &hir::Expr<'_>,
- applicability: &mut Applicability,
- ) -> Vec<String> {
- if_chain! {
- if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, format_arg) = a.kind;
- if let hir::ExprKind::Match(format_arg_expr, _, _) = format_arg.kind;
- if let hir::ExprKind::Tup(format_arg_expr_tup) = format_arg_expr.kind;
-
- then {
- format_arg_expr_tup
- .iter()
- .map(|a| snippet_with_applicability(cx, a.span, "..", applicability).into_owned())
- .collect()
- } else {
- unreachable!()
- }
- }
- }
-
fn is_call(node: &hir::ExprKind<'_>) -> bool {
match node {
hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, expr) => {
let mut applicability = Applicability::MachineApplicable;
//Special handling for `format!` as arg_root
- if_chain! {
- if let hir::ExprKind::Block(block, None) = &arg_root.kind;
- if block.stmts.len() == 1;
- if let hir::StmtKind::Local(local) = &block.stmts[0].kind;
- if let Some(arg_root) = &local.init;
- if let hir::ExprKind::Call(inner_fun, inner_args) = arg_root.kind;
- if is_expn_of(inner_fun.span, "format").is_some() && inner_args.len() == 1;
- if let hir::ExprKind::Call(_, format_args) = &inner_args[0].kind;
- then {
- let fmt_spec = &format_args[0];
- let fmt_args = &format_args[1];
-
- let mut args = vec![snippet(cx, fmt_spec.span, "..").into_owned()];
-
- args.extend(generate_format_arg_snippet(cx, fmt_args, &mut applicability));
-
- let sugg = args.join(", ");
-
- span_lint_and_sugg(
- cx,
- EXPECT_FUN_CALL,
- span_replace_word,
- &format!("use of `{}` followed by a function call", name),
- "try this",
- format!("unwrap_or_else({} panic!({}))", closure_args, sugg),
- applicability,
- );
-
- return;
- }
+ if let Some(format_expn) = FormatExpn::parse(arg_root) {
+ let span = match *format_expn.format_args.value_args {
+ [] => format_expn.format_args.format_string_span,
+ [.., last] => format_expn.format_args.format_string_span.to(last.span),
+ };
+ let sugg = snippet_with_applicability(cx, span, "..", &mut applicability);
+ span_lint_and_sugg(
+ cx,
+ EXPECT_FUN_CALL,
+ span_replace_word,
+ &format!("use of `{}` followed by a function call", name),
+ "try this",
+ format!("unwrap_or_else({} panic!({}))", closure_args, sugg),
+ applicability,
+ );
+ return;
}
let mut arg_root_snippet: Cow<'_, _> = snippet_with_applicability(cx, arg_root.span, "..", &mut applicability);
use rustc_span::hygiene::MacroKind;
if expr.span.from_expansion() {
let data = expr.span.ctxt().outer_expn_data();
- matches!(
- data.kind,
- ExpnKind::Macro {
- kind: MacroKind::Attr,
- name: _,
- proc_macro: _
- }
- )
+ matches!(data.kind, ExpnKind::Macro(MacroKind::Attr, _))
} else {
false
}
},
Tuple(..) => ty.tuple_fields().any(|ty| is_mutable_type(cx, ty, span)),
Adt(..) => {
- cx.tcx.layout_of(cx.param_env.and(ty)).is_ok()
- && !ty.has_escaping_bound_vars()
+ !ty.has_escaping_bound_vars()
+ && cx.tcx.layout_of(cx.param_env.and(ty)).is_ok()
&& !ty.is_freeze(cx.tcx.at(span), cx.param_env)
},
_ => false,
impl<'tcx> LateLintPass<'tcx> for NeedlessBool {
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
use self::Expression::{Bool, RetBool};
+ if e.span.from_expansion() {
+ return;
+ }
if let ExprKind::If(pred, then_block, Some(else_expr)) = e.kind {
let reduce = |ret, not| {
let mut applicability = Applicability::MachineApplicable;
use rustc_middle::mir::{
self, traversal,
visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor as _},
+ Mutability,
};
use rustc_middle::ty::{self, fold::TypeVisitor, Ty};
use rustc_mir::dataflow::{Analysis, AnalysisDomain, GenKill, GenKillAnalysis, ResultsCursor};
let mir = cx.tcx.optimized_mir(def_id.to_def_id());
+ let possible_origin = {
+ let mut vis = PossibleOriginVisitor::new(mir);
+ vis.visit_body(mir);
+ vis.into_map(cx)
+ };
let maybe_storage_live_result = MaybeStorageLive
.into_engine(cx.tcx, mir)
.pass_name("redundant_clone")
.iterate_to_fixpoint()
.into_results_cursor(mir);
let mut possible_borrower = {
- let mut vis = PossibleBorrowerVisitor::new(cx, mir);
+ let mut vis = PossibleBorrowerVisitor::new(cx, mir, possible_origin);
vis.visit_body(mir);
vis.into_map(cx, maybe_storage_live_result)
};
possible_borrower: TransitiveRelation<mir::Local>,
body: &'a mir::Body<'tcx>,
cx: &'a LateContext<'tcx>,
+ possible_origin: FxHashMap<mir::Local, HybridBitSet<mir::Local>>,
}
impl<'a, 'tcx> PossibleBorrowerVisitor<'a, 'tcx> {
- fn new(cx: &'a LateContext<'tcx>, body: &'a mir::Body<'tcx>) -> Self {
+ fn new(
+ cx: &'a LateContext<'tcx>,
+ body: &'a mir::Body<'tcx>,
+ possible_origin: FxHashMap<mir::Local, HybridBitSet<mir::Local>>,
+ ) -> Self {
Self {
possible_borrower: TransitiveRelation::default(),
cx,
body,
+ possible_origin,
}
}
..
} = &terminator.kind
{
+ // TODO add doc
// If the call returns something with lifetimes,
// let's conservatively assume the returned value contains lifetime of all the arguments.
// For example, given `let y: Foo<'a> = foo(x)`, `y` is considered to be a possible borrower of `x`.
- if ContainsRegion.visit_ty(self.body.local_decls[*dest].ty).is_continue() {
- return;
- }
+
+ let mut immutable_borrowers = vec![];
+ let mut mutable_borrowers = vec![];
for op in args {
match op {
mir::Operand::Copy(p) | mir::Operand::Move(p) => {
- self.possible_borrower.add(p.local, *dest);
+ if let ty::Ref(_, _, Mutability::Mut) = self.body.local_decls[p.local].ty.kind() {
+ mutable_borrowers.push(p.local);
+ } else {
+ immutable_borrowers.push(p.local);
+ }
},
mir::Operand::Constant(..) => (),
}
}
+
+ let mut mutable_variables: Vec<mir::Local> = mutable_borrowers
+ .iter()
+ .filter_map(|r| self.possible_origin.get(r))
+ .flat_map(HybridBitSet::iter)
+ .collect();
+
+ if ContainsRegion.visit_ty(self.body.local_decls[*dest].ty).is_break() {
+ mutable_variables.push(*dest);
+ }
+
+ for y in mutable_variables {
+ for x in &immutable_borrowers {
+ self.possible_borrower.add(*x, y);
+ }
+ for x in &mutable_borrowers {
+ self.possible_borrower.add(*x, y);
+ }
+ }
+ }
+ }
+}
+
+/// Collect possible borrowed for every `&mut` local.
+/// For exampel, `_1 = &mut _2` generate _1: {_2,...}
+/// Known Problems: not sure all borrowed are tracked
+struct PossibleOriginVisitor<'a, 'tcx> {
+ possible_origin: TransitiveRelation<mir::Local>,
+ body: &'a mir::Body<'tcx>,
+}
+
+impl<'a, 'tcx> PossibleOriginVisitor<'a, 'tcx> {
+ fn new(body: &'a mir::Body<'tcx>) -> Self {
+ Self {
+ possible_origin: TransitiveRelation::default(),
+ body,
+ }
+ }
+
+ fn into_map(self, cx: &LateContext<'tcx>) -> FxHashMap<mir::Local, HybridBitSet<mir::Local>> {
+ let mut map = FxHashMap::default();
+ for row in (1..self.body.local_decls.len()).map(mir::Local::from_usize) {
+ if is_copy(cx, self.body.local_decls[row].ty) {
+ continue;
+ }
+
+ let borrowers = self.possible_origin.reachable_from(&row);
+ if !borrowers.is_empty() {
+ let mut bs = HybridBitSet::new_empty(self.body.local_decls.len());
+ for &c in borrowers {
+ if c != mir::Local::from_usize(0) {
+ bs.insert(c);
+ }
+ }
+
+ if !bs.is_empty() {
+ map.insert(row, bs);
+ }
+ }
+ }
+ map
+ }
+}
+
+impl<'a, 'tcx> mir::visit::Visitor<'tcx> for PossibleOriginVisitor<'a, 'tcx> {
+ fn visit_assign(&mut self, place: &mir::Place<'tcx>, rvalue: &mir::Rvalue<'_>, _location: mir::Location) {
+ let lhs = place.local;
+ match rvalue {
+ // Only consider `&mut`, which can modify origin place
+ mir::Rvalue::Ref(_, rustc_middle::mir::BorrowKind::Mut { .. }, borrowed) |
+ // _2: &mut _;
+ // _3 = move _2
+ mir::Rvalue::Use(mir::Operand::Move(borrowed)) |
+ // _3 = move _2 as &mut _;
+ mir::Rvalue::Cast(_, mir::Operand::Move(borrowed), _)
+ => {
+ self.possible_origin.add(lhs, borrowed.local);
+ },
+ _ => {},
}
}
}
declare_clippy_lint! {
/// **What it does:** Checks for use of redundant allocations anywhere in the code.
///
- /// **Why is this bad?** Expressions such as `Rc<&T>`, `Rc<Rc<T>>`, `Rc<Box<T>>`, `Box<&T>`
- /// add an unnecessary level of indirection.
+ /// **Why is this bad?** Expressions such as `Rc<&T>`, `Rc<Rc<T>>`, `Rc<Arc<T>>`, `Rc<Box<T>>`, Arc<&T>`, `Arc<Rc<T>>`,
+ /// `Arc<Arc<T>>`, `Arc<Box<T>>`, `Box<&T>`, `Box<Rc<T>>`, `Box<Arc<T>>`, `Box<Box<T>>`, add an unnecessary level of indirection.
///
/// **Known problems:** None.
///
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::{snippet, snippet_with_applicability};
use clippy_utils::{get_qpath_generic_tys, is_ty_param_diagnostic_item, is_ty_param_lang_item};
use rustc_errors::Applicability;
use rustc_hir::{self as hir, def_id::DefId, LangItem, QPath, TyKind};
use super::{utils, REDUNDANT_ALLOCATION};
pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool {
- if Some(def_id) == cx.tcx.lang_items().owned_box() {
- if let Some(span) = utils::match_borrows_parameter(cx, qpath) {
- let mut applicability = Applicability::MachineApplicable;
- span_lint_and_sugg(
- cx,
- REDUNDANT_ALLOCATION,
- hir_ty.span,
- "usage of `Box<&T>`",
- "try",
- snippet_with_applicability(cx, span, "..", &mut applicability).to_string(),
- applicability,
- );
- return true;
- }
+ let outer_sym = if Some(def_id) == cx.tcx.lang_items().owned_box() {
+ "Box"
+ } else if cx.tcx.is_diagnostic_item(sym::Rc, def_id) {
+ "Rc"
+ } else if cx.tcx.is_diagnostic_item(sym::Arc, def_id) {
+ "Arc"
+ } else {
+ return false;
+ };
+
+ if let Some(span) = utils::match_borrows_parameter(cx, qpath) {
+ let mut applicability = Applicability::MaybeIncorrect;
+ let generic_snippet = snippet_with_applicability(cx, span, "..", &mut applicability);
+ span_lint_and_then(
+ cx,
+ REDUNDANT_ALLOCATION,
+ hir_ty.span,
+ &format!("usage of `{}<{}>`", outer_sym, generic_snippet),
+ |diag| {
+ diag.span_suggestion(hir_ty.span, "try", format!("{}", generic_snippet), applicability);
+ diag.note(&format!(
+ "`{generic}` is already a pointer, `{outer}<{generic}>` allocates a pointer on the heap",
+ outer = outer_sym,
+ generic = generic_snippet
+ ));
+ },
+ );
+ return true;
}
- if cx.tcx.is_diagnostic_item(sym::Rc, def_id) {
- if let Some(ty) = is_ty_param_diagnostic_item(cx, qpath, sym::Rc) {
- let mut applicability = Applicability::MachineApplicable;
- span_lint_and_sugg(
- cx,
- REDUNDANT_ALLOCATION,
- hir_ty.span,
- "usage of `Rc<Rc<T>>`",
- "try",
- snippet_with_applicability(cx, ty.span, "..", &mut applicability).to_string(),
- applicability,
- );
- true
- } else if let Some(ty) = is_ty_param_lang_item(cx, qpath, LangItem::OwnedBox) {
- let qpath = match &ty.kind {
- TyKind::Path(qpath) => qpath,
- _ => return false,
- };
- let inner_span = match get_qpath_generic_tys(qpath).next() {
- Some(ty) => ty.span,
- None => return false,
- };
- let mut applicability = Applicability::MachineApplicable;
- span_lint_and_sugg(
- cx,
- REDUNDANT_ALLOCATION,
- hir_ty.span,
- "usage of `Rc<Box<T>>`",
- "try",
- format!(
- "Rc<{}>",
- snippet_with_applicability(cx, inner_span, "..", &mut applicability)
- ),
- applicability,
- );
- true
- } else {
- utils::match_borrows_parameter(cx, qpath).map_or(false, |span| {
- let mut applicability = Applicability::MachineApplicable;
- span_lint_and_sugg(
- cx,
- REDUNDANT_ALLOCATION,
+ let (inner_sym, ty) = if let Some(ty) = is_ty_param_lang_item(cx, qpath, LangItem::OwnedBox) {
+ ("Box", ty)
+ } else if let Some(ty) = is_ty_param_diagnostic_item(cx, qpath, sym::Rc) {
+ ("Rc", ty)
+ } else if let Some(ty) = is_ty_param_diagnostic_item(cx, qpath, sym::Arc) {
+ ("Arc", ty)
+ } else {
+ return false;
+ };
+
+ let inner_qpath = match &ty.kind {
+ TyKind::Path(inner_qpath) => inner_qpath,
+ _ => return false,
+ };
+ let inner_span = match get_qpath_generic_tys(inner_qpath).next() {
+ Some(ty) => ty.span,
+ None => return false,
+ };
+ if inner_sym == outer_sym {
+ let mut applicability = Applicability::MaybeIncorrect;
+ let generic_snippet = snippet_with_applicability(cx, inner_span, "..", &mut applicability);
+ span_lint_and_then(
+ cx,
+ REDUNDANT_ALLOCATION,
+ hir_ty.span,
+ &format!("usage of `{}<{}<{}>>`", outer_sym, inner_sym, generic_snippet),
+ |diag| {
+ diag.span_suggestion(
hir_ty.span,
- "usage of `Rc<&T>`",
"try",
- snippet_with_applicability(cx, span, "..", &mut applicability).to_string(),
+ format!("{}<{}>", outer_sym, generic_snippet),
applicability,
);
- true
- })
- }
+ diag.note(&format!(
+ "`{inner}<{generic}>` is already on the heap, `{outer}<{inner}<{generic}>>` makes an extra allocation",
+ outer = outer_sym,
+ inner = inner_sym,
+ generic = generic_snippet
+ ));
+ },
+ );
} else {
- false
+ let generic_snippet = snippet(cx, inner_span, "..");
+ span_lint_and_then(
+ cx,
+ REDUNDANT_ALLOCATION,
+ hir_ty.span,
+ &format!("usage of `{}<{}<{}>>`", outer_sym, inner_sym, generic_snippet),
+ |diag| {
+ diag.note(&format!(
+ "`{inner}<{generic}>` is already on the heap, `{outer}<{inner}<{generic}>>` makes an extra allocation",
+ outer = outer_sym,
+ inner = inner_sym,
+ generic = generic_snippet
+ ));
+ diag.help(&format!(
+ "consider using just `{outer}<{generic}>` or `{inner}<{generic}>`",
+ outer = outer_sym,
+ inner = inner_sym,
+ generic = generic_snippet
+ ));
+ },
+ );
}
+ true
}
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
if expr.span.from_expansion() {
if let Some(callee) = expr.span.source_callee() {
- if let ExpnKind::Macro {
- kind: MacroKind::Bang,
- name: symbol,
- proc_macro: _,
- } = callee.kind
- {
+ if let ExpnKind::Macro(MacroKind::Bang, symbol) = callee.kind {
if let ExprKind::Binary(ref cmp, left, _) = expr.kind {
let op = cmp.node;
if op.is_comparison() && cx.typeck_results().expr_ty(left).is_unit() {
lint_name: &str,
item: &'hir Item<'_>,
) -> Option<(String, &'static str)> {
- let result = cx.lint_store.check_lint_name(lint_name, Some(sym::clippy));
+ let result = cx
+ .lint_store
+ .check_lint_name(cx.sess(), lint_name, Some(sym::clippy), &[]);
if let CheckLintNameResult::Tool(Ok(lint_lst)) = result {
if let Some(group) = get_lint_group(cx, lint_lst[0]) {
if EXCLUDED_LINT_GROUPS.contains(&group.as_str()) {
use crate::{is_expn_of, match_def_path, paths};
use if_chain::if_chain;
-use rustc_ast::ast;
+use rustc_ast::ast::{self, LitKind};
use rustc_hir as hir;
use rustc_hir::{BorrowKind, Expr, ExprKind, StmtKind, UnOp};
use rustc_lint::LateContext;
-use rustc_span::source_map::Span;
+use rustc_span::{sym, ExpnKind, Span, Symbol};
/// Converts a hir binary operator to the corresponding `ast` type.
#[must_use]
}
None
}
+
+/// A parsed `format!` expansion
+pub struct FormatExpn<'tcx> {
+ /// Span of `format!(..)`
+ pub call_site: Span,
+ /// Inner `format_args!` expansion
+ pub format_args: FormatArgsExpn<'tcx>,
+}
+
+impl FormatExpn<'tcx> {
+ /// Parses an expanded `format!` invocation
+ pub fn parse(expr: &'tcx Expr<'tcx>) -> Option<Self> {
+ if_chain! {
+ if let ExprKind::Block(block, _) = expr.kind;
+ if let [stmt] = block.stmts;
+ if let StmtKind::Local(local) = stmt.kind;
+ if let Some(init) = local.init;
+ if let ExprKind::Call(_, [format_args]) = init.kind;
+ let expn_data = expr.span.ctxt().outer_expn_data();
+ if let ExpnKind::Macro(_, sym::format) = expn_data.kind;
+ if let Some(format_args) = FormatArgsExpn::parse(format_args);
+ then {
+ Some(FormatExpn {
+ call_site: expn_data.call_site,
+ format_args,
+ })
+ } else {
+ None
+ }
+ }
+ }
+}
+
+/// A parsed `format_args!` expansion
+pub struct FormatArgsExpn<'tcx> {
+ /// Span of the first argument, the format string
+ pub format_string_span: Span,
+ /// Values passed after the format string
+ pub value_args: Vec<&'tcx Expr<'tcx>>,
+
+ /// String literal expressions which represent the format string split by "{}"
+ pub format_string_parts: &'tcx [Expr<'tcx>],
+ /// Symbols corresponding to [`format_string_parts`]
+ pub format_string_symbols: Vec<Symbol>,
+ /// Expressions like `ArgumentV1::new(arg0, Debug::fmt)`
+ pub args: &'tcx [Expr<'tcx>],
+ /// The final argument passed to `Arguments::new_v1_formatted`, if applicable
+ pub fmt_expr: Option<&'tcx Expr<'tcx>>,
+}
+
+impl FormatArgsExpn<'tcx> {
+ /// Parses an expanded `format_args!` or `format_args_nl!` invocation
+ pub fn parse(expr: &'tcx Expr<'tcx>) -> Option<Self> {
+ if_chain! {
+ if let ExpnKind::Macro(_, name) = expr.span.ctxt().outer_expn_data().kind;
+ let name = name.as_str();
+ if name.ends_with("format_args") || name.ends_with("format_args_nl");
+ if let ExprKind::Call(_, args) = expr.kind;
+ if let Some((strs_ref, args, fmt_expr)) = match args {
+ // Arguments::new_v1
+ [strs_ref, args] => Some((strs_ref, args, None)),
+ // Arguments::new_v1_formatted
+ [strs_ref, args, fmt_expr] => Some((strs_ref, args, Some(fmt_expr))),
+ _ => None,
+ };
+ if let ExprKind::AddrOf(BorrowKind::Ref, _, strs_arr) = strs_ref.kind;
+ if let ExprKind::Array(format_string_parts) = strs_arr.kind;
+ if let Some(format_string_symbols) = format_string_parts
+ .iter()
+ .map(|e| {
+ if let ExprKind::Lit(lit) = &e.kind {
+ if let LitKind::Str(symbol, _style) = lit.node {
+ return Some(symbol);
+ }
+ }
+ None
+ })
+ .collect();
+ if let ExprKind::AddrOf(BorrowKind::Ref, _, args) = args.kind;
+ if let ExprKind::Match(args, [arm], _) = args.kind;
+ if let ExprKind::Tup(value_args) = args.kind;
+ if let Some(value_args) = value_args
+ .iter()
+ .map(|e| match e.kind {
+ ExprKind::AddrOf(_, _, e) => Some(e),
+ _ => None,
+ })
+ .collect();
+ if let ExprKind::Array(args) = arm.body.kind;
+ then {
+ Some(FormatArgsExpn {
+ format_string_span: strs_ref.span,
+ value_args,
+ format_string_parts,
+ format_string_symbols,
+ args,
+ fmt_expr,
+ })
+ } else {
+ None
+ }
+ }
+ }
+}
_ => return Res::Err,
};
let tcx = cx.tcx;
- let crates = tcx.crates();
+ let crates = tcx.crates(());
let krate = try_res!(crates.iter().find(|&&num| tcx.crate_name(num).as_str() == krate));
let first = try_res!(item_child_by_name(tcx, krate.as_def_id(), first));
let last = path
let data = span.ctxt().outer_expn_data();
let new_span = data.call_site;
- if let ExpnKind::Macro {
- kind: MacroKind::Bang,
- name: mac_name,
- proc_macro: _,
- } = data.kind
- {
+ if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind {
if mac_name.as_str() == name {
return Some(new_span);
}
let data = span.ctxt().outer_expn_data();
let new_span = data.call_site;
- if let ExpnKind::Macro {
- kind: MacroKind::Bang,
- name: mac_name,
- proc_macro: _,
- } = data.kind
- {
+ if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind {
if mac_name.as_str() == name {
return Some(new_span);
}
}
if let Some(suffix) = self.suffix {
+ if output.ends_with('.') {
+ output.push('0');
+ }
output.push('_');
output.push_str(suffix);
}
pub const DEREF_MUT_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "DerefMut", "deref_mut"];
pub const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"];
pub const DIR_BUILDER: [&str; 3] = ["std", "fs", "DirBuilder"];
-pub const DISPLAY_FMT_METHOD: [&str; 4] = ["core", "fmt", "Display", "fmt"];
pub const DISPLAY_TRAIT: [&str; 3] = ["core", "fmt", "Display"];
pub const DOUBLE_ENDED_ITERATOR: [&str; 4] = ["core", "iter", "traits", "DoubleEndedIterator"];
pub const DROP: [&str; 3] = ["core", "mem", "drop"];
pub const F64_EPSILON: [&str; 4] = ["core", "f64", "<impl f64>", "EPSILON"];
pub const FILE: [&str; 3] = ["std", "fs", "File"];
pub const FILE_TYPE: [&str; 3] = ["std", "fs", "FileType"];
-pub const FMT_ARGUMENTS_NEW_V1: [&str; 4] = ["core", "fmt", "Arguments", "new_v1"];
-pub const FMT_ARGUMENTS_NEW_V1_FORMATTED: [&str; 4] = ["core", "fmt", "Arguments", "new_v1_formatted"];
-pub const FMT_ARGUMENTV1_NEW: [&str; 4] = ["core", "fmt", "ArgumentV1", "new"];
pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"];
pub const FROM_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "FromIterator"];
pub const FROM_ITERATOR_METHOD: [&str; 6] = ["core", "iter", "traits", "collect", "FromIterator", "from_iter"];
use rustc_span::sym;
use rustc_span::symbol::{Ident, Symbol};
use rustc_span::DUMMY_SP;
+use rustc_trait_selection::infer::InferCtxtExt;
use rustc_trait_selection::traits::query::normalize::AtExt;
use crate::{match_def_path, must_use_attr};
}
/// Checks whether a type implements a trait.
+/// The function returns false in case the type contains an inference variable.
/// See also `get_trait_def_id`.
pub fn implements_trait<'tcx>(
cx: &LateContext<'tcx>,
trait_id: DefId,
ty_params: &[GenericArg<'tcx>],
) -> bool {
- // Do not check on infer_types to avoid panic in evaluate_obligation.
- if ty.has_infer_types() {
- return false;
- }
+ // Clippy shouldn't have infer types
+ assert!(!ty.needs_infer());
+
let ty = cx.tcx.erase_regions(ty);
if ty.has_escaping_bound_vars() {
return false;
}
let ty_params = cx.tcx.mk_substs(ty_params.iter());
- cx.tcx.type_implements_trait((trait_id, ty, ty_params, cx.param_env))
+ cx.tcx.infer_ctxt().enter(|infcx| {
+ infcx
+ .type_implements_trait(trait_id, ty, ty_params, cx.param_env)
+ .must_apply_modulo_regions()
+ })
}
/// Checks whether this type implements `Drop`.
recursive_visitor.visit_expr(expression);
recursive_visitor.seen_return_break_continue
}
+
+pub struct UsedAfterExprVisitor<'a, 'tcx> {
+ cx: &'a LateContext<'tcx>,
+ expr: &'tcx Expr<'tcx>,
+ definition: HirId,
+ past_expr: bool,
+ used_after_expr: bool,
+}
+impl<'a, 'tcx> UsedAfterExprVisitor<'a, 'tcx> {
+ pub fn is_found(cx: &'a LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
+ utils::path_to_local(expr).map_or(false, |definition| {
+ let mut visitor = UsedAfterExprVisitor {
+ cx,
+ expr,
+ definition,
+ past_expr: false,
+ used_after_expr: false,
+ };
+ utils::get_enclosing_block(cx, definition).map_or(false, |block| {
+ visitor.visit_block(block);
+ visitor.used_after_expr
+ })
+ })
+ }
+}
+
+impl<'a, 'tcx> intravisit::Visitor<'tcx> for UsedAfterExprVisitor<'a, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+ NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
+ }
+
+ fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
+ if self.used_after_expr {
+ return;
+ }
+
+ if expr.hir_id == self.expr.hir_id {
+ self.past_expr = true;
+ } else if self.past_expr && utils::path_to_local_id(expr, self.definition) {
+ self.used_after_expr = true;
+ } else {
+ intravisit::walk_expr(self, expr);
+ }
+ }
+}
[toolchain]
-channel = "nightly-2021-07-01"
+channel = "nightly-2021-07-15"
components = ["llvm-tools-preview", "rustc-dev", "rust-src"]
-#![allow(non_fmt_panic)]
+#![allow(non_fmt_panics)]
macro_rules! assert_const {
($len:expr) => {
--- /dev/null
+#![allow(dead_code)]
+#![deny(clippy::if_same_then_else, clippy::branches_sharing_code)]
+
+// ##################################
+// # Issue clippy#7369
+// ##################################
+#[derive(Debug)]
+pub struct FooBar {
+ foo: Vec<u32>,
+}
+
+impl FooBar {
+ pub fn bar(&mut self) {
+ if true {
+ self.foo.pop();
+ } else {
+ self.baz();
+
+ self.foo.pop();
+
+ self.baz()
+ }
+ }
+
+ fn baz(&mut self) {}
+}
+
+fn main() {}
+++ /dev/null
-// aux-build:macro_rules.rs
-
-#![warn(clippy::default_numeric_fallback)]
-#![allow(unused)]
-#![allow(clippy::never_loop)]
-#![allow(clippy::no_effect)]
-#![allow(clippy::unnecessary_operation)]
-#![allow(clippy::branches_sharing_code)]
-
-#[macro_use]
-extern crate macro_rules;
-
-mod basic_expr {
- fn test() {
- // Should lint unsuffixed literals typed `i32`.
- let x = 22;
- let x = [1, 2, 3];
- let x = if true { (1, 2) } else { (3, 4) };
- let x = match 1 {
- 1 => 1,
- _ => 2,
- };
-
- // Should lint unsuffixed literals typed `f64`.
- let x = 0.12;
-
- // Should NOT lint suffixed literals.
- let x = 22_i32;
- let x = 0.12_f64;
-
- // Should NOT lint literals in init expr if `Local` has a type annotation.
- let x: f64 = 0.1;
- let x: [i32; 3] = [1, 2, 3];
- let x: (i32, i32) = if true { (1, 2) } else { (3, 4) };
- let x: _ = 1;
- }
-}
-
-mod nested_local {
- fn test() {
- let x: _ = {
- // Should lint this because this literal is not bound to any types.
- let y = 1;
-
- // Should NOT lint this because this literal is bound to `_` of outer `Local`.
- 1
- };
-
- let x: _ = if true {
- // Should lint this because this literal is not bound to any types.
- let y = 1;
-
- // Should NOT lint this because this literal is bound to `_` of outer `Local`.
- 1
- } else {
- // Should lint this because this literal is not bound to any types.
- let y = 1;
-
- // Should NOT lint this because this literal is bound to `_` of outer `Local`.
- 2
- };
- }
-}
-
-mod function_def {
- fn ret_i32() -> i32 {
- // Even though the output type is specified,
- // this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
- 1
- }
-
- fn test() {
- // Should lint this because return type is inferred to `i32` and NOT bound to a concrete
- // type.
- let f = || -> _ { 1 };
-
- // Even though the output type is specified,
- // this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
- let f = || -> i32 { 1 };
- }
-}
-
-mod function_calls {
- fn concrete_arg(x: i32) {}
-
- fn generic_arg<T>(t: T) {}
-
- fn test() {
- // Should NOT lint this because the argument type is bound to a concrete type.
- concrete_arg(1);
-
- // Should lint this because the argument type is inferred to `i32` and NOT bound to a concrete type.
- generic_arg(1);
-
- // Should lint this because the argument type is inferred to `i32` and NOT bound to a concrete type.
- let x: _ = generic_arg(1);
- }
-}
-
-mod struct_ctor {
- struct ConcreteStruct {
- x: i32,
- }
-
- struct GenericStruct<T> {
- x: T,
- }
-
- fn test() {
- // Should NOT lint this because the field type is bound to a concrete type.
- ConcreteStruct { x: 1 };
-
- // Should lint this because the field type is inferred to `i32` and NOT bound to a concrete type.
- GenericStruct { x: 1 };
-
- // Should lint this because the field type is inferred to `i32` and NOT bound to a concrete type.
- let _ = GenericStruct { x: 1 };
- }
-}
-
-mod method_calls {
- struct StructForMethodCallTest {}
-
- impl StructForMethodCallTest {
- fn concrete_arg(&self, x: i32) {}
-
- fn generic_arg<T>(&self, t: T) {}
- }
-
- fn test() {
- let s = StructForMethodCallTest {};
-
- // Should NOT lint this because the argument type is bound to a concrete type.
- s.concrete_arg(1);
-
- // Should lint this because the argument type is bound to a concrete type.
- s.generic_arg(1);
- }
-}
-
-mod in_macro {
- macro_rules! internal_macro {
- () => {
- let x = 22;
- };
- }
-
- // Should lint in internal macro.
- fn internal() {
- internal_macro!();
- }
-
- // Should NOT lint in external macro.
- fn external() {
- default_numeric_fallback!();
- }
-}
-
-fn main() {}
+++ /dev/null
-error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback.rs:16:17
- |
-LL | let x = 22;
- | ^^ help: consider adding suffix: `22_i32`
- |
- = note: `-D clippy::default-numeric-fallback` implied by `-D warnings`
-
-error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback.rs:17:18
- |
-LL | let x = [1, 2, 3];
- | ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback.rs:17:21
- |
-LL | let x = [1, 2, 3];
- | ^ help: consider adding suffix: `2_i32`
-
-error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback.rs:17:24
- |
-LL | let x = [1, 2, 3];
- | ^ help: consider adding suffix: `3_i32`
-
-error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback.rs:18:28
- |
-LL | let x = if true { (1, 2) } else { (3, 4) };
- | ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback.rs:18:31
- |
-LL | let x = if true { (1, 2) } else { (3, 4) };
- | ^ help: consider adding suffix: `2_i32`
-
-error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback.rs:18:44
- |
-LL | let x = if true { (1, 2) } else { (3, 4) };
- | ^ help: consider adding suffix: `3_i32`
-
-error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback.rs:18:47
- |
-LL | let x = if true { (1, 2) } else { (3, 4) };
- | ^ help: consider adding suffix: `4_i32`
-
-error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback.rs:19:23
- |
-LL | let x = match 1 {
- | ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback.rs:20:13
- |
-LL | 1 => 1,
- | ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback.rs:20:18
- |
-LL | 1 => 1,
- | ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback.rs:21:18
- |
-LL | _ => 2,
- | ^ help: consider adding suffix: `2_i32`
-
-error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback.rs:25:17
- |
-LL | let x = 0.12;
- | ^^^^ help: consider adding suffix: `0.12_f64`
-
-error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback.rs:43:21
- |
-LL | let y = 1;
- | ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback.rs:51:21
- |
-LL | let y = 1;
- | ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback.rs:57:21
- |
-LL | let y = 1;
- | ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback.rs:69:9
- |
-LL | 1
- | ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback.rs:75:27
- |
-LL | let f = || -> _ { 1 };
- | ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback.rs:79:29
- |
-LL | let f = || -> i32 { 1 };
- | ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback.rs:93:21
- |
-LL | generic_arg(1);
- | ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback.rs:96:32
- |
-LL | let x: _ = generic_arg(1);
- | ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback.rs:114:28
- |
-LL | GenericStruct { x: 1 };
- | ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback.rs:117:36
- |
-LL | let _ = GenericStruct { x: 1 };
- | ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback.rs:137:23
- |
-LL | s.generic_arg(1);
- | ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback.rs:144:21
- |
-LL | let x = 22;
- | ^^ help: consider adding suffix: `22_i32`
-...
-LL | internal_macro!();
- | ------------------ in this macro invocation
- |
- = note: this error originates in the macro `internal_macro` (in Nightly builds, run with -Z macro-backtrace for more info)
-
-error: aborting due to 25 previous errors
-
--- /dev/null
+// run-rustfix
+// aux-build:macro_rules.rs
+
+#![warn(clippy::default_numeric_fallback)]
+#![allow(unused)]
+#![allow(clippy::never_loop)]
+#![allow(clippy::no_effect)]
+#![allow(clippy::unnecessary_operation)]
+#![allow(clippy::branches_sharing_code)]
+#![allow(clippy::match_single_binding)]
+
+#[macro_use]
+extern crate macro_rules;
+
+mod basic_expr {
+ fn test() {
+ // Should lint unsuffixed literals typed `f64`.
+ let x = 0.12_f64;
+ let x = [1.0_f64, 2.0_f64, 3.0_f64];
+ let x = if true { (1.0_f64, 2.0_f64) } else { (3.0_f64, 4.0_f64) };
+ let x = match 1.0_f64 {
+ _ => 1.0_f64,
+ };
+
+ // Should NOT lint suffixed literals.
+ let x = 0.12_f64;
+
+ // Should NOT lint literals in init expr if `Local` has a type annotation.
+ let x: f64 = 0.1;
+ let x: [f64; 3] = [1., 2., 3.];
+ let x: (f64, f64) = if true { (1., 2.) } else { (3., 4.) };
+ let x: _ = 1.;
+ }
+}
+
+mod nested_local {
+ fn test() {
+ let x: _ = {
+ // Should lint this because this literal is not bound to any types.
+ let y = 1.0_f64;
+
+ // Should NOT lint this because this literal is bound to `_` of outer `Local`.
+ 1.
+ };
+
+ let x: _ = if true {
+ // Should lint this because this literal is not bound to any types.
+ let y = 1.0_f64;
+
+ // Should NOT lint this because this literal is bound to `_` of outer `Local`.
+ 1.
+ } else {
+ // Should lint this because this literal is not bound to any types.
+ let y = 1.0_f64;
+
+ // Should NOT lint this because this literal is bound to `_` of outer `Local`.
+ 2.
+ };
+ }
+}
+
+mod function_def {
+ fn ret_f64() -> f64 {
+ // Even though the output type is specified,
+ // this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
+ 1.0_f64
+ }
+
+ fn test() {
+ // Should lint this because return type is inferred to `f64` and NOT bound to a concrete
+ // type.
+ let f = || -> _ { 1.0_f64 };
+
+ // Even though the output type is specified,
+ // this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
+ let f = || -> f64 { 1.0_f64 };
+ }
+}
+
+mod function_calls {
+ fn concrete_arg(f: f64) {}
+
+ fn generic_arg<T>(t: T) {}
+
+ fn test() {
+ // Should NOT lint this because the argument type is bound to a concrete type.
+ concrete_arg(1.);
+
+ // Should lint this because the argument type is inferred to `f64` and NOT bound to a concrete type.
+ generic_arg(1.0_f64);
+
+ // Should lint this because the argument type is inferred to `f64` and NOT bound to a concrete type.
+ let x: _ = generic_arg(1.0_f64);
+ }
+}
+
+mod struct_ctor {
+ struct ConcreteStruct {
+ x: f64,
+ }
+
+ struct GenericStruct<T> {
+ x: T,
+ }
+
+ fn test() {
+ // Should NOT lint this because the field type is bound to a concrete type.
+ ConcreteStruct { x: 1. };
+
+ // Should lint this because the field type is inferred to `f64` and NOT bound to a concrete type.
+ GenericStruct { x: 1.0_f64 };
+
+ // Should lint this because the field type is inferred to `f64` and NOT bound to a concrete type.
+ let _ = GenericStruct { x: 1.0_f64 };
+ }
+}
+
+mod enum_ctor {
+ enum ConcreteEnum {
+ X(f64),
+ }
+
+ enum GenericEnum<T> {
+ X(T),
+ }
+
+ fn test() {
+ // Should NOT lint this because the field type is bound to a concrete type.
+ ConcreteEnum::X(1.);
+
+ // Should lint this because the field type is inferred to `f64` and NOT bound to a concrete type.
+ GenericEnum::X(1.0_f64);
+ }
+}
+
+mod method_calls {
+ struct StructForMethodCallTest {}
+
+ impl StructForMethodCallTest {
+ fn concrete_arg(&self, f: f64) {}
+
+ fn generic_arg<T>(&self, t: T) {}
+ }
+
+ fn test() {
+ let s = StructForMethodCallTest {};
+
+ // Should NOT lint this because the argument type is bound to a concrete type.
+ s.concrete_arg(1.);
+
+ // Should lint this because the argument type is bound to a concrete type.
+ s.generic_arg(1.0_f64);
+ }
+}
+
+mod in_macro {
+ macro_rules! internal_macro {
+ () => {
+ let x = 22.0_f64;
+ };
+ }
+
+ // Should lint in internal macro.
+ fn internal() {
+ internal_macro!();
+ }
+
+ // Should NOT lint in external macro.
+ fn external() {
+ default_numeric_fallback!();
+ }
+}
+
+fn main() {}
--- /dev/null
+// run-rustfix
+// aux-build:macro_rules.rs
+
+#![warn(clippy::default_numeric_fallback)]
+#![allow(unused)]
+#![allow(clippy::never_loop)]
+#![allow(clippy::no_effect)]
+#![allow(clippy::unnecessary_operation)]
+#![allow(clippy::branches_sharing_code)]
+#![allow(clippy::match_single_binding)]
+
+#[macro_use]
+extern crate macro_rules;
+
+mod basic_expr {
+ fn test() {
+ // Should lint unsuffixed literals typed `f64`.
+ let x = 0.12;
+ let x = [1., 2., 3.];
+ let x = if true { (1., 2.) } else { (3., 4.) };
+ let x = match 1. {
+ _ => 1.,
+ };
+
+ // Should NOT lint suffixed literals.
+ let x = 0.12_f64;
+
+ // Should NOT lint literals in init expr if `Local` has a type annotation.
+ let x: f64 = 0.1;
+ let x: [f64; 3] = [1., 2., 3.];
+ let x: (f64, f64) = if true { (1., 2.) } else { (3., 4.) };
+ let x: _ = 1.;
+ }
+}
+
+mod nested_local {
+ fn test() {
+ let x: _ = {
+ // Should lint this because this literal is not bound to any types.
+ let y = 1.;
+
+ // Should NOT lint this because this literal is bound to `_` of outer `Local`.
+ 1.
+ };
+
+ let x: _ = if true {
+ // Should lint this because this literal is not bound to any types.
+ let y = 1.;
+
+ // Should NOT lint this because this literal is bound to `_` of outer `Local`.
+ 1.
+ } else {
+ // Should lint this because this literal is not bound to any types.
+ let y = 1.;
+
+ // Should NOT lint this because this literal is bound to `_` of outer `Local`.
+ 2.
+ };
+ }
+}
+
+mod function_def {
+ fn ret_f64() -> f64 {
+ // Even though the output type is specified,
+ // this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
+ 1.
+ }
+
+ fn test() {
+ // Should lint this because return type is inferred to `f64` and NOT bound to a concrete
+ // type.
+ let f = || -> _ { 1. };
+
+ // Even though the output type is specified,
+ // this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
+ let f = || -> f64 { 1. };
+ }
+}
+
+mod function_calls {
+ fn concrete_arg(f: f64) {}
+
+ fn generic_arg<T>(t: T) {}
+
+ fn test() {
+ // Should NOT lint this because the argument type is bound to a concrete type.
+ concrete_arg(1.);
+
+ // Should lint this because the argument type is inferred to `f64` and NOT bound to a concrete type.
+ generic_arg(1.);
+
+ // Should lint this because the argument type is inferred to `f64` and NOT bound to a concrete type.
+ let x: _ = generic_arg(1.);
+ }
+}
+
+mod struct_ctor {
+ struct ConcreteStruct {
+ x: f64,
+ }
+
+ struct GenericStruct<T> {
+ x: T,
+ }
+
+ fn test() {
+ // Should NOT lint this because the field type is bound to a concrete type.
+ ConcreteStruct { x: 1. };
+
+ // Should lint this because the field type is inferred to `f64` and NOT bound to a concrete type.
+ GenericStruct { x: 1. };
+
+ // Should lint this because the field type is inferred to `f64` and NOT bound to a concrete type.
+ let _ = GenericStruct { x: 1. };
+ }
+}
+
+mod enum_ctor {
+ enum ConcreteEnum {
+ X(f64),
+ }
+
+ enum GenericEnum<T> {
+ X(T),
+ }
+
+ fn test() {
+ // Should NOT lint this because the field type is bound to a concrete type.
+ ConcreteEnum::X(1.);
+
+ // Should lint this because the field type is inferred to `f64` and NOT bound to a concrete type.
+ GenericEnum::X(1.);
+ }
+}
+
+mod method_calls {
+ struct StructForMethodCallTest {}
+
+ impl StructForMethodCallTest {
+ fn concrete_arg(&self, f: f64) {}
+
+ fn generic_arg<T>(&self, t: T) {}
+ }
+
+ fn test() {
+ let s = StructForMethodCallTest {};
+
+ // Should NOT lint this because the argument type is bound to a concrete type.
+ s.concrete_arg(1.);
+
+ // Should lint this because the argument type is bound to a concrete type.
+ s.generic_arg(1.);
+ }
+}
+
+mod in_macro {
+ macro_rules! internal_macro {
+ () => {
+ let x = 22.;
+ };
+ }
+
+ // Should lint in internal macro.
+ fn internal() {
+ internal_macro!();
+ }
+
+ // Should NOT lint in external macro.
+ fn external() {
+ default_numeric_fallback!();
+ }
+}
+
+fn main() {}
--- /dev/null
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:18:17
+ |
+LL | let x = 0.12;
+ | ^^^^ help: consider adding suffix: `0.12_f64`
+ |
+ = note: `-D clippy::default-numeric-fallback` implied by `-D warnings`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:19:18
+ |
+LL | let x = [1., 2., 3.];
+ | ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:19:22
+ |
+LL | let x = [1., 2., 3.];
+ | ^^ help: consider adding suffix: `2.0_f64`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:19:26
+ |
+LL | let x = [1., 2., 3.];
+ | ^^ help: consider adding suffix: `3.0_f64`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:20:28
+ |
+LL | let x = if true { (1., 2.) } else { (3., 4.) };
+ | ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:20:32
+ |
+LL | let x = if true { (1., 2.) } else { (3., 4.) };
+ | ^^ help: consider adding suffix: `2.0_f64`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:20:46
+ |
+LL | let x = if true { (1., 2.) } else { (3., 4.) };
+ | ^^ help: consider adding suffix: `3.0_f64`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:20:50
+ |
+LL | let x = if true { (1., 2.) } else { (3., 4.) };
+ | ^^ help: consider adding suffix: `4.0_f64`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:21:23
+ |
+LL | let x = match 1. {
+ | ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:22:18
+ |
+LL | _ => 1.,
+ | ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:40:21
+ |
+LL | let y = 1.;
+ | ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:48:21
+ |
+LL | let y = 1.;
+ | ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:54:21
+ |
+LL | let y = 1.;
+ | ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:66:9
+ |
+LL | 1.
+ | ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:72:27
+ |
+LL | let f = || -> _ { 1. };
+ | ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:76:29
+ |
+LL | let f = || -> f64 { 1. };
+ | ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:90:21
+ |
+LL | generic_arg(1.);
+ | ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:93:32
+ |
+LL | let x: _ = generic_arg(1.);
+ | ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:111:28
+ |
+LL | GenericStruct { x: 1. };
+ | ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:114:36
+ |
+LL | let _ = GenericStruct { x: 1. };
+ | ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:132:24
+ |
+LL | GenericEnum::X(1.);
+ | ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:152:23
+ |
+LL | s.generic_arg(1.);
+ | ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:159:21
+ |
+LL | let x = 22.;
+ | ^^^ help: consider adding suffix: `22.0_f64`
+...
+LL | internal_macro!();
+ | ------------------ in this macro invocation
+ |
+ = note: this error originates in the macro `internal_macro` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 23 previous errors
+
--- /dev/null
+// run-rustfix
+// aux-build:macro_rules.rs
+
+#![warn(clippy::default_numeric_fallback)]
+#![allow(unused)]
+#![allow(clippy::never_loop)]
+#![allow(clippy::no_effect)]
+#![allow(clippy::unnecessary_operation)]
+#![allow(clippy::branches_sharing_code)]
+
+#[macro_use]
+extern crate macro_rules;
+
+mod basic_expr {
+ fn test() {
+ // Should lint unsuffixed literals typed `i32`.
+ let x = 22_i32;
+ let x = [1_i32, 2_i32, 3_i32];
+ let x = if true { (1_i32, 2_i32) } else { (3_i32, 4_i32) };
+ let x = match 1_i32 {
+ 1_i32 => 1_i32,
+ _ => 2_i32,
+ };
+
+ // Should NOT lint suffixed literals.
+ let x = 22_i32;
+
+ // Should NOT lint literals in init expr if `Local` has a type annotation.
+ let x: [i32; 3] = [1, 2, 3];
+ let x: (i32, i32) = if true { (1, 2) } else { (3, 4) };
+ let x: _ = 1;
+ }
+}
+
+mod nested_local {
+ fn test() {
+ let x: _ = {
+ // Should lint this because this literal is not bound to any types.
+ let y = 1_i32;
+
+ // Should NOT lint this because this literal is bound to `_` of outer `Local`.
+ 1
+ };
+
+ let x: _ = if true {
+ // Should lint this because this literal is not bound to any types.
+ let y = 1_i32;
+
+ // Should NOT lint this because this literal is bound to `_` of outer `Local`.
+ 1
+ } else {
+ // Should lint this because this literal is not bound to any types.
+ let y = 1_i32;
+
+ // Should NOT lint this because this literal is bound to `_` of outer `Local`.
+ 2
+ };
+ }
+}
+
+mod function_def {
+ fn ret_i32() -> i32 {
+ // Even though the output type is specified,
+ // this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
+ 1_i32
+ }
+
+ fn test() {
+ // Should lint this because return type is inferred to `i32` and NOT bound to a concrete
+ // type.
+ let f = || -> _ { 1_i32 };
+
+ // Even though the output type is specified,
+ // this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
+ let f = || -> i32 { 1_i32 };
+ }
+}
+
+mod function_calls {
+ fn concrete_arg(x: i32) {}
+
+ fn generic_arg<T>(t: T) {}
+
+ fn test() {
+ // Should NOT lint this because the argument type is bound to a concrete type.
+ concrete_arg(1);
+
+ // Should lint this because the argument type is inferred to `i32` and NOT bound to a concrete type.
+ generic_arg(1_i32);
+
+ // Should lint this because the argument type is inferred to `i32` and NOT bound to a concrete type.
+ let x: _ = generic_arg(1_i32);
+ }
+}
+
+mod struct_ctor {
+ struct ConcreteStruct {
+ x: i32,
+ }
+
+ struct GenericStruct<T> {
+ x: T,
+ }
+
+ fn test() {
+ // Should NOT lint this because the field type is bound to a concrete type.
+ ConcreteStruct { x: 1 };
+
+ // Should lint this because the field type is inferred to `i32` and NOT bound to a concrete type.
+ GenericStruct { x: 1_i32 };
+
+ // Should lint this because the field type is inferred to `i32` and NOT bound to a concrete type.
+ let _ = GenericStruct { x: 1_i32 };
+ }
+}
+
+mod enum_ctor {
+ enum ConcreteEnum {
+ X(i32),
+ }
+
+ enum GenericEnum<T> {
+ X(T),
+ }
+
+ fn test() {
+ // Should NOT lint this because the field type is bound to a concrete type.
+ ConcreteEnum::X(1);
+
+ // Should lint this because the field type is inferred to `i32` and NOT bound to a concrete type.
+ GenericEnum::X(1_i32);
+ }
+}
+
+mod method_calls {
+ struct StructForMethodCallTest {}
+
+ impl StructForMethodCallTest {
+ fn concrete_arg(&self, x: i32) {}
+
+ fn generic_arg<T>(&self, t: T) {}
+ }
+
+ fn test() {
+ let s = StructForMethodCallTest {};
+
+ // Should NOT lint this because the argument type is bound to a concrete type.
+ s.concrete_arg(1);
+
+ // Should lint this because the argument type is bound to a concrete type.
+ s.generic_arg(1_i32);
+ }
+}
+
+mod in_macro {
+ macro_rules! internal_macro {
+ () => {
+ let x = 22_i32;
+ };
+ }
+
+ // Should lint in internal macro.
+ fn internal() {
+ internal_macro!();
+ }
+
+ // Should NOT lint in external macro.
+ fn external() {
+ default_numeric_fallback!();
+ }
+}
+
+fn main() {}
--- /dev/null
+// run-rustfix
+// aux-build:macro_rules.rs
+
+#![warn(clippy::default_numeric_fallback)]
+#![allow(unused)]
+#![allow(clippy::never_loop)]
+#![allow(clippy::no_effect)]
+#![allow(clippy::unnecessary_operation)]
+#![allow(clippy::branches_sharing_code)]
+
+#[macro_use]
+extern crate macro_rules;
+
+mod basic_expr {
+ fn test() {
+ // Should lint unsuffixed literals typed `i32`.
+ let x = 22;
+ let x = [1, 2, 3];
+ let x = if true { (1, 2) } else { (3, 4) };
+ let x = match 1 {
+ 1 => 1,
+ _ => 2,
+ };
+
+ // Should NOT lint suffixed literals.
+ let x = 22_i32;
+
+ // Should NOT lint literals in init expr if `Local` has a type annotation.
+ let x: [i32; 3] = [1, 2, 3];
+ let x: (i32, i32) = if true { (1, 2) } else { (3, 4) };
+ let x: _ = 1;
+ }
+}
+
+mod nested_local {
+ fn test() {
+ let x: _ = {
+ // Should lint this because this literal is not bound to any types.
+ let y = 1;
+
+ // Should NOT lint this because this literal is bound to `_` of outer `Local`.
+ 1
+ };
+
+ let x: _ = if true {
+ // Should lint this because this literal is not bound to any types.
+ let y = 1;
+
+ // Should NOT lint this because this literal is bound to `_` of outer `Local`.
+ 1
+ } else {
+ // Should lint this because this literal is not bound to any types.
+ let y = 1;
+
+ // Should NOT lint this because this literal is bound to `_` of outer `Local`.
+ 2
+ };
+ }
+}
+
+mod function_def {
+ fn ret_i32() -> i32 {
+ // Even though the output type is specified,
+ // this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
+ 1
+ }
+
+ fn test() {
+ // Should lint this because return type is inferred to `i32` and NOT bound to a concrete
+ // type.
+ let f = || -> _ { 1 };
+
+ // Even though the output type is specified,
+ // this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
+ let f = || -> i32 { 1 };
+ }
+}
+
+mod function_calls {
+ fn concrete_arg(x: i32) {}
+
+ fn generic_arg<T>(t: T) {}
+
+ fn test() {
+ // Should NOT lint this because the argument type is bound to a concrete type.
+ concrete_arg(1);
+
+ // Should lint this because the argument type is inferred to `i32` and NOT bound to a concrete type.
+ generic_arg(1);
+
+ // Should lint this because the argument type is inferred to `i32` and NOT bound to a concrete type.
+ let x: _ = generic_arg(1);
+ }
+}
+
+mod struct_ctor {
+ struct ConcreteStruct {
+ x: i32,
+ }
+
+ struct GenericStruct<T> {
+ x: T,
+ }
+
+ fn test() {
+ // Should NOT lint this because the field type is bound to a concrete type.
+ ConcreteStruct { x: 1 };
+
+ // Should lint this because the field type is inferred to `i32` and NOT bound to a concrete type.
+ GenericStruct { x: 1 };
+
+ // Should lint this because the field type is inferred to `i32` and NOT bound to a concrete type.
+ let _ = GenericStruct { x: 1 };
+ }
+}
+
+mod enum_ctor {
+ enum ConcreteEnum {
+ X(i32),
+ }
+
+ enum GenericEnum<T> {
+ X(T),
+ }
+
+ fn test() {
+ // Should NOT lint this because the field type is bound to a concrete type.
+ ConcreteEnum::X(1);
+
+ // Should lint this because the field type is inferred to `i32` and NOT bound to a concrete type.
+ GenericEnum::X(1);
+ }
+}
+
+mod method_calls {
+ struct StructForMethodCallTest {}
+
+ impl StructForMethodCallTest {
+ fn concrete_arg(&self, x: i32) {}
+
+ fn generic_arg<T>(&self, t: T) {}
+ }
+
+ fn test() {
+ let s = StructForMethodCallTest {};
+
+ // Should NOT lint this because the argument type is bound to a concrete type.
+ s.concrete_arg(1);
+
+ // Should lint this because the argument type is bound to a concrete type.
+ s.generic_arg(1);
+ }
+}
+
+mod in_macro {
+ macro_rules! internal_macro {
+ () => {
+ let x = 22;
+ };
+ }
+
+ // Should lint in internal macro.
+ fn internal() {
+ internal_macro!();
+ }
+
+ // Should NOT lint in external macro.
+ fn external() {
+ default_numeric_fallback!();
+ }
+}
+
+fn main() {}
--- /dev/null
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:17:17
+ |
+LL | let x = 22;
+ | ^^ help: consider adding suffix: `22_i32`
+ |
+ = note: `-D clippy::default-numeric-fallback` implied by `-D warnings`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:18:18
+ |
+LL | let x = [1, 2, 3];
+ | ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:18:21
+ |
+LL | let x = [1, 2, 3];
+ | ^ help: consider adding suffix: `2_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:18:24
+ |
+LL | let x = [1, 2, 3];
+ | ^ help: consider adding suffix: `3_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:19:28
+ |
+LL | let x = if true { (1, 2) } else { (3, 4) };
+ | ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:19:31
+ |
+LL | let x = if true { (1, 2) } else { (3, 4) };
+ | ^ help: consider adding suffix: `2_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:19:44
+ |
+LL | let x = if true { (1, 2) } else { (3, 4) };
+ | ^ help: consider adding suffix: `3_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:19:47
+ |
+LL | let x = if true { (1, 2) } else { (3, 4) };
+ | ^ help: consider adding suffix: `4_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:20:23
+ |
+LL | let x = match 1 {
+ | ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:21:13
+ |
+LL | 1 => 1,
+ | ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:21:18
+ |
+LL | 1 => 1,
+ | ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:22:18
+ |
+LL | _ => 2,
+ | ^ help: consider adding suffix: `2_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:39:21
+ |
+LL | let y = 1;
+ | ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:47:21
+ |
+LL | let y = 1;
+ | ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:53:21
+ |
+LL | let y = 1;
+ | ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:65:9
+ |
+LL | 1
+ | ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:71:27
+ |
+LL | let f = || -> _ { 1 };
+ | ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:75:29
+ |
+LL | let f = || -> i32 { 1 };
+ | ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:89:21
+ |
+LL | generic_arg(1);
+ | ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:92:32
+ |
+LL | let x: _ = generic_arg(1);
+ | ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:110:28
+ |
+LL | GenericStruct { x: 1 };
+ | ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:113:36
+ |
+LL | let _ = GenericStruct { x: 1 };
+ | ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:131:24
+ |
+LL | GenericEnum::X(1);
+ | ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:151:23
+ |
+LL | s.generic_arg(1);
+ | ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:158:21
+ |
+LL | let x = 22;
+ | ^^ help: consider adding suffix: `22_i32`
+...
+LL | internal_macro!();
+ | ------------------ in this macro invocation
+ |
+ = note: this error originates in the macro `internal_macro` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 25 previous errors
+
LL | #[warn(clippy::temporary_cstring_as_ptr)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr`
-error: lint `clippy::panic_params` has been renamed to `non_fmt_panic`
+error: lint `clippy::panic_params` has been renamed to `non_fmt_panics`
--> $DIR/deprecated.rs:11:8
|
LL | #[warn(clippy::panic_params)]
- | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panic`
+ | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics`
error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints`
--> $DIR/deprecated.rs:12:8
fn test_deref_with_trait_method() {
let _ = [Bar].iter().map(|s| s.to_string()).collect::<Vec<_>>();
}
+
+fn mutable_closure_used_again(x: Vec<i32>, y: Vec<i32>, z: Vec<i32>) {
+ let mut res = Vec::new();
+ let mut add_to_res = |n| res.push(n);
+ x.into_iter().for_each(&mut add_to_res);
+ y.into_iter().for_each(&mut add_to_res);
+ z.into_iter().for_each(add_to_res);
+}
+
+fn mutable_closure_in_loop() {
+ let mut value = 0;
+ let mut closure = |n| value += n;
+ for _ in 0..5 {
+ Some(1).map(&mut closure);
+ }
+}
fn test_deref_with_trait_method() {
let _ = [Bar].iter().map(|s| s.to_string()).collect::<Vec<_>>();
}
+
+fn mutable_closure_used_again(x: Vec<i32>, y: Vec<i32>, z: Vec<i32>) {
+ let mut res = Vec::new();
+ let mut add_to_res = |n| res.push(n);
+ x.into_iter().for_each(|x| add_to_res(x));
+ y.into_iter().for_each(|x| add_to_res(x));
+ z.into_iter().for_each(|x| add_to_res(x));
+}
+
+fn mutable_closure_in_loop() {
+ let mut value = 0;
+ let mut closure = |n| value += n;
+ for _ in 0..5 {
+ Some(1).map(|n| closure(n));
+ }
+}
LL | let a = Some(1u8).map(|a| closure(a));
| ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `closure`
-error: aborting due to 13 previous errors
+error: redundant closure
+ --> $DIR/eta.rs:227:28
+ |
+LL | x.into_iter().for_each(|x| add_to_res(x));
+ | ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut add_to_res`
+
+error: redundant closure
+ --> $DIR/eta.rs:228:28
+ |
+LL | y.into_iter().for_each(|x| add_to_res(x));
+ | ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut add_to_res`
+
+error: redundant closure
+ --> $DIR/eta.rs:229:28
+ |
+LL | z.into_iter().for_each(|x| add_to_res(x));
+ | ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `add_to_res`
+
+error: redundant closure
+ --> $DIR/eta.rs:236:21
+ |
+LL | Some(1).map(|n| closure(n));
+ | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut closure`
+
+error: aborting due to 17 previous errors
-error: use of `writeln!(stderr(), ...).unwrap()`. Consider using `eprintln!` instead
+error: use of `writeln!(stderr(), ...).unwrap()`
--> $DIR/explicit_write_non_rustfix.rs:7:5
|
LL | writeln!(std::io::stderr(), "foo {}", bar).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::explicit-write` implied by `-D warnings`
+ = help: consider using `eprintln!` instead
error: aborting due to previous error
// Wrap it with braces
let v: Vec<String> = vec!["foo".to_string(), "bar".to_string()];
let _s: String = (&*v.join("\n")).to_string();
+
+ format!("prepend {:+}", "s");
}
// Wrap it with braces
let v: Vec<String> = vec!["foo".to_string(), "bar".to_string()];
let _s: String = format!("{}", &*v.join("\n"));
+
+ format!("prepend {:+}", "s");
}
+++ /dev/null
-// run-rustfix
-#![warn(clippy::all)]
-#![allow(clippy::boxed_local, clippy::needless_pass_by_value)]
-#![allow(clippy::blacklisted_name, unused_variables, dead_code)]
-
-use std::boxed::Box;
-use std::rc::Rc;
-
-pub struct MyStruct {}
-
-pub struct SubT<T> {
- foo: T,
-}
-
-pub enum MyEnum {
- One,
- Two,
-}
-
-// Rc<&T>
-
-pub fn test1<T>(foo: &T) {}
-
-pub fn test2(foo: &MyStruct) {}
-
-pub fn test3(foo: &MyEnum) {}
-
-pub fn test4_neg(foo: Rc<SubT<&usize>>) {}
-
-// Rc<Rc<T>>
-
-pub fn test5(a: Rc<bool>) {}
-
-// Rc<Box<T>>
-
-pub fn test6(a: Rc<bool>) {}
-
-// Box<&T>
-
-pub fn test7<T>(foo: &T) {}
-
-pub fn test8(foo: &MyStruct) {}
-
-pub fn test9(foo: &MyEnum) {}
-
-pub fn test10_neg(foo: Box<SubT<&usize>>) {}
-
-fn main() {}
-// run-rustfix
#![warn(clippy::all)]
#![allow(clippy::boxed_local, clippy::needless_pass_by_value)]
#![allow(clippy::blacklisted_name, unused_variables, dead_code)]
-
-use std::boxed::Box;
-use std::rc::Rc;
+#![allow(unused_imports)]
pub struct MyStruct {}
Two,
}
-// Rc<&T>
+mod outer_box {
+ use crate::MyEnum;
+ use crate::MyStruct;
+ use crate::SubT;
+ use std::boxed::Box;
+ use std::rc::Rc;
+ use std::sync::Arc;
-pub fn test1<T>(foo: Rc<&T>) {}
+ pub fn box_test6<T>(foo: Box<Rc<T>>) {}
-pub fn test2(foo: Rc<&MyStruct>) {}
+ pub fn box_test7<T>(foo: Box<Arc<T>>) {}
-pub fn test3(foo: Rc<&MyEnum>) {}
+ pub fn box_test8() -> Box<Rc<SubT<usize>>> {
+ unimplemented!();
+ }
-pub fn test4_neg(foo: Rc<SubT<&usize>>) {}
+ pub fn box_test9<T>(foo: Box<Arc<T>>) -> Box<Arc<SubT<T>>> {
+ unimplemented!();
+ }
+}
-// Rc<Rc<T>>
+mod outer_rc {
+ use crate::MyEnum;
+ use crate::MyStruct;
+ use crate::SubT;
+ use std::boxed::Box;
+ use std::rc::Rc;
+ use std::sync::Arc;
-pub fn test5(a: Rc<Rc<bool>>) {}
+ pub fn rc_test5(a: Rc<Box<bool>>) {}
-// Rc<Box<T>>
+ pub fn rc_test7(a: Rc<Arc<bool>>) {}
-pub fn test6(a: Rc<Box<bool>>) {}
+ pub fn rc_test8() -> Rc<Box<SubT<usize>>> {
+ unimplemented!();
+ }
-// Box<&T>
+ pub fn rc_test9<T>(foo: Rc<Arc<T>>) -> Rc<Arc<SubT<T>>> {
+ unimplemented!();
+ }
+}
-pub fn test7<T>(foo: Box<&T>) {}
+mod outer_arc {
+ use crate::MyEnum;
+ use crate::MyStruct;
+ use crate::SubT;
+ use std::boxed::Box;
+ use std::rc::Rc;
+ use std::sync::Arc;
-pub fn test8(foo: Box<&MyStruct>) {}
+ pub fn arc_test5(a: Arc<Box<bool>>) {}
-pub fn test9(foo: Box<&MyEnum>) {}
+ pub fn arc_test6(a: Arc<Rc<bool>>) {}
-pub fn test10_neg(foo: Box<SubT<&usize>>) {}
+ pub fn arc_test8() -> Arc<Box<SubT<usize>>> {
+ unimplemented!();
+ }
+
+ pub fn arc_test9<T>(foo: Arc<Rc<T>>) -> Arc<Rc<SubT<T>>> {
+ unimplemented!();
+ }
+}
fn main() {}
-error: usage of `Rc<&T>`
- --> $DIR/redundant_allocation.rs:22:22
+error: usage of `Box<Rc<T>>`
+ --> $DIR/redundant_allocation.rs:25:30
|
-LL | pub fn test1<T>(foo: Rc<&T>) {}
- | ^^^^^^ help: try: `&T`
+LL | pub fn box_test6<T>(foo: Box<Rc<T>>) {}
+ | ^^^^^^^^^^
|
= note: `-D clippy::redundant-allocation` implied by `-D warnings`
+ = note: `Rc<T>` is already on the heap, `Box<Rc<T>>` makes an extra allocation
+ = help: consider using just `Box<T>` or `Rc<T>`
-error: usage of `Rc<&T>`
- --> $DIR/redundant_allocation.rs:24:19
+error: usage of `Box<Arc<T>>`
+ --> $DIR/redundant_allocation.rs:27:30
|
-LL | pub fn test2(foo: Rc<&MyStruct>) {}
- | ^^^^^^^^^^^^^ help: try: `&MyStruct`
+LL | pub fn box_test7<T>(foo: Box<Arc<T>>) {}
+ | ^^^^^^^^^^^
+ |
+ = note: `Arc<T>` is already on the heap, `Box<Arc<T>>` makes an extra allocation
+ = help: consider using just `Box<T>` or `Arc<T>`
+
+error: usage of `Box<Rc<SubT<usize>>>`
+ --> $DIR/redundant_allocation.rs:29:27
+ |
+LL | pub fn box_test8() -> Box<Rc<SubT<usize>>> {
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `Rc<SubT<usize>>` is already on the heap, `Box<Rc<SubT<usize>>>` makes an extra allocation
+ = help: consider using just `Box<SubT<usize>>` or `Rc<SubT<usize>>`
+
+error: usage of `Box<Arc<T>>`
+ --> $DIR/redundant_allocation.rs:33:30
+ |
+LL | pub fn box_test9<T>(foo: Box<Arc<T>>) -> Box<Arc<SubT<T>>> {
+ | ^^^^^^^^^^^
+ |
+ = note: `Arc<T>` is already on the heap, `Box<Arc<T>>` makes an extra allocation
+ = help: consider using just `Box<T>` or `Arc<T>`
+
+error: usage of `Box<Arc<SubT<T>>>`
+ --> $DIR/redundant_allocation.rs:33:46
+ |
+LL | pub fn box_test9<T>(foo: Box<Arc<T>>) -> Box<Arc<SubT<T>>> {
+ | ^^^^^^^^^^^^^^^^^
+ |
+ = note: `Arc<SubT<T>>` is already on the heap, `Box<Arc<SubT<T>>>` makes an extra allocation
+ = help: consider using just `Box<SubT<T>>` or `Arc<SubT<T>>`
+
+error: usage of `Rc<Box<bool>>`
+ --> $DIR/redundant_allocation.rs:46:24
+ |
+LL | pub fn rc_test5(a: Rc<Box<bool>>) {}
+ | ^^^^^^^^^^^^^
+ |
+ = note: `Box<bool>` is already on the heap, `Rc<Box<bool>>` makes an extra allocation
+ = help: consider using just `Rc<bool>` or `Box<bool>`
+
+error: usage of `Rc<Arc<bool>>`
+ --> $DIR/redundant_allocation.rs:48:24
+ |
+LL | pub fn rc_test7(a: Rc<Arc<bool>>) {}
+ | ^^^^^^^^^^^^^
+ |
+ = note: `Arc<bool>` is already on the heap, `Rc<Arc<bool>>` makes an extra allocation
+ = help: consider using just `Rc<bool>` or `Arc<bool>`
+
+error: usage of `Rc<Box<SubT<usize>>>`
+ --> $DIR/redundant_allocation.rs:50:26
+ |
+LL | pub fn rc_test8() -> Rc<Box<SubT<usize>>> {
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `Box<SubT<usize>>` is already on the heap, `Rc<Box<SubT<usize>>>` makes an extra allocation
+ = help: consider using just `Rc<SubT<usize>>` or `Box<SubT<usize>>`
+
+error: usage of `Rc<Arc<T>>`
+ --> $DIR/redundant_allocation.rs:54:29
+ |
+LL | pub fn rc_test9<T>(foo: Rc<Arc<T>>) -> Rc<Arc<SubT<T>>> {
+ | ^^^^^^^^^^
+ |
+ = note: `Arc<T>` is already on the heap, `Rc<Arc<T>>` makes an extra allocation
+ = help: consider using just `Rc<T>` or `Arc<T>`
-error: usage of `Rc<&T>`
- --> $DIR/redundant_allocation.rs:26:19
+error: usage of `Rc<Arc<SubT<T>>>`
+ --> $DIR/redundant_allocation.rs:54:44
+ |
+LL | pub fn rc_test9<T>(foo: Rc<Arc<T>>) -> Rc<Arc<SubT<T>>> {
+ | ^^^^^^^^^^^^^^^^
|
-LL | pub fn test3(foo: Rc<&MyEnum>) {}
- | ^^^^^^^^^^^ help: try: `&MyEnum`
+ = note: `Arc<SubT<T>>` is already on the heap, `Rc<Arc<SubT<T>>>` makes an extra allocation
+ = help: consider using just `Rc<SubT<T>>` or `Arc<SubT<T>>`
-error: usage of `Rc<Rc<T>>`
- --> $DIR/redundant_allocation.rs:32:17
+error: usage of `Arc<Box<bool>>`
+ --> $DIR/redundant_allocation.rs:67:25
|
-LL | pub fn test5(a: Rc<Rc<bool>>) {}
- | ^^^^^^^^^^^^ help: try: `Rc<bool>`
+LL | pub fn arc_test5(a: Arc<Box<bool>>) {}
+ | ^^^^^^^^^^^^^^
+ |
+ = note: `Box<bool>` is already on the heap, `Arc<Box<bool>>` makes an extra allocation
+ = help: consider using just `Arc<bool>` or `Box<bool>`
-error: usage of `Rc<Box<T>>`
- --> $DIR/redundant_allocation.rs:36:17
+error: usage of `Arc<Rc<bool>>`
+ --> $DIR/redundant_allocation.rs:69:25
+ |
+LL | pub fn arc_test6(a: Arc<Rc<bool>>) {}
+ | ^^^^^^^^^^^^^
|
-LL | pub fn test6(a: Rc<Box<bool>>) {}
- | ^^^^^^^^^^^^^ help: try: `Rc<bool>`
+ = note: `Rc<bool>` is already on the heap, `Arc<Rc<bool>>` makes an extra allocation
+ = help: consider using just `Arc<bool>` or `Rc<bool>`
-error: usage of `Box<&T>`
- --> $DIR/redundant_allocation.rs:40:22
+error: usage of `Arc<Box<SubT<usize>>>`
+ --> $DIR/redundant_allocation.rs:71:27
|
-LL | pub fn test7<T>(foo: Box<&T>) {}
- | ^^^^^^^ help: try: `&T`
+LL | pub fn arc_test8() -> Arc<Box<SubT<usize>>> {
+ | ^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `Box<SubT<usize>>` is already on the heap, `Arc<Box<SubT<usize>>>` makes an extra allocation
+ = help: consider using just `Arc<SubT<usize>>` or `Box<SubT<usize>>`
-error: usage of `Box<&T>`
- --> $DIR/redundant_allocation.rs:42:19
+error: usage of `Arc<Rc<T>>`
+ --> $DIR/redundant_allocation.rs:75:30
+ |
+LL | pub fn arc_test9<T>(foo: Arc<Rc<T>>) -> Arc<Rc<SubT<T>>> {
+ | ^^^^^^^^^^
|
-LL | pub fn test8(foo: Box<&MyStruct>) {}
- | ^^^^^^^^^^^^^^ help: try: `&MyStruct`
+ = note: `Rc<T>` is already on the heap, `Arc<Rc<T>>` makes an extra allocation
+ = help: consider using just `Arc<T>` or `Rc<T>`
-error: usage of `Box<&T>`
- --> $DIR/redundant_allocation.rs:44:19
+error: usage of `Arc<Rc<SubT<T>>>`
+ --> $DIR/redundant_allocation.rs:75:45
+ |
+LL | pub fn arc_test9<T>(foo: Arc<Rc<T>>) -> Arc<Rc<SubT<T>>> {
+ | ^^^^^^^^^^^^^^^^
|
-LL | pub fn test9(foo: Box<&MyEnum>) {}
- | ^^^^^^^^^^^^ help: try: `&MyEnum`
+ = note: `Rc<SubT<T>>` is already on the heap, `Arc<Rc<SubT<T>>>` makes an extra allocation
+ = help: consider using just `Arc<SubT<T>>` or `Rc<SubT<T>>`
-error: aborting due to 8 previous errors
+error: aborting due to 15 previous errors
--- /dev/null
+// run-rustfix
+#![warn(clippy::all)]
+#![allow(clippy::boxed_local, clippy::needless_pass_by_value)]
+#![allow(clippy::blacklisted_name, unused_variables, dead_code)]
+#![allow(unused_imports)]
+
+pub struct MyStruct {}
+
+pub struct SubT<T> {
+ foo: T,
+}
+
+pub enum MyEnum {
+ One,
+ Two,
+}
+
+mod outer_box {
+ use crate::MyEnum;
+ use crate::MyStruct;
+ use crate::SubT;
+ use std::boxed::Box;
+ use std::rc::Rc;
+ use std::sync::Arc;
+
+ pub fn box_test1<T>(foo: &T) {}
+
+ pub fn box_test2(foo: &MyStruct) {}
+
+ pub fn box_test3(foo: &MyEnum) {}
+
+ pub fn box_test4_neg(foo: Box<SubT<&usize>>) {}
+
+ pub fn box_test5<T>(foo: Box<T>) {}
+}
+
+mod outer_rc {
+ use crate::MyEnum;
+ use crate::MyStruct;
+ use crate::SubT;
+ use std::boxed::Box;
+ use std::rc::Rc;
+ use std::sync::Arc;
+
+ pub fn rc_test1<T>(foo: &T) {}
+
+ pub fn rc_test2(foo: &MyStruct) {}
+
+ pub fn rc_test3(foo: &MyEnum) {}
+
+ pub fn rc_test4_neg(foo: Rc<SubT<&usize>>) {}
+
+ pub fn rc_test6(a: Rc<bool>) {}
+}
+
+mod outer_arc {
+ use crate::MyEnum;
+ use crate::MyStruct;
+ use crate::SubT;
+ use std::boxed::Box;
+ use std::rc::Rc;
+ use std::sync::Arc;
+
+ pub fn arc_test1<T>(foo: &T) {}
+
+ pub fn arc_test2(foo: &MyStruct) {}
+
+ pub fn arc_test3(foo: &MyEnum) {}
+
+ pub fn arc_test4_neg(foo: Arc<SubT<&usize>>) {}
+
+ pub fn arc_test7(a: Arc<bool>) {}
+}
+
+fn main() {}
--- /dev/null
+// run-rustfix
+#![warn(clippy::all)]
+#![allow(clippy::boxed_local, clippy::needless_pass_by_value)]
+#![allow(clippy::blacklisted_name, unused_variables, dead_code)]
+#![allow(unused_imports)]
+
+pub struct MyStruct {}
+
+pub struct SubT<T> {
+ foo: T,
+}
+
+pub enum MyEnum {
+ One,
+ Two,
+}
+
+mod outer_box {
+ use crate::MyEnum;
+ use crate::MyStruct;
+ use crate::SubT;
+ use std::boxed::Box;
+ use std::rc::Rc;
+ use std::sync::Arc;
+
+ pub fn box_test1<T>(foo: Box<&T>) {}
+
+ pub fn box_test2(foo: Box<&MyStruct>) {}
+
+ pub fn box_test3(foo: Box<&MyEnum>) {}
+
+ pub fn box_test4_neg(foo: Box<SubT<&usize>>) {}
+
+ pub fn box_test5<T>(foo: Box<Box<T>>) {}
+}
+
+mod outer_rc {
+ use crate::MyEnum;
+ use crate::MyStruct;
+ use crate::SubT;
+ use std::boxed::Box;
+ use std::rc::Rc;
+ use std::sync::Arc;
+
+ pub fn rc_test1<T>(foo: Rc<&T>) {}
+
+ pub fn rc_test2(foo: Rc<&MyStruct>) {}
+
+ pub fn rc_test3(foo: Rc<&MyEnum>) {}
+
+ pub fn rc_test4_neg(foo: Rc<SubT<&usize>>) {}
+
+ pub fn rc_test6(a: Rc<Rc<bool>>) {}
+}
+
+mod outer_arc {
+ use crate::MyEnum;
+ use crate::MyStruct;
+ use crate::SubT;
+ use std::boxed::Box;
+ use std::rc::Rc;
+ use std::sync::Arc;
+
+ pub fn arc_test1<T>(foo: Arc<&T>) {}
+
+ pub fn arc_test2(foo: Arc<&MyStruct>) {}
+
+ pub fn arc_test3(foo: Arc<&MyEnum>) {}
+
+ pub fn arc_test4_neg(foo: Arc<SubT<&usize>>) {}
+
+ pub fn arc_test7(a: Arc<Arc<bool>>) {}
+}
+
+fn main() {}
--- /dev/null
+error: usage of `Box<&T>`
+ --> $DIR/redundant_allocation_fixable.rs:26:30
+ |
+LL | pub fn box_test1<T>(foo: Box<&T>) {}
+ | ^^^^^^^ help: try: `&T`
+ |
+ = note: `-D clippy::redundant-allocation` implied by `-D warnings`
+ = note: `&T` is already a pointer, `Box<&T>` allocates a pointer on the heap
+
+error: usage of `Box<&MyStruct>`
+ --> $DIR/redundant_allocation_fixable.rs:28:27
+ |
+LL | pub fn box_test2(foo: Box<&MyStruct>) {}
+ | ^^^^^^^^^^^^^^ help: try: `&MyStruct`
+ |
+ = note: `&MyStruct` is already a pointer, `Box<&MyStruct>` allocates a pointer on the heap
+
+error: usage of `Box<&MyEnum>`
+ --> $DIR/redundant_allocation_fixable.rs:30:27
+ |
+LL | pub fn box_test3(foo: Box<&MyEnum>) {}
+ | ^^^^^^^^^^^^ help: try: `&MyEnum`
+ |
+ = note: `&MyEnum` is already a pointer, `Box<&MyEnum>` allocates a pointer on the heap
+
+error: usage of `Box<Box<T>>`
+ --> $DIR/redundant_allocation_fixable.rs:34:30
+ |
+LL | pub fn box_test5<T>(foo: Box<Box<T>>) {}
+ | ^^^^^^^^^^^ help: try: `Box<T>`
+ |
+ = note: `Box<T>` is already on the heap, `Box<Box<T>>` makes an extra allocation
+
+error: usage of `Rc<&T>`
+ --> $DIR/redundant_allocation_fixable.rs:45:29
+ |
+LL | pub fn rc_test1<T>(foo: Rc<&T>) {}
+ | ^^^^^^ help: try: `&T`
+ |
+ = note: `&T` is already a pointer, `Rc<&T>` allocates a pointer on the heap
+
+error: usage of `Rc<&MyStruct>`
+ --> $DIR/redundant_allocation_fixable.rs:47:26
+ |
+LL | pub fn rc_test2(foo: Rc<&MyStruct>) {}
+ | ^^^^^^^^^^^^^ help: try: `&MyStruct`
+ |
+ = note: `&MyStruct` is already a pointer, `Rc<&MyStruct>` allocates a pointer on the heap
+
+error: usage of `Rc<&MyEnum>`
+ --> $DIR/redundant_allocation_fixable.rs:49:26
+ |
+LL | pub fn rc_test3(foo: Rc<&MyEnum>) {}
+ | ^^^^^^^^^^^ help: try: `&MyEnum`
+ |
+ = note: `&MyEnum` is already a pointer, `Rc<&MyEnum>` allocates a pointer on the heap
+
+error: usage of `Rc<Rc<bool>>`
+ --> $DIR/redundant_allocation_fixable.rs:53:24
+ |
+LL | pub fn rc_test6(a: Rc<Rc<bool>>) {}
+ | ^^^^^^^^^^^^ help: try: `Rc<bool>`
+ |
+ = note: `Rc<bool>` is already on the heap, `Rc<Rc<bool>>` makes an extra allocation
+
+error: usage of `Arc<&T>`
+ --> $DIR/redundant_allocation_fixable.rs:64:30
+ |
+LL | pub fn arc_test1<T>(foo: Arc<&T>) {}
+ | ^^^^^^^ help: try: `&T`
+ |
+ = note: `&T` is already a pointer, `Arc<&T>` allocates a pointer on the heap
+
+error: usage of `Arc<&MyStruct>`
+ --> $DIR/redundant_allocation_fixable.rs:66:27
+ |
+LL | pub fn arc_test2(foo: Arc<&MyStruct>) {}
+ | ^^^^^^^^^^^^^^ help: try: `&MyStruct`
+ |
+ = note: `&MyStruct` is already a pointer, `Arc<&MyStruct>` allocates a pointer on the heap
+
+error: usage of `Arc<&MyEnum>`
+ --> $DIR/redundant_allocation_fixable.rs:68:27
+ |
+LL | pub fn arc_test3(foo: Arc<&MyEnum>) {}
+ | ^^^^^^^^^^^^ help: try: `&MyEnum`
+ |
+ = note: `&MyEnum` is already a pointer, `Arc<&MyEnum>` allocates a pointer on the heap
+
+error: usage of `Arc<Arc<bool>>`
+ --> $DIR/redundant_allocation_fixable.rs:72:25
+ |
+LL | pub fn arc_test7(a: Arc<Arc<bool>>) {}
+ | ^^^^^^^^^^^^^^ help: try: `Arc<bool>`
+ |
+ = note: `Arc<bool>` is already on the heap, `Arc<Arc<bool>>` makes an extra allocation
+
+error: aborting due to 12 previous errors
+
issue_5405();
manually_drop();
clone_then_move_cloned();
+ hashmap_neg();
+ false_negative_5707();
}
#[derive(Clone)]
let mut x = S(String::new());
x.0.clone().chars().for_each(|_| x.m());
}
+
+fn hashmap_neg() {
+ // issue 5707
+ use std::collections::HashMap;
+ use std::path::PathBuf;
+
+ let p = PathBuf::from("/");
+
+ let mut h: HashMap<&str, &str> = HashMap::new();
+ h.insert("orig-p", p.to_str().unwrap());
+
+ let mut q = p.clone();
+ q.push("foo");
+
+ println!("{:?} {}", h, q.display());
+}
+
+fn false_negative_5707() {
+ fn foo(_x: &Alpha, _y: &mut Alpha) {}
+
+ let x = Alpha;
+ let mut y = Alpha;
+ foo(&x, &mut y);
+ let _z = x.clone(); // pr 7346 can't lint on `x`
+ drop(y);
+}
issue_5405();
manually_drop();
clone_then_move_cloned();
+ hashmap_neg();
+ false_negative_5707();
}
#[derive(Clone)]
let mut x = S(String::new());
x.0.clone().chars().for_each(|_| x.m());
}
+
+fn hashmap_neg() {
+ // issue 5707
+ use std::collections::HashMap;
+ use std::path::PathBuf;
+
+ let p = PathBuf::from("/");
+
+ let mut h: HashMap<&str, &str> = HashMap::new();
+ h.insert("orig-p", p.to_str().unwrap());
+
+ let mut q = p.clone();
+ q.push("foo");
+
+ println!("{:?} {}", h, q.display());
+}
+
+fn false_negative_5707() {
+ fn foo(_x: &Alpha, _y: &mut Alpha) {}
+
+ let x = Alpha;
+ let mut y = Alpha;
+ foo(&x, &mut y);
+ let _z = x.clone(); // pr 7346 can't lint on `x`
+ drop(y);
+}
| ^^^^^
error: redundant clone
- --> $DIR/redundant_clone.rs:63:25
+ --> $DIR/redundant_clone.rs:65:25
|
LL | if b { (a.clone(), a.clone()) } else { (Alpha, a) }
| ^^^^^^^^ help: remove this
|
note: this value is dropped without further use
- --> $DIR/redundant_clone.rs:63:24
+ --> $DIR/redundant_clone.rs:65:24
|
LL | if b { (a.clone(), a.clone()) } else { (Alpha, a) }
| ^
error: redundant clone
- --> $DIR/redundant_clone.rs:120:15
+ --> $DIR/redundant_clone.rs:122:15
|
LL | let _s = s.clone();
| ^^^^^^^^ help: remove this
|
note: this value is dropped without further use
- --> $DIR/redundant_clone.rs:120:14
+ --> $DIR/redundant_clone.rs:122:14
|
LL | let _s = s.clone();
| ^
error: redundant clone
- --> $DIR/redundant_clone.rs:121:15
+ --> $DIR/redundant_clone.rs:123:15
|
LL | let _t = t.clone();
| ^^^^^^^^ help: remove this
|
note: this value is dropped without further use
- --> $DIR/redundant_clone.rs:121:14
+ --> $DIR/redundant_clone.rs:123:14
|
LL | let _t = t.clone();
| ^
error: redundant clone
- --> $DIR/redundant_clone.rs:131:19
+ --> $DIR/redundant_clone.rs:133:19
|
LL | let _f = f.clone();
| ^^^^^^^^ help: remove this
|
note: this value is dropped without further use
- --> $DIR/redundant_clone.rs:131:18
+ --> $DIR/redundant_clone.rs:133:18
|
LL | let _f = f.clone();
| ^
error: redundant clone
- --> $DIR/redundant_clone.rs:143:14
+ --> $DIR/redundant_clone.rs:145:14
|
LL | let y = x.clone().join("matthias");
| ^^^^^^^^ help: remove this
|
note: cloned value is neither consumed nor mutated
- --> $DIR/redundant_clone.rs:143:13
+ --> $DIR/redundant_clone.rs:145:13
|
LL | let y = x.clone().join("matthias");
| ^^^^^^^^^