Skip to content

Commit c06b586

Browse files
shepmastercuviper
authored andcommitted
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. (cherry picked from commit 5530744)
1 parent 20c571f commit c06b586

26 files changed

+674
-413
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 type paths
552+
553+
lint_mismatched_lifetime_syntaxes_suggestion_mixed_only_paths =
554+
use `'_` for type 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: 116 additions & 29 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,8 +492,8 @@ 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

@@ -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
}

compiler/rustc_lint/src/lints.rs

Lines changed: 50 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use rustc_span::{Ident, MacroRulesNormalizedIdent, Span, Symbol, sym};
2222

2323
use crate::builtin::{InitError, ShorthandAssocTyCollector, TypeAliasBounds};
2424
use crate::errors::{OverruledAttributeSub, RequestedLevel};
25+
use crate::lifetime_syntax::LifetimeSyntaxCategories;
2526
use crate::{LateContext, fluent_generated as fluent};
2627

2728
// array_into_iter.rs
@@ -3176,30 +3177,59 @@ pub(crate) struct ReservedMultihash {
31763177

31773178
#[derive(Debug)]
31783179
pub(crate) struct MismatchedLifetimeSyntaxes {
3179-
pub lifetime_name: String,
3180-
pub inputs: Vec<Span>,
3181-
pub outputs: Vec<Span>,
3180+
pub inputs: LifetimeSyntaxCategories<Vec<Span>>,
3181+
pub outputs: LifetimeSyntaxCategories<Vec<Span>>,
31823182

31833183
pub suggestions: Vec<MismatchedLifetimeSyntaxesSuggestion>,
31843184
}
31853185

31863186
impl<'a, G: EmissionGuarantee> LintDiagnostic<'a, G> for MismatchedLifetimeSyntaxes {
31873187
fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, G>) {
3188-
diag.primary_message(fluent::lint_mismatched_lifetime_syntaxes);
3188+
let counts = self.inputs.len() + self.outputs.len();
3189+
let message = match counts {
3190+
LifetimeSyntaxCategories { hidden: 0, elided: 0, named: 0 } => {
3191+
panic!("No lifetime mismatch detected")
3192+
}
3193+
3194+
LifetimeSyntaxCategories { hidden: _, elided: _, named: 0 } => {
3195+
fluent::lint_mismatched_lifetime_syntaxes_hiding_while_elided
3196+
}
3197+
3198+
LifetimeSyntaxCategories { hidden: _, elided: 0, named: _ } => {
3199+
fluent::lint_mismatched_lifetime_syntaxes_hiding_while_named
3200+
}
3201+
3202+
LifetimeSyntaxCategories { hidden: 0, elided: _, named: _ } => {
3203+
fluent::lint_mismatched_lifetime_syntaxes_eliding_while_named
3204+
}
31893205

3190-
diag.arg("lifetime_name", self.lifetime_name);
3206+
LifetimeSyntaxCategories { hidden: _, elided: _, named: _ } => {
3207+
fluent::lint_mismatched_lifetime_syntaxes_hiding_and_eliding_while_named
3208+
}
3209+
};
3210+
diag.primary_message(message);
31913211

3192-
diag.arg("n_inputs", self.inputs.len());
3193-
for input in self.inputs {
3194-
let a = diag.eagerly_translate(fluent::lint_label_mismatched_lifetime_syntaxes_inputs);
3195-
diag.span_label(input, a);
3212+
for s in self.inputs.hidden {
3213+
diag.span_label(s, fluent::lint_mismatched_lifetime_syntaxes_input_hidden);
3214+
}
3215+
for s in self.inputs.elided {
3216+
diag.span_label(s, fluent::lint_mismatched_lifetime_syntaxes_input_elided);
3217+
}
3218+
for s in self.inputs.named {
3219+
diag.span_label(s, fluent::lint_mismatched_lifetime_syntaxes_input_named);
31963220
}
31973221

3198-
diag.arg("n_outputs", self.outputs.len());
3199-
for output in self.outputs {
3200-
let a = diag.eagerly_translate(fluent::lint_label_mismatched_lifetime_syntaxes_outputs);
3201-
diag.span_label(output, a);
3222+
for s in self.outputs.hidden {
3223+
diag.span_label(s, fluent::lint_mismatched_lifetime_syntaxes_output_hidden);
3224+
}
3225+
for s in self.outputs.elided {
3226+
diag.span_label(s, fluent::lint_mismatched_lifetime_syntaxes_output_elided);
32023227
}
3228+
for s in self.outputs.named {
3229+
diag.span_label(s, fluent::lint_mismatched_lifetime_syntaxes_output_named);
3230+
}
3231+
3232+
diag.help(fluent::lint_mismatched_lifetime_syntaxes_help);
32033233

32043234
let mut suggestions = self.suggestions.into_iter();
32053235
if let Some(s) = suggestions.next() {
@@ -3267,14 +3297,20 @@ impl Subdiagnostic for MismatchedLifetimeSyntaxesSuggestion {
32673297
}
32683298

32693299
Mixed { implicit_suggestions, explicit_anonymous_suggestions, tool_only } => {
3300+
let message = if implicit_suggestions.is_empty() {
3301+
fluent::lint_mismatched_lifetime_syntaxes_suggestion_mixed_only_paths
3302+
} else {
3303+
fluent::lint_mismatched_lifetime_syntaxes_suggestion_mixed
3304+
};
3305+
32703306
let implicit_suggestions =
32713307
implicit_suggestions.into_iter().map(|s| (s, String::new()));
32723308

32733309
let suggestions =
32743310
implicit_suggestions.chain(explicit_anonymous_suggestions).collect();
32753311

32763312
diag.multipart_suggestion_with_style(
3277-
fluent::lint_mismatched_lifetime_syntaxes_suggestion_mixed,
3313+
message,
32783314
suggestions,
32793315
Applicability::MaybeIncorrect,
32803316
style(tool_only),

0 commit comments

Comments
 (0)