1+ use std:: { iter:: once, ops:: ControlFlow } ;
2+
13use clippy_utils:: { diagnostics:: span_lint_and_sugg, source:: snippet} ;
24use rustc_ast:: {
35 ast:: { Expr , ExprKind } ,
@@ -62,6 +64,7 @@ impl EarlyLintPass for RawStrings {
6264 && let ExprKind :: Lit ( lit) = expr. kind
6365 && let LitKind :: StrRaw ( max) | LitKind :: ByteStrRaw ( max) | LitKind :: CStrRaw ( max) = lit. kind
6466 {
67+ let str = lit. symbol . as_str ( ) ;
6568 let prefix = match lit. kind {
6669 LitKind :: StrRaw ( ..) => "r" ,
6770 LitKind :: ByteStrRaw ( ..) => "br" ,
@@ -72,7 +75,7 @@ impl EarlyLintPass for RawStrings {
7275 return ;
7376 }
7477
75- if !lit . symbol . as_str ( ) . contains ( [ '\\' , '"' ] ) {
78+ if !str . contains ( [ '\\' , '"' ] ) {
7679 span_lint_and_sugg (
7780 cx,
7881 NEEDLESS_RAW_STRING ,
@@ -86,25 +89,41 @@ impl EarlyLintPass for RawStrings {
8689 return ;
8790 }
8891
89- #[ expect( clippy:: cast_possible_truncation) ]
90- let req = lit
91- . symbol
92- . as_str ( )
93- . as_bytes ( )
94- . split ( |& b| b == b'"' )
95- . skip ( 1 )
96- . map_while ( |bs| {
97- let num = bs. iter ( ) . take_while ( |& & b| b == b'#' ) . count ( ) as u8 ;
98- if num. saturating_sub ( 1 ) == max {
99- // Since `num - 1` is the max number of hashes it can have, we can
100- // short-circuit with full confidence this is correct
101- return None ;
92+ let req = {
93+ let mut following_quote = false ;
94+ let mut req = 0 ;
95+ // `once` so a raw string ending in hashes is still checked
96+ let num = str. as_bytes ( ) . iter ( ) . chain ( once ( & 0 ) ) . try_fold ( 0u8 , |acc, & b| {
97+ match b {
98+ b'"' => ( following_quote, req) = ( true , 1 ) ,
99+ // I'm a bit surprised the compiler didn't optimize this out, there's no
100+ // branch but it still ends up doing an unnecessary comparison, it's:
101+ // - cmp r9b,1h
102+ // - sbb cl,-1h
103+ // which will add 1 if it's true. With this change, it becomes:
104+ // - add cl,r9b
105+ // isn't that so much nicer?
106+ b'#' => req += u8:: from ( following_quote) ,
107+ _ => {
108+ if following_quote {
109+ following_quote = false ;
110+
111+ if req == max {
112+ return ControlFlow :: Break ( req) ;
113+ }
114+
115+ return ControlFlow :: Continue ( acc. max ( req) ) ;
116+ }
117+ } ,
102118 }
103119
104- Some ( num + 1 )
105- } )
106- . max ( )
107- . unwrap_or ( 0 ) ;
120+ ControlFlow :: Continue ( acc)
121+ } ) ;
122+
123+ match num {
124+ ControlFlow :: Continue ( num) | ControlFlow :: Break ( num) => num,
125+ }
126+ } ;
108127
109128 if req < max {
110129 let hashes = "#" . repeat ( req as usize ) ;
0 commit comments