Skip to content

Commit 5d1f8a3

Browse files
committed
fix: use snippet_with_context for spans that are likely to contain macro expns
1 parent 95eeaab commit 5d1f8a3

File tree

4 files changed

+131
-26
lines changed

4 files changed

+131
-26
lines changed

clippy_lints_internal/src/collapsible_span_lint_calls.rs

Lines changed: 44 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
use clippy_utils::diagnostics::span_lint_and_sugg;
2-
use clippy_utils::source::snippet;
2+
use clippy_utils::source::{snippet, snippet_with_context};
33
use clippy_utils::{SpanlessEq, is_lint_allowed, peel_blocks_with_stmt, sym};
44
use rustc_errors::Applicability;
55
use rustc_hir::{Closure, Expr, ExprKind};
66
use rustc_lint::{LateContext, LateLintPass};
77
use rustc_session::{declare_lint_pass, declare_tool_lint};
8-
use rustc_span::Span;
8+
use rustc_span::{Span, SyntaxContext};
99

1010
use std::borrow::{Borrow, Cow};
1111

@@ -88,33 +88,42 @@ impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls {
8888
&& let ExprKind::MethodCall(ps, recv, span_call_args, _) = &only_expr.kind
8989
&& let ExprKind::Path(..) = recv.kind
9090
{
91-
let and_then_snippets =
92-
get_and_then_snippets(cx, call_cx.span, call_lint.span, call_sp.span, call_msg.span);
91+
let mut app = Applicability::MachineApplicable;
92+
let expr_ctxt = expr.span.ctxt();
93+
let and_then_snippets = get_and_then_snippets(
94+
cx,
95+
expr_ctxt,
96+
call_cx.span,
97+
call_lint.span,
98+
call_sp.span,
99+
call_msg.span,
100+
&mut app,
101+
);
93102
let mut sle = SpanlessEq::new(cx).deny_side_effects();
94103
match ps.ident.name {
95104
sym::span_suggestion if sle.eq_expr(call_sp, &span_call_args[0]) => {
96-
suggest_suggestion(
97-
cx,
98-
expr,
99-
&and_then_snippets,
100-
&span_suggestion_snippets(cx, span_call_args),
101-
);
105+
let snippets = span_suggestion_snippets(cx, expr_ctxt, span_call_args, &mut app);
106+
suggest_suggestion(cx, expr, &and_then_snippets, &snippets, app);
102107
},
103108
sym::span_help if sle.eq_expr(call_sp, &span_call_args[0]) => {
104-
let help_snippet = snippet(cx, span_call_args[1].span, r#""...""#);
105-
suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), true);
109+
let help_snippet =
110+
snippet_with_context(cx, span_call_args[1].span, expr_ctxt, r#""...""#, &mut app).0;
111+
suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), true, app);
106112
},
107113
sym::span_note if sle.eq_expr(call_sp, &span_call_args[0]) => {
108-
let note_snippet = snippet(cx, span_call_args[1].span, r#""...""#);
109-
suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), true);
114+
let note_snippet =
115+
snippet_with_context(cx, span_call_args[1].span, expr_ctxt, r#""...""#, &mut app).0;
116+
suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), true, app);
110117
},
111118
sym::help => {
112-
let help_snippet = snippet(cx, span_call_args[0].span, r#""...""#);
113-
suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), false);
119+
let help_snippet =
120+
snippet_with_context(cx, span_call_args[0].span, expr_ctxt, r#""...""#, &mut app).0;
121+
suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), false, app);
114122
},
115123
sym::note => {
116-
let note_snippet = snippet(cx, span_call_args[0].span, r#""...""#);
117-
suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), false);
124+
let note_snippet =
125+
snippet_with_context(cx, span_call_args[0].span, expr_ctxt, r#""...""#, &mut app).0;
126+
suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), false, app);
118127
},
119128
_ => (),
120129
}
@@ -131,15 +140,17 @@ struct AndThenSnippets {
131140

132141
fn get_and_then_snippets(
133142
cx: &LateContext<'_>,
143+
expr_ctxt: SyntaxContext,
134144
cx_span: Span,
135145
lint_span: Span,
136146
span_span: Span,
137147
msg_span: Span,
148+
app: &mut Applicability,
138149
) -> AndThenSnippets {
139150
let cx_snippet = snippet(cx, cx_span, "cx");
140151
let lint_snippet = snippet(cx, lint_span, "..");
141152
let span_snippet = snippet(cx, span_span, "span");
142-
let msg_snippet = snippet(cx, msg_span, r#""...""#);
153+
let msg_snippet = snippet_with_context(cx, msg_span, expr_ctxt, r#""...""#, app).0;
143154

144155
AndThenSnippets {
145156
cx: cx_snippet,
@@ -155,9 +166,14 @@ struct SpanSuggestionSnippets {
155166
applicability: Cow<'static, str>,
156167
}
157168

158-
fn span_suggestion_snippets<'hir>(cx: &LateContext<'_>, span_call_args: &'hir [Expr<'hir>]) -> SpanSuggestionSnippets {
159-
let help_snippet = snippet(cx, span_call_args[1].span, r#""...""#);
160-
let sugg_snippet = snippet(cx, span_call_args[2].span, "..");
169+
fn span_suggestion_snippets<'hir>(
170+
cx: &LateContext<'_>,
171+
expr_ctxt: SyntaxContext,
172+
span_call_args: &'hir [Expr<'hir>],
173+
app: &mut Applicability,
174+
) -> SpanSuggestionSnippets {
175+
let help_snippet = snippet_with_context(cx, span_call_args[1].span, expr_ctxt, r#""...""#, app).0;
176+
let sugg_snippet = snippet_with_context(cx, span_call_args[2].span, expr_ctxt, "..", app).0;
161177
let applicability_snippet = snippet(cx, span_call_args[3].span, "Applicability::MachineApplicable");
162178

163179
SpanSuggestionSnippets {
@@ -172,6 +188,7 @@ fn suggest_suggestion(
172188
expr: &Expr<'_>,
173189
and_then_snippets: &AndThenSnippets,
174190
span_suggestion_snippets: &SpanSuggestionSnippets,
191+
app: Applicability,
175192
) {
176193
span_lint_and_sugg(
177194
cx,
@@ -189,7 +206,7 @@ fn suggest_suggestion(
189206
span_suggestion_snippets.sugg,
190207
span_suggestion_snippets.applicability
191208
),
192-
Applicability::MachineApplicable,
209+
app,
193210
);
194211
}
195212

@@ -199,6 +216,7 @@ fn suggest_help(
199216
and_then_snippets: &AndThenSnippets,
200217
help: &str,
201218
with_span: bool,
219+
app: Applicability,
202220
) {
203221
let option_span = if with_span {
204222
format!("Some({})", and_then_snippets.span)
@@ -216,7 +234,7 @@ fn suggest_help(
216234
"span_lint_and_help({}, {}, {}, {}, {}, {help})",
217235
and_then_snippets.cx, and_then_snippets.lint, and_then_snippets.span, and_then_snippets.msg, &option_span,
218236
),
219-
Applicability::MachineApplicable,
237+
app,
220238
);
221239
}
222240

@@ -226,6 +244,7 @@ fn suggest_note(
226244
and_then_snippets: &AndThenSnippets,
227245
note: &str,
228246
with_span: bool,
247+
app: Applicability,
229248
) {
230249
let note_span = if with_span {
231250
format!("Some({})", and_then_snippets.span)
@@ -243,6 +262,6 @@ fn suggest_note(
243262
"span_lint_and_note({}, {}, {}, {}, {note_span}, {note})",
244263
and_then_snippets.cx, and_then_snippets.lint, and_then_snippets.span, and_then_snippets.msg,
245264
),
246-
Applicability::MachineApplicable,
265+
app,
247266
);
248267
}

tests/ui-internal/collapsible_span_lint_calls.fixed

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,15 @@ impl EarlyLintPass for Pass {
5050
span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
5151
db.help(help_msg).help(help_msg);
5252
});
53+
54+
// Issue #15880
55+
#[expect(clippy::disallowed_names)]
56+
let foo = "foo";
57+
span_lint_and_sugg(cx, TEST_LINT, expr.span, lint_msg, format!("try using {foo}"), format!("{foo}.use"), Applicability::MachineApplicable);
58+
span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), format!("try using {foo}"));
59+
span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, format!("try using {foo}"));
60+
span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), format!("required because of {foo}"));
61+
span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, None, format!("required because of {foo}"));
5362
}
5463
}
5564

tests/ui-internal/collapsible_span_lint_calls.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,35 @@ impl EarlyLintPass for Pass {
6565
span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
6666
db.help(help_msg).help(help_msg);
6767
});
68+
69+
// Issue #15880
70+
#[expect(clippy::disallowed_names)]
71+
let foo = "foo";
72+
span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
73+
//~^ collapsible_span_lint_calls
74+
db.span_suggestion(
75+
expr.span,
76+
format!("try using {foo}"),
77+
format!("{foo}.use"),
78+
Applicability::MachineApplicable,
79+
);
80+
});
81+
span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
82+
//~^ collapsible_span_lint_calls
83+
db.span_help(expr.span, format!("try using {foo}"));
84+
});
85+
span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
86+
//~^ collapsible_span_lint_calls
87+
db.help(format!("try using {foo}"));
88+
});
89+
span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
90+
//~^ collapsible_span_lint_calls
91+
db.span_note(expr.span, format!("required because of {foo}"));
92+
});
93+
span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
94+
//~^ collapsible_span_lint_calls
95+
db.note(format!("required because of {foo}"));
96+
});
6897
}
6998
}
7099

tests/ui-internal/collapsible_span_lint_calls.stderr

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,5 +49,53 @@ LL | | db.note(note_msg);
4949
LL | | });
5050
| |__________^ help: collapse into: `span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, None, note_msg)`
5151

52-
error: aborting due to 5 previous errors
52+
error: this call is collapsible
53+
--> tests/ui-internal/collapsible_span_lint_calls.rs:72:9
54+
|
55+
LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
56+
LL | |
57+
LL | | db.span_suggestion(
58+
LL | | expr.span,
59+
... |
60+
LL | | );
61+
LL | | });
62+
| |__________^ help: collapse into: `span_lint_and_sugg(cx, TEST_LINT, expr.span, lint_msg, format!("try using {foo}"), format!("{foo}.use"), Applicability::MachineApplicable)`
63+
64+
error: this call is collapsible
65+
--> tests/ui-internal/collapsible_span_lint_calls.rs:81:9
66+
|
67+
LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
68+
LL | |
69+
LL | | db.span_help(expr.span, format!("try using {foo}"));
70+
LL | | });
71+
| |__________^ help: collapse into: `span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), format!("try using {foo}"))`
72+
73+
error: this call is collapsible
74+
--> tests/ui-internal/collapsible_span_lint_calls.rs:85:9
75+
|
76+
LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
77+
LL | |
78+
LL | | db.help(format!("try using {foo}"));
79+
LL | | });
80+
| |__________^ help: collapse into: `span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, format!("try using {foo}"))`
81+
82+
error: this call is collapsible
83+
--> tests/ui-internal/collapsible_span_lint_calls.rs:89:9
84+
|
85+
LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
86+
LL | |
87+
LL | | db.span_note(expr.span, format!("required because of {foo}"));
88+
LL | | });
89+
| |__________^ help: collapse into: `span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), format!("required because of {foo}"))`
90+
91+
error: this call is collapsible
92+
--> tests/ui-internal/collapsible_span_lint_calls.rs:93:9
93+
|
94+
LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
95+
LL | |
96+
LL | | db.note(format!("required because of {foo}"));
97+
LL | | });
98+
| |__________^ help: collapse into: `span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, None, format!("required because of {foo}"))`
99+
100+
error: aborting due to 10 previous errors
53101

0 commit comments

Comments
 (0)