X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=clippy_lints%2Fsrc%2Fexplicit_write.rs;h=f326fd83d18e70a16c17de0a8ca5681f5562ee58;hb=82f613ee3b12a6a90bd9e99fbbab947674d6ec7a;hp=7b7839c502d89d56124e76e01751be9f682082de;hpb=15b433af48c3d4c4cc0e0e4871275b0dd05a35d1;p=rust.git diff --git a/clippy_lints/src/explicit_write.rs b/clippy_lints/src/explicit_write.rs index 7b7839c502d..f326fd83d18 100644 --- a/clippy_lints/src/explicit_write.rs +++ b/clippy_lints/src/explicit_write.rs @@ -1,26 +1,29 @@ -use crate::utils::{is_expn_of, match_function_call, paths, span_lint, span_lint_and_sugg}; +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; +use clippy_utils::macros::FormatArgsExpn; +use clippy_utils::{is_expn_of, match_function_call, paths}; use if_chain::if_chain; -use rustc::hir::*; -use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass}; -use rustc::{declare_lint_pass, declare_tool_lint}; use rustc_errors::Applicability; -use syntax::ast::LitKind; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; declare_clippy_lint! { - /// **What it does:** Checks for usage of `write!()` / `writeln()!` which can be + /// ### What it does + /// Checks for usage of `write!()` / `writeln()!` which can be /// replaced with `(e)print!()` / `(e)println!()` /// - /// **Why is this bad?** Using `(e)println! is clearer and more concise + /// ### Why is this bad? + /// Using `(e)println! is clearer and more concise /// - /// **Known problems:** None. - /// - /// **Example:** + /// ### Example /// ```rust /// # use std::io::Write; /// # let bar = "furchtbar"; /// // this would be clearer as `eprintln!("foo: {:?}", bar);` /// writeln!(&mut std::io::stderr(), "foo: {:?}", bar).unwrap(); /// ``` + #[clippy::version = "pre 1.29.0"] pub EXPLICIT_WRITE, complexity, "using the `write!()` family of functions instead of the `print!()` family of functions, when using the latter would work" @@ -28,33 +31,30 @@ declare_lint_pass!(ExplicitWrite => [EXPLICIT_WRITE]); -impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ExplicitWrite { - fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) { +impl<'tcx> LateLintPass<'tcx> for ExplicitWrite { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if_chain! { // match call to unwrap - if let ExprKind::MethodCall(ref unwrap_fun, _, ref unwrap_args) = expr.kind; - if unwrap_fun.ident.name == sym!(unwrap); + 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.len() > 0; - if let ExprKind::MethodCall(ref write_fun, _, ref 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.len() > 0; - 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(cx, 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 @@ -68,82 +68,40 @@ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) { // 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_parts { + 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: &HirVec) -> Option { - if_chain! { - // Obtain the string that should be printed - if write_args.len() > 1; - if let ExprKind::Call(_, ref output_args) = write_args[1].kind; - if output_args.len() > 0; - if let ExprKind::AddrOf(_, ref output_string_expr) = output_args[0].kind; - if let ExprKind::Array(ref 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 -}