@@ -10,8 +10,8 @@ use rustc_lint::{LateContext, LintContext};
10
10
use rustc_session:: Session ;
11
11
use rustc_span:: source_map:: { original_sp, SourceMap } ;
12
12
use rustc_span:: {
13
- hygiene, BytePos , FileNameDisplayPreference , Pos , SourceFile , SourceFileAndLine , Span , SpanData , SyntaxContext ,
14
- DUMMY_SP ,
13
+ hygiene, BytePos , FileNameDisplayPreference , Pos , RelativeBytePos , SourceFile , SourceFileAndLine , Span , SpanData ,
14
+ SyntaxContext , DUMMY_SP ,
15
15
} ;
16
16
use std:: borrow:: Cow ;
17
17
use std:: fmt;
@@ -75,6 +75,12 @@ pub trait SpanRangeExt: SpanRange {
75
75
get_source_text ( cx. sess ( ) . source_map ( ) , self . into_range ( ) )
76
76
}
77
77
78
+ /// Calls the given function with the indent of the referenced line and returns the value.
79
+ /// Passes an empty string if the indent cannot be determined.
80
+ fn with_line_indent < T > ( self , cx : & impl LintContext , f : impl for < ' a > FnOnce ( & ' a str ) -> T ) -> T {
81
+ with_line_indent ( cx. sess ( ) . source_map ( ) , self . into_range ( ) , f)
82
+ }
83
+
78
84
/// Calls the given function with the source text referenced and returns the value. Returns
79
85
/// `None` if the source text cannot be retrieved.
80
86
fn with_source_text < T > ( self , cx : & impl LintContext , f : impl for < ' a > FnOnce ( & ' a str ) -> T ) -> Option < T > {
@@ -110,11 +116,29 @@ pub trait SpanRangeExt: SpanRange {
110
116
map_range ( cx. sess ( ) . source_map ( ) , self . into_range ( ) , f)
111
117
}
112
118
119
+ /// Calls the given function with the both the text of the source file and the referenced range,
120
+ /// and creates a new span from the result. Returns `None` if the source text cannot be
121
+ /// retrieved, or no result is returned.
122
+ ///
123
+ /// The new range must reside within the same source file.
124
+ fn map_range_as_pos_len (
125
+ self ,
126
+ cx : & impl LintContext ,
127
+ f : impl for < ' a > FnOnce ( & ' a str , Range < usize > ) -> Option < ( usize , usize ) > ,
128
+ ) -> Option < Range < BytePos > > {
129
+ map_range_as_pos_len ( cx. sess ( ) . source_map ( ) , self . into_range ( ) , f)
130
+ }
131
+
113
132
/// Extends the range to include all preceding whitespace characters.
114
133
fn with_leading_whitespace ( self , cx : & impl LintContext ) -> Range < BytePos > {
115
134
with_leading_whitespace ( cx. sess ( ) . source_map ( ) , self . into_range ( ) )
116
135
}
117
136
137
+ /// Extends the range to include all trailing whitespace characters.
138
+ fn with_trailing_whitespace ( self , cx : & impl LintContext ) -> Range < BytePos > {
139
+ with_trailing_whitespace ( cx. sess ( ) . source_map ( ) , self . into_range ( ) )
140
+ }
141
+
118
142
/// Trims the leading whitespace from the range.
119
143
fn trim_start ( self , cx : & impl LintContext ) -> Range < BytePos > {
120
144
trim_start ( cx. sess ( ) . source_map ( ) , self . into_range ( ) )
@@ -139,7 +163,7 @@ fn get_source_text(sm: &SourceMap, sp: Range<BytePos>) -> Option<SourceFileRange
139
163
if !Lrc :: ptr_eq ( & start. sf , & end. sf ) || start. pos > end. pos {
140
164
return None ;
141
165
}
142
- let range = start. pos . to_usize ( ) ..end. pos . to_usize ( ) ;
166
+ let range = RelativeBytePos ( start. pos . 0 ) ..RelativeBytePos ( end. pos . 0 ) ;
143
167
Some ( SourceFileRange { sf : start. sf , range } )
144
168
}
145
169
@@ -161,12 +185,31 @@ fn with_source_text_and_range<T>(
161
185
if let Some ( src) = get_source_text ( sm, sp)
162
186
&& let Some ( text) = & src. sf . src
163
187
{
164
- Some ( f ( text, src. range ) )
188
+ Some ( f ( text, src. usize_range ( ) ) )
165
189
} else {
166
190
None
167
191
}
168
192
}
169
193
194
+ fn with_line_indent < T > ( sm : & SourceMap , sp : Range < BytePos > , f : impl for < ' a > FnOnce ( & ' a str ) -> T ) -> T {
195
+ let src = get_source_text ( sm, sp) ;
196
+ let indent = if let Some ( src) = & src
197
+ && let Some ( line) = src. sf . lookup_line ( src. range . start )
198
+ && let Some ( start) = src. sf . lines ( ) . get ( line)
199
+ && let Some ( text) = src. sf . src . as_ref ( )
200
+ {
201
+ let text = if let Some ( end) = src. sf . lines ( ) . get ( line + 1 ) {
202
+ & text[ start. to_usize ( ) ..end. to_usize ( ) ]
203
+ } else {
204
+ & text[ start. to_usize ( ) ..]
205
+ } ;
206
+ & text[ ..text. len ( ) - text. trim_start ( ) . len ( ) ]
207
+ } else {
208
+ ""
209
+ } ;
210
+ f ( indent)
211
+ }
212
+
170
213
#[ expect( clippy:: cast_possible_truncation) ]
171
214
fn map_range (
172
215
sm : & SourceMap ,
@@ -175,7 +218,7 @@ fn map_range(
175
218
) -> Option < Range < BytePos > > {
176
219
if let Some ( src) = get_source_text ( sm, sp. clone ( ) )
177
220
&& let Some ( text) = & src. sf . src
178
- && let Some ( range) = f ( text, src. range . clone ( ) )
221
+ && let Some ( range) = f ( text, src. usize_range ( ) )
179
222
{
180
223
debug_assert ! (
181
224
range. start <= text. len( ) && range. end <= text. len( ) ,
@@ -184,21 +227,54 @@ fn map_range(
184
227
text. len( ) ,
185
228
) ;
186
229
debug_assert ! ( range. start <= range. end, "Range `{range:?}` has overlapping bounds" ) ;
187
- let dstart = ( range. start as u32 ) . wrapping_sub ( src. range . start as u32 ) ;
188
- let dend = ( range. end as u32 ) . wrapping_sub ( src. range . start as u32 ) ;
230
+ let dstart = ( range. start as u32 ) . wrapping_sub ( src. range . start . 0 ) ;
231
+ let dend = ( range. end as u32 ) . wrapping_sub ( src. range . start . 0 ) ;
189
232
Some ( BytePos ( sp. start . 0 . wrapping_add ( dstart) ) ..BytePos ( sp. start . 0 . wrapping_add ( dend) ) )
190
233
} else {
191
234
None
192
235
}
193
236
}
194
237
238
+ #[ expect( clippy:: cast_possible_truncation) ]
239
+ fn map_range_as_pos_len (
240
+ sm : & SourceMap ,
241
+ sp : Range < BytePos > ,
242
+ f : impl for < ' a > FnOnce ( & ' a str , Range < usize > ) -> Option < ( usize , usize ) > ,
243
+ ) -> Option < Range < BytePos > > {
244
+ if let Some ( src) = get_source_text ( sm, sp. clone ( ) )
245
+ && let Some ( text) = & src. sf . src
246
+ && let Some ( ( pos, len) ) = f ( text, src. usize_range ( ) )
247
+ {
248
+ debug_assert ! (
249
+ pos + len <= text. len( ) ,
250
+ "Range `{:?}` is outside the source file (file `{}`, length `{}`)" ,
251
+ pos..pos + len,
252
+ src. sf. name. display( FileNameDisplayPreference :: Local ) ,
253
+ text. len( ) ,
254
+ ) ;
255
+ let delta = ( pos as u32 ) . wrapping_sub ( src. range . start . 0 ) ;
256
+ let pos = sp. start . 0 . wrapping_add ( delta) ;
257
+ Some ( BytePos ( pos) ..BytePos ( pos + len as u32 ) )
258
+ } else {
259
+ None
260
+ }
261
+ }
262
+
195
263
fn with_leading_whitespace ( sm : & SourceMap , sp : Range < BytePos > ) -> Range < BytePos > {
196
264
map_range ( sm, sp. clone ( ) , |src, range| {
197
265
Some ( src. get ( ..range. start ) ?. trim_end ( ) . len ( ) ..range. end )
198
266
} )
199
267
. unwrap_or ( sp)
200
268
}
201
269
270
+ fn with_trailing_whitespace ( sm : & SourceMap , sp : Range < BytePos > ) -> Range < BytePos > {
271
+ map_range ( sm, sp. clone ( ) , |src, range| {
272
+ let tail = src. get ( range. end ..) ?;
273
+ Some ( range. start ..range. end + ( tail. len ( ) - tail. trim_start ( ) . len ( ) ) )
274
+ } )
275
+ . unwrap_or ( sp)
276
+ }
277
+
202
278
fn trim_start ( sm : & SourceMap , sp : Range < BytePos > ) -> Range < BytePos > {
203
279
map_range ( sm, sp. clone ( ) , |src, range| {
204
280
let src = src. get ( range. clone ( ) ) ?;
@@ -216,13 +292,18 @@ fn write_source_text_to(sm: &SourceMap, sp: Range<BytePos>, dst: &mut impl fmt::
216
292
217
293
pub struct SourceFileRange {
218
294
pub sf : Lrc < SourceFile > ,
219
- pub range : Range < usize > ,
295
+ pub range : Range < RelativeBytePos > ,
220
296
}
221
297
impl SourceFileRange {
222
298
/// Attempts to get the text from the source file. This can fail if the source text isn't
223
299
/// loaded.
224
300
pub fn as_str ( & self ) -> Option < & str > {
225
- self . sf . src . as_ref ( ) . and_then ( |x| x. get ( self . range . clone ( ) ) )
301
+ self . sf . src . as_ref ( ) . and_then ( |x| x. get ( self . usize_range ( ) ) )
302
+ }
303
+
304
+ /// Gets the range of the source text as a `usize` range.
305
+ pub fn usize_range ( & self ) -> Range < usize > {
306
+ self . range . start . 0 as usize ..self . range . end . 0 as usize
226
307
}
227
308
}
228
309
0 commit comments