Fix explicit_outlives_requirements
lint in macros
Show the suggestion if and only if the bounds are from the same source context.
This commit is contained in:
parent
2d8651a927
commit
94d6245003
4 changed files with 98 additions and 38 deletions
|
@ -1377,21 +1377,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||||
|
|
||||||
let ident = self.lower_ident(ident);
|
let ident = self.lower_ident(ident);
|
||||||
let param_span = ident.span;
|
let param_span = ident.span;
|
||||||
let span = bounds
|
let span =
|
||||||
.iter()
|
bounds.iter().fold(param_span.shrink_to_hi(), |span, bound| span.to(bound.span()));
|
||||||
.fold(Some(param_span.shrink_to_hi()), |span: Option<Span>, bound| {
|
|
||||||
let bound_span = bound.span();
|
|
||||||
// We include bounds that come from a `#[derive(_)]` but point at the user's code,
|
|
||||||
// as we use this method to get a span appropriate for suggestions.
|
|
||||||
if !bound_span.can_be_used_for_suggestions() {
|
|
||||||
None
|
|
||||||
} else if let Some(span) = span {
|
|
||||||
Some(span.to(bound_span))
|
|
||||||
} else {
|
|
||||||
Some(bound_span)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.unwrap_or(param_span.shrink_to_hi());
|
|
||||||
match kind {
|
match kind {
|
||||||
GenericParamKind::Const { .. } => None,
|
GenericParamKind::Const { .. } => None,
|
||||||
GenericParamKind::Type { .. } => {
|
GenericParamKind::Type { .. } => {
|
||||||
|
|
|
@ -55,7 +55,7 @@ use rustc_session::lint::{BuiltinLintDiagnostics, FutureIncompatibilityReason};
|
||||||
use rustc_span::edition::Edition;
|
use rustc_span::edition::Edition;
|
||||||
use rustc_span::source_map::Spanned;
|
use rustc_span::source_map::Spanned;
|
||||||
use rustc_span::symbol::{kw, sym, Ident, Symbol};
|
use rustc_span::symbol::{kw, sym, Ident, Symbol};
|
||||||
use rustc_span::{BytePos, InnerSpan, Span};
|
use rustc_span::{BytePos, InnerSpan, Span, SyntaxContext};
|
||||||
use rustc_target::abi::{Abi, VariantIdx};
|
use rustc_target::abi::{Abi, VariantIdx};
|
||||||
use rustc_trait_selection::infer::{InferCtxtExt, TyCtxtInferExt};
|
use rustc_trait_selection::infer::{InferCtxtExt, TyCtxtInferExt};
|
||||||
use rustc_trait_selection::traits::{self, misc::can_type_implement_copy, EvaluationResult};
|
use rustc_trait_selection::traits::{self, misc::can_type_implement_copy, EvaluationResult};
|
||||||
|
@ -2184,6 +2184,7 @@ impl ExplicitOutlivesRequirements {
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
bounds: &hir::GenericBounds<'_>,
|
bounds: &hir::GenericBounds<'_>,
|
||||||
inferred_outlives: &[ty::Region<'tcx>],
|
inferred_outlives: &[ty::Region<'tcx>],
|
||||||
|
span_cx: SyntaxContext,
|
||||||
) -> Vec<(usize, Span)> {
|
) -> Vec<(usize, Span)> {
|
||||||
use rustc_middle::middle::resolve_lifetime::Region;
|
use rustc_middle::middle::resolve_lifetime::Region;
|
||||||
|
|
||||||
|
@ -2191,23 +2192,28 @@ impl ExplicitOutlivesRequirements {
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.filter_map(|(i, bound)| {
|
.filter_map(|(i, bound)| {
|
||||||
if let hir::GenericBound::Outlives(lifetime) = bound {
|
let hir::GenericBound::Outlives(lifetime) = bound else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
|
||||||
let is_inferred = match tcx.named_region(lifetime.hir_id) {
|
let is_inferred = match tcx.named_region(lifetime.hir_id) {
|
||||||
Some(Region::EarlyBound(def_id)) => inferred_outlives.iter().any(|r| {
|
Some(Region::EarlyBound(def_id)) => inferred_outlives
|
||||||
if let ty::ReEarlyBound(ebr) = **r {
|
.iter()
|
||||||
ebr.def_id == def_id
|
.any(|r| matches!(**r, ty::ReEarlyBound(ebr) if { ebr.def_id == def_id })),
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
_ => false,
|
_ => false,
|
||||||
};
|
};
|
||||||
is_inferred.then_some((i, bound.span()))
|
|
||||||
} else {
|
if !is_inferred {
|
||||||
None
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let span = bound.span();
|
||||||
|
if span.ctxt() != span_cx || in_external_macro(tcx.sess, span) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some((i, span))
|
||||||
})
|
})
|
||||||
.filter(|(_, span)| !in_external_macro(tcx.sess, *span))
|
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2312,9 +2318,9 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements {
|
||||||
// FIXME we can also infer bounds on associated types,
|
// FIXME we can also infer bounds on associated types,
|
||||||
// and should check for them here.
|
// and should check for them here.
|
||||||
match predicate.bounded_ty.kind {
|
match predicate.bounded_ty.kind {
|
||||||
hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) => {
|
hir::TyKind::Path(hir::QPath::Resolved(None, path)) => {
|
||||||
let Res::Def(DefKind::TyParam, def_id) = path.res else {
|
let Res::Def(DefKind::TyParam, def_id) = path.res else {
|
||||||
continue
|
continue;
|
||||||
};
|
};
|
||||||
let index = ty_generics.param_def_id_to_index[&def_id];
|
let index = ty_generics.param_def_id_to_index[&def_id];
|
||||||
(
|
(
|
||||||
|
@ -2335,8 +2341,12 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let bound_spans =
|
let bound_spans = self.collect_outlives_bound_spans(
|
||||||
self.collect_outlives_bound_spans(cx.tcx, bounds, &relevant_lifetimes);
|
cx.tcx,
|
||||||
|
bounds,
|
||||||
|
&relevant_lifetimes,
|
||||||
|
span.ctxt(),
|
||||||
|
);
|
||||||
bound_count += bound_spans.len();
|
bound_count += bound_spans.len();
|
||||||
|
|
||||||
let drop_predicate = bound_spans.len() == bounds.len();
|
let drop_predicate = bound_spans.len() == bounds.len();
|
||||||
|
|
|
@ -15,7 +15,11 @@ macro_rules! make_foo {
|
||||||
struct Foo<$a, 'b> where 'b: $a {
|
struct Foo<$a, 'b> where 'b: $a {
|
||||||
foo: &$a &'b (),
|
foo: &$a &'b (),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Foo2<$a, 'b: $a> {
|
||||||
|
foo: &$a &'b (),
|
||||||
}
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
gimme_a! {make_foo!}
|
gimme_a! {make_foo!}
|
||||||
|
@ -25,4 +29,41 @@ struct Bar<'a, 'b: 'a> {
|
||||||
bar: &'a &'b (),
|
bar: &'a &'b (),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! make_quux {
|
||||||
|
() => {
|
||||||
|
struct Quux<'a, 'b> where 'b: 'a {
|
||||||
|
//~^ ERROR: outlives requirements can be inferred
|
||||||
|
baz: &'a &'b (),
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Quux2<'a, 'b: 'a> {
|
||||||
|
//~^ ERROR: outlives requirements can be inferred
|
||||||
|
baz: &'a &'b (),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
make_quux!{}
|
||||||
|
|
||||||
|
macro_rules! make_baz {
|
||||||
|
() => {
|
||||||
|
make_baz!{ 'a }
|
||||||
|
};
|
||||||
|
($a:lifetime) => {
|
||||||
|
struct Baz<$a, 'b> where 'b: $a {
|
||||||
|
baz: &$a &'b (),
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Baz2<$a, 'b: $a> {
|
||||||
|
baz: &$a &'b (),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
make_baz!{ 'a }
|
||||||
|
|
||||||
|
mod baz {
|
||||||
|
make_baz!{}
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
error: outlives requirements can be inferred
|
error: outlives requirements can be inferred
|
||||||
--> $DIR/edition-lint-infer-outlives-macro.rs:23:18
|
--> $DIR/edition-lint-infer-outlives-macro.rs:27:18
|
||||||
|
|
|
|
||||||
LL | struct Bar<'a, 'b: 'a> {
|
LL | struct Bar<'a, 'b: 'a> {
|
||||||
| ^^^^ help: remove this bound
|
| ^^^^ help: remove this bound
|
||||||
|
@ -10,5 +10,27 @@ note: the lint level is defined here
|
||||||
LL | #![deny(explicit_outlives_requirements)]
|
LL | #![deny(explicit_outlives_requirements)]
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
error: aborting due to previous error
|
error: outlives requirements can be inferred
|
||||||
|
--> $DIR/edition-lint-infer-outlives-macro.rs:34:28
|
||||||
|
|
|
||||||
|
LL | struct Quux<'a, 'b> where 'b: 'a {
|
||||||
|
| ^^^^^^^^^^^^^ help: remove this bound
|
||||||
|
...
|
||||||
|
LL | make_quux!{}
|
||||||
|
| ------------ in this macro invocation
|
||||||
|
|
|
||||||
|
= note: this error originates in the macro `make_quux` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
|
error: outlives requirements can be inferred
|
||||||
|
--> $DIR/edition-lint-infer-outlives-macro.rs:39:28
|
||||||
|
|
|
||||||
|
LL | struct Quux2<'a, 'b: 'a> {
|
||||||
|
| ^^^^ help: remove this bound
|
||||||
|
...
|
||||||
|
LL | make_quux!{}
|
||||||
|
| ------------ in this macro invocation
|
||||||
|
|
|
||||||
|
= note: this error originates in the macro `make_quux` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
|
error: aborting due to 3 previous errors
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue