@@ -5,7 +5,7 @@ use rustc_errors::Applicability;
5
5
use std:: borrow:: Cow ;
6
6
use syntax:: ast:: * ;
7
7
use syntax:: parse:: { parser, token} ;
8
- use syntax:: tokenstream:: TokenStream ;
8
+ use syntax:: tokenstream:: { TokenStream , TokenTree } ;
9
9
10
10
/// **What it does:** This lint warns when you use `println!("")` to
11
11
/// print a newline.
@@ -200,8 +200,8 @@ impl EarlyLintPass for Pass {
200
200
}
201
201
} else if mac. node . path == "print" {
202
202
span_lint ( cx, PRINT_STDOUT , mac. span , "use of `print!`" ) ;
203
- if let Some ( fmtstr) = check_tts ( cx, & mac. node . tts , false ) . 0 {
204
- if check_newlines ( & fmtstr) {
203
+ if let ( Some ( fmtstr) , _ , is_raw ) = check_tts ( cx, & mac. node . tts , false ) {
204
+ if check_newlines ( & fmtstr, is_raw ) {
205
205
span_lint (
206
206
cx,
207
207
PRINT_WITH_NEWLINE ,
@@ -212,8 +212,8 @@ impl EarlyLintPass for Pass {
212
212
}
213
213
}
214
214
} else if mac. node . path == "write" {
215
- if let Some ( fmtstr) = check_tts ( cx, & mac. node . tts , true ) . 0 {
216
- if check_newlines ( & fmtstr) {
215
+ if let ( Some ( fmtstr) , _ , is_raw ) = check_tts ( cx, & mac. node . tts , true ) {
216
+ if check_newlines ( & fmtstr, is_raw ) {
217
217
span_lint (
218
218
cx,
219
219
WRITE_WITH_NEWLINE ,
@@ -252,8 +252,9 @@ impl EarlyLintPass for Pass {
252
252
}
253
253
254
254
/// Checks the arguments of `print[ln]!` and `write[ln]!` calls. It will return a tuple of two
255
- /// options. The first part of the tuple is `format_str` of the macros. The second part of the tuple
256
- /// is in the `write[ln]!` case the expression the `format_str` should be written to.
255
+ /// options and a bool. The first part of the tuple is `format_str` of the macros. The second part
256
+ /// of the tuple is in the `write[ln]!` case the expression the `format_str` should be written to.
257
+ /// The final part is a boolean flag indicating if the string is a raw string.
257
258
///
258
259
/// Example:
259
260
///
@@ -263,34 +264,49 @@ impl EarlyLintPass for Pass {
263
264
/// ```
264
265
/// will return
265
266
/// ```rust,ignore
266
- /// (Some("string to write: {}"), Some(buf))
267
+ /// (Some("string to write: {}"), Some(buf), false )
267
268
/// ```
268
- fn check_tts < ' a > ( cx : & EarlyContext < ' a > , tts : & TokenStream , is_write : bool ) -> ( Option < String > , Option < Expr > ) {
269
+ fn check_tts < ' a > ( cx : & EarlyContext < ' a > , tts : & TokenStream , is_write : bool ) -> ( Option < String > , Option < Expr > , bool ) {
269
270
use fmt_macros:: * ;
270
271
let tts = tts. clone ( ) ;
272
+ let mut is_raw = false ;
273
+ if let TokenStream ( Some ( tokens) ) = & tts {
274
+ for token in tokens. iter ( ) {
275
+ if let ( TokenTree :: Token ( _, token:: Token :: Literal ( lit, _) ) , _) = token {
276
+ match lit {
277
+ token:: Lit :: Str_ ( _) => break ,
278
+ token:: Lit :: StrRaw ( _, _) => {
279
+ is_raw = true ;
280
+ break ;
281
+ } ,
282
+ _ => { } ,
283
+ }
284
+ }
285
+ }
286
+ }
271
287
let mut parser = parser:: Parser :: new ( & cx. sess . parse_sess , tts, None , false , false ) ;
272
288
let mut expr: Option < Expr > = None ;
273
289
if is_write {
274
290
expr = match parser. parse_expr ( ) . map_err ( |mut err| err. cancel ( ) ) {
275
291
Ok ( p) => Some ( p. into_inner ( ) ) ,
276
- Err ( _) => return ( None , None ) ,
292
+ Err ( _) => return ( None , None , is_raw ) ,
277
293
} ;
278
294
// might be `writeln!(foo)`
279
295
if parser. expect ( & token:: Comma ) . map_err ( |mut err| err. cancel ( ) ) . is_err ( ) {
280
- return ( None , expr) ;
296
+ return ( None , expr, is_raw ) ;
281
297
}
282
298
}
283
299
284
300
let fmtstr = match parser. parse_str ( ) . map_err ( |mut err| err. cancel ( ) ) {
285
301
Ok ( token) => token. 0 . to_string ( ) ,
286
- Err ( _) => return ( None , expr) ,
302
+ Err ( _) => return ( None , expr, is_raw ) ,
287
303
} ;
288
304
let tmp = fmtstr. clone ( ) ;
289
305
let mut args = vec ! [ ] ;
290
306
let mut fmt_parser = Parser :: new ( & tmp, None , Vec :: new ( ) , false ) ;
291
307
while let Some ( piece) = fmt_parser. next ( ) {
292
308
if !fmt_parser. errors . is_empty ( ) {
293
- return ( None , expr) ;
309
+ return ( None , expr, is_raw ) ;
294
310
}
295
311
if let Piece :: NextArgument ( arg) = piece {
296
312
if arg. format . ty == "?" {
@@ -312,11 +328,11 @@ fn check_tts<'a>(cx: &EarlyContext<'a>, tts: &TokenStream, is_write: bool) -> (O
312
328
ty : "" ,
313
329
} ;
314
330
if !parser. eat ( & token:: Comma ) {
315
- return ( Some ( fmtstr) , expr) ;
331
+ return ( Some ( fmtstr) , expr, is_raw ) ;
316
332
}
317
333
let token_expr = match parser. parse_expr ( ) . map_err ( |mut err| err. cancel ( ) ) {
318
334
Ok ( expr) => expr,
319
- Err ( _) => return ( Some ( fmtstr) , None ) ,
335
+ Err ( _) => return ( Some ( fmtstr) , None , is_raw ) ,
320
336
} ;
321
337
match & token_expr. node {
322
338
ExprKind :: Lit ( _) => {
@@ -366,7 +382,14 @@ fn check_tts<'a>(cx: &EarlyContext<'a>, tts: &TokenStream, is_write: bool) -> (O
366
382
}
367
383
368
384
// Checks if `s` constains a single newline that terminates it
369
- fn check_newlines ( s : & str ) -> bool {
385
+ // Literal and escaped newlines are both checked (only literal for raw strings)
386
+ fn check_newlines ( s : & str , is_raw : bool ) -> bool {
387
+ if s. ends_with ( '\n' ) {
388
+ return true ;
389
+ } else if is_raw {
390
+ return false ;
391
+ }
392
+
370
393
if s. len ( ) < 2 {
371
394
return false ;
372
395
}
0 commit comments