Skip to content

Commit f363b0d

Browse files
authored
Preserve input body when attribute macro expansion fails (#1245, #1244)
1 parent 8a69e14 commit f363b0d

29 files changed

+211
-63
lines changed

juniper_codegen/CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ All user visible changes to `juniper_codegen` crate will be documented in this f
1010

1111
### BC Breaks
1212

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

6566

6667

juniper_codegen/src/common/diagnostic.rs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ use std::fmt;
22

33
use proc_macro2::Span;
44

5-
pub(crate) use self::polyfill::{abort_if_dirty, emit_error, entry_point, Diagnostic, ResultExt};
5+
pub(crate) use self::polyfill::{
6+
abort_if_dirty, emit_error, entry_point, entry_point_with_preserved_body, Diagnostic, ResultExt,
7+
};
68

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

259261
/// This is the entry point for a macro to support [`Diagnostic`]s.
260262
pub(crate) fn entry_point<F>(f: F) -> proc_macro::TokenStream
263+
where
264+
F: FnOnce() -> proc_macro::TokenStream + UnwindSafe,
265+
{
266+
entry_point_with_preserved_body(TokenStream::new(), f)
267+
}
268+
269+
/// This is the entry point for an attribute macro to support [`Diagnostic`]s, while preserving
270+
/// the `body` input [`proc_macro::TokenStream`] on errors.
271+
pub(crate) fn entry_point_with_preserved_body<F>(
272+
body: impl Into<TokenStream>,
273+
f: F,
274+
) -> proc_macro::TokenStream
261275
where
262276
F: FnOnce() -> proc_macro::TokenStream + UnwindSafe,
263277
{
@@ -267,7 +281,9 @@ mod polyfill {
267281
ENTERED_ENTRY_POINT.with(|flag| flag.set(flag.get() - 1));
268282

269283
let gen_error = || {
270-
quote! { #( #err_storage )* }
284+
let body = body.into();
285+
286+
quote! { #body #( #err_storage )* }
271287
};
272288

273289
match caught {

juniper_codegen/src/lib.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -756,7 +756,7 @@ pub fn derive_scalar(input: TokenStream) -> TokenStream {
756756
/// [`ScalarValue`]: juniper::ScalarValue
757757
#[proc_macro_attribute]
758758
pub fn graphql_scalar(attr: TokenStream, body: TokenStream) -> TokenStream {
759-
diagnostic::entry_point(|| {
759+
diagnostic::entry_point_with_preserved_body(body.clone(), || {
760760
graphql_scalar::attr::expand(attr.into(), body.into())
761761
.unwrap_or_abort()
762762
.into()
@@ -1318,7 +1318,7 @@ pub fn derive_scalar_value(input: TokenStream) -> TokenStream {
13181318
/// [4]: https://doc.rust-lang.org/stable/std/primitive.unit.html
13191319
#[proc_macro_attribute]
13201320
pub fn graphql_interface(attr: TokenStream, body: TokenStream) -> TokenStream {
1321-
diagnostic::entry_point(|| {
1321+
diagnostic::entry_point_with_preserved_body(body.clone(), || {
13221322
self::graphql_interface::attr::expand(attr.into(), body.into())
13231323
.unwrap_or_abort()
13241324
.into()
@@ -1825,7 +1825,7 @@ pub fn derive_object(body: TokenStream) -> TokenStream {
18251825
/// [1]: https://spec.graphql.org/October2021#sec-Objects
18261826
#[proc_macro_attribute]
18271827
pub fn graphql_object(attr: TokenStream, body: TokenStream) -> TokenStream {
1828-
diagnostic::entry_point(|| {
1828+
diagnostic::entry_point_with_preserved_body(body.clone(), || {
18291829
self::graphql_object::attr::expand(attr.into(), body.into())
18301830
.unwrap_or_abort()
18311831
.into()
@@ -1879,7 +1879,7 @@ pub fn graphql_object(attr: TokenStream, body: TokenStream) -> TokenStream {
18791879
/// [1]: https://spec.graphql.org/October2021#sec-Subscription
18801880
#[proc_macro_attribute]
18811881
pub fn graphql_subscription(attr: TokenStream, body: TokenStream) -> TokenStream {
1882-
diagnostic::entry_point(|| {
1882+
diagnostic::entry_point_with_preserved_body(body.clone(), || {
18831883
self::graphql_subscription::attr::expand(attr.into(), body.into())
18841884
.unwrap_or_abort()
18851885
.into()
@@ -2486,7 +2486,7 @@ pub fn derive_union(body: TokenStream) -> TokenStream {
24862486
/// [4]: https://doc.rust-lang.org/stable/std/primitive.unit.html
24872487
#[proc_macro_attribute]
24882488
pub fn graphql_union(attr: TokenStream, body: TokenStream) -> TokenStream {
2489-
diagnostic::entry_point(|| {
2489+
diagnostic::entry_point_with_preserved_body(body.clone(), || {
24902490
self::graphql_union::attr::expand(attr.into(), body.into())
24912491
.unwrap_or_abort()
24922492
.into()

tests/codegen/fail/interface/struct/attr_fields_duplicate.stderr

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,9 @@ error: GraphQL interface must have a different name for each field
44
|
55
4 | struct Character {
66
| ^^^^^^
7+
8+
error: cannot find attribute `graphql` in this scope
9+
--> fail/interface/struct/attr_fields_duplicate.rs:7:7
10+
|
11+
7 | #[graphql(name = "id")]
12+
| ^^^^^^^

tests/codegen/fail/interface/trait/fields_duplicate.stderr

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,9 @@ error: GraphQL interface must have a different name for each field
44
|
55
4 | trait Character {
66
| ^^^^^
7+
8+
error: cannot find attribute `graphql` in this scope
9+
--> fail/interface/trait/fields_duplicate.rs:7:7
10+
|
11+
7 | #[graphql(name = "id")]
12+
| ^^^^^^^
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
use juniper::graphql_interface;
2+
3+
#[graphql_interface]
4+
trait Character {
5+
fn id(&self) -> &str;
6+
7+
#[graphql(ignore)]
8+
fn id2(&self) -> &str {
9+
self.self.id()
10+
}
11+
}
12+
13+
fn main() {}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
error: #[graphql_interface] attribute is applicable to trait and struct definitions only
2+
--> fail/interface/trait/wrong_syntax.rs:3:1
3+
|
4+
3 | #[graphql_interface]
5+
| ^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: this error originates in the attribute macro `graphql_interface` (in Nightly builds, run with -Z macro-backtrace for more info)
8+
9+
error: cannot find attribute `graphql` in this scope
10+
--> fail/interface/trait/wrong_syntax.rs:7:7
11+
|
12+
7 | #[graphql(ignore)]
13+
| ^^^^^^^
14+
15+
error[E0609]: no field `self` on type `&Self`
16+
--> fail/interface/trait/wrong_syntax.rs:9:14
17+
|
18+
4 | trait Character {
19+
| --------------- type parameter 'Self' declared here
20+
...
21+
9 | self.self.id()
22+
| ^^^^

tests/codegen/fail/object/attr_field_double_underscored.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use juniper::graphql_object;
33
struct ObjA;
44

55
#[graphql_object]
6-
impl Character for ObjA {
6+
impl ObjA {
77
fn __id(&self) -> &str {
88
"funA"
99
}
Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
error: #[graphql_object] attribute is applicable to non-trait `impl` blocks only
2-
--> fail/object/attr_field_double_underscored.rs:5:1
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.
2+
· note: https://spec.graphql.org/October2021#sec-Schema
3+
--> fail/object/attr_field_double_underscored.rs:7:8
34
|
4-
5 | #[graphql_object]
5-
| ^^^^^^^^^^^^^^^^^
6-
|
7-
= note: this error originates in the attribute macro `graphql_object` (in Nightly builds, run with -Z macro-backtrace for more info)
5+
7 | fn __id(&self) -> &str {
6+
| ^^^^

tests/codegen/fail/object/attr_fields_duplicate.stderr

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,9 @@ error: GraphQL object must have a different name for each field
44
|
55
6 | impl ObjA {
66
| ^^^^
7+
8+
error: cannot find attribute `graphql` in this scope
9+
--> fail/object/attr_fields_duplicate.rs:11:7
10+
|
11+
11 | #[graphql(name = "id")]
12+
| ^^^^^^^

0 commit comments

Comments
 (0)