Skip to content

Commit 58fdc1a

Browse files
committed
add debug assertions for overlapping spans and empty replacements
1 parent f2f0175 commit 58fdc1a

File tree

2 files changed

+61
-1
lines changed

2 files changed

+61
-1
lines changed

clippy_utils/src/diagnostics.rs

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
//! Thank you!
99
//! ~The `INTERNAL_METADATA_COLLECTOR` lint
1010
11-
use rustc_errors::{Applicability, Diag, DiagMessage, MultiSpan, SubdiagMessage};
11+
use rustc_errors::{
12+
Applicability, Diag, DiagMessage, EmissionGuarantee, MultiSpan, SubdiagMessage, SubstitutionPart, Suggestions,
13+
};
1214
use rustc_hir::HirId;
1315
use rustc_lint::{LateContext, Lint, LintContext};
1416
use rustc_span::Span;
@@ -28,6 +30,42 @@ fn docs_link(diag: &mut Diag<'_, ()>, lint: &'static Lint) {
2830
}
2931
}
3032

33+
/// Makes sure that a diagnostic is well formed.
34+
///
35+
/// rustc debug asserts a few properties about spans,
36+
/// but the clippy repo uses a distributed rustc build with debug assertions disabled,
37+
/// so this has historically led to problems during subtree syncs where those debug assertions
38+
/// only started triggered there.
39+
///
40+
/// This function makes sure we also validate them in debug clippy builds.
41+
fn validate_diag(diag: &Diag<'_, impl EmissionGuarantee>) {
42+
let suggestions = match &diag.suggestions {
43+
Suggestions::Enabled(suggs) => &**suggs,
44+
Suggestions::Sealed(suggs) => &**suggs,
45+
Suggestions::Disabled => return,
46+
};
47+
48+
for substitution in suggestions.iter().flat_map(|s| &s.substitutions) {
49+
assert_eq!(
50+
substitution
51+
.parts
52+
.iter()
53+
.find(|SubstitutionPart { snippet, span }| snippet.is_empty() && span.is_empty()),
54+
None,
55+
"span must not be empty and have no suggestion"
56+
);
57+
58+
assert_eq!(
59+
substitution
60+
.parts
61+
.array_windows()
62+
.find(|[a, b]| a.span.overlaps(b.span)),
63+
None,
64+
"suggestion must not have overlapping parts"
65+
);
66+
}
67+
}
68+
3169
/// Emit a basic lint message with a `msg` and a `span`.
3270
///
3371
/// This is the most primitive of our lint emission methods and can
@@ -64,6 +102,9 @@ pub fn span_lint<T: LintContext>(cx: &T, lint: &'static Lint, sp: impl Into<Mult
64102
cx.span_lint(lint, sp, |diag| {
65103
diag.primary_message(msg);
66104
docs_link(diag, lint);
105+
106+
#[cfg(debug_assertions)]
107+
validate_diag(diag);
67108
});
68109
}
69110

@@ -118,6 +159,9 @@ pub fn span_lint_and_help<T: LintContext>(
118159
diag.help(help.into());
119160
}
120161
docs_link(diag, lint);
162+
163+
#[cfg(debug_assertions)]
164+
validate_diag(diag);
121165
});
122166
}
123167

@@ -175,6 +219,9 @@ pub fn span_lint_and_note<T: LintContext>(
175219
diag.note(note.into());
176220
}
177221
docs_link(diag, lint);
222+
223+
#[cfg(debug_assertions)]
224+
validate_diag(diag);
178225
});
179226
}
180227

@@ -208,6 +255,9 @@ where
208255
diag.primary_message(msg);
209256
f(diag);
210257
docs_link(diag, lint);
258+
259+
#[cfg(debug_assertions)]
260+
validate_diag(diag);
211261
});
212262
}
213263

@@ -240,6 +290,9 @@ pub fn span_lint_hir(cx: &LateContext<'_>, lint: &'static Lint, hir_id: HirId, s
240290
cx.tcx.node_span_lint(lint, hir_id, sp, |diag| {
241291
diag.primary_message(msg);
242292
docs_link(diag, lint);
293+
294+
#[cfg(debug_assertions)]
295+
validate_diag(diag);
243296
});
244297
}
245298

@@ -280,6 +333,9 @@ pub fn span_lint_hir_and_then(
280333
diag.primary_message(msg);
281334
f(diag);
282335
docs_link(diag, lint);
336+
337+
#[cfg(debug_assertions)]
338+
validate_diag(diag);
283339
});
284340
}
285341

@@ -328,5 +384,8 @@ pub fn span_lint_and_sugg<T: LintContext>(
328384
) {
329385
span_lint_and_then(cx, lint, sp, msg.into(), |diag| {
330386
diag.span_suggestion(sp, help.into(), sugg, applicability);
387+
388+
#[cfg(debug_assertions)]
389+
validate_diag(diag);
331390
});
332391
}

clippy_utils/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#![feature(rustc_private)]
1010
#![feature(assert_matches)]
1111
#![feature(unwrap_infallible)]
12+
#![feature(array_windows)]
1213
#![recursion_limit = "512"]
1314
#![allow(
1415
clippy::missing_errors_doc,

0 commit comments

Comments
 (0)