Skip to content

Commit a61b7ba

Browse files
committed
Rework clippy_utils::source.
1 parent 4a7ea53 commit a61b7ba

14 files changed

+726
-316
lines changed

clippy_lints/src/cognitive_complexity.rs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use clippy_config::Conf;
22
use clippy_utils::diagnostics::span_lint_and_help;
3-
use clippy_utils::source::{IntoSpan, SpanExt};
3+
use clippy_utils::source::SpanExt;
44
use clippy_utils::ty::is_type_diagnostic_item;
55
use clippy_utils::visitors::for_each_expr_without_closures;
66
use clippy_utils::{LimitStack, get_async_fn_body, is_async_fn, sym};
@@ -110,14 +110,15 @@ impl CognitiveComplexity {
110110
FnKind::ItemFn(ident, _, _) | FnKind::Method(ident, _) => ident.span,
111111
FnKind::Closure => {
112112
let header_span = body_span.with_hi(decl.output.span().lo());
113-
if let Some(range) = header_span.map_range(cx, |_, src, range| {
114-
let mut idxs = src.get(range.clone())?.match_indices('|');
115-
Some(range.start + idxs.next()?.0..range.start + idxs.next()?.0 + 1)
116-
}) {
117-
range.with_ctxt(header_span.ctxt())
118-
} else {
113+
let Some(s) = header_span.map_range(cx, |range| {
114+
range.edit_range(|_, src, range| {
115+
let mut idxs = src.get(range.clone())?.match_indices('|');
116+
Some(range.start + idxs.next()?.0..range.start + idxs.next()?.0 + 1)
117+
})
118+
}) else {
119119
return;
120-
}
120+
};
121+
s
121122
},
122123
};
123124

clippy_lints/src/collapsible_if.rs

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use clippy_config::Conf;
22
use clippy_utils::diagnostics::span_lint_and_then;
33
use clippy_utils::msrvs::{self, Msrv};
4-
use clippy_utils::source::{IntoSpan as _, SpanExt, snippet, snippet_block_with_applicability};
4+
use clippy_utils::source::{SourceFileRange, SpanExt, snippet, snippet_block_with_applicability};
55
use clippy_utils::{span_contains_non_whitespace, tokenize_with_text};
66
use rustc_ast::BinOpKind;
77
use rustc_errors::Applicability;
@@ -113,15 +113,15 @@ impl CollapsibleIf {
113113
span_extract_keyword(cx.tcx.sess.source_map(), up_to_else, "else")
114114
&& let Some(else_if_keyword_span) =
115115
span_extract_keyword(cx.tcx.sess.source_map(), else_before_if, "if")
116+
&& let Some(else_keyword_span) =
117+
else_keyword_span.map_range(cx, SourceFileRange::add_leading_whitespace)
118+
&& let Some(else_open_bracket) = else_block.span.map_range(cx, |range| {
119+
range.set_to_first_char_if(|c| c == '{')?.add_leading_whitespace()
120+
})
121+
&& let Some(else_closing_bracket) = else_block.span.map_range(cx, |range| {
122+
range.set_to_last_char_if(|c| c == '}')?.add_leading_whitespace()
123+
})
116124
{
117-
let else_keyword_span = else_keyword_span.with_leading_whitespace(cx).into_span();
118-
let else_open_bracket = else_block.span.split_at(1).0.with_leading_whitespace(cx).into_span();
119-
let else_closing_bracket = {
120-
let end = else_block.span.shrink_to_hi();
121-
end.with_lo(end.lo() - BytePos(1))
122-
.with_leading_whitespace(cx)
123-
.into_span()
124-
};
125125
let sugg = vec![
126126
// Remove the outer else block `else`
127127
(else_keyword_span, String::new()),
@@ -173,20 +173,19 @@ impl CollapsibleIf {
173173
&& let ctxt = expr.span.ctxt()
174174
&& inner.span.ctxt() == ctxt
175175
&& !block_starts_with_significant_tokens(cx, then, inner, self.lint_commented_code)
176+
&& let Some(then_open_bracket) = then.span.map_range(cx, |range| {
177+
range.set_to_first_char_if(|c| c == '{')?.add_leading_whitespace()
178+
})
179+
&& let Some(then_closing_bracket) = then.span.map_range(cx, |range| {
180+
range.set_to_last_char_if(|c| c == '}')?.add_leading_whitespace()
181+
})
176182
{
177183
span_lint_and_then(
178184
cx,
179185
COLLAPSIBLE_IF,
180186
expr.span,
181187
"this `if` statement can be collapsed",
182188
|diag| {
183-
let then_open_bracket = then.span.split_at(1).0.with_leading_whitespace(cx).into_span();
184-
let then_closing_bracket = {
185-
let end = then.span.shrink_to_hi();
186-
end.with_lo(end.lo() - BytePos(1))
187-
.with_leading_whitespace(cx)
188-
.into_span()
189-
};
190189
let inner_if = inner.span.split_at(2).0;
191190
let mut sugg = vec![
192191
// Remove the outer then block `{`

clippy_lints/src/copies.rs

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use clippy_config::Conf;
22
use clippy_utils::diagnostics::{span_lint, span_lint_and_note, span_lint_and_then};
3-
use clippy_utils::source::{IntoSpan, SpanExt, first_line_of_span, indent_of, reindent_multiline, snippet};
3+
use clippy_utils::source::{SpanExt, first_line_of_span, indent_of, reindent_multiline, snippet};
44
use clippy_utils::ty::{InteriorMut, needs_ordered_drop};
55
use clippy_utils::visitors::for_each_expr_without_closures;
66
use clippy_utils::{
@@ -249,21 +249,25 @@ fn lint_branches_sharing_code<'tcx>(
249249
let suggestion = reindent_multiline(&suggestion, true, cond_indent);
250250
(replace_span, suggestion.to_string())
251251
});
252-
let end_suggestion = res.end_span(last_block, sm).map(|span| {
252+
let end_suggestion = res.end_span(last_block, sm).and_then(|span| {
253253
let moved_snipped = reindent_multiline(&snippet(cx, span, "_"), true, None);
254254
let indent = indent_of(cx, expr.span.shrink_to_hi());
255255
let suggestion = "}\n".to_string() + &moved_snipped;
256256
let suggestion = reindent_multiline(&suggestion, true, indent);
257257

258-
let span = span.with_hi(last_block.span.hi());
259258
// Improve formatting if the inner block has indentation (i.e. normal Rust formatting)
260-
let span = span
261-
.map_range(cx, |_, src, range| {
262-
(range.start > 4 && src.get(range.start - 4..range.start)? == " ")
263-
.then_some(range.start - 4..range.end)
264-
})
265-
.map_or(span, |range| range.with_ctxt(span.ctxt()));
266-
(span, suggestion.to_string())
259+
let span = span.map_range(cx, |range| {
260+
range
261+
.set_end_if_after(last_block.span.hi())?
262+
.edit_range(|_, src, range| {
263+
if src.get(..range.start)?.ends_with(" ") {
264+
Some(range.start - 4..range.end)
265+
} else {
266+
Some(range)
267+
}
268+
})
269+
})?;
270+
Some((span, suggestion.to_string()))
267271
});
268272

269273
let (span, msg, end_span) = match (&start_suggestion, &end_suggestion) {

clippy_lints/src/default_constructed_unit_structs.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ impl LateLintPass<'_> for DefaultConstructedUnitStructs {
7575
&& !base.is_suggestable_infer_ty()
7676
{
7777
let mut removals = vec![(expr.span.with_lo(qpath.qself_span().hi()), String::new())];
78-
if expr.span.with_source_text(cx, |s| s.starts_with('<')) == Some(true) {
78+
if expr.span.check_source_text(cx, |s| s.starts_with('<')) {
7979
// Remove `<`, '>` has already been removed by the existing removal expression.
8080
removals.push((expr.span.with_hi(qpath.qself_span().lo()), String::new()));
8181
}

clippy_lints/src/implicit_hasher.rs

Lines changed: 16 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use rustc_session::declare_lint_pass;
1212
use rustc_span::Span;
1313

1414
use clippy_utils::diagnostics::span_lint_and_then;
15-
use clippy_utils::source::{IntoSpan, SpanExt, snippet};
15+
use clippy_utils::source::{SpanExt, snippet};
1616
use clippy_utils::sym;
1717
use clippy_utils::ty::is_type_diagnostic_item;
1818

@@ -117,17 +117,13 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitHasher {
117117
if !item.span.eq_ctxt(target.span()) {
118118
return;
119119
}
120-
121-
let generics_suggestion_span = impl_.generics.span.substitute_dummy({
122-
let range = (item.span.lo()..target.span().lo()).map_range(cx, |_, src, range| {
123-
Some(src.get(range.clone())?.find("impl")? + 4..range.end)
124-
});
125-
if let Some(range) = range {
126-
range.with_ctxt(item.span.ctxt())
127-
} else {
128-
return;
129-
}
130-
});
120+
let Some(generics_span) = item.span.map_range(cx, |file| {
121+
file.set_end_if_within(target.span().lo())?
122+
.edit_range(|_, src, range| Some(src.get(range.clone())?.find("impl")? + 4..range.end))
123+
}) else {
124+
return;
125+
};
126+
let generics_suggestion_span = impl_.generics.span.substitute_dummy(generics_span);
131127

132128
let mut ctr_vis = ImplicitHasherConstructorVisitor::new(cx, target);
133129
for item in impl_.items.iter().map(|item| cx.tcx.hir_impl_item(item.id)) {
@@ -164,19 +160,17 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitHasher {
164160
if generics.span.from_expansion() {
165161
continue;
166162
}
167-
let generics_suggestion_span = generics.span.substitute_dummy({
168-
let range =
169-
(item.span.lo()..body.params[0].pat.span.lo()).map_range(cx, |_, src, range| {
163+
let Some(generics_span) = item.span.map_range(cx, |file| {
164+
file.set_end_if_within(body.params[0].pat.span.lo())?
165+
.edit_range(|_, src, range| {
170166
let (pre, post) = src.get(range.clone())?.split_once("fn")?;
171167
let pos = post.find('(')? + pre.len() + 2;
172168
Some(pos..pos)
173-
});
174-
if let Some(range) = range {
175-
range.with_ctxt(item.span.ctxt())
176-
} else {
177-
return;
178-
}
179-
});
169+
})
170+
}) else {
171+
return;
172+
};
173+
let generics_suggestion_span = generics.span.substitute_dummy(generics_span);
180174

181175
let mut ctr_vis = ImplicitHasherConstructorVisitor::new(cx, target);
182176
ctr_vis.visit_body(body);

clippy_lints/src/ineffective_open_options.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -64,15 +64,15 @@ impl<'tcx> LateLintPass<'tcx> for IneffectiveOpenOptions {
6464
match name.ident.name {
6565
sym::append => append = true,
6666
sym::write
67-
if let Some(range) = call_span.map_range(cx, |_, text, range| {
68-
if text.get(..range.start)?.ends_with('.') {
69-
Some(range.start - 1..range.end)
70-
} else {
71-
None
72-
}
67+
if let Some(call_span) = call_span.map_range(cx, |range| {
68+
range.add_leading_whitespace()?.edit_range(|_, text, range| {
69+
text.get(..range.start)?
70+
.ends_with('.')
71+
.then_some(range.start.wrapping_sub(1)..range.end)
72+
})
7373
}) =>
7474
{
75-
write = Some(call_span.with_lo(range.start));
75+
write = Some(call_span);
7676
},
7777
_ => {},
7878
}

clippy_lints/src/matches/single_match.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use super::{MATCH_BOOL, SINGLE_MATCH, SINGLE_MATCH_ELSE};
2525
/// match arms.
2626
fn empty_arm_has_comment(cx: &LateContext<'_>, span: Span) -> bool {
2727
if let Some(ff) = span.get_source_range(cx)
28-
&& let Some(text) = ff.as_str()
28+
&& let Some(text) = ff.src_text()
2929
{
3030
text.as_bytes().windows(2).any(|w| w == b"//" || w == b"/*")
3131
} else {

clippy_lints/src/methods/manual_inspect.rs

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use clippy_utils::diagnostics::span_lint_and_then;
22
use clippy_utils::msrvs::{self, Msrv};
3-
use clippy_utils::source::{IntoSpan, SpanExt};
3+
use clippy_utils::source::{SourceFileRange, SpanExt};
44
use clippy_utils::ty::get_field_by_name;
55
use clippy_utils::visitors::{for_each_expr, for_each_expr_without_closures};
66
use clippy_utils::{ExprUseNode, expr_use_ctxt, is_diag_item_method, is_diag_trait_item, path_to_local_id, sym};
@@ -89,7 +89,7 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name:
8989
})
9090
.is_none();
9191

92-
if ret_count != 0 {
92+
if !can_lint || ret_count != 0 {
9393
// A return expression that didn't return the original value was found.
9494
return;
9595
}
@@ -98,18 +98,25 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name:
9898
let mut addr_of_edits = Vec::with_capacity(delayed.len());
9999
for x in delayed {
100100
match x {
101-
UseKind::Return(s) => edits.push((s.with_leading_whitespace(cx).with_ctxt(s.ctxt()), String::new())),
101+
UseKind::Return(s) => {
102+
if let Some(s) = s.map_range(cx, SourceFileRange::add_leading_whitespace) {
103+
edits.push((s, String::new()));
104+
} else {
105+
return;
106+
}
107+
},
102108
UseKind::Borrowed(s) => {
103-
let range = s.map_range(cx, |_, src, range| {
104-
let src = src.get(range.clone())?;
105-
let trimmed = src.trim_start_matches([' ', '\t', '\n', '\r', '(']);
106-
trimmed.starts_with('&').then(|| {
107-
let pos = range.start + src.len() - trimmed.len();
108-
pos..pos + 1
109+
if let Some(s) = s.map_range(cx, |range| {
110+
range.edit_range(|_, src, range| {
111+
let src = src.get(range.clone())?;
112+
let trimmed = src.trim_start_matches([' ', '\t', '\n', '\r', '(']);
113+
trimmed.starts_with('&').then(|| {
114+
let pos = range.start + src.len() - trimmed.len();
115+
pos..pos + 1
116+
})
109117
})
110-
});
111-
if let Some(range) = range {
112-
addr_of_edits.push((range.with_ctxt(s.ctxt()), String::new()));
118+
}) {
119+
addr_of_edits.push((s, String::new()));
113120
} else {
114121
requires_copy = true;
115122
requires_deref = true;
@@ -156,10 +163,10 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name:
156163
}
157164
}
158165

159-
if can_lint
160-
&& (!requires_copy || cx.type_is_copy_modulo_regions(arg_ty))
166+
if (!requires_copy || cx.type_is_copy_modulo_regions(arg_ty))
161167
// This case could be handled, but a fair bit of care would need to be taken.
162168
&& (!requires_deref || arg_ty.is_freeze(cx.tcx, cx.typing_env()))
169+
&& let Some(final_expr_span) = final_expr.span.map_range(cx, SourceFileRange::add_leading_whitespace)
163170
{
164171
if requires_deref {
165172
edits.push((param.span.shrink_to_lo(), "&".into()));
@@ -172,13 +179,7 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name:
172179
_ => return,
173180
};
174181
edits.push((name_span, edit.to_string()));
175-
edits.push((
176-
final_expr
177-
.span
178-
.with_leading_whitespace(cx)
179-
.with_ctxt(final_expr.span.ctxt()),
180-
String::new(),
181-
));
182+
edits.push((final_expr_span, String::new()));
182183
let app = if edits.iter().any(|(s, _)| s.from_expansion()) {
183184
Applicability::MaybeIncorrect
184185
} else {

clippy_lints/src/multiple_bound_locations.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@ impl EarlyLintPass for MultipleBoundLocations {
5757
&& let Some(Some(bound_span)) = pred
5858
.bounded_ty
5959
.span
60-
.with_source_text(cx, |src| generic_params_with_bounds.get(src))
60+
.get_source_text(cx)
61+
.map(|src| generic_params_with_bounds.get(&*src))
6162
{
6263
emit_lint(cx, *bound_span, pred.bounded_ty.span);
6364
}

clippy_lints/src/needless_else.rs

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use clippy_utils::diagnostics::span_lint_and_sugg;
2-
use clippy_utils::source::{IntoSpan, SpanExt};
2+
use clippy_utils::source::SpanExt;
33
use rustc_ast::ast::{Expr, ExprKind};
44
use rustc_errors::Applicability;
55
use rustc_lint::{EarlyContext, EarlyLintPass};
@@ -38,19 +38,34 @@ impl EarlyLintPass for NeedlessElse {
3838
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
3939
if let ExprKind::If(_, then_block, Some(else_clause)) = &expr.kind
4040
&& let ExprKind::Block(block, _) = &else_clause.kind
41+
&& !then_block.span.from_expansion()
4142
&& !expr.span.from_expansion()
4243
&& !else_clause.span.from_expansion()
4344
&& block.stmts.is_empty()
44-
&& let range = (then_block.span.hi()..expr.span.hi()).trim_start(cx)
45-
&& range.clone().check_source_text(cx, |src| {
46-
// Ignore else blocks that contain comments or #[cfg]s
47-
!src.contains(['/', '#'])
45+
// Only take the span of `else { .. }` if no comments/cfgs/macros exist.
46+
&& let Some(lint_sp) = else_clause.span.map_range(cx, |range| {
47+
range.set_start_if_before(then_block.span.hi())?.edit_range(|_, text, range| {
48+
let text = text.get(range.clone())?;
49+
let trimmed = text.trim_start();
50+
if trimmed
51+
.strip_prefix("else")?
52+
.trim_start()
53+
.strip_prefix('{')?
54+
.strip_suffix('}')?
55+
.chars()
56+
.all(char::is_whitespace)
57+
{
58+
Some(range.start + (text.len() - trimmed.len())..range.end)
59+
} else {
60+
None
61+
}
62+
})
4863
})
4964
{
5065
span_lint_and_sugg(
5166
cx,
5267
NEEDLESS_ELSE,
53-
range.with_ctxt(expr.span.ctxt()),
68+
lint_sp,
5469
"this `else` branch is empty",
5570
"you can remove it",
5671
String::new(),

0 commit comments

Comments
 (0)