diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9ed4e47..95c0bcc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,6 +25,15 @@ jobs: - run: cargo install cargo-expand - run: cargo test --verbose + msrv: + name: MSRV + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@1.71.0 + # Only the library is covered by rust-version; dev-dependencies need a newer toolchain. + - run: cargo check --lib + fmt: name: Fmt runs-on: ubuntu-latest @@ -43,7 +52,7 @@ jobs: - uses: dtolnay/rust-toolchain@nightly with: components: clippy - - run: cargo clippy -- -Dwarnings + - run: cargo clippy --all-targets -- -Dwarnings outdated: name: Outdated diff --git a/Cargo.toml b/Cargo.toml index 2b424d4..ce7c5d6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ name = "async-recursion" version = "1.1.1" authors = ["Robert Usher <266585+dcchut@users.noreply.github.com>"] edition = "2018" +rust-version = "1.71" license = "MIT OR Apache-2.0" description = "Recursion for async functions" repository = "https://github.com/dcchut/async-recursion" @@ -15,7 +16,7 @@ proc-macro = true [dependencies] proc-macro2 = { version = "1.0", default-features = false } quote = { version = "1.0", default-features = false } -syn = { version = "2.0", features = ["full", "visit-mut", "parsing", "printing", "proc-macro", "clone-impls"], default-features = false } +syn = { version = "3.0", features = ["full", "visit-mut", "parsing", "printing", "proc-macro", "clone-impls"], default-features = false } [dev-dependencies] futures-executor = "0.3" diff --git a/src/expand.rs b/src/expand.rs index dbf5cf7..312c221 100644 --- a/src/expand.rs +++ b/src/expand.rs @@ -2,7 +2,7 @@ use proc_macro2::{Span, TokenStream}; use quote::{quote, ToTokens}; use syn::{ parse_quote, punctuated::Punctuated, visit_mut::VisitMut, Block, Lifetime, Receiver, - ReturnType, Signature, TypeReference, WhereClause, + ReceiverKind, ReturnType, Signature, TypeReference, WhereClause, }; use crate::parse::{AsyncItem, RecursionArgs}; @@ -50,22 +50,21 @@ struct ReferenceVisitor { impl VisitMut for ReferenceVisitor { fn visit_receiver_mut(&mut self, receiver: &mut Receiver) { - self.self_lifetime = Some(if let Some((_, lt)) = &mut receiver.reference { - self.self_receiver = true; - - if let Some(lt) = lt { - lt.clone() - } else { - // Use 'life_self to avoid collisions with 'life lifetimes. - let new_lifetime: Lifetime = parse_quote!('life_self); - lt.replace(new_lifetime.clone()); - - self.self_receiver_new_lifetime = true; + // TODO: Support ReceiverKind::Typed as well + let ReceiverKind::Reference(_, lt, _) = &mut receiver.kind else { + return; + }; - new_lifetime - } + self.self_receiver = true; + self.self_lifetime = Some(if let Some(lt) = lt { + lt.clone() } else { - return; + // Use 'life_self to avoid collisions with 'life lifetimes. + let new_lifetime: Lifetime = parse_quote!('life_self); + lt.replace(new_lifetime.clone()); + + self.self_receiver_new_lifetime = true; + new_lifetime }); } @@ -127,7 +126,8 @@ fn transform_sig(sig: &mut Signature, args: &RecursionArgs) { // Add an S : 'async_recursion bound to any generic parameter for param in sig.generics.type_params() { - let ident = param.ident.clone(); + let mut ident = param.ident.clone(); + ident.set_span(Span::call_site()); where_clause_generics.push(ident); requires_lifetime = true; } diff --git a/tests/generic_parameters.rs b/tests/generic_parameters.rs index 66c9e1c..a57b5b7 100644 --- a/tests/generic_parameters.rs +++ b/tests/generic_parameters.rs @@ -33,6 +33,8 @@ pub async fn generic_parameter(mut x: S) -> u64 { } } +// x is only used in the recursive call, but we need it to test a non-Send T under ?Send +#[allow(clippy::only_used_in_recursion)] #[async_recursion(?Send)] pub async fn generic_parameter_no_send(x: T, y: u64) -> u64 { if y > 0 { diff --git a/tests/lifetimes.rs b/tests/lifetimes.rs index d705dad..77fb07d 100644 --- a/tests/lifetimes.rs +++ b/tests/lifetimes.rs @@ -34,10 +34,10 @@ async fn contains_value_2<'a, 'b, T: PartialEq>(value: &'b T, node: &'b Node<'a, contains_value(value, node).await } -// The reference inside foo needs a `async_recursion bound +// The reference inside name needs a `async_recursion bound #[async_recursion] -async fn count_down(foo: Option<&str>) -> i32 { - let _ = foo; +async fn count_down(name: Option<&str>) -> i32 { + let _ = name; 0 } @@ -63,17 +63,17 @@ fn lifetime_expansion_works() { node.left = Some(&left); node.right = Some(&right); - assert_eq!(contains_value(&3, &node).await, true); - assert_eq!(contains_value(&4, &node).await, false); - assert_eq!(contains_value(&17, &node).await, true); - assert_eq!(contains_value(&13, &node).await, true); - assert_eq!(contains_value(&12, &node).await, false); + assert!(contains_value(&3, &node).await); + assert!(!contains_value(&4, &node).await); + assert!(contains_value(&17, &node).await); + assert!(contains_value(&13, &node).await); + assert!(!contains_value(&12, &node).await); - assert_eq!(contains_value_2(&3, &node).await, true); - assert_eq!(contains_value_2(&4, &node).await, false); - assert_eq!(contains_value_2(&17, &node).await, true); - assert_eq!(contains_value_2(&13, &node).await, true); - assert_eq!(contains_value_2(&12, &node).await, false); + assert!(contains_value_2(&3, &node).await); + assert!(!contains_value_2(&4, &node).await); + assert!(contains_value_2(&17, &node).await); + assert!(contains_value_2(&13, &node).await); + assert!(!contains_value_2(&12, &node).await); count_down(None).await; explicit_async_recursion_bound(None).await; diff --git a/tests/struct_methods.rs b/tests/struct_methods.rs index 1592d6f..6ce96ca 100644 --- a/tests/struct_methods.rs +++ b/tests/struct_methods.rs @@ -69,7 +69,8 @@ fn struct_method_with_generic_parameter_works() { block_on(async move { let e = Empty {}; assert_eq!( - e.generic_parameter::<*const u64>(&(0 as *const u64)).await, + e.generic_parameter::<*const u64>(&std::ptr::null::()) + .await, 0 ); })