Skip to content

Commit bbcc594

Browse files
committed
Rework the suspicious formatting lints.
1 parent 0ce07f6 commit bbcc594

15 files changed

+1105
-396
lines changed

clippy_lints/src/formatting.rs

Lines changed: 231 additions & 159 deletions
Large diffs are not rendered by default.

clippy_lints/src/implicit_hasher.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -162,11 +162,13 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitHasher {
162162
continue;
163163
}
164164
let generics_suggestion_span = generics.span.substitute_dummy({
165-
let range = (item.span.lo()..body.params[0].pat.span.lo()).map_range(cx, |src, range| {
166-
let (pre, post) = src.get(range.clone())?.split_once("fn")?;
167-
let pos = post.find('(')? + pre.len() + 2;
168-
Some(pos..pos)
169-
});
165+
let range = (item.span.lo()..body.params[0].pat.span.lo()).map_range_as_pos_len(
166+
cx,
167+
|src, range| {
168+
let (pre, post) = src.get(range.clone())?.split_once("fn")?;
169+
Some((post.find('(')? + pre.len() + 2, 0))
170+
},
171+
);
170172
if let Some(range) = range {
171173
range.with_ctxt(item.span.ctxt())
172174
} else {

clippy_lints/src/methods/manual_inspect.rs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -100,14 +100,12 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name:
100100
match x {
101101
UseKind::Return(s) => edits.push((s.with_leading_whitespace(cx).with_ctxt(s.ctxt()), String::new())),
102102
UseKind::Borrowed(s) => {
103-
#[expect(clippy::range_plus_one)]
104-
let range = s.map_range(cx, |src, range| {
103+
let range = s.map_range_as_pos_len(cx, |src, range| {
105104
let src = src.get(range.clone())?;
106105
let trimmed = src.trim_start_matches([' ', '\t', '\n', '\r', '(']);
107-
trimmed.starts_with('&').then(|| {
108-
let pos = range.start + src.len() - trimmed.len();
109-
pos..pos + 1
110-
})
106+
trimmed
107+
.starts_with('&')
108+
.then_some((range.start + src.len() - trimmed.len(), 1))
111109
});
112110
if let Some(range) = range {
113111
addr_of_edits.push((range.with_ctxt(s.ctxt()), String::new()));

clippy_utils/src/source.rs

Lines changed: 100 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ use rustc_lint::{LateContext, LintContext};
1010
use rustc_session::Session;
1111
use rustc_span::source_map::{original_sp, SourceMap};
1212
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,
1515
};
1616
use std::borrow::Cow;
1717
use std::fmt;
@@ -75,6 +75,12 @@ pub trait SpanRangeExt: SpanRange {
7575
get_source_text(cx.sess().source_map(), self.into_range())
7676
}
7777

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+
7884
/// Calls the given function with the source text referenced and returns the value. Returns
7985
/// `None` if the source text cannot be retrieved.
8086
fn with_source_text<T>(self, cx: &impl LintContext, f: impl for<'a> FnOnce(&'a str) -> T) -> Option<T> {
@@ -97,6 +103,16 @@ pub trait SpanRangeExt: SpanRange {
97103
with_source_text_and_range(cx.sess().source_map(), self.into_range(), f)
98104
}
99105

106+
/// Checks if the referenced source text satisfies the given predicate. Returns `false` if the
107+
/// source text cannot be retrieved.
108+
fn check_source_text_with_range(
109+
self,
110+
cx: &impl LintContext,
111+
pred: impl for<'a> FnOnce(&'a str, Range<usize>) -> bool,
112+
) -> bool {
113+
self.with_source_text_and_range(cx, pred).unwrap_or(false)
114+
}
115+
100116
/// Calls the given function with the both the text of the source file and the referenced range,
101117
/// and creates a new span with the returned range. Returns `None` if the source text cannot be
102118
/// retrieved, or no result is returned.
@@ -110,11 +126,29 @@ pub trait SpanRangeExt: SpanRange {
110126
map_range(cx.sess().source_map(), self.into_range(), f)
111127
}
112128

129+
/// Calls the given function with the both the text of the source file and the referenced range,
130+
/// and creates a new span from the result. Returns `None` if the source text cannot be
131+
/// retrieved, or no result is returned.
132+
///
133+
/// The new range must reside within the same source file.
134+
fn map_range_as_pos_len(
135+
self,
136+
cx: &impl LintContext,
137+
f: impl for<'a> FnOnce(&'a str, Range<usize>) -> Option<(usize, usize)>,
138+
) -> Option<Range<BytePos>> {
139+
map_range_as_pos_len(cx.sess().source_map(), self.into_range(), f)
140+
}
141+
113142
/// Extends the range to include all preceding whitespace characters.
114143
fn with_leading_whitespace(self, cx: &impl LintContext) -> Range<BytePos> {
115144
with_leading_whitespace(cx.sess().source_map(), self.into_range())
116145
}
117146

147+
/// Extends the range to include all trailing whitespace characters.
148+
fn with_trailing_whitespace(self, cx: &impl LintContext) -> Range<BytePos> {
149+
with_trailing_whitespace(cx.sess().source_map(), self.into_range())
150+
}
151+
118152
/// Trims the leading whitespace from the range.
119153
fn trim_start(self, cx: &impl LintContext) -> Range<BytePos> {
120154
trim_start(cx.sess().source_map(), self.into_range())
@@ -139,7 +173,7 @@ fn get_source_text(sm: &SourceMap, sp: Range<BytePos>) -> Option<SourceFileRange
139173
if !Lrc::ptr_eq(&start.sf, &end.sf) || start.pos > end.pos {
140174
return None;
141175
}
142-
let range = start.pos.to_usize()..end.pos.to_usize();
176+
let range = RelativeBytePos(start.pos.0)..RelativeBytePos(end.pos.0);
143177
Some(SourceFileRange { sf: start.sf, range })
144178
}
145179

@@ -161,12 +195,31 @@ fn with_source_text_and_range<T>(
161195
if let Some(src) = get_source_text(sm, sp)
162196
&& let Some(text) = &src.sf.src
163197
{
164-
Some(f(text, src.range))
198+
Some(f(text, src.usize_range()))
165199
} else {
166200
None
167201
}
168202
}
169203

204+
fn with_line_indent<T>(sm: &SourceMap, sp: Range<BytePos>, f: impl for<'a> FnOnce(&'a str) -> T) -> T {
205+
let src = get_source_text(sm, sp);
206+
let indent = if let Some(src) = &src
207+
&& let Some(line) = src.sf.lookup_line(src.range.start)
208+
&& let Some(start) = src.sf.lines().get(line)
209+
&& let Some(text) = src.sf.src.as_ref()
210+
{
211+
let text = if let Some(end) = src.sf.lines().get(line + 1) {
212+
&text[start.to_usize()..end.to_usize()]
213+
} else {
214+
&text[start.to_usize()..]
215+
};
216+
&text[..text.len() - text.trim_start().len()]
217+
} else {
218+
""
219+
};
220+
f(indent)
221+
}
222+
170223
#[expect(clippy::cast_possible_truncation)]
171224
fn map_range(
172225
sm: &SourceMap,
@@ -175,7 +228,7 @@ fn map_range(
175228
) -> Option<Range<BytePos>> {
176229
if let Some(src) = get_source_text(sm, sp.clone())
177230
&& let Some(text) = &src.sf.src
178-
&& let Some(range) = f(text, src.range.clone())
231+
&& let Some(range) = f(text, src.usize_range())
179232
{
180233
debug_assert!(
181234
range.start <= text.len() && range.end <= text.len(),
@@ -184,21 +237,54 @@ fn map_range(
184237
text.len(),
185238
);
186239
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);
240+
let dstart = (range.start as u32).wrapping_sub(src.range.start.0);
241+
let dend = (range.end as u32).wrapping_sub(src.range.start.0);
189242
Some(BytePos(sp.start.0.wrapping_add(dstart))..BytePos(sp.start.0.wrapping_add(dend)))
190243
} else {
191244
None
192245
}
193246
}
194247

248+
#[expect(clippy::cast_possible_truncation)]
249+
fn map_range_as_pos_len(
250+
sm: &SourceMap,
251+
sp: Range<BytePos>,
252+
f: impl for<'a> FnOnce(&'a str, Range<usize>) -> Option<(usize, usize)>,
253+
) -> Option<Range<BytePos>> {
254+
if let Some(src) = get_source_text(sm, sp.clone())
255+
&& let Some(text) = &src.sf.src
256+
&& let Some((pos, len)) = f(text, src.usize_range())
257+
{
258+
debug_assert!(
259+
pos + len <= text.len(),
260+
"Range `{:?}` is outside the source file (file `{}`, length `{}`)",
261+
pos..pos + len,
262+
src.sf.name.display(FileNameDisplayPreference::Local),
263+
text.len(),
264+
);
265+
let delta = (pos as u32).wrapping_sub(src.range.start.0);
266+
let pos = sp.start.0.wrapping_add(delta);
267+
Some(BytePos(pos)..BytePos(pos + len as u32))
268+
} else {
269+
None
270+
}
271+
}
272+
195273
fn with_leading_whitespace(sm: &SourceMap, sp: Range<BytePos>) -> Range<BytePos> {
196274
map_range(sm, sp.clone(), |src, range| {
197275
Some(src.get(..range.start)?.trim_end().len()..range.end)
198276
})
199277
.unwrap_or(sp)
200278
}
201279

280+
fn with_trailing_whitespace(sm: &SourceMap, sp: Range<BytePos>) -> Range<BytePos> {
281+
map_range(sm, sp.clone(), |src, range| {
282+
let tail = src.get(range.end..)?;
283+
Some(range.start..range.end + (tail.len() - tail.trim_start().len()))
284+
})
285+
.unwrap_or(sp)
286+
}
287+
202288
fn trim_start(sm: &SourceMap, sp: Range<BytePos>) -> Range<BytePos> {
203289
map_range(sm, sp.clone(), |src, range| {
204290
let src = src.get(range.clone())?;
@@ -216,13 +302,18 @@ fn write_source_text_to(sm: &SourceMap, sp: Range<BytePos>, dst: &mut impl fmt::
216302

217303
pub struct SourceFileRange {
218304
pub sf: Lrc<SourceFile>,
219-
pub range: Range<usize>,
305+
pub range: Range<RelativeBytePos>,
220306
}
221307
impl SourceFileRange {
222308
/// Attempts to get the text from the source file. This can fail if the source text isn't
223309
/// loaded.
224310
pub fn as_str(&self) -> Option<&str> {
225-
self.sf.src.as_ref().and_then(|x| x.get(self.range.clone()))
311+
self.sf.src.as_ref().and_then(|x| x.get(self.usize_range()))
312+
}
313+
314+
/// Gets the range of the source text as a `usize` range.
315+
pub fn usize_range(&self) -> Range<usize> {
316+
self.range.start.0 as usize..self.range.end.0 as usize
226317
}
227318
}
228319

tests/ui/formatting.rs

Lines changed: 0 additions & 85 deletions
This file was deleted.

tests/ui/formatting.stderr

Lines changed: 0 additions & 54 deletions
This file was deleted.

0 commit comments

Comments
 (0)