Skip to content

Commit 677cf7b

Browse files
committed
Reword mismatched-lifetime-syntaxes text based on feedback
Key changes include: - Removal of the word "syntax" from the lint message. More accurately, it could have been something like "syntax group" or "syntax category", but avoiding it completely is easier. - The primary lint message now reflects exactly which mismatch is occurring, instead of trying to be general. A new `help` line is general across the mismatch kinds. - Suggestions have been reduced to be more minimal, no longer also changing non-idiomatic but unrelated aspects. - Suggestion text no longer mentions changes when those changes don't occur in that specific suggestion.
1 parent 9c3064e commit 677cf7b

25 files changed

+674
-414
lines changed

compiler/rustc_lint/messages.ftl

Lines changed: 38 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -508,27 +508,50 @@ lint_metavariable_still_repeating = variable `{$name}` is still repeating at thi
508508
509509
lint_metavariable_wrong_operator = meta-variable repeats with different Kleene operator
510510
511-
lint_mismatched_lifetime_syntaxes =
512-
lifetime flowing from input to output with different syntax can be confusing
513-
.label_mismatched_lifetime_syntaxes_inputs =
514-
{$n_inputs ->
515-
[one] this lifetime flows
516-
*[other] these lifetimes flow
517-
} to the output
518-
.label_mismatched_lifetime_syntaxes_outputs =
519-
the {$n_outputs ->
520-
[one] lifetime gets
521-
*[other] lifetimes get
522-
} resolved as `{$lifetime_name}`
511+
lint_mismatched_lifetime_syntaxes_eliding_while_named =
512+
eliding a lifetime that's named elsewhere is confusing
513+
514+
lint_mismatched_lifetime_syntaxes_help =
515+
the same lifetime is referred to in inconsistent ways, making the signature confusing
516+
517+
lint_mismatched_lifetime_syntaxes_hiding_and_eliding_while_named =
518+
hiding or eliding a lifetime that's named elsewhere is confusing
519+
520+
lint_mismatched_lifetime_syntaxes_hiding_while_elided =
521+
hiding a lifetime that's elided elsewhere is confusing
522+
523+
lint_mismatched_lifetime_syntaxes_hiding_while_named =
524+
hiding a lifetime that's named elsewhere is confusing
525+
526+
lint_mismatched_lifetime_syntaxes_input_elided =
527+
the lifetime is elided here
528+
529+
lint_mismatched_lifetime_syntaxes_input_hidden =
530+
the lifetime is hidden here
531+
532+
lint_mismatched_lifetime_syntaxes_input_named =
533+
the lifetime is named here
534+
535+
lint_mismatched_lifetime_syntaxes_output_elided =
536+
the same lifetime is elided here
537+
538+
lint_mismatched_lifetime_syntaxes_output_hidden =
539+
the same lifetime is hidden here
540+
541+
lint_mismatched_lifetime_syntaxes_output_named =
542+
the same lifetime is named here
523543
524544
lint_mismatched_lifetime_syntaxes_suggestion_explicit =
525-
one option is to consistently use `{$lifetime_name}`
545+
consistently use `{$lifetime_name}`
526546
527547
lint_mismatched_lifetime_syntaxes_suggestion_implicit =
528-
one option is to consistently remove the lifetime
548+
remove the lifetime name from references
529549
530550
lint_mismatched_lifetime_syntaxes_suggestion_mixed =
531-
one option is to remove the lifetime for references and use the anonymous lifetime for paths
551+
remove the lifetime name from references and use `'_` for paths
552+
553+
lint_mismatched_lifetime_syntaxes_suggestion_mixed_only_paths =
554+
use `'_` for paths
532555
533556
lint_missing_unsafe_on_extern = extern blocks should be unsafe
534557
.suggestion = needs `unsafe` before the extern keyword

compiler/rustc_lint/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ mod invalid_from_utf8;
5555
mod late;
5656
mod let_underscore;
5757
mod levels;
58-
mod lifetime_syntax;
58+
pub mod lifetime_syntax;
5959
mod lints;
6060
mod macro_expr_fragment_specifier_2024_migration;
6161
mod map_unit_fn;

compiler/rustc_lint/src/lifetime_syntax.rs

Lines changed: 118 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -140,43 +140,115 @@ fn report_mismatches<'tcx>(
140140
}
141141
}
142142

143-
fn lifetimes_use_matched_syntax(input_info: &[Info<'_>], output_info: &[Info<'_>]) -> bool {
144-
// Categorize lifetimes into source/syntax buckets.
145-
let mut n_hidden = 0;
146-
let mut n_elided = 0;
147-
let mut n_named = 0;
143+
#[derive(Debug, Copy, Clone, PartialEq)]
144+
enum LifetimeSyntaxCategory {
145+
Hidden,
146+
Elided,
147+
Named,
148+
}
148149

149-
for info in input_info.iter().chain(output_info) {
150+
impl LifetimeSyntaxCategory {
151+
fn new(syntax_source: (hir::LifetimeSyntax, LifetimeSource)) -> Option<Self> {
150152
use LifetimeSource::*;
151153
use hir::LifetimeSyntax::*;
152154

153-
let syntax_source = (info.lifetime.syntax, info.lifetime.source);
154-
155155
match syntax_source {
156-
// Ignore any other kind of lifetime.
157-
(_, Other) => continue,
158-
159156
// E.g. `&T`.
160-
(Implicit, Reference | OutlivesBound | PreciseCapturing) |
157+
(Implicit, Reference) |
161158
// E.g. `&'_ T`.
162-
(ExplicitAnonymous, Reference | OutlivesBound | PreciseCapturing) |
159+
(ExplicitAnonymous, Reference) |
163160
// E.g. `ContainsLifetime<'_>`.
164-
(ExplicitAnonymous, Path { .. }) => n_elided += 1,
161+
(ExplicitAnonymous, Path { .. }) |
162+
// E.g. `+ '_`, `+ use<'_>`.
163+
(ExplicitAnonymous, OutlivesBound | PreciseCapturing) => {
164+
Some(Self::Elided)
165+
}
165166

166167
// E.g. `ContainsLifetime`.
167-
(Implicit, Path { .. }) => n_hidden += 1,
168+
(Implicit, Path { .. }) => {
169+
Some(Self::Hidden)
170+
}
168171

169172
// E.g. `&'a T`.
170-
(ExplicitBound, Reference | OutlivesBound | PreciseCapturing) |
173+
(ExplicitBound, Reference) |
171174
// E.g. `ContainsLifetime<'a>`.
172-
(ExplicitBound, Path { .. }) => n_named += 1,
173-
};
175+
(ExplicitBound, Path { .. }) |
176+
// E.g. `+ 'a`, `+ use<'a>`.
177+
(ExplicitBound, OutlivesBound | PreciseCapturing) => {
178+
Some(Self::Named)
179+
}
180+
181+
(Implicit, OutlivesBound | PreciseCapturing) |
182+
(_, Other) => {
183+
None
184+
}
185+
}
186+
}
187+
}
188+
189+
#[derive(Debug, Default)]
190+
pub struct LifetimeSyntaxCategories<T> {
191+
pub hidden: T,
192+
pub elided: T,
193+
pub named: T,
194+
}
195+
196+
impl<T> LifetimeSyntaxCategories<T> {
197+
fn select(&mut self, category: LifetimeSyntaxCategory) -> &mut T {
198+
use LifetimeSyntaxCategory::*;
199+
200+
match category {
201+
Elided => &mut self.elided,
202+
Hidden => &mut self.hidden,
203+
Named => &mut self.named,
204+
}
205+
}
206+
}
207+
208+
impl<T> LifetimeSyntaxCategories<Vec<T>> {
209+
pub fn len(&self) -> LifetimeSyntaxCategories<usize> {
210+
LifetimeSyntaxCategories {
211+
hidden: self.hidden.len(),
212+
elided: self.elided.len(),
213+
named: self.named.len(),
214+
}
215+
}
216+
217+
pub fn flatten(&self) -> impl Iterator<Item = &T> {
218+
let Self { hidden, elided, named } = self;
219+
[hidden.iter(), elided.iter(), named.iter()].into_iter().flatten()
220+
}
221+
}
222+
223+
impl std::ops::Add for LifetimeSyntaxCategories<usize> {
224+
type Output = Self;
225+
226+
fn add(self, rhs: Self) -> Self::Output {
227+
Self {
228+
hidden: self.hidden + rhs.hidden,
229+
elided: self.elided + rhs.elided,
230+
named: self.named + rhs.named,
231+
}
232+
}
233+
}
234+
235+
fn lifetimes_use_matched_syntax(input_info: &[Info<'_>], output_info: &[Info<'_>]) -> bool {
236+
let mut syntax_counts = LifetimeSyntaxCategories::<usize>::default();
237+
238+
for info in input_info.iter().chain(output_info) {
239+
if let Some(category) = info.lifetime_syntax_category() {
240+
*syntax_counts.select(category) += 1;
241+
}
174242
}
175243

176-
let syntax_counts = (n_hidden, n_elided, n_named);
177244
tracing::debug!(?syntax_counts);
178245

179-
matches!(syntax_counts, (_, 0, 0) | (0, _, 0) | (0, 0, _))
246+
matches!(
247+
syntax_counts,
248+
LifetimeSyntaxCategories { hidden: _, elided: 0, named: 0 }
249+
| LifetimeSyntaxCategories { hidden: 0, elided: _, named: 0 }
250+
| LifetimeSyntaxCategories { hidden: 0, elided: 0, named: _ }
251+
)
180252
}
181253

182254
fn emit_mismatch_diagnostic<'tcx>(
@@ -238,7 +310,7 @@ fn emit_mismatch_diagnostic<'tcx>(
238310
use LifetimeSource::*;
239311
use hir::LifetimeSyntax::*;
240312

241-
let syntax_source = (info.lifetime.syntax, info.lifetime.source);
313+
let syntax_source = info.syntax_source();
242314

243315
if let (_, Other) = syntax_source {
244316
// Ignore any other kind of lifetime.
@@ -259,7 +331,6 @@ fn emit_mismatch_diagnostic<'tcx>(
259331
// E.g. `&'_ T`.
260332
(ExplicitAnonymous, Reference) => {
261333
suggest_change_to_implicit.push(info);
262-
suggest_change_to_mixed_implicit.push(info);
263334
suggest_change_to_explicit_bound.push(info);
264335
}
265336

@@ -319,12 +390,22 @@ fn emit_mismatch_diagnostic<'tcx>(
319390
}
320391
}
321392

393+
let categorize = |infos: &[Info<'_>]| {
394+
let mut categories = LifetimeSyntaxCategories::<Vec<_>>::default();
395+
for info in infos {
396+
if let Some(category) = info.lifetime_syntax_category() {
397+
categories.select(category).push(info.reporting_span());
398+
}
399+
}
400+
categories
401+
};
402+
403+
let inputs = categorize(input_info);
404+
let outputs = categorize(output_info);
405+
322406
let make_implicit_suggestions =
323407
|infos: &[&Info<'_>]| infos.iter().map(|i| i.removing_span()).collect::<Vec<_>>();
324408

325-
let inputs = input_info.iter().map(|info| info.reporting_span()).collect();
326-
let outputs = output_info.iter().map(|info| info.reporting_span()).collect();
327-
328409
let explicit_bound_suggestion = bound_lifetime.map(|info| {
329410
build_mismatch_suggestion(info.lifetime_name(), &suggest_change_to_explicit_bound)
330411
});
@@ -399,8 +480,6 @@ fn emit_mismatch_diagnostic<'tcx>(
399480
?explicit_anonymous_suggestion,
400481
);
401482

402-
let lifetime_name = bound_lifetime.map(|info| info.lifetime_name()).unwrap_or("'_").to_owned();
403-
404483
// We can produce a number of suggestions which may overwhelm
405484
// the user. Instead, we order the suggestions based on Rust
406485
// idioms. The "best" choice is shown to the user and the
@@ -413,21 +492,21 @@ fn emit_mismatch_diagnostic<'tcx>(
413492

414493
cx.emit_span_lint(
415494
MISMATCHED_LIFETIME_SYNTAXES,
416-
Vec::clone(&inputs),
417-
lints::MismatchedLifetimeSyntaxes { lifetime_name, inputs, outputs, suggestions },
495+
inputs.flatten().copied().collect::<Vec<_>>(),
496+
lints::MismatchedLifetimeSyntaxes { inputs, outputs, suggestions },
418497
);
419498
}
420499

421500
fn build_mismatch_suggestion(
422501
lifetime_name: &str,
423502
infos: &[&Info<'_>],
424503
) -> lints::MismatchedLifetimeSyntaxesSuggestion {
425-
let lifetime_name_sugg = lifetime_name.to_owned();
504+
let lifetime_name = lifetime_name.to_owned();
426505

427506
let suggestions = infos.iter().map(|info| info.suggestion(&lifetime_name)).collect();
428507

429508
lints::MismatchedLifetimeSyntaxesSuggestion::Explicit {
430-
lifetime_name_sugg,
509+
lifetime_name,
431510
suggestions,
432511
tool_only: false,
433512
}
@@ -441,6 +520,14 @@ struct Info<'tcx> {
441520
}
442521

443522
impl<'tcx> Info<'tcx> {
523+
fn syntax_source(&self) -> (hir::LifetimeSyntax, LifetimeSource) {
524+
(self.lifetime.syntax, self.lifetime.source)
525+
}
526+
527+
fn lifetime_syntax_category(&self) -> Option<LifetimeSyntaxCategory> {
528+
LifetimeSyntaxCategory::new(self.syntax_source())
529+
}
530+
444531
fn lifetime_name(&self) -> &str {
445532
self.lifetime.ident.as_str()
446533
}

0 commit comments

Comments
 (0)