Skip to content

Commit 7e3b351

Browse files
committed
suggest removing unnecessary lifetime annotations
1 parent c217c37 commit 7e3b351

File tree

71 files changed

+1327
-3
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

71 files changed

+1327
-3
lines changed

compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use crate::{
2121
WriteKind,
2222
};
2323

24-
use super::{find_use, RegionName, UseSpans};
24+
use super::{find_use, region_errors, RegionName, UseSpans};
2525

2626
#[derive(Debug)]
2727
pub(crate) enum BorrowExplanation<'tcx> {
@@ -268,6 +268,10 @@ impl<'tcx> BorrowExplanation<'tcx> {
268268
);
269269
};
270270

271+
if let ConstraintCategory::TypeAnnotation = category {
272+
err.span_help(span, "you can remove unnecessary lifetime annotations here");
273+
region_errors::suggest_relax_self(tcx, err, body.source.def_id(), span);
274+
}
271275
self.add_lifetime_bound_suggestion_to_diagnostic(err, &category, span, region_name);
272276
}
273277
_ => {}

compiler/rustc_borrowck/src/diagnostics/region_errors.rs

+86-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
33
use rustc_data_structures::fx::FxHashSet;
44
use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan};
5+
use rustc_hir::def::DefKind;
56
use rustc_hir::def_id::DefId;
67
use rustc_hir::intravisit::Visitor;
78
use rustc_hir::{self as hir, Item, ItemKind, Node};
@@ -18,7 +19,7 @@ use rustc_middle::mir::{ConstraintCategory, ReturnConstraint};
1819
use rustc_middle::ty::subst::InternalSubsts;
1920
use rustc_middle::ty::Region;
2021
use rustc_middle::ty::TypeVisitor;
21-
use rustc_middle::ty::{self, RegionVid, Ty};
22+
use rustc_middle::ty::{self, DefIdTree, RegionVid, Ty, TyCtxt};
2223
use rustc_span::symbol::{kw, sym, Ident};
2324
use rustc_span::Span;
2425

@@ -715,12 +716,53 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
715716
}
716717
}
717718

719+
if let ConstraintCategory::TypeAnnotation = category {
720+
self.handle_annotation_error(errci, &mut diag);
721+
}
718722
self.add_static_impl_trait_suggestion(&mut diag, *fr, fr_name, *outlived_fr);
719723
self.suggest_adding_lifetime_params(&mut diag, *fr, *outlived_fr);
720724

721725
diag
722726
}
723727

728+
/// Adds a secondary label to lifetime annotation errors:
729+
/// ```
730+
/// fn test<'a, 'b>(s: &'a str) {
731+
/// let closure = |_: &'b str| {}; // Primary label: type annotation requires 'a: 'b.
732+
/// closure(s); // Secondary label: because of this call.
733+
/// }
734+
/// ```
735+
///
736+
/// Also Suggests replacing `Self`. See `suggest_relax_self`.
737+
fn handle_annotation_error(&self, errci: &ErrorConstraintInfo<'tcx>, err: &mut Diagnostic) {
738+
let ErrorConstraintInfo { fr, outlived_fr, span, category: _, .. } = errci.clone();
739+
740+
// Try to report the second most relevant constraint to provide more context.
741+
let constraint2 = self.regioncx.best_blame_constraint_filtered(
742+
&self.body,
743+
fr,
744+
NllRegionVariableOrigin::FreeRegion,
745+
|r| self.regioncx.provides_universal_region(r, fr, outlived_fr),
746+
|constraint| match constraint.category {
747+
ConstraintCategory::TypeAnnotation => false,
748+
_ => true,
749+
},
750+
);
751+
debug!("suggest_relax_annotation: constraint2={:?}", constraint2);
752+
let map = self.infcx.tcx.sess.source_map();
753+
let same_line = map
754+
.lookup_line(constraint2.cause.span.hi())
755+
.and_then(|line1| map.lookup_line(span.hi()).map(|line2| line1.line == line2.line))
756+
.unwrap_or(false);
757+
let desc = constraint2.category.description();
758+
if !desc.is_empty() && !same_line {
759+
err.span_label(constraint2.cause.span, format!("because of {desc}here"));
760+
}
761+
762+
let body_did = self.body.source.def_id();
763+
suggest_relax_self(self.infcx.tcx, err, body_did, span);
764+
}
765+
724766
/// Adds a suggestion to errors where an `impl Trait` is returned.
725767
///
726768
/// ```text
@@ -903,3 +945,46 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
903945
suggest_adding_lifetime_params(self.infcx.tcx, sub, ty_sup, ty_sub, diag);
904946
}
905947
}
948+
949+
/// Use of `Self` can impose unnecessary region constraints:
950+
/// ```
951+
/// struct MyStruct<'a>(&'a str);
952+
/// impl<'a> MyStruct<'a> {
953+
/// fn test<'x>(s: &'x str) {
954+
/// let _ = Self(s); // requires 'x: 'a.
955+
/// }
956+
/// }
957+
/// ```
958+
/// Here we suggest replacing `Self` with `MyStruct<'_>`.
959+
/// Assumes that `span` points to hir::Path that may name `Self`.
960+
pub(crate) fn suggest_relax_self<'tcx>(
961+
tcx: TyCtxt<'tcx>,
962+
err: &mut Diagnostic,
963+
body_did: DefId,
964+
span: Span,
965+
) {
966+
use rustc_lexer as lex;
967+
fn tokenize(mut input: &str) -> impl Iterator<Item = (lex::TokenKind, &str)> {
968+
std::iter::from_fn(move || {
969+
if input.is_empty() {
970+
return None;
971+
}
972+
let token = lex::first_token(input);
973+
let token_str = &input[..(token.len as usize)];
974+
input = &input[(token.len as usize)..];
975+
Some((token.kind, token_str))
976+
})
977+
}
978+
979+
let snippet = tcx.sess.source_map().span_to_snippet(span).unwrap_or_default();
980+
let has_self = tokenize(&snippet)
981+
.find(|(tok, s)| *tok == lex::TokenKind::Ident && *s == "Self")
982+
.and_then(|_| tcx.opt_parent(tcx.typeck_root_def_id(body_did)))
983+
.filter(|parent_did| tcx.def_kind(parent_did) == DefKind::Impl);
984+
985+
if let Some(impl_did) = has_self {
986+
let suggested_ty =
987+
tcx.fold_regions(tcx.type_of(impl_did), |_, _| tcx.mk_region(ty::ReErased));
988+
err.help(format!("consider replacing `Self` with `{suggested_ty}`"));
989+
}
990+
}

compiler/rustc_borrowck/src/diagnostics/region_name.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -867,7 +867,8 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
867867
};
868868

869869
let tcx = self.infcx.tcx;
870-
let body_parent_did = tcx.opt_parent(self.mir_def_id().to_def_id())?;
870+
let typeck_root_did = tcx.typeck_root_def_id(self.mir_def_id().to_def_id());
871+
let body_parent_did = tcx.opt_parent(typeck_root_did)?;
871872
if tcx.parent(region.def_id) != body_parent_did
872873
|| tcx.def_kind(body_parent_did) != DefKind::Impl
873874
{

compiler/rustc_borrowck/src/region_infer/mod.rs

+26
Original file line numberDiff line numberDiff line change
@@ -2066,6 +2066,25 @@ impl<'tcx> RegionInferenceContext<'tcx> {
20662066
from_region: RegionVid,
20672067
from_region_origin: NllRegionVariableOrigin,
20682068
target_test: impl Fn(RegionVid) -> bool,
2069+
) -> BlameConstraint<'tcx> {
2070+
self.best_blame_constraint_filtered(
2071+
body,
2072+
from_region,
2073+
from_region_origin,
2074+
target_test,
2075+
|_| true,
2076+
)
2077+
}
2078+
2079+
/// Same as `best_blame_constraint` but this can exclude some constraints from being reported
2080+
/// as the "best" according to `filter`.
2081+
pub(crate) fn best_blame_constraint_filtered(
2082+
&self,
2083+
body: &Body<'tcx>,
2084+
from_region: RegionVid,
2085+
from_region_origin: NllRegionVariableOrigin,
2086+
target_test: impl Fn(RegionVid) -> bool,
2087+
filter: impl Fn(&BlameConstraint<'tcx>) -> bool,
20692088
) -> BlameConstraint<'tcx> {
20702089
debug!(
20712090
"best_blame_constraint(from_region={:?}, from_region_origin={:?})",
@@ -2127,6 +2146,13 @@ impl<'tcx> RegionInferenceContext<'tcx> {
21272146
}
21282147
}
21292148
})
2149+
// Downgrade the category of filtered constraints to be ignored later.
2150+
.map(|mut blame| {
2151+
if !filter(&blame) {
2152+
blame.category = ConstraintCategory::Internal;
2153+
}
2154+
blame
2155+
})
21302156
.collect();
21312157
debug!("best_blame_constraint: categorized_path={:#?}", categorized_path);
21322158

src/test/ui/associated-types/associated-types-subtyping-1.stderr

+3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ LL | fn method2<'a,'b,T>(x: &'a T, y: &'b T)
88
...
99
LL | let a: <T as Trait<'a>>::Type = make_any();
1010
| ^^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'b` must outlive `'a`
11+
...
12+
LL | let _c: <T as Trait<'b>>::Type = a;
13+
| - because of assignment here
1114
|
1215
= help: consider adding the following bound: `'b: 'a`
1316

src/test/ui/consts/const-eval/const-eval-intrinsic-promotion.stderr

+6
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ LL | &std::intrinsics::size_of::<i32>();
77
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use
88
LL | }
99
| - temporary value is freed at the end of this statement
10+
|
11+
help: you can remove unnecessary lifetime annotations here
12+
--> $DIR/const-eval-intrinsic-promotion.rs:4:12
13+
|
14+
LL | let x: &'static usize =
15+
| ^^^^^^^^^^^^^^
1016

1117
error: aborting due to previous error
1218

src/test/ui/consts/const-eval/dont_promote_unstable_const_fn.stderr

+18
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@ LL | let _: &'static u32 = &foo();
1515
| type annotation requires that borrow lasts for `'static`
1616
LL | }
1717
| - temporary value is freed at the end of this statement
18+
|
19+
help: you can remove unnecessary lifetime annotations here
20+
--> $DIR/dont_promote_unstable_const_fn.rs:17:12
21+
|
22+
LL | let _: &'static u32 = &foo();
23+
| ^^^^^^^^^^^^
1824

1925
error[E0716]: temporary value dropped while borrowed
2026
--> $DIR/dont_promote_unstable_const_fn.rs:21:28
@@ -26,6 +32,12 @@ LL | let _: &'static u32 = &meh();
2632
...
2733
LL | }
2834
| - temporary value is freed at the end of this statement
35+
|
36+
help: you can remove unnecessary lifetime annotations here
37+
--> $DIR/dont_promote_unstable_const_fn.rs:21:12
38+
|
39+
LL | let _: &'static u32 = &meh();
40+
| ^^^^^^^^^^^^
2941

3042
error[E0716]: temporary value dropped while borrowed
3143
--> $DIR/dont_promote_unstable_const_fn.rs:22:26
@@ -37,6 +49,12 @@ LL | let x: &'static _ = &std::time::Duration::from_millis(42).subsec_millis
3749
LL |
3850
LL | }
3951
| - temporary value is freed at the end of this statement
52+
|
53+
help: you can remove unnecessary lifetime annotations here
54+
--> $DIR/dont_promote_unstable_const_fn.rs:22:12
55+
|
56+
LL | let x: &'static _ = &std::time::Duration::from_millis(42).subsec_millis();
57+
| ^^^^^^^^^^
4058

4159
error: aborting due to 4 previous errors
4260

src/test/ui/consts/const-eval/dont_promote_unstable_const_fn_cross_crate.stderr

+12
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ LL | let _: &'static u32 = &foo();
88
LL | let _x: &'static u32 = &foo();
99
LL | }
1010
| - temporary value is freed at the end of this statement
11+
|
12+
help: you can remove unnecessary lifetime annotations here
13+
--> $DIR/dont_promote_unstable_const_fn_cross_crate.rs:8:12
14+
|
15+
LL | let _: &'static u32 = &foo();
16+
| ^^^^^^^^^^^^
1117

1218
error[E0716]: temporary value dropped while borrowed
1319
--> $DIR/dont_promote_unstable_const_fn_cross_crate.rs:9:29
@@ -18,6 +24,12 @@ LL | let _x: &'static u32 = &foo();
1824
| type annotation requires that borrow lasts for `'static`
1925
LL | }
2026
| - temporary value is freed at the end of this statement
27+
|
28+
help: you can remove unnecessary lifetime annotations here
29+
--> $DIR/dont_promote_unstable_const_fn_cross_crate.rs:9:13
30+
|
31+
LL | let _x: &'static u32 = &foo();
32+
| ^^^^^^^^^^^^
2133

2234
error: aborting due to 2 previous errors
2335

src/test/ui/consts/const-eval/promoted_const_fn_fail.stderr

+6
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ LL | let x: &'static u8 = &(bar() + 1);
88
...
99
LL | }
1010
| - temporary value is freed at the end of this statement
11+
|
12+
help: you can remove unnecessary lifetime annotations here
13+
--> $DIR/promoted_const_fn_fail.rs:19:12
14+
|
15+
LL | let x: &'static u8 = &(bar() + 1);
16+
| ^^^^^^^^^^^
1117

1218
error: aborting due to previous error
1319

src/test/ui/consts/const-eval/promoted_const_fn_fail_deny_const_err.stderr

+6
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ LL | let x: &'static u8 = &(bar() + 1);
88
...
99
LL | }
1010
| - temporary value is freed at the end of this statement
11+
|
12+
help: you can remove unnecessary lifetime annotations here
13+
--> $DIR/promoted_const_fn_fail_deny_const_err.rs:20:12
14+
|
15+
LL | let x: &'static u8 = &(bar() + 1);
16+
| ^^^^^^^^^^^
1117

1218
error: aborting due to previous error
1319

src/test/ui/consts/const-eval/promoted_raw_ptr_ops.stderr

+24
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ LL | let x: &'static bool = &(42 as *const i32 == 43 as *const i32);
88
...
99
LL | }
1010
| - temporary value is freed at the end of this statement
11+
|
12+
help: you can remove unnecessary lifetime annotations here
13+
--> $DIR/promoted_raw_ptr_ops.rs:2:12
14+
|
15+
LL | let x: &'static bool = &(42 as *const i32 == 43 as *const i32);
16+
| ^^^^^^^^^^^^^
1117

1218
error[E0716]: temporary value dropped while borrowed
1319
--> $DIR/promoted_raw_ptr_ops.rs:4:30
@@ -19,6 +25,12 @@ LL | let y: &'static usize = &(&1 as *const i32 as usize + 1);
1925
...
2026
LL | }
2127
| - temporary value is freed at the end of this statement
28+
|
29+
help: you can remove unnecessary lifetime annotations here
30+
--> $DIR/promoted_raw_ptr_ops.rs:4:12
31+
|
32+
LL | let y: &'static usize = &(&1 as *const i32 as usize + 1);
33+
| ^^^^^^^^^^^^^^
2234

2335
error[E0716]: temporary value dropped while borrowed
2436
--> $DIR/promoted_raw_ptr_ops.rs:6:28
@@ -30,6 +42,12 @@ LL | let z: &'static i32 = &(unsafe { *(42 as *const i32) });
3042
...
3143
LL | }
3244
| - temporary value is freed at the end of this statement
45+
|
46+
help: you can remove unnecessary lifetime annotations here
47+
--> $DIR/promoted_raw_ptr_ops.rs:6:12
48+
|
49+
LL | let z: &'static i32 = &(unsafe { *(42 as *const i32) });
50+
| ^^^^^^^^^^^^
3351

3452
error[E0716]: temporary value dropped while borrowed
3553
--> $DIR/promoted_raw_ptr_ops.rs:8:29
@@ -41,6 +59,12 @@ LL | let a: &'static bool = &(main as fn() == main as fn());
4159
LL |
4260
LL | }
4361
| - temporary value is freed at the end of this statement
62+
|
63+
help: you can remove unnecessary lifetime annotations here
64+
--> $DIR/promoted_raw_ptr_ops.rs:8:12
65+
|
66+
LL | let a: &'static bool = &(main as fn() == main as fn());
67+
| ^^^^^^^^^^^^^
4468

4569
error: aborting due to 4 previous errors
4670

src/test/ui/consts/const-eval/transmute-const-promotion.stderr

+6
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ LL | let x: &'static u32 = unsafe { &mem::transmute(3.0f32) };
88
LL |
99
LL | }
1010
| - temporary value is freed at the end of this statement
11+
|
12+
help: you can remove unnecessary lifetime annotations here
13+
--> $DIR/transmute-const-promotion.rs:4:12
14+
|
15+
LL | let x: &'static u32 = unsafe { &mem::transmute(3.0f32) };
16+
| ^^^^^^^^^^^^
1117

1218
error: aborting due to previous error
1319

src/test/ui/consts/const-eval/union_promotion.stderr

+6
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ LL | | };
1010
| |_____^ creates a temporary which is freed while still in use
1111
LL | }
1212
| - temporary value is freed at the end of this statement
13+
|
14+
help: you can remove unnecessary lifetime annotations here
15+
--> $DIR/union_promotion.rs:10:12
16+
|
17+
LL | let x: &'static bool = &unsafe {
18+
| ^^^^^^^^^^^^^
1319

1420
error: aborting due to previous error
1521

0 commit comments

Comments
 (0)