Skip to content

Fix SingleUseLifetime ICE #106935

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 26 additions & 21 deletions compiler/rustc_lint/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -825,34 +825,39 @@ pub trait LintContext: Sized {
debug!(?param_span, ?use_span, ?deletion_span);
db.span_label(param_span, "this lifetime...");
db.span_label(use_span, "...is used only here");
let msg = "elide the single-use lifetime";
let (use_span, replace_lt) = if elide {
let use_span = sess.source_map().span_extend_while(
use_span,
char::is_whitespace,
).unwrap_or(use_span);
(use_span, String::new())
} else {
(use_span, "'_".to_owned())
};
db.multipart_suggestion(
msg,
vec![(deletion_span, String::new()), (use_span, replace_lt)],
Applicability::MachineApplicable,
);
if let Some(deletion_span) = deletion_span {
let msg = "elide the single-use lifetime";
let (use_span, replace_lt) = if elide {
let use_span = sess.source_map().span_extend_while(
use_span,
char::is_whitespace,
).unwrap_or(use_span);
(use_span, String::new())
} else {
(use_span, "'_".to_owned())
};
debug!(?deletion_span, ?use_span);
db.multipart_suggestion(
msg,
vec![(deletion_span, String::new()), (use_span, replace_lt)],
Applicability::MachineApplicable,
);
}
},
BuiltinLintDiagnostics::SingleUseLifetime {
param_span: _,
use_span: None,
deletion_span,
} => {
debug!(?deletion_span);
db.span_suggestion(
deletion_span,
"elide the unused lifetime",
"",
Applicability::MachineApplicable,
);
if let Some(deletion_span) = deletion_span {
db.span_suggestion(
deletion_span,
"elide the unused lifetime",
"",
Applicability::MachineApplicable,
);
}
},
BuiltinLintDiagnostics::NamedArgumentUsedPositionally{ position_sp_to_replace, position_sp_for_msg, named_arg_sp, named_arg_name, is_formatting_arg} => {
db.span_label(named_arg_sp, "this named argument is referred to by position in formatting string");
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_lint_defs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -502,7 +502,7 @@ pub enum BuiltinLintDiagnostics {
param_span: Span,
/// Span of the code that should be removed when eliding this lifetime.
/// This span should include leading or trailing comma.
deletion_span: Span,
deletion_span: Option<Span>,
/// Span of the single use, or None if the lifetime is never used.
/// If true, the lifetime will be fully elided.
use_span: Option<(Span, bool)>,
Expand Down
22 changes: 19 additions & 3 deletions compiler/rustc_resolve/src/late/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2188,15 +2188,31 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
let deletion_span = || {
if params.len() == 1 {
// if sole lifetime, remove the entire `<>` brackets
generics_span
Some(generics_span)
} else if param_index == 0 {
// if removing within `<>` brackets, we also want to
// delete a leading or trailing comma as appropriate
param.span().to(params[param_index + 1].span().shrink_to_lo())
match (
param.span().find_ancestor_inside(generics_span),
params[param_index + 1].span().find_ancestor_inside(generics_span),
) {
(Some(param_span), Some(next_param_span)) => {
Some(param_span.to(next_param_span.shrink_to_lo()))
}
_ => None,
}
} else {
// if removing within `<>` brackets, we also want to
// delete a leading or trailing comma as appropriate
params[param_index - 1].span().shrink_to_hi().to(param.span())
match (
param.span().find_ancestor_inside(generics_span),
params[param_index - 1].span().find_ancestor_inside(generics_span),
) {
(Some(param_span), Some(prev_param_span)) => {
Some(prev_param_span.shrink_to_hi().to(param_span))
}
_ => None,
}
}
};
match use_set {
Expand Down
100 changes: 100 additions & 0 deletions tests/ui/single-use-lifetime/issue-104440.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
#![feature(decl_macro, rustc_attrs)]
#![deny(single_use_lifetimes)]

mod type_params {
macro m($T:ident) {
fn f<$T: Clone, T: PartialEq>(t1: $T, t2: T) -> ($T, bool) {
(t1.clone(), t2 == t2)
}
}

#[rustc_macro_transparency = "semitransparent"]
macro n($T:ident) {
fn g<$T: Clone>(t1: $T, t2: T) -> (T, $T) {
(t1.clone(), t2.clone())
}
fn h<T: Clone>(t1: $T, t2: T) -> (T, $T) {
(t1.clone(), t2.clone())
}
}

#[rustc_macro_transparency = "transparent"]
macro p($T:ident) {
fn j<$T: Clone>(t1: $T, t2: T) -> (T, $T) {
(t1.clone(), t2.clone())
}
fn k<T: Clone>(t1: $T, t2: T) -> (T, $T) {
(t1.clone(), t2.clone())
}
}

m!(T);
n!(T);
p!(T);
}

mod lifetime_params {
macro m($a:lifetime) {
fn f<'b, 'c, $a: 'b, 'a: 'c>(t1: &$a(), t2: &'a ()) -> (&'b (), &'c ()) { //~ ERROR lifetime parameter `'a` only used once
(t1, t2)
}
}

#[rustc_macro_transparency = "semitransparent"]
macro n($a:lifetime) {
fn g<$a>(t1: &$a(), t2: &'a ()) -> (&'a (), &$a ()) {
(t1, t2)
}
fn h<'a>(t1: &$a(), t2: &'a ()) -> (&'a (), &$a ()) {
(t1, t2)
}
}

#[rustc_macro_transparency = "transparent"]
macro p($a:lifetime) {
fn j<$a>(t1: &$a(), t2: &'a ()) -> (&'a (), &$a ()) {
(t1, t2)
}
fn k<'a>(t1: &$a(), t2: &'a ()) -> (&'a (), &$a ()) {
(t1, t2)
}
}

m!('a); //~ ERROR lifetime parameter `'a` only used once
n!('a);
p!('a);
}

mod const_params {
macro m($C:ident) {
fn f<const $C: usize, const C: usize>(t1: [(); $C], t2: [(); C]) -> ([(); $C], [(); C]) {
(t1, t2)
}
}

#[rustc_macro_transparency = "semitransparent"]
macro n($C:ident) {
fn g<const $C: usize>(t1: [(); $C], t2: [(); C]) -> ([(); C], [(); $C]) {
(t1, t2)
}
fn h<const C: usize>(t1: [(); $C], t2: [(); C]) -> ([(); C], [(); $C]) {
(t1, t2)
}
}

#[rustc_macro_transparency = "transparent"]
macro p($C:ident) {
fn j<const $C: usize>(t1: [(); $C], t2: [(); C]) -> ([(); C], [(); $C]) {
(t1, t2)
}
fn k<const C: usize>(t1: [(); $C], t2: [(); C]) -> ([(); C], [(); $C]) {
(t1, t2)
}
}

m!(C);
n!(C);
p!(C);
}

fn main() {}
28 changes: 28 additions & 0 deletions tests/ui/single-use-lifetime/issue-104440.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
error: lifetime parameter `'a` only used once
--> $DIR/issue-104440.rs:63:8
|
LL | m!('a);
| ^^
| |
| this lifetime...
| ...is used only here
|
note: the lint level is defined here
--> $DIR/issue-104440.rs:2:9
|
LL | #![deny(single_use_lifetimes)]
| ^^^^^^^^^^^^^^^^^^^^

error: lifetime parameter `'a` only used once
--> $DIR/issue-104440.rs:38:30
|
LL | fn f<'b, 'c, $a: 'b, 'a: 'c>(t1: &$a(), t2: &'a ()) -> (&'b (), &'c ()) {
| ^^ this lifetime... -- ...is used only here
...
LL | m!('a);
| ------ in this macro invocation
|
= note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info)

error: aborting due to 2 previous errors