Skip to content

Commit e488f79

Browse files
committed
Tag the inserted lifetime bounds with the span of the other bounds
Fixes rust-lang/rust#93828 (which is really a bug in `async_trait`, not rustc). Before: help: consider further restricting this bound | 13 | async fn publish<T + std::marker::Send: IntoUrl>(&self, url: T) -> String { | +++++++++++++++++++ After: help: consider further restricting this bound | 13 | async fn publish<T: IntoUrl + std::marker::Send>(&self, url: T) -> String { | +++++++++++++++++++
1 parent b8eb3f6 commit e488f79

File tree

3 files changed

+87
-5
lines changed

3 files changed

+87
-5
lines changed

src/expand.rs

+17-5
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use proc_macro2::TokenStream;
55
use quote::{format_ident, quote, quote_spanned, ToTokens};
66
use std::collections::BTreeSet as Set;
77
use syn::punctuated::Punctuated;
8+
use syn::spanned::Spanned;
89
use syn::visit_mut::{self, VisitMut};
910
use syn::{
1011
parse_quote, parse_quote_spanned, Attribute, Block, FnArg, GenericParam, Generics, Ident,
@@ -186,18 +187,30 @@ fn transform_sig(
186187
{
187188
match param {
188189
GenericParam::Type(param) => {
190+
let span = if param.bounds.is_empty() {
191+
// This should use `Span::def_site()`, but that's not stable.
192+
param.ident.span()
193+
} else {
194+
param.bounds.span()
195+
};
189196
let param = &param.ident;
190-
let span = param.span();
197+
let life = quote_spanned!(span=> : 'async_trait);
191198
where_clause_or_default(&mut sig.generics.where_clause)
192199
.predicates
193-
.push(parse_quote_spanned!(span=> #param: 'async_trait));
200+
.push(parse_quote!(#param #life));
194201
}
195202
GenericParam::Lifetime(param) => {
203+
let span = if param.bounds.is_empty() {
204+
// This should use `Span::def_site()`, but that's not stable.
205+
param.lifetime.span()
206+
} else {
207+
param.bounds.span()
208+
};
196209
let param = &param.lifetime;
197-
let span = param.span();
210+
let life = quote_spanned!(span=> : 'async_trait);
198211
where_clause_or_default(&mut sig.generics.where_clause)
199212
.predicates
200-
.push(parse_quote_spanned!(span=> #param: 'async_trait));
213+
.push(parse_quote!(#param #life));
201214
}
202215
GenericParam::Const(_) => {}
203216
}
@@ -385,7 +398,6 @@ fn transform_block(context: Context, sig: &mut Signature, block: &mut Block) {
385398
}
386399

387400
fn positional_arg(i: usize, pat: &Pat) -> Ident {
388-
use syn::spanned::Spanned;
389401
format_ident!("__arg{}", i, span = pat.span())
390402
}
391403

tests/ui/issue-93828.rs

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
use async_trait::async_trait;
2+
3+
struct Client;
4+
struct Client2;
5+
trait IntoUrl {}
6+
7+
#[async_trait]
8+
pub trait ClientExt {
9+
async fn publish<T: IntoUrl>(&self, url: T) -> String;
10+
}
11+
12+
// https://github.com/rust-lang/rust/issues/93828
13+
#[async_trait]
14+
impl ClientExt for Client {
15+
async fn publish<T: IntoUrl>(&self, url: T) -> String {
16+
"Foo".to_string()
17+
}
18+
}
19+
20+
// Variant test case with no bounds at all.
21+
// This doesn't actually work correctly yet. It ought to insert the colon,
22+
// but getting it to do that would require a way to tell rustc that the bounds
23+
// don't actually exist in the source code, and it needs to insert them.
24+
#[async_trait]
25+
impl ClientExt for Client2 {
26+
async fn publish<T>(&self, url: T) -> String {
27+
"Foo".to_string()
28+
}
29+
}
30+
31+
fn main() {}

tests/ui/issue-93828.stderr

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
error: future cannot be sent between threads safely
2+
--> tests/ui/issue-93828.rs:15:59
3+
|
4+
15 | async fn publish<T: IntoUrl>(&self, url: T) -> String {
5+
| ___________________________________________________________^
6+
16 | | "Foo".to_string()
7+
17 | | }
8+
| |_____^ future created by async block is not `Send`
9+
|
10+
note: captured value is not `Send`
11+
--> tests/ui/issue-93828.rs:15:41
12+
|
13+
15 | async fn publish<T: IntoUrl>(&self, url: T) -> String {
14+
| ^^^ has type `T` which is not `Send`
15+
= note: required for the cast to the object type `dyn Future<Output = String> + Send`
16+
help: consider further restricting this bound
17+
|
18+
15 | async fn publish<T: IntoUrl + std::marker::Send>(&self, url: T) -> String {
19+
| +++++++++++++++++++
20+
21+
error: future cannot be sent between threads safely
22+
--> tests/ui/issue-93828.rs:26:50
23+
|
24+
26 | async fn publish<T>(&self, url: T) -> String {
25+
| __________________________________________________^
26+
27 | | "Foo".to_string()
27+
28 | | }
28+
| |_____^ future created by async block is not `Send`
29+
|
30+
note: captured value is not `Send`
31+
--> tests/ui/issue-93828.rs:26:32
32+
|
33+
26 | async fn publish<T>(&self, url: T) -> String {
34+
| ^^^ has type `T` which is not `Send`
35+
= note: required for the cast to the object type `dyn Future<Output = String> + Send`
36+
help: consider further restricting this bound
37+
|
38+
26 | async fn publish<T + std::marker::Send>(&self, url: T) -> String {
39+
| +++++++++++++++++++

0 commit comments

Comments
 (0)