Skip to content

Commit 89afb45

Browse files
committed
Distinguish hidden and anonymous lifetimes in references in HIR
An upcoming lint will want to be able to know if a reference has a hidden lifetime (`&u8`) or an anonymous lifetime (`&'_ u8`). The code previously treated hidden lifetimes as anonymous. To preserve diagnostic quality, lifetime suggestions now need to know if they are in the context of a reference or a path, as there is different syntax for each. For example, a reference changes from `&u8` to `&'a u8` (add a space with no comma), while a path changes from `T` to `T<'a>` (add angle brackets) or `T<X>` to `T<'a, X>` (add a comma and a space). There's a minor ugliness introduced to -Zunpretty=hir output, as there's a non-idiomatic space character introduced after the ampersand, but it's called unpretty for a reason!
1 parent 87e60a7 commit 89afb45

File tree

6 files changed

+67
-29
lines changed

6 files changed

+67
-29
lines changed

compiler/rustc_ast_lowering/src/lib.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1206,7 +1206,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
12061206
self.next_node_id()
12071207
};
12081208
let span = self.tcx.sess.source_map().start_point(t.span).shrink_to_hi();
1209-
Lifetime { ident: Ident::new(kw::UnderscoreLifetime, span), id }
1209+
Lifetime { ident: Ident::new(kw::Empty, span), id }
12101210
});
12111211
let lifetime = self.lower_lifetime(&region);
12121212
hir::TyKind::Ref(lifetime, self.lower_mt(mt, itctx))
@@ -1222,7 +1222,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
12221222
self.next_node_id()
12231223
};
12241224
let span = self.tcx.sess.source_map().start_point(t.span).shrink_to_hi();
1225-
Lifetime { ident: Ident::new(kw::UnderscoreLifetime, span), id }
1225+
Lifetime { ident: Ident::new(kw::Empty, span), id }
12261226
});
12271227
let lifetime = self.lower_lifetime(&region);
12281228
let kind = hir::TyKind::Ref(lifetime, self.lower_mt(mt, itctx));

compiler/rustc_hir/src/hir.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -161,9 +161,11 @@ impl Lifetime {
161161
self.ident.name == kw::Empty || self.ident.name == kw::UnderscoreLifetime
162162
}
163163

164-
pub fn suggestion_position(&self) -> (LifetimeSuggestionPosition, Span) {
164+
pub fn suggestion_position(&self, is_ref: bool) -> (LifetimeSuggestionPosition, Span) {
165165
if self.ident.name == kw::Empty {
166-
if self.ident.span.is_empty() {
166+
if is_ref {
167+
(LifetimeSuggestionPosition::Ampersand, self.ident.span)
168+
} else if self.ident.span.is_empty() {
167169
(LifetimeSuggestionPosition::ElidedPathArgument, self.ident.span)
168170
} else {
169171
(LifetimeSuggestionPosition::ElidedPath, self.ident.span.shrink_to_hi())
@@ -177,9 +179,9 @@ impl Lifetime {
177179
}
178180
}
179181

180-
pub fn suggestion(&self, new_lifetime: &str) -> (Span, String) {
182+
pub fn suggestion(&self, new_lifetime: &str, is_ref: bool) -> (Span, String) {
181183
debug_assert!(new_lifetime.starts_with('\''));
182-
let (pos, span) = self.suggestion_position();
184+
let (pos, span) = self.suggestion_position(is_ref);
183185
let code = match pos {
184186
LifetimeSuggestionPosition::Normal => format!("{new_lifetime}"),
185187
LifetimeSuggestionPosition::Ampersand => format!("{new_lifetime} "),

compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs

+22-6
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ struct BoundVarContext<'a, 'tcx> {
6464
tcx: TyCtxt<'tcx>,
6565
rbv: &'a mut ResolveBoundVars,
6666
scope: ScopeRef<'a>,
67+
is_ref: bool,
6768
}
6869

6970
#[derive(Debug)]
@@ -245,8 +246,12 @@ pub(crate) fn provide(providers: &mut Providers) {
245246
#[instrument(level = "debug", skip(tcx))]
246247
fn resolve_bound_vars(tcx: TyCtxt<'_>, local_def_id: hir::OwnerId) -> ResolveBoundVars {
247248
let mut rbv = ResolveBoundVars::default();
248-
let mut visitor =
249-
BoundVarContext { tcx, rbv: &mut rbv, scope: &Scope::Root { opt_parent_item: None } };
249+
let mut visitor = BoundVarContext {
250+
tcx,
251+
rbv: &mut rbv,
252+
scope: &Scope::Root { opt_parent_item: None },
253+
is_ref: false,
254+
};
250255
match tcx.hir_owner_node(local_def_id) {
251256
hir::OwnerNode::Item(item) => visitor.visit_item(item),
252257
hir::OwnerNode::ForeignItem(item) => visitor.visit_foreign_item(item),
@@ -648,7 +653,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
648653
match *arg {
649654
hir::PreciseCapturingArg::Lifetime(lt) => match lt.res {
650655
LifetimeName::Param(def_id) => {
651-
self.resolve_lifetime_ref(def_id, lt);
656+
self.resolve_lifetime_ref(def_id, lt, false);
652657
}
653658
LifetimeName::Error => {}
654659
LifetimeName::ImplicitObjectLifetimeDefault
@@ -701,6 +706,8 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
701706

702707
#[instrument(level = "debug", skip(self))]
703708
fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx, AmbigArg>) {
709+
let old_is_ref = self.is_ref;
710+
704711
match ty.kind {
705712
hir::TyKind::BareFn(c) => {
706713
let (mut bound_vars, binders): (FxIndexMap<LocalDefId, ResolvedArg>, Vec<_>) = c
@@ -797,6 +804,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
797804
}
798805
}
799806
hir::TyKind::Ref(lifetime_ref, ref mt) => {
807+
self.is_ref = true;
800808
self.visit_lifetime(lifetime_ref);
801809
let scope = Scope::ObjectLifetimeDefault {
802810
lifetime: self.rbv.defs.get(&lifetime_ref.hir_id.local_id).cloned(),
@@ -821,6 +829,8 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
821829
}
822830
_ => intravisit::walk_ty(self, ty),
823831
}
832+
833+
self.is_ref = old_is_ref;
824834
}
825835

826836
#[instrument(level = "debug", skip(self))]
@@ -878,7 +888,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
878888
self.insert_lifetime(lifetime_ref, ResolvedArg::StaticLifetime)
879889
}
880890
hir::LifetimeName::Param(param_def_id) => {
881-
self.resolve_lifetime_ref(param_def_id, lifetime_ref)
891+
self.resolve_lifetime_ref(param_def_id, lifetime_ref, self.is_ref)
882892
}
883893
// If we've already reported an error, just ignore `lifetime_ref`.
884894
hir::LifetimeName::Error => {}
@@ -888,6 +898,9 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
888898
}
889899

890900
fn visit_path(&mut self, path: &hir::Path<'tcx>, hir_id: HirId) {
901+
let old_is_ref = self.is_ref;
902+
self.is_ref = false;
903+
891904
for (i, segment) in path.segments.iter().enumerate() {
892905
let depth = path.segments.len() - i - 1;
893906
if let Some(args) = segment.args {
@@ -897,6 +910,8 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
897910
if let Res::Def(DefKind::TyParam | DefKind::ConstParam, param_def_id) = path.res {
898911
self.resolve_type_ref(param_def_id.expect_local(), hir_id);
899912
}
913+
914+
self.is_ref = old_is_ref;
900915
}
901916

902917
fn visit_fn(
@@ -1092,7 +1107,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
10921107
F: for<'b> FnOnce(&mut BoundVarContext<'b, 'tcx>),
10931108
{
10941109
let BoundVarContext { tcx, rbv, .. } = self;
1095-
let mut this = BoundVarContext { tcx: *tcx, rbv, scope: &wrap_scope };
1110+
let mut this = BoundVarContext { tcx: *tcx, rbv, scope: &wrap_scope, is_ref: false };
10961111
let span = debug_span!("scope", scope = ?this.scope.debug_truncated());
10971112
{
10981113
let _enter = span.enter();
@@ -1201,6 +1216,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
12011216
&mut self,
12021217
region_def_id: LocalDefId,
12031218
lifetime_ref: &'tcx hir::Lifetime,
1219+
is_ref: bool,
12041220
) {
12051221
// Walk up the scope chain, tracking the number of fn scopes
12061222
// that we pass through, until we find a lifetime with the
@@ -1266,7 +1282,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
12661282
(generics.span, "<'a>".to_owned())
12671283
};
12681284

1269-
let lifetime_sugg = lifetime_ref.suggestion("'a");
1285+
let lifetime_sugg = lifetime_ref.suggestion("'a", is_ref);
12701286
let suggestions = vec![lifetime_sugg, new_param_sugg];
12711287

12721288
diag.span_label(

compiler/rustc_trait_selection/src/error_reporting/infer/region.rs

+30-1
Original file line numberDiff line numberDiff line change
@@ -853,14 +853,42 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
853853
needle: hir::LifetimeName,
854854
new_lt: &'a str,
855855
add_lt_suggs: &'a mut Vec<(Span, String)>,
856+
is_ref: bool,
856857
}
857858

858859
impl<'hir> hir::intravisit::Visitor<'hir> for LifetimeReplaceVisitor<'_> {
859860
fn visit_lifetime(&mut self, lt: &'hir hir::Lifetime) {
860861
if lt.res == self.needle {
861-
self.add_lt_suggs.push(lt.suggestion(self.new_lt));
862+
self.add_lt_suggs.push(lt.suggestion(self.new_lt, self.is_ref));
862863
}
863864
}
865+
866+
fn visit_ty(
867+
&mut self,
868+
ty: &'hir rustc_hir::Ty<'hir, rustc_hir::AmbigArg>,
869+
) -> Self::Result {
870+
use rustc_hir::TyKind::*;
871+
872+
let old_is_ref = self.is_ref;
873+
874+
match ty.kind {
875+
Ref(..) => {
876+
self.is_ref = true;
877+
}
878+
879+
Path(..) => {
880+
self.is_ref = false;
881+
}
882+
883+
InferDelegation(..) | Slice(..) | Array(..) | Ptr(..) | BareFn(..)
884+
| UnsafeBinder(..) | Never | Tup(..) | OpaqueDef(..) | TraitAscription(..)
885+
| TraitObject(..) | Typeof(..) | Err(..) | Pat(..) | Infer(..) => {}
886+
}
887+
888+
rustc_hir::intravisit::walk_ty(self, ty);
889+
890+
self.is_ref = old_is_ref;
891+
}
864892
}
865893

866894
let (lifetime_def_id, lifetime_scope) = match self
@@ -897,6 +925,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
897925
needle: hir::LifetimeName::Param(lifetime_def_id),
898926
add_lt_suggs,
899927
new_lt: &new_lt,
928+
is_ref: false,
900929
};
901930
match self.tcx.expect_hir_owner_node(lifetime_scope) {
902931
hir::OwnerNode::Item(i) => visitor.visit_item(i),

compiler/rustc_trait_selection/src/errors.rs

+4-13
Original file line numberDiff line numberDiff line change
@@ -567,15 +567,6 @@ impl Subdiagnostic for AddLifetimeParamsSuggestion<'_> {
567567

568568
impl<'v> Visitor<'v> for ImplicitLifetimeFinder {
569569
fn visit_ty(&mut self, ty: &'v hir::Ty<'v, AmbigArg>) {
570-
let make_suggestion = |ident: Ident| {
571-
if ident.name == kw::Empty && ident.span.is_empty() {
572-
format!("{}, ", self.suggestion_param_name)
573-
} else if ident.name == kw::UnderscoreLifetime && ident.span.is_empty() {
574-
format!("{} ", self.suggestion_param_name)
575-
} else {
576-
self.suggestion_param_name.clone()
577-
}
578-
};
579570
match ty.kind {
580571
hir::TyKind::Path(hir::QPath::Resolved(_, path)) => {
581572
for segment in path.segments {
@@ -603,9 +594,9 @@ impl Subdiagnostic for AddLifetimeParamsSuggestion<'_> {
603594
if let hir::GenericArg::Lifetime(lifetime) = arg
604595
&& lifetime.is_anonymous()
605596
{
606-
self.suggestions.push((
607-
lifetime.ident.span,
608-
make_suggestion(lifetime.ident),
597+
self.suggestions.push(lifetime.suggestion(
598+
&self.suggestion_param_name,
599+
false,
609600
));
610601
}
611602
}
@@ -615,7 +606,7 @@ impl Subdiagnostic for AddLifetimeParamsSuggestion<'_> {
615606
}
616607
hir::TyKind::Ref(lifetime, ..) if lifetime.is_anonymous() => {
617608
self.suggestions
618-
.push((lifetime.ident.span, make_suggestion(lifetime.ident)));
609+
.push(lifetime.suggestion(&self.suggestion_param_name, true));
619610
}
620611
_ => {}
621612
}

tests/ui/unpretty/debug-fmt-hir.stdout

+3-3
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,13 @@ struct Bar {
1313
}
1414

1515
impl fmt::Debug for Bar {
16-
fn fmt(&self, f: &'_ mut fmt::Formatter<'_>)
16+
fn fmt(&self, f: & mut fmt::Formatter<'_>)
1717
->
1818
fmt::Result {
1919
debug_struct_field2_finish(f, "Bar", "a", &self.a, "b", &&self.b)
2020
}
2121
}
2222

23-
fn debug_struct_field2_finish<'a>(name: &'_ str, name1: &'_ str,
24-
value1: &'a dyn fmt::Debug, name2: &'_ str, value2: &'a dyn fmt::Debug)
23+
fn debug_struct_field2_finish<'a>(name: & str, name1: & str,
24+
value1: &'a dyn fmt::Debug, name2: & str, value2: &'a dyn fmt::Debug)
2525
-> fmt::Result { loop { } }

0 commit comments

Comments
 (0)