1
+ use std:: ops:: Deref ;
1
2
use rustc:: hir:: * ;
2
3
use rustc:: hir:: map:: Node :: { NodeImplItem , NodeItem } ;
3
4
use rustc:: lint:: * ;
4
5
use syntax:: ast:: LitKind ;
5
6
use syntax:: symbol:: InternedString ;
7
+ use syntax_pos:: Span ;
6
8
use utils:: { is_expn_of, match_def_path, match_path, resolve_node, span_lint} ;
7
9
use utils:: { paths, opt_def_id} ;
8
10
11
+ /// **What it does:** This lint warns when you using `println!("")` to
12
+ /// print a newline.
13
+ ///
14
+ /// **Why is this bad?** You should use `println!()`, which is simpler.
15
+ ///
16
+ /// **Known problems:** None.
17
+ ///
18
+ /// **Example:**
19
+ /// ```rust
20
+ /// println!("");
21
+ /// ```
22
+ declare_lint ! {
23
+ pub PRINTLN_EMPTY_STRING ,
24
+ Warn ,
25
+ "using `print!()` with a format string that ends in a newline"
26
+ }
27
+
9
28
/// **What it does:** This lint warns when you using `print!()` with a format
10
29
/// string that
11
30
/// ends in a newline.
@@ -64,7 +83,7 @@ pub struct Pass;
64
83
65
84
impl LintPass for Pass {
66
85
fn get_lints ( & self ) -> LintArray {
67
- lint_array ! ( PRINT_WITH_NEWLINE , PRINT_STDOUT , USE_DEBUG )
86
+ lint_array ! ( PRINT_WITH_NEWLINE , PRINTLN_EMPTY_STRING , PRINT_STDOUT , USE_DEBUG )
68
87
}
69
88
}
70
89
@@ -88,10 +107,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
88
107
89
108
span_lint( cx, PRINT_STDOUT , span, & format!( "use of `{}!`" , name) ) ;
90
109
91
- // Check print! with format string ending in "\n".
92
110
if_let_chain!{ [
93
- name == "print" ,
94
-
95
111
// ensure we're calling Arguments::new_v1
96
112
args. len( ) == 1 ,
97
113
let ExprCall ( ref args_fun, ref args_args) = args[ 0 ] . node,
@@ -102,20 +118,13 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
102
118
let ExprAddrOf ( _, ref match_expr) = args_args[ 1 ] . node,
103
119
let ExprMatch ( ref args, _, _) = match_expr. node,
104
120
let ExprTup ( ref args) = args. node,
105
-
106
- // collect the format string parts and check the last one
107
121
let Some ( ( fmtstr, fmtlen) ) = get_argument_fmtstr_parts( & args_args[ 0 ] ) ,
108
- let Some ( '\n' ) = fmtstr. chars( ) . last( ) ,
109
-
110
- // "foo{}bar" is made into two strings + one argument,
111
- // if the format string starts with `{}` (eg. "{}foo"),
112
- // the string array is prepended an empty string "".
113
- // We only want to check the last string after any `{}`:
114
- args. len( ) < fmtlen,
115
122
] , {
116
- span_lint( cx, PRINT_WITH_NEWLINE , span,
117
- "using `print!()` with a format string that ends in a \
118
- newline, consider using `println!()` instead") ;
123
+ match name {
124
+ "print" => check_print( cx, span, args, fmtstr, fmtlen) ,
125
+ "println" => check_println( cx, span, fmtstr, fmtlen) ,
126
+ _ => ( ) ,
127
+ }
119
128
} }
120
129
}
121
130
}
@@ -135,6 +144,46 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
135
144
}
136
145
}
137
146
147
+ // Check for print!("... \n", ...).
148
+ fn check_print < ' a , ' tcx > (
149
+ cx : & LateContext < ' a , ' tcx > ,
150
+ span : Span ,
151
+ args : & HirVec < Expr > ,
152
+ fmtstr : InternedString ,
153
+ fmtlen : usize ,
154
+ ) {
155
+ if_let_chain ! { [
156
+ // check the final format string part
157
+ let Some ( '\n' ) = fmtstr. chars( ) . last( ) ,
158
+
159
+ // "foo{}bar" is made into two strings + one argument,
160
+ // if the format string starts with `{}` (eg. "{}foo"),
161
+ // the string array is prepended an empty string "".
162
+ // We only want to check the last string after any `{}`:
163
+ args. len( ) < fmtlen,
164
+ ] , {
165
+ span_lint( cx, PRINT_WITH_NEWLINE , span,
166
+ "using `print!()` with a format string that ends in a \
167
+ newline, consider using `println!()` instead") ;
168
+ } }
169
+ }
170
+
171
+ /// Check for println!("")
172
+ fn check_println < ' a , ' tcx > ( cx : & LateContext < ' a , ' tcx > , span : Span , fmtstr : InternedString , fmtlen : usize ) {
173
+ if_let_chain ! { [
174
+ // check that the string is empty
175
+ fmtlen == 1 ,
176
+ fmtstr. deref( ) == "\n " ,
177
+
178
+ // check the presence of that string
179
+ let Ok ( snippet) = cx. sess( ) . codemap( ) . span_to_snippet( span) ,
180
+ snippet. contains( "\" \" " ) ,
181
+ ] , {
182
+ span_lint( cx, PRINT_WITH_NEWLINE , span,
183
+ "using `println!(\" \" )`, consider using `println!()` instead" ) ;
184
+ } }
185
+ }
186
+
138
187
fn is_in_debug_impl ( cx : & LateContext , expr : & Expr ) -> bool {
139
188
let map = & cx. tcx . hir ;
140
189
0 commit comments