|
10 | 10 | use crate::rustc::hir::*;
|
11 | 11 | use crate::rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
|
12 | 12 | use crate::rustc::{declare_tool_lint, lint_array};
|
13 |
| -use crate::utils::opt_def_id; |
14 |
| -use crate::utils::{is_expn_of, match_def_path, resolve_node, span_lint}; |
| 13 | +use crate::rustc_errors::Applicability; |
| 14 | +use crate::syntax::ast::LitKind; |
| 15 | +use crate::utils::{is_expn_of, match_def_path, opt_def_id, resolve_node, span_lint, span_lint_and_sugg}; |
15 | 16 | use if_chain::if_chain;
|
16 | 17 |
|
17 | 18 | /// **What it does:** Checks for usage of `write!()` / `writeln()!` which can be
|
@@ -81,32 +82,85 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
|
81 | 82 | } else {
|
82 | 83 | ""
|
83 | 84 | };
|
84 |
| - if let Some(macro_name) = calling_macro { |
85 |
| - span_lint( |
86 |
| - cx, |
87 |
| - EXPLICIT_WRITE, |
88 |
| - expr.span, |
89 |
| - &format!( |
90 |
| - "use of `{}!({}(), ...).unwrap()`. Consider using `{}{}!` instead", |
91 |
| - macro_name, |
92 |
| - dest_name, |
93 |
| - prefix, |
94 |
| - macro_name.replace("write", "print") |
95 |
| - ) |
96 |
| - ); |
| 85 | + |
| 86 | + // We need to remove the last trailing newline from the string because the |
| 87 | + // underlying `fmt::write` function doesn't know whether `println!` or `print!` was |
| 88 | + // used. |
| 89 | + if let Some(mut write_output) = write_output_string(write_args) { |
| 90 | + if write_output.ends_with('\n') { |
| 91 | + write_output.pop(); |
| 92 | + } |
| 93 | + |
| 94 | + if let Some(macro_name) = calling_macro { |
| 95 | + span_lint_and_sugg( |
| 96 | + cx, |
| 97 | + EXPLICIT_WRITE, |
| 98 | + expr.span, |
| 99 | + &format!( |
| 100 | + "use of `{}!({}(), ...).unwrap()`", |
| 101 | + macro_name, |
| 102 | + dest_name |
| 103 | + ), |
| 104 | + "try this", |
| 105 | + format!("{}{}!(\"{}\")", prefix, macro_name.replace("write", "print"), write_output.escape_default()), |
| 106 | + Applicability::MachineApplicable |
| 107 | + ); |
| 108 | + } else { |
| 109 | + span_lint_and_sugg( |
| 110 | + cx, |
| 111 | + EXPLICIT_WRITE, |
| 112 | + expr.span, |
| 113 | + &format!("use of `{}().write_fmt(...).unwrap()`", dest_name), |
| 114 | + "try this", |
| 115 | + format!("{}print!(\"{}\")", prefix, write_output.escape_default()), |
| 116 | + Applicability::MachineApplicable |
| 117 | + ); |
| 118 | + } |
97 | 119 | } else {
|
98 |
| - span_lint( |
99 |
| - cx, |
100 |
| - EXPLICIT_WRITE, |
101 |
| - expr.span, |
102 |
| - &format!( |
103 |
| - "use of `{}().write_fmt(...).unwrap()`. Consider using `{}print!` instead", |
104 |
| - dest_name, |
105 |
| - prefix, |
106 |
| - ) |
107 |
| - ); |
| 120 | + // We don't have a proper suggestion |
| 121 | + if let Some(macro_name) = calling_macro { |
| 122 | + span_lint( |
| 123 | + cx, |
| 124 | + EXPLICIT_WRITE, |
| 125 | + expr.span, |
| 126 | + &format!( |
| 127 | + "use of `{}!({}(), ...).unwrap()`. Consider using `{}{}!` instead", |
| 128 | + macro_name, |
| 129 | + dest_name, |
| 130 | + prefix, |
| 131 | + macro_name.replace("write", "print") |
| 132 | + ) |
| 133 | + ); |
| 134 | + } else { |
| 135 | + span_lint( |
| 136 | + cx, |
| 137 | + EXPLICIT_WRITE, |
| 138 | + expr.span, |
| 139 | + &format!("use of `{}().write_fmt(...).unwrap()`. Consider using `{}print!` instead", dest_name, prefix), |
| 140 | + ); |
| 141 | + } |
108 | 142 | }
|
| 143 | + |
109 | 144 | }
|
110 | 145 | }
|
111 | 146 | }
|
112 | 147 | }
|
| 148 | + |
| 149 | +// Extract the output string from the given `write_args`. |
| 150 | +fn write_output_string(write_args: &HirVec<Expr>) -> Option<String> { |
| 151 | + if_chain! { |
| 152 | + // Obtain the string that should be printed |
| 153 | + if write_args.len() > 1; |
| 154 | + if let ExprKind::Call(_, ref output_args) = write_args[1].node; |
| 155 | + if output_args.len() > 0; |
| 156 | + if let ExprKind::AddrOf(_, ref output_string_expr) = output_args[0].node; |
| 157 | + if let ExprKind::Array(ref string_exprs) = output_string_expr.node; |
| 158 | + if string_exprs.len() > 0; |
| 159 | + if let ExprKind::Lit(ref lit) = string_exprs[0].node; |
| 160 | + if let LitKind::Str(ref write_output, _) = lit.node; |
| 161 | + then { |
| 162 | + return Some(write_output.to_string()) |
| 163 | + } |
| 164 | + } |
| 165 | + None |
| 166 | +} |
0 commit comments