3 use rustc
::hir
::map
::Node
::{NodeImplItem, NodeItem}
;
5 use syntax
::ast
::LitKind
;
6 use syntax
::symbol
::InternedString
;
8 use utils
::{is_expn_of, match_def_path, match_path, resolve_node, span_lint, span_lint_and_sugg}
;
9 use utils
::{opt_def_id, paths}
;
11 /// **What it does:** This lint warns when you using `println!("")` to
14 /// **Why is this bad?** You should use `println!()`, which is simpler.
16 /// **Known problems:** None.
23 pub PRINTLN_EMPTY_STRING
,
25 "using `print!()` with a format string that ends in a newline"
28 /// **What it does:** This lint warns when you using `print!()` with a format
30 /// ends in a newline.
32 /// **Why is this bad?** You should use `println!()` instead, which appends the
35 /// **Known problems:** None.
39 /// print!("Hello {}!\n", name);
42 pub PRINT_WITH_NEWLINE
,
44 "using `print!()` with a format string that ends in a newline"
47 /// **What it does:** Checks for printing on *stdout*. The purpose of this lint
48 /// is to catch debugging remnants.
50 /// **Why is this bad?** People often print on *stdout* while debugging an
51 /// application and might forget to remove those prints afterward.
53 /// **Known problems:** Only catches `print!` and `println!` calls.
57 /// println!("Hello world!");
65 /// **What it does:** Checks for use of `Debug` formatting. The purpose of this
66 /// lint is to catch debugging remnants.
68 /// **Why is this bad?** The purpose of the `Debug` trait is to facilitate
69 /// debugging Rust code. It should not be used in in user-facing output.
73 /// println!("{:?}", foo);
78 "use of `Debug`-based formatting"
81 #[derive(Copy, Clone, Debug)]
84 impl LintPass
for Pass
{
85 fn get_lints(&self) -> LintArray
{
86 lint_array
!(PRINT_WITH_NEWLINE
, PRINTLN_EMPTY_STRING
, PRINT_STDOUT
, USE_DEBUG
)
90 impl<'a
, 'tcx
> LateLintPass
<'a
, 'tcx
> for Pass
{
91 fn check_expr(&mut self, cx
: &LateContext
<'a
, 'tcx
>, expr
: &'tcx Expr
) {
93 if let ExprCall(ref fun
, ref args
) = expr
.node
;
94 if let ExprPath(ref qpath
) = fun
.node
;
95 if let Some(fun_id
) = opt_def_id(resolve_node(cx
, qpath
, fun
.hir_id
));
98 // Search for `std::io::_print(..)` which is unique in a
99 // `print!` expansion.
100 if match_def_path(cx
.tcx
, fun_id
, &paths
::IO_PRINT
) {
101 if let Some(span
) = is_expn_of(expr
.span
, "print") {
102 // `println!` uses `print!`.
103 let (span
, name
) = match is_expn_of(span
, "println") {
104 Some(span
) => (span
, "println"),
105 None
=> (span
, "print"),
108 span_lint(cx
, PRINT_STDOUT
, span
, &format
!("use of `{}!`", name
));
111 // ensure we're calling Arguments::new_v1
113 if let ExprCall(ref args_fun
, ref args_args
) = args
[0].node
;
114 if let ExprPath(ref qpath
) = args_fun
.node
;
115 if let Some(const_def_id
) = opt_def_id(resolve_node(cx
, qpath
, args_fun
.hir_id
));
116 if match_def_path(cx
.tcx
, const_def_id
, &paths
::FMT_ARGUMENTS_NEWV1
);
117 if args_args
.len() == 2;
118 if let ExprAddrOf(_
, ref match_expr
) = args_args
[1].node
;
119 if let ExprMatch(ref args
, _
, _
) = match_expr
.node
;
120 if let ExprTup(ref args
) = args
.node
;
121 if let Some((fmtstr
, fmtlen
)) = get_argument_fmtstr_parts(&args_args
[0]);
124 "print" => check_print(cx
, span
, args
, fmtstr
, fmtlen
),
125 "println" => check_println(cx
, span
, fmtstr
, fmtlen
),
132 // Search for something like
133 // `::std::fmt::ArgumentV1::new(__arg0, ::std::fmt::Debug::fmt)`
134 else if args
.len() == 2 && match_def_path(cx
.tcx
, fun_id
, &paths
::FMT_ARGUMENTV1_NEW
) {
135 if let ExprPath(ref qpath
) = args
[1].node
{
136 if let Some(def_id
) = opt_def_id(cx
.tables
.qpath_def(qpath
, args
[1].hir_id
)) {
137 if match_def_path(cx
.tcx
, def_id
, &paths
::DEBUG_FMT_METHOD
)
138 && !is_in_debug_impl(cx
, expr
) && is_expn_of(expr
.span
, "panic").is_none() {
139 span_lint(cx
, USE_DEBUG
, args
[0].span
, "use of `Debug`-based formatting");
149 // Check for print!("... \n", ...).
150 fn check_print
<'a
, 'tcx
>(
151 cx
: &LateContext
<'a
, 'tcx
>,
154 fmtstr
: InternedString
,
158 // check the final format string part
159 if let Some('
\n'
) = fmtstr
.chars().last();
161 // "foo{}bar" is made into two strings + one argument,
162 // if the format string starts with `{}` (eg. "{}foo"),
163 // the string array is prepended an empty string "".
164 // We only want to check the last string after any `{}`:
165 if args
.len() < fmtlen
;
167 span_lint(cx
, PRINT_WITH_NEWLINE
, span
,
168 "using `print!()` with a format string that ends in a \
169 newline, consider using `println!()` instead");
174 /// Check for println!("")
175 fn check_println
<'a
, 'tcx
>(cx
: &LateContext
<'a
, 'tcx
>, span
: Span
, fmtstr
: InternedString
, fmtlen
: usize) {
177 // check that the string is empty
179 if fmtstr
.deref() == "\n";
181 // check the presence of that string
182 if let Ok(snippet
) = cx
.sess().codemap().span_to_snippet(span
);
183 if snippet
.contains("\"\"");
189 "using `println!(\"\")`",
191 "println!()".to_string(),
197 fn is_in_debug_impl(cx
: &LateContext
, expr
: &Expr
) -> bool
{
198 let map
= &cx
.tcx
.hir
;
201 if let Some(NodeImplItem(item
)) = map
.find(map
.get_parent(expr
.id
)) {
203 if let Some(NodeItem(item
)) = map
.find(map
.get_parent(item
.id
)) {
204 if let ItemImpl(_
, _
, _
, _
, Some(ref tr
), _
, _
) = item
.node
{
205 return match_path(&tr
.path
, &["Debug"]);
213 /// Returns the slice of format string parts in an `Arguments::new_v1` call.
214 fn get_argument_fmtstr_parts(expr
: &Expr
) -> Option
<(InternedString
, usize)> {
216 if let ExprAddrOf(_
, ref expr
) = expr
.node
; // &["…", "…", …]
217 if let ExprArray(ref exprs
) = expr
.node
;
218 if let Some(expr
) = exprs
.last();
219 if let ExprLit(ref lit
) = expr
.node
;
220 if let LitKind
::Str(ref lit
, _
) = lit
.node
;
222 return Some((lit
.as_str(), exprs
.len()));