Skip to content

Commit

Permalink
Preserve input body when attribute macro expansion fails (#1245, #1244)
Browse files Browse the repository at this point in the history
  • Loading branch information
tyranron authored Jan 30, 2024
1 parent 8a69e14 commit f363b0d
Show file tree
Hide file tree
Showing 29 changed files with 211 additions and 63 deletions.
3 changes: 2 additions & 1 deletion juniper_codegen/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ All user visible changes to `juniper_codegen` crate will be documented in this f

### BC Breaks

- `#[graphql_object]` and `#[graphql_subscription]` expansions now preserve defined `impl` blocks "as is" and reuse defined methods in opaque way. ([#971])
- `#[graphql_object]` and `#[graphql_subscription]` expansions now preserve defined `impl` blocks "as is" and reuse defined methods in opaque way. ([#971], [#1245])
- Renamed `rename = "<policy>"` attribute argument to `rename_all = "<policy>"` (following `serde` style). ([#971])
- Redesigned `#[graphql_interface]` macro: ([#1009])
- Removed support for `dyn` attribute argument (interface values as trait objects).
Expand Down Expand Up @@ -61,6 +61,7 @@ All user visible changes to `juniper_codegen` crate will be documented in this f
[#1051]: /../../issues/1051
[#1054]: /../../pull/1054
[#1157]: /../../pull/1157
[#1245]: /../../pull/1245



Expand Down
20 changes: 18 additions & 2 deletions juniper_codegen/src/common/diagnostic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ use std::fmt;

use proc_macro2::Span;

pub(crate) use self::polyfill::{abort_if_dirty, emit_error, entry_point, Diagnostic, ResultExt};
pub(crate) use self::polyfill::{
abort_if_dirty, emit_error, entry_point, entry_point_with_preserved_body, Diagnostic, ResultExt,
};

/// URL of the GraphQL specification (October 2021 Edition).
pub(crate) const SPEC_URL: &str = "https://spec.graphql.org/October2021";
Expand Down Expand Up @@ -258,6 +260,18 @@ mod polyfill {

/// This is the entry point for a macro to support [`Diagnostic`]s.
pub(crate) fn entry_point<F>(f: F) -> proc_macro::TokenStream
where
F: FnOnce() -> proc_macro::TokenStream + UnwindSafe,
{
entry_point_with_preserved_body(TokenStream::new(), f)
}

/// This is the entry point for an attribute macro to support [`Diagnostic`]s, while preserving
/// the `body` input [`proc_macro::TokenStream`] on errors.
pub(crate) fn entry_point_with_preserved_body<F>(
body: impl Into<TokenStream>,
f: F,
) -> proc_macro::TokenStream
where
F: FnOnce() -> proc_macro::TokenStream + UnwindSafe,
{
Expand All @@ -267,7 +281,9 @@ mod polyfill {
ENTERED_ENTRY_POINT.with(|flag| flag.set(flag.get() - 1));

let gen_error = || {
quote! { #( #err_storage )* }
let body = body.into();

quote! { #body #( #err_storage )* }
};

match caught {
Expand Down
10 changes: 5 additions & 5 deletions juniper_codegen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -756,7 +756,7 @@ pub fn derive_scalar(input: TokenStream) -> TokenStream {
/// [`ScalarValue`]: juniper::ScalarValue
#[proc_macro_attribute]
pub fn graphql_scalar(attr: TokenStream, body: TokenStream) -> TokenStream {
diagnostic::entry_point(|| {
diagnostic::entry_point_with_preserved_body(body.clone(), || {
graphql_scalar::attr::expand(attr.into(), body.into())
.unwrap_or_abort()
.into()
Expand Down Expand Up @@ -1318,7 +1318,7 @@ pub fn derive_scalar_value(input: TokenStream) -> TokenStream {
/// [4]: https://doc.rust-lang.org/stable/std/primitive.unit.html
#[proc_macro_attribute]
pub fn graphql_interface(attr: TokenStream, body: TokenStream) -> TokenStream {
diagnostic::entry_point(|| {
diagnostic::entry_point_with_preserved_body(body.clone(), || {
self::graphql_interface::attr::expand(attr.into(), body.into())
.unwrap_or_abort()
.into()
Expand Down Expand Up @@ -1825,7 +1825,7 @@ pub fn derive_object(body: TokenStream) -> TokenStream {
/// [1]: https://spec.graphql.org/October2021#sec-Objects
#[proc_macro_attribute]
pub fn graphql_object(attr: TokenStream, body: TokenStream) -> TokenStream {
diagnostic::entry_point(|| {
diagnostic::entry_point_with_preserved_body(body.clone(), || {
self::graphql_object::attr::expand(attr.into(), body.into())
.unwrap_or_abort()
.into()
Expand Down Expand Up @@ -1879,7 +1879,7 @@ pub fn graphql_object(attr: TokenStream, body: TokenStream) -> TokenStream {
/// [1]: https://spec.graphql.org/October2021#sec-Subscription
#[proc_macro_attribute]
pub fn graphql_subscription(attr: TokenStream, body: TokenStream) -> TokenStream {
diagnostic::entry_point(|| {
diagnostic::entry_point_with_preserved_body(body.clone(), || {
self::graphql_subscription::attr::expand(attr.into(), body.into())
.unwrap_or_abort()
.into()
Expand Down Expand Up @@ -2486,7 +2486,7 @@ pub fn derive_union(body: TokenStream) -> TokenStream {
/// [4]: https://doc.rust-lang.org/stable/std/primitive.unit.html
#[proc_macro_attribute]
pub fn graphql_union(attr: TokenStream, body: TokenStream) -> TokenStream {
diagnostic::entry_point(|| {
diagnostic::entry_point_with_preserved_body(body.clone(), || {
self::graphql_union::attr::expand(attr.into(), body.into())
.unwrap_or_abort()
.into()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,9 @@ error: GraphQL interface must have a different name for each field
|
4 | struct Character {
| ^^^^^^

error: cannot find attribute `graphql` in this scope
--> fail/interface/struct/attr_fields_duplicate.rs:7:7
|
7 | #[graphql(name = "id")]
| ^^^^^^^
6 changes: 6 additions & 0 deletions tests/codegen/fail/interface/trait/fields_duplicate.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,9 @@ error: GraphQL interface must have a different name for each field
|
4 | trait Character {
| ^^^^^

error: cannot find attribute `graphql` in this scope
--> fail/interface/trait/fields_duplicate.rs:7:7
|
7 | #[graphql(name = "id")]
| ^^^^^^^
13 changes: 13 additions & 0 deletions tests/codegen/fail/interface/trait/wrong_syntax.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
use juniper::graphql_interface;

#[graphql_interface]
trait Character {
fn id(&self) -> &str;

#[graphql(ignore)]
fn id2(&self) -> &str {
self.self.id()
}
}

fn main() {}
22 changes: 22 additions & 0 deletions tests/codegen/fail/interface/trait/wrong_syntax.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
error: #[graphql_interface] attribute is applicable to trait and struct definitions only
--> fail/interface/trait/wrong_syntax.rs:3:1
|
3 | #[graphql_interface]
| ^^^^^^^^^^^^^^^^^^^^
|
= note: this error originates in the attribute macro `graphql_interface` (in Nightly builds, run with -Z macro-backtrace for more info)

error: cannot find attribute `graphql` in this scope
--> fail/interface/trait/wrong_syntax.rs:7:7
|
7 | #[graphql(ignore)]
| ^^^^^^^

error[E0609]: no field `self` on type `&Self`
--> fail/interface/trait/wrong_syntax.rs:9:14
|
4 | trait Character {
| --------------- type parameter 'Self' declared here
...
9 | self.self.id()
| ^^^^
2 changes: 1 addition & 1 deletion tests/codegen/fail/object/attr_field_double_underscored.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use juniper::graphql_object;
struct ObjA;

#[graphql_object]
impl Character for ObjA {
impl ObjA {
fn __id(&self) -> &str {
"funA"
}
Expand Down
11 changes: 5 additions & 6 deletions tests/codegen/fail/object/attr_field_double_underscored.stderr
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
error: #[graphql_object] attribute is applicable to non-trait `impl` blocks only
--> fail/object/attr_field_double_underscored.rs:5:1
error: All types and directives defined within a schema must not have a name which begins with `__` (two underscores), as this is used exclusively by GraphQL’s introspection system.
· note: https://spec.graphql.org/October2021#sec-Schema
--> fail/object/attr_field_double_underscored.rs:7:8
|
5 | #[graphql_object]
| ^^^^^^^^^^^^^^^^^
|
= note: this error originates in the attribute macro `graphql_object` (in Nightly builds, run with -Z macro-backtrace for more info)
7 | fn __id(&self) -> &str {
| ^^^^
6 changes: 6 additions & 0 deletions tests/codegen/fail/object/attr_fields_duplicate.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,9 @@ error: GraphQL object must have a different name for each field
|
6 | impl ObjA {
| ^^^^

error: cannot find attribute `graphql` in this scope
--> fail/object/attr_fields_duplicate.rs:11:7
|
11 | #[graphql(name = "id")]
| ^^^^^^^
14 changes: 14 additions & 0 deletions tests/codegen/fail/object/attr_wrong_syntax.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
use juniper::graphql_object;

struct MyObject {
my_field: i32,
}

#[graphql_object]
impl MyObject {
fn my_field(&self) -> i32 {
self.self.my_field
}
}

fn main() {}
15 changes: 15 additions & 0 deletions tests/codegen/fail/object/attr_wrong_syntax.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
error: #[graphql_object] attribute is applicable to non-trait `impl` blocks only
--> fail/object/attr_wrong_syntax.rs:7:1
|
7 | #[graphql_object]
| ^^^^^^^^^^^^^^^^^
|
= note: this error originates in the attribute macro `graphql_object` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0609]: no field `self` on type `&MyObject`
--> fail/object/attr_wrong_syntax.rs:10:14
|
10 | self.self.my_field
| ^^^^ unknown field
|
= note: available fields are: `my_field`
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
use std::pin::Pin;
use std::{future, pin::Pin};

use futures::{stream, Stream};
use juniper::graphql_subscription;

type Stream<'a, I> = Pin<Box<dyn futures::Stream<Item = I> + Send + 'a>>;
type BoxStream<'a, I> = Pin<Box<dyn Stream<Item = I> + Send + 'a>>;

struct Obj;

#[graphql_subscription]
impl Obj {
async fn id(&self, __num: i32) -> Stream<'static, &'static str> {
async fn id(&self, __num: i32) -> BoxStream<'static, &'static str> {
Box::pin(stream::once(future::ready("funA")))
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
error: All types and directives defined within a schema must not have a name which begins with `__` (two underscores), as this is used exclusively by GraphQL’s introspection system.
· note: https://spec.graphql.org/October2021#sec-Schema
--> fail/subscription/argument_double_underscored.rs:11:24
--> fail/subscription/argument_double_underscored.rs:12:24
|
11 | async fn id(&self, __num: i32) -> Stream<'static, &'static str> {
12 | async fn id(&self, __num: i32) -> BoxStream<'static, &'static str> {
| ^^^^^
8 changes: 4 additions & 4 deletions tests/codegen/fail/subscription/argument_non_input_type.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use std::pin::Pin;
use std::{future, pin::Pin};

use futures::{future, stream};
use futures::{stream, Stream};
use juniper::{graphql_subscription, GraphQLObject};

type Stream<'a, I> = Pin<Box<dyn futures::Stream<Item = I> + Send + 'a>>;
type BoxStream<'a, I> = Pin<Box<dyn Stream<Item = I> + Send + 'a>>;

#[derive(GraphQLObject)]
struct ObjA {
Expand All @@ -14,7 +14,7 @@ struct ObjB;

#[graphql_subscription]
impl ObjB {
async fn id(&self, obj: ObjA) -> Stream<'static, &'static str> {
async fn id(&self, obj: ObjA) -> BoxStream<'static, &'static str> {
Box::pin(stream::once(future::ready("funA")))
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
warning: unused variable: `obj`
--> fail/subscription/argument_non_input_type.rs:17:24
|
17 | async fn id(&self, obj: ObjA) -> Stream<'static, &'static str> {
17 | async fn id(&self, obj: ObjA) -> BoxStream<'static, &'static str> {
| ^^^ help: if this is intentional, prefix it with an underscore: `_obj`
|
= note: `#[warn(unused_variables)]` on by default

error[E0277]: the trait bound `ObjA: IsInputType<__S>` is not satisfied
--> fail/subscription/argument_non_input_type.rs:17:29
|
17 | async fn id(&self, obj: ObjA) -> Stream<'static, &'static str> {
17 | async fn id(&self, obj: ObjA) -> BoxStream<'static, &'static str> {
| ^^^^ the trait `IsInputType<__S>` is not implemented for `ObjA`
|
= help: the following other types implement trait `IsInputType<S>`:
Expand All @@ -29,7 +29,7 @@ error[E0277]: the trait bound `ObjA: FromInputValue<__S>` is not satisfied
15 | #[graphql_subscription]
| ----------------------- required by a bound introduced by this call
16 | impl ObjB {
17 | async fn id(&self, obj: ObjA) -> Stream<'static, &'static str> {
17 | async fn id(&self, obj: ObjA) -> BoxStream<'static, &'static str> {
| ^^^^ the trait `FromInputValue<__S>` is not implemented for `ObjA`
|
= help: the following other types implement trait `FromInputValue<S>`:
Expand Down Expand Up @@ -72,7 +72,7 @@ error[E0277]: the trait bound `ObjA: FromInputValue<__S>` is not satisfied
error[E0277]: the trait bound `ObjA: FromInputValue<__S>` is not satisfied
--> fail/subscription/argument_non_input_type.rs:17:29
|
17 | async fn id(&self, obj: ObjA) -> Stream<'static, &'static str> {
17 | async fn id(&self, obj: ObjA) -> BoxStream<'static, &'static str> {
| ^^^^ the trait `FromInputValue<__S>` is not implemented for `ObjA`
|
= help: the following other types implement trait `FromInputValue<S>`:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use std::pin::Pin;
use std::{future, pin::Pin};

use futures::{future, stream};
use futures::{stream, Stream};
use juniper::graphql_subscription;

type Stream<'a, I> = Pin<Box<dyn futures::Stream<Item = I> + Send + 'a>>;
type BoxStream<'a, I> = Pin<Box<dyn Stream<Item = I> + Send + 'a>>;

struct ObjA;

Expand All @@ -12,7 +12,7 @@ impl ObjA {
async fn wrong(
&self,
#[graphql(default = [true, false, false])] input: [bool; 2],
) -> Stream<'static, bool> {
) -> BoxStream<'static, bool> {
Box::pin(stream::once(future::ready(input[0])))
}
}
Expand Down
9 changes: 5 additions & 4 deletions tests/codegen/fail/subscription/field_double_underscored.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
use std::pin::Pin;
use std::{future, pin::Pin};

use futures::{stream, Stream};
use juniper::graphql_subscription;

type Stream<'a, I> = Pin<Box<dyn futures::Stream<Item = I> + Send + 'a>>;
type BoxStream<'a, I> = Pin<Box<dyn Stream<Item = I> + Send + 'a>>;

struct ObjA;

#[graphql_subscription]
impl Character for ObjA {
async fn __id() -> Stream<'static, &'static str> {
impl ObjA {
async fn __id() -> BoxStream<'static, &'static str> {
Box::pin(stream::once(future::ready("funA")))
}
}
Expand Down
13 changes: 6 additions & 7 deletions tests/codegen/fail/subscription/field_double_underscored.stderr
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
error: #[graphql_subscription] attribute is applicable to non-trait `impl` blocks only
--> fail/subscription/field_double_underscored.rs:9:1
|
9 | #[graphql_subscription]
| ^^^^^^^^^^^^^^^^^^^^^^^
|
= note: this error originates in the attribute macro `graphql_subscription` (in Nightly builds, run with -Z macro-backtrace for more info)
error: All types and directives defined within a schema must not have a name which begins with `__` (two underscores), as this is used exclusively by GraphQL’s introspection system.
· note: https://spec.graphql.org/October2021#sec-Schema
--> fail/subscription/field_double_underscored.rs:12:14
|
12 | async fn __id() -> BoxStream<'static, &'static str> {
| ^^^^
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use std::pin::Pin;
use std::{future, pin::Pin};

use futures::{future, stream};
use futures::{stream, Stream};
use juniper::{graphql_subscription, GraphQLInputObject};

type Stream<'a, I> = Pin<Box<dyn futures::Stream<Item = I> + Send + 'a>>;
type BoxStream<'a, I> = Pin<Box<dyn Stream<Item = I> + Send + 'a>>;

#[derive(GraphQLInputObject)]
struct ObjB {
Expand All @@ -14,7 +14,7 @@ struct ObjA;

#[graphql_subscription]
impl ObjA {
async fn id(&self) -> Stream<'static, ObjB> {
async fn id(&self) -> BoxStream<'static, ObjB> {
Box::pin(stream::once(future::ready(ObjB { id: 34 })))
}
}
Expand Down
Loading

0 comments on commit f363b0d

Please sign in to comment.