Skip to content

Commit 7b437f6

Browse files
committed
Properly set spans on introduced lifetimes.
1 parent c509624 commit 7b437f6

File tree

4 files changed

+111
-40
lines changed

4 files changed

+111
-40
lines changed

src/expand.rs

Lines changed: 55 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@ use syn::{
1212
WhereClause,
1313
};
1414

15+
macro_rules! parse_quote_spanned {
16+
($span:expr => $($t:tt)*) => (
17+
syn::parse2(quote_spanned!($span => $($t)*)).unwrap()
18+
)
19+
}
20+
1521
impl ToTokens for Item {
1622
fn to_tokens(&self, tokens: &mut TokenStream) {
1723
match self {
@@ -21,7 +27,7 @@ impl ToTokens for Item {
2127
}
2228
}
2329

24-
#[derive(Clone, Copy)]
30+
#[derive(Debug, Clone, Copy)]
2531
enum Context<'a> {
2632
Trait {
2733
generics: &'a Generics,
@@ -46,6 +52,13 @@ impl Context<'_> {
4652
}
4753
})
4854
}
55+
56+
fn generics_span(&self) -> Span {
57+
match self {
58+
Context::Trait { generics, .. } => generics.span(),
59+
Context::Impl { impl_generics } => impl_generics.span(),
60+
}
61+
}
4962
}
5063

5164
type Supertraits = Punctuated<TypeParamBound, Token![+]>;
@@ -78,7 +91,7 @@ pub fn expand(input: &mut Item, is_local: bool) {
7891
}
7992
}
8093
Item::Impl(input) => {
81-
let mut lifetimes = CollectLifetimes::new("'impl");
94+
let mut lifetimes = CollectLifetimes::new("'impl", input.generics.span());
8295
lifetimes.visit_type_mut(&mut *input.self_ty);
8396
lifetimes.visit_path_mut(&mut input.trait_.as_mut().unwrap().1);
8497
let params = &input.generics.params;
@@ -133,21 +146,18 @@ fn transform_sig(
133146
ReturnType::Type(_, ret) => quote!(#ret),
134147
};
135148

136-
let mut lifetimes = CollectLifetimes::new("'life");
149+
let default_span = sig.ident.span()
150+
.join(sig.paren_token.span)
151+
.unwrap_or_else(|| sig.ident.span());
152+
153+
let mut lifetimes = CollectLifetimes::new("'life", default_span);
137154
for arg in sig.inputs.iter_mut() {
138155
match arg {
139156
FnArg::Receiver(arg) => lifetimes.visit_receiver_mut(arg),
140157
FnArg::Typed(arg) => lifetimes.visit_type_mut(&mut arg.ty),
141158
}
142159
}
143160

144-
let where_clause = sig
145-
.generics
146-
.where_clause
147-
.get_or_insert_with(|| WhereClause {
148-
where_token: Default::default(),
149-
predicates: Punctuated::new(),
150-
});
151161
for param in sig
152162
.generics
153163
.params
@@ -157,26 +167,31 @@ fn transform_sig(
157167
match param {
158168
GenericParam::Type(param) => {
159169
let param = &param.ident;
160-
where_clause
170+
let span = param.span();
171+
where_clause_or_default(&mut sig.generics.where_clause)
161172
.predicates
162-
.push(parse_quote!(#param: 'async_trait));
173+
.push(parse_quote_spanned!(span => #param: 'async_trait));
163174
}
164175
GenericParam::Lifetime(param) => {
165176
let param = &param.lifetime;
166-
where_clause
177+
let span = param.span();
178+
where_clause_or_default(&mut sig.generics.where_clause)
167179
.predicates
168-
.push(parse_quote!(#param: 'async_trait));
180+
.push(parse_quote_spanned!(span => #param: 'async_trait));
169181
}
170182
GenericParam::Const(_) => {}
171183
}
172184
}
185+
173186
for elided in lifetimes.elided {
174-
sig.generics.params.push(parse_quote!(#elided));
175-
where_clause
187+
push_param(&mut sig.generics, parse_quote!(#elided));
188+
where_clause_or_default(&mut sig.generics.where_clause)
176189
.predicates
177-
.push(parse_quote!(#elided: 'async_trait));
190+
.push(parse_quote_spanned!(elided.span() => #elided: 'async_trait));
178191
}
179-
sig.generics.params.push(parse_quote!('async_trait));
192+
193+
push_param(&mut sig.generics, parse_quote_spanned!(default_span => 'async_trait));
194+
180195
if has_self {
181196
let bound: Ident = match sig.inputs.iter().next() {
182197
Some(FnArg::Receiver(Receiver {
@@ -200,10 +215,11 @@ fn transform_sig(
200215
Context::Trait { supertraits, .. } => !has_default || has_bound(supertraits, &bound),
201216
Context::Impl { .. } => true,
202217
};
218+
let where_clause = where_clause_or_default(&mut sig.generics.where_clause);
203219
where_clause.predicates.push(if assume_bound || is_local {
204-
parse_quote!(Self: 'async_trait)
220+
parse_quote_spanned!(where_clause.span() => Self: 'async_trait)
205221
} else {
206-
parse_quote!(Self: ::core::marker::#bound + 'async_trait)
222+
parse_quote_spanned!(where_clause.span() => Self: ::core::marker::#bound + 'async_trait)
207223
});
208224
}
209225

@@ -228,9 +244,9 @@ fn transform_sig(
228244
}
229245

230246
let bounds = if is_local {
231-
quote!('async_trait)
247+
quote_spanned!(context.generics_span() => 'async_trait)
232248
} else {
233-
quote!(::core::marker::Send + 'async_trait)
249+
quote_spanned!(context.generics_span() => ::core::marker::Send + 'async_trait)
234250
};
235251

236252
sig.output = parse_quote! {
@@ -333,3 +349,20 @@ fn has_bound(supertraits: &Supertraits, marker: &Ident) -> bool {
333349
}
334350
false
335351
}
352+
353+
fn where_clause_or_default(clause: &mut Option<WhereClause>) -> &mut WhereClause {
354+
clause.get_or_insert_with(|| WhereClause {
355+
where_token: Default::default(),
356+
predicates: Punctuated::new(),
357+
})
358+
}
359+
360+
fn push_param(generics: &mut Generics, param: GenericParam) {
361+
let span = param.span();
362+
if generics.params.is_empty() {
363+
generics.lt_token = parse_quote_spanned!(span => <);
364+
generics.gt_token = parse_quote_spanned!(span => >);
365+
}
366+
367+
generics.params.push(param);
368+
}

src/lifetime.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,44 @@
11
use proc_macro2::Span;
2+
use syn::spanned::Spanned;
23
use syn::visit_mut::{self, VisitMut};
34
use syn::{GenericArgument, Lifetime, Receiver, TypeReference};
45

56
pub struct CollectLifetimes {
67
pub elided: Vec<Lifetime>,
78
pub explicit: Vec<Lifetime>,
89
pub name: &'static str,
10+
pub default_span: Span,
911
}
1012

1113
impl CollectLifetimes {
12-
pub fn new(name: &'static str) -> Self {
14+
pub fn new(name: &'static str, default_span: Span) -> Self {
1315
CollectLifetimes {
1416
elided: Vec::new(),
1517
explicit: Vec::new(),
1618
name,
19+
default_span,
1720
}
1821
}
1922

2023
fn visit_opt_lifetime(&mut self, lifetime: &mut Option<Lifetime>) {
2124
match lifetime {
22-
None => *lifetime = Some(self.next_lifetime()),
25+
None => *lifetime = Some(self.next_lifetime(None)),
2326
Some(lifetime) => self.visit_lifetime(lifetime),
2427
}
2528
}
2629

2730
fn visit_lifetime(&mut self, lifetime: &mut Lifetime) {
2831
if lifetime.ident == "_" {
29-
*lifetime = self.next_lifetime();
32+
*lifetime = self.next_lifetime(lifetime.span());
3033
} else {
3134
self.explicit.push(lifetime.clone());
3235
}
3336
}
3437

35-
fn next_lifetime(&mut self) -> Lifetime {
38+
fn next_lifetime<S: Into<Option<Span>>>(&mut self, span: S) -> Lifetime {
3639
let name = format!("{}{}", self.name, self.elided.len());
37-
let life = Lifetime::new(&name, Span::call_site());
40+
let span = span.into().unwrap_or(self.default_span);
41+
let life = Lifetime::new(&name, span);
3842
self.elided.push(life.clone());
3943
life
4044
}

tests/ui/lifetime-span.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,19 @@ impl<'r> Trait<'r> for B {
1818
async fn method(&self) { }
1919
}
2020

21+
#[async_trait]
22+
pub trait Trait2 {
23+
async fn method<'r>(&'r self);
24+
}
25+
26+
#[async_trait]
27+
impl Trait2 for A {
28+
async fn method(&self) { }
29+
}
30+
31+
#[async_trait]
32+
impl<'r> Trait2<'r> for B {
33+
async fn method(&'r self) { }
34+
}
35+
2136
fn main() {}

tests/ui/lifetime-span.stderr

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,43 @@ error[E0726]: implicit elided lifetime not allowed here
44
12 | impl Trait for A {
55
| ^^^^^- help: indicate the anonymous lifetime: `<'_>`
66

7+
error[E0107]: this trait takes 0 lifetime arguments but 1 lifetime argument was supplied
8+
--> $DIR/lifetime-span.rs:32:10
9+
|
10+
32 | impl<'r> Trait2<'r> for B {
11+
| ^^^^^^---- help: remove these generics
12+
| |
13+
| expected 0 lifetime arguments
14+
|
15+
note: trait defined here, with 0 lifetime parameters
16+
--> $DIR/lifetime-span.rs:22:11
17+
|
18+
22 | pub trait Trait2 {
19+
| ^^^^^^
20+
721
error[E0195]: lifetime parameters or bounds on method `method` do not match the trait declaration
8-
--> $DIR/lifetime-span.rs:11:1
22+
--> $DIR/lifetime-span.rs:13:14
923
|
10-
6 | #[async_trait]
11-
| -------------- lifetimes in impl do not match this method in trait
24+
8 | async fn method(&'r self);
25+
| ---------------- lifetimes in impl do not match this method in trait
1226
...
13-
11 | #[async_trait]
14-
| ^^^^^^^^^^^^^^ lifetimes do not match method in trait
15-
|
16-
= note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)
27+
13 | async fn method(&self) { }
28+
| ^^^^^^^^^^^^^ lifetimes do not match method in trait
1729

1830
error[E0195]: lifetime parameters or bounds on method `method` do not match the trait declaration
19-
--> $DIR/lifetime-span.rs:16:1
31+
--> $DIR/lifetime-span.rs:18:14
2032
|
21-
6 | #[async_trait]
22-
| -------------- lifetimes in impl do not match this method in trait
33+
8 | async fn method(&'r self);
34+
| ---------------- lifetimes in impl do not match this method in trait
2335
...
24-
16 | #[async_trait]
25-
| ^^^^^^^^^^^^^^ lifetimes do not match method in trait
36+
18 | async fn method(&self) { }
37+
| ^^^^^^^^^^^^^ lifetimes do not match method in trait
38+
39+
error[E0195]: lifetime parameters or bounds on method `method` do not match the trait declaration
40+
--> $DIR/lifetime-span.rs:33:14
2641
|
27-
= note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)
42+
23 | async fn method<'r>(&'r self);
43+
| ---- lifetimes in impl do not match this method in trait
44+
...
45+
33 | async fn method(&'r self) { }
46+
| ^^^^^^^^^^^^^^^^ lifetimes do not match method in trait

0 commit comments

Comments
 (0)