Skip to content

Commit 18bf19c

Browse files
Rollup merge of #106064 - lukas-code:outlives-macro, r=cjgillot
Partially fix `explicit_outlives_requirements` lint in macros Show the suggestion if and only if the bounds are from the same source context. fixes #106044 fixes #106063
2 parents 3fba7b4 + 1eba6c4 commit 18bf19c

File tree

7 files changed

+458
-88
lines changed

7 files changed

+458
-88
lines changed

compiler/rustc_ast_lowering/src/item.rs

+15-15
Original file line numberDiff line numberDiff line change
@@ -1316,6 +1316,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
13161316
param.id,
13171317
&param.kind,
13181318
&param.bounds,
1319+
param.colon_span,
1320+
generics.span,
13191321
itctx,
13201322
PredicateOrigin::GenericParam,
13211323
)
@@ -1365,6 +1367,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
13651367
id: NodeId,
13661368
kind: &GenericParamKind,
13671369
bounds: &[GenericBound],
1370+
colon_span: Option<Span>,
1371+
parent_span: Span,
13681372
itctx: &ImplTraitContext,
13691373
origin: PredicateOrigin,
13701374
) -> Option<hir::WherePredicate<'hir>> {
@@ -1377,21 +1381,17 @@ impl<'hir> LoweringContext<'_, 'hir> {
13771381

13781382
let ident = self.lower_ident(ident);
13791383
let param_span = ident.span;
1380-
let span = bounds
1381-
.iter()
1382-
.fold(Some(param_span.shrink_to_hi()), |span: Option<Span>, bound| {
1383-
let bound_span = bound.span();
1384-
// We include bounds that come from a `#[derive(_)]` but point at the user's code,
1385-
// as we use this method to get a span appropriate for suggestions.
1386-
if !bound_span.can_be_used_for_suggestions() {
1387-
None
1388-
} else if let Some(span) = span {
1389-
Some(span.to(bound_span))
1390-
} else {
1391-
Some(bound_span)
1392-
}
1393-
})
1394-
.unwrap_or(param_span.shrink_to_hi());
1384+
1385+
// Reconstruct the span of the entire predicate from the individual generic bounds.
1386+
let span_start = colon_span.unwrap_or_else(|| param_span.shrink_to_hi());
1387+
let span = bounds.iter().fold(span_start, |span_accum, bound| {
1388+
match bound.span().find_ancestor_inside(parent_span) {
1389+
Some(bound_span) => span_accum.to(bound_span),
1390+
None => span_accum,
1391+
}
1392+
});
1393+
let span = self.lower_span(span);
1394+
13951395
match kind {
13961396
GenericParamKind::Const { .. } => None,
13971397
GenericParamKind::Type { .. } => {

compiler/rustc_ast_lowering/src/lib.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -2245,14 +2245,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
22452245
) -> (hir::GenericParam<'hir>, Option<hir::WherePredicate<'hir>>, hir::TyKind<'hir>) {
22462246
// Add a definition for the in-band `Param`.
22472247
let def_id = self.local_def_id(node_id);
2248+
let span = self.lower_span(span);
22482249

22492250
// Set the name to `impl Bound1 + Bound2`.
22502251
let param = hir::GenericParam {
22512252
hir_id: self.lower_node_id(node_id),
22522253
def_id,
22532254
name: ParamName::Plain(self.lower_ident(ident)),
22542255
pure_wrt_drop: false,
2255-
span: self.lower_span(span),
2256+
span,
22562257
kind: hir::GenericParamKind::Type { default: None, synthetic: true },
22572258
colon_span: None,
22582259
};
@@ -2262,6 +2263,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
22622263
node_id,
22632264
&GenericParamKind::Type { default: None },
22642265
bounds,
2266+
/* colon_span */ None,
2267+
span,
22652268
&ImplTraitContext::Universal,
22662269
hir::PredicateOrigin::ImplTrait,
22672270
);
@@ -2271,7 +2274,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
22712274
let ty = hir::TyKind::Path(hir::QPath::Resolved(
22722275
None,
22732276
self.arena.alloc(hir::Path {
2274-
span: self.lower_span(span),
2277+
span,
22752278
res,
22762279
segments:
22772280
arena_vec![self; hir::PathSegment::new(self.lower_ident(ident), hir_id, res)],

compiler/rustc_lint/src/builtin.rs

+85-63
Original file line numberDiff line numberDiff line change
@@ -2184,30 +2184,36 @@ impl ExplicitOutlivesRequirements {
21842184
tcx: TyCtxt<'tcx>,
21852185
bounds: &hir::GenericBounds<'_>,
21862186
inferred_outlives: &[ty::Region<'tcx>],
2187+
predicate_span: Span,
21872188
) -> Vec<(usize, Span)> {
21882189
use rustc_middle::middle::resolve_lifetime::Region;
21892190

21902191
bounds
21912192
.iter()
21922193
.enumerate()
21932194
.filter_map(|(i, bound)| {
2194-
if let hir::GenericBound::Outlives(lifetime) = bound {
2195-
let is_inferred = match tcx.named_region(lifetime.hir_id) {
2196-
Some(Region::EarlyBound(def_id)) => inferred_outlives.iter().any(|r| {
2197-
if let ty::ReEarlyBound(ebr) = **r {
2198-
ebr.def_id == def_id
2199-
} else {
2200-
false
2201-
}
2202-
}),
2203-
_ => false,
2204-
};
2205-
is_inferred.then_some((i, bound.span()))
2206-
} else {
2207-
None
2195+
let hir::GenericBound::Outlives(lifetime) = bound else {
2196+
return None;
2197+
};
2198+
2199+
let is_inferred = match tcx.named_region(lifetime.hir_id) {
2200+
Some(Region::EarlyBound(def_id)) => inferred_outlives
2201+
.iter()
2202+
.any(|r| matches!(**r, ty::ReEarlyBound(ebr) if { ebr.def_id == def_id })),
2203+
_ => false,
2204+
};
2205+
2206+
if !is_inferred {
2207+
return None;
22082208
}
2209+
2210+
let span = bound.span().find_ancestor_inside(predicate_span)?;
2211+
if in_external_macro(tcx.sess, span) {
2212+
return None;
2213+
}
2214+
2215+
Some((i, span))
22092216
})
2210-
.filter(|(_, span)| !in_external_macro(tcx.sess, *span))
22112217
.collect()
22122218
}
22132219

@@ -2273,9 +2279,9 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements {
22732279
use rustc_middle::middle::resolve_lifetime::Region;
22742280

22752281
let def_id = item.owner_id.def_id;
2276-
if let hir::ItemKind::Struct(_, ref hir_generics)
2277-
| hir::ItemKind::Enum(_, ref hir_generics)
2278-
| hir::ItemKind::Union(_, ref hir_generics) = item.kind
2282+
if let hir::ItemKind::Struct(_, hir_generics)
2283+
| hir::ItemKind::Enum(_, hir_generics)
2284+
| hir::ItemKind::Union(_, hir_generics) = item.kind
22792285
{
22802286
let inferred_outlives = cx.tcx.inferred_outlives_of(def_id);
22812287
if inferred_outlives.is_empty() {
@@ -2290,53 +2296,58 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements {
22902296
let mut dropped_predicate_count = 0;
22912297
let num_predicates = hir_generics.predicates.len();
22922298
for (i, where_predicate) in hir_generics.predicates.iter().enumerate() {
2293-
let (relevant_lifetimes, bounds, span, in_where_clause) = match where_predicate {
2294-
hir::WherePredicate::RegionPredicate(predicate) => {
2295-
if let Some(Region::EarlyBound(region_def_id)) =
2296-
cx.tcx.named_region(predicate.lifetime.hir_id)
2297-
{
2298-
(
2299-
Self::lifetimes_outliving_lifetime(
2300-
inferred_outlives,
2301-
region_def_id,
2302-
),
2303-
&predicate.bounds,
2304-
predicate.span,
2305-
predicate.in_where_clause,
2306-
)
2307-
} else {
2308-
continue;
2309-
}
2310-
}
2311-
hir::WherePredicate::BoundPredicate(predicate) => {
2312-
// FIXME we can also infer bounds on associated types,
2313-
// and should check for them here.
2314-
match predicate.bounded_ty.kind {
2315-
hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) => {
2316-
let Res::Def(DefKind::TyParam, def_id) = path.res else {
2317-
continue
2318-
};
2319-
let index = ty_generics.param_def_id_to_index[&def_id];
2299+
let (relevant_lifetimes, bounds, predicate_span, in_where_clause) =
2300+
match where_predicate {
2301+
hir::WherePredicate::RegionPredicate(predicate) => {
2302+
if let Some(Region::EarlyBound(region_def_id)) =
2303+
cx.tcx.named_region(predicate.lifetime.hir_id)
2304+
{
23202305
(
2321-
Self::lifetimes_outliving_type(inferred_outlives, index),
2306+
Self::lifetimes_outliving_lifetime(
2307+
inferred_outlives,
2308+
region_def_id,
2309+
),
23222310
&predicate.bounds,
23232311
predicate.span,
2324-
predicate.origin == PredicateOrigin::WhereClause,
2312+
predicate.in_where_clause,
23252313
)
2326-
}
2327-
_ => {
2314+
} else {
23282315
continue;
23292316
}
23302317
}
2331-
}
2332-
_ => continue,
2333-
};
2318+
hir::WherePredicate::BoundPredicate(predicate) => {
2319+
// FIXME we can also infer bounds on associated types,
2320+
// and should check for them here.
2321+
match predicate.bounded_ty.kind {
2322+
hir::TyKind::Path(hir::QPath::Resolved(None, path)) => {
2323+
let Res::Def(DefKind::TyParam, def_id) = path.res else {
2324+
continue;
2325+
};
2326+
let index = ty_generics.param_def_id_to_index[&def_id];
2327+
(
2328+
Self::lifetimes_outliving_type(inferred_outlives, index),
2329+
&predicate.bounds,
2330+
predicate.span,
2331+
predicate.origin == PredicateOrigin::WhereClause,
2332+
)
2333+
}
2334+
_ => {
2335+
continue;
2336+
}
2337+
}
2338+
}
2339+
_ => continue,
2340+
};
23342341
if relevant_lifetimes.is_empty() {
23352342
continue;
23362343
}
23372344

2338-
let bound_spans =
2339-
self.collect_outlives_bound_spans(cx.tcx, bounds, &relevant_lifetimes);
2345+
let bound_spans = self.collect_outlives_bound_spans(
2346+
cx.tcx,
2347+
bounds,
2348+
&relevant_lifetimes,
2349+
predicate_span,
2350+
);
23402351
bound_count += bound_spans.len();
23412352

23422353
let drop_predicate = bound_spans.len() == bounds.len();
@@ -2345,15 +2356,15 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements {
23452356
}
23462357

23472358
if drop_predicate && !in_where_clause {
2348-
lint_spans.push(span);
2359+
lint_spans.push(predicate_span);
23492360
} else if drop_predicate && i + 1 < num_predicates {
23502361
// If all the bounds on a predicate were inferable and there are
23512362
// further predicates, we want to eat the trailing comma.
23522363
let next_predicate_span = hir_generics.predicates[i + 1].span();
2353-
where_lint_spans.push(span.to(next_predicate_span.shrink_to_lo()));
2364+
where_lint_spans.push(predicate_span.to(next_predicate_span.shrink_to_lo()));
23542365
} else {
23552366
where_lint_spans.extend(self.consolidate_outlives_bound_spans(
2356-
span.shrink_to_lo(),
2367+
predicate_span.shrink_to_lo(),
23572368
bounds,
23582369
bound_spans,
23592370
));
@@ -2374,24 +2385,35 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements {
23742385
} else {
23752386
hir_generics.span.shrink_to_hi().to(where_span)
23762387
};
2377-
lint_spans.push(full_where_span);
2388+
2389+
// Due to macro expansions, the `full_where_span` might not actually contain all predicates.
2390+
if where_lint_spans.iter().all(|&sp| full_where_span.contains(sp)) {
2391+
lint_spans.push(full_where_span);
2392+
} else {
2393+
lint_spans.extend(where_lint_spans);
2394+
}
23782395
} else {
23792396
lint_spans.extend(where_lint_spans);
23802397
}
23812398

23822399
if !lint_spans.is_empty() {
2400+
// Do not automatically delete outlives requirements from macros.
2401+
let applicability = if lint_spans.iter().all(|sp| sp.can_be_used_for_suggestions())
2402+
{
2403+
Applicability::MachineApplicable
2404+
} else {
2405+
Applicability::MaybeIncorrect
2406+
};
2407+
23832408
cx.struct_span_lint(
23842409
EXPLICIT_OUTLIVES_REQUIREMENTS,
23852410
lint_spans.clone(),
23862411
fluent::lint_builtin_explicit_outlives,
23872412
|lint| {
23882413
lint.set_arg("count", bound_count).multipart_suggestion(
23892414
fluent::suggestion,
2390-
lint_spans
2391-
.into_iter()
2392-
.map(|span| (span, String::new()))
2393-
.collect::<Vec<_>>(),
2394-
Applicability::MachineApplicable,
2415+
lint_spans.into_iter().map(|span| (span, String::new())).collect(),
2416+
applicability,
23952417
)
23962418
},
23972419
);

compiler/rustc_span/src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -796,6 +796,9 @@ impl Span {
796796

797797
/// Returns a `Span` that would enclose both `self` and `end`.
798798
///
799+
/// Note that this can also be used to extend the span "backwards":
800+
/// `start.to(end)` and `end.to(start)` return the same `Span`.
801+
///
799802
/// ```text
800803
/// ____ ___
801804
/// self lorem ipsum end

0 commit comments

Comments
 (0)