1+ use std:: iter;
2+
13use clippy_utils:: diagnostics:: span_lint_and_sugg;
24use rustc_ast:: ast;
35use rustc_errors:: Applicability ;
@@ -94,57 +96,30 @@ impl EarlyLintPass for TabsInDocComments {
9496///
9597/// scans the string for groups of tabs and returns the start(inclusive) and end positions
9698/// (exclusive) of all groups
97- /// e.g. "sd\tasd\t\taa" will be converted to [(2, 3), (6, 8)] as
99+ /// e.g. "sd\tasd\t\taa" will yield [(2, 3), (6, 8)] as
98100/// 012 3456 7 89
99101/// ^-^ ^---^
100- fn get_chunks_of_tabs ( the_str : & str ) -> Vec < ( u32 , u32 ) > {
102+ fn get_chunks_of_tabs ( the_str : & str ) -> impl Iterator < Item = ( u32 , u32 ) > {
101103 let line_length_way_to_long = "doc comment longer than 2^32 chars" ;
102- let mut spans: Vec < ( u32 , u32 ) > = vec ! [ ] ;
103- let mut current_start: u32 = 0 ;
104-
105- // tracker to decide if the last group of tabs is not closed by a non-tab character
106- let mut is_active = false ;
107-
108- // Note that we specifically need the char _byte_ indices here, not the positional indexes
109- // within the char array to deal with multi-byte characters properly. `char_indices` does
110- // exactly that. It provides an iterator over tuples of the form `(byte position, char)`.
111- let char_indices: Vec < _ > = the_str. char_indices ( ) . collect ( ) ;
112-
113- if let [ ( _, '\t' ) ] = char_indices. as_slice ( ) {
114- return vec ! [ ( 0 , 1 ) ] ;
115- }
116-
117- for entry in char_indices. windows ( 2 ) {
118- match entry {
119- [ ( _, '\t' ) , ( _, '\t' ) ] => {
120- // either string starts with double tab, then we have to set it active,
121- // otherwise is_active is true anyway
122- is_active = true ;
123- } ,
124- [ ( _, _) , ( index_b, '\t' ) ] => {
125- // as ['\t', '\t'] is excluded, this has to be a start of a tab group,
126- // set indices accordingly
127- is_active = true ;
128- current_start = u32:: try_from ( * index_b) . unwrap ( ) ;
129- } ,
130- [ ( _, '\t' ) , ( index_b, _) ] => {
131- // this now has to be an end of the group, hence we have to push a new tuple
132- is_active = false ;
133- spans. push ( ( current_start, u32:: try_from ( * index_b) . unwrap ( ) ) ) ;
134- } ,
135- _ => { } ,
104+ let mut haystack = the_str. as_bytes ( ) ;
105+ let mut offset = 0 ;
106+
107+ iter:: from_fn ( move || {
108+ if let Some ( i) = memchr:: memchr ( b'\t' , haystack) {
109+ let len = 1 + haystack[ i + 1 ..] . iter ( ) . take_while ( |& & x| x == b'\t' ) . count ( ) ;
110+ let start = offset + i;
111+ let end = start + len;
112+ haystack = & haystack[ i + len..] ;
113+ offset = end;
114+ Some ( (
115+ u32:: try_from ( start) . expect ( line_length_way_to_long) ,
116+ u32:: try_from ( end) . expect ( line_length_way_to_long) ,
117+ ) )
118+ } else {
119+ haystack = & [ ] ;
120+ None
136121 }
137- }
138-
139- // only possible when tabs are at the end, insert last group
140- if is_active {
141- spans. push ( (
142- current_start,
143- u32:: try_from ( char_indices. last ( ) . unwrap ( ) . 0 + 1 ) . expect ( line_length_way_to_long) ,
144- ) ) ;
145- }
146-
147- spans
122+ } )
148123}
149124
150125#[ cfg( test) ]
@@ -153,77 +128,77 @@ mod tests_for_get_chunks_of_tabs {
153128
154129 #[ test]
155130 fn test_unicode_han_string ( ) {
156- let res = get_chunks_of_tabs ( " \u{4f4d} \t " ) ;
131+ let res: Vec < _ > = get_chunks_of_tabs ( " \u{4f4d} \t " ) . collect ( ) ;
157132
158133 assert_eq ! ( res, vec![ ( 4 , 5 ) ] ) ;
159134 }
160135
161136 #[ test]
162137 fn test_empty_string ( ) {
163- let res = get_chunks_of_tabs ( "" ) ;
138+ let res: Vec < _ > = get_chunks_of_tabs ( "" ) . collect ( ) ;
164139
165140 assert_eq ! ( res, vec![ ] ) ;
166141 }
167142
168143 #[ test]
169144 fn test_simple ( ) {
170- let res = get_chunks_of_tabs ( "sd\t \t \t aa" ) ;
145+ let res: Vec < _ > = get_chunks_of_tabs ( "sd\t \t \t aa" ) . collect ( ) ;
171146
172147 assert_eq ! ( res, vec![ ( 2 , 5 ) ] ) ;
173148 }
174149
175150 #[ test]
176151 fn test_only_t ( ) {
177- let res = get_chunks_of_tabs ( "\t \t " ) ;
152+ let res: Vec < _ > = get_chunks_of_tabs ( "\t \t " ) . collect ( ) ;
178153
179154 assert_eq ! ( res, vec![ ( 0 , 2 ) ] ) ;
180155 }
181156
182157 #[ test]
183158 fn test_only_one_t ( ) {
184- let res = get_chunks_of_tabs ( "\t " ) ;
159+ let res: Vec < _ > = get_chunks_of_tabs ( "\t " ) . collect ( ) ;
185160
186161 assert_eq ! ( res, vec![ ( 0 , 1 ) ] ) ;
187162 }
188163
189164 #[ test]
190165 fn test_double ( ) {
191- let res = get_chunks_of_tabs ( "sd\t asd\t \t aa" ) ;
166+ let res: Vec < _ > = get_chunks_of_tabs ( "sd\t asd\t \t aa" ) . collect ( ) ;
192167
193168 assert_eq ! ( res, vec![ ( 2 , 3 ) , ( 6 , 8 ) ] ) ;
194169 }
195170
196171 #[ test]
197172 fn test_start ( ) {
198- let res = get_chunks_of_tabs ( "\t \t aa" ) ;
173+ let res: Vec < _ > = get_chunks_of_tabs ( "\t \t aa" ) . collect ( ) ;
199174
200175 assert_eq ! ( res, vec![ ( 0 , 2 ) ] ) ;
201176 }
202177
203178 #[ test]
204179 fn test_end ( ) {
205- let res = get_chunks_of_tabs ( "aa\t \t " ) ;
180+ let res: Vec < _ > = get_chunks_of_tabs ( "aa\t \t " ) . collect ( ) ;
206181
207182 assert_eq ! ( res, vec![ ( 2 , 4 ) ] ) ;
208183 }
209184
210185 #[ test]
211186 fn test_start_single ( ) {
212- let res = get_chunks_of_tabs ( "\t aa" ) ;
187+ let res: Vec < _ > = get_chunks_of_tabs ( "\t aa" ) . collect ( ) ;
213188
214189 assert_eq ! ( res, vec![ ( 0 , 1 ) ] ) ;
215190 }
216191
217192 #[ test]
218193 fn test_end_single ( ) {
219- let res = get_chunks_of_tabs ( "aa\t " ) ;
194+ let res: Vec < _ > = get_chunks_of_tabs ( "aa\t " ) . collect ( ) ;
220195
221196 assert_eq ! ( res, vec![ ( 2 , 3 ) ] ) ;
222197 }
223198
224199 #[ test]
225200 fn test_no_tabs ( ) {
226- let res = get_chunks_of_tabs ( "dsfs" ) ;
201+ let res: Vec < _ > = get_chunks_of_tabs ( "dsfs" ) . collect ( ) ;
227202
228203 assert_eq ! ( res, vec![ ] ) ;
229204 }
0 commit comments