From f363b0d79ef04c6e2db6c77827174876ca8a38ea Mon Sep 17 00:00:00 2001
From: Kai Ren <tyranron@gmail.com>
Date: Tue, 30 Jan 2024 16:41:11 +0100
Subject: [PATCH] Preserve input body when attribute macro expansion fails
 (#1245, #1244)

---
 juniper_codegen/CHANGELOG.md                  |  3 ++-
 juniper_codegen/src/common/diagnostic.rs      | 20 +++++++++++++++--
 juniper_codegen/src/lib.rs                    | 10 ++++-----
 .../struct/attr_fields_duplicate.stderr       |  6 +++++
 .../interface/trait/fields_duplicate.stderr   |  6 +++++
 .../fail/interface/trait/wrong_syntax.rs      | 13 +++++++++++
 .../fail/interface/trait/wrong_syntax.stderr  | 22 +++++++++++++++++++
 .../object/attr_field_double_underscored.rs   |  2 +-
 .../attr_field_double_underscored.stderr      | 11 +++++-----
 .../fail/object/attr_fields_duplicate.stderr  |  6 +++++
 .../codegen/fail/object/attr_wrong_syntax.rs  | 14 ++++++++++++
 .../fail/object/attr_wrong_syntax.stderr      | 15 +++++++++++++
 .../argument_double_underscored.rs            |  7 +++---
 .../argument_double_underscored.stderr        |  4 ++--
 .../subscription/argument_non_input_type.rs   |  8 +++----
 .../argument_non_input_type.stderr            |  8 +++----
 .../argument_wrong_default_array.rs           |  8 +++----
 .../subscription/field_double_underscored.rs  |  9 ++++----
 .../field_double_underscored.stderr           | 13 +++++------
 .../field_non_output_return_type.rs           |  8 +++----
 .../fail/subscription/field_not_async.rs      |  7 +++---
 .../fail/subscription/field_not_async.stderr  |  4 ++--
 .../fail/subscription/fields_duplicate.rs     |  9 ++++----
 .../fail/subscription/fields_duplicate.stderr | 10 +++++++--
 .../subscription/name_double_underscored.rs   |  7 +++---
 .../name_double_underscored.stderr            |  4 ++--
 .../codegen/fail/subscription/wrong_syntax.rs | 19 ++++++++++++++++
 .../fail/subscription/wrong_syntax.stderr     | 15 +++++++++++++
 .../union/trait_with_attr_on_method.stderr    |  6 +++++
 29 files changed, 211 insertions(+), 63 deletions(-)
 create mode 100644 tests/codegen/fail/interface/trait/wrong_syntax.rs
 create mode 100644 tests/codegen/fail/interface/trait/wrong_syntax.stderr
 create mode 100644 tests/codegen/fail/object/attr_wrong_syntax.rs
 create mode 100644 tests/codegen/fail/object/attr_wrong_syntax.stderr
 create mode 100644 tests/codegen/fail/subscription/wrong_syntax.rs
 create mode 100644 tests/codegen/fail/subscription/wrong_syntax.stderr

diff --git a/juniper_codegen/CHANGELOG.md b/juniper_codegen/CHANGELOG.md
index aad31ee3..7c53fd57 100644
--- a/juniper_codegen/CHANGELOG.md
+++ b/juniper_codegen/CHANGELOG.md
@@ -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).
@@ -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
 
 
 
diff --git a/juniper_codegen/src/common/diagnostic.rs b/juniper_codegen/src/common/diagnostic.rs
index 87556bf6..15a8d0ef 100644
--- a/juniper_codegen/src/common/diagnostic.rs
+++ b/juniper_codegen/src/common/diagnostic.rs
@@ -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";
@@ -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,
     {
@@ -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 {
diff --git a/juniper_codegen/src/lib.rs b/juniper_codegen/src/lib.rs
index e6c81b60..d69cd714 100644
--- a/juniper_codegen/src/lib.rs
+++ b/juniper_codegen/src/lib.rs
@@ -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()
@@ -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()
@@ -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()
@@ -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()
@@ -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()
diff --git a/tests/codegen/fail/interface/struct/attr_fields_duplicate.stderr b/tests/codegen/fail/interface/struct/attr_fields_duplicate.stderr
index cad33766..d998a582 100644
--- a/tests/codegen/fail/interface/struct/attr_fields_duplicate.stderr
+++ b/tests/codegen/fail/interface/struct/attr_fields_duplicate.stderr
@@ -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")]
+  |       ^^^^^^^
diff --git a/tests/codegen/fail/interface/trait/fields_duplicate.stderr b/tests/codegen/fail/interface/trait/fields_duplicate.stderr
index 19bcfb51..5eb97986 100644
--- a/tests/codegen/fail/interface/trait/fields_duplicate.stderr
+++ b/tests/codegen/fail/interface/trait/fields_duplicate.stderr
@@ -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")]
+  |       ^^^^^^^
diff --git a/tests/codegen/fail/interface/trait/wrong_syntax.rs b/tests/codegen/fail/interface/trait/wrong_syntax.rs
new file mode 100644
index 00000000..bd76f94d
--- /dev/null
+++ b/tests/codegen/fail/interface/trait/wrong_syntax.rs
@@ -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() {}
diff --git a/tests/codegen/fail/interface/trait/wrong_syntax.stderr b/tests/codegen/fail/interface/trait/wrong_syntax.stderr
new file mode 100644
index 00000000..17c04567
--- /dev/null
+++ b/tests/codegen/fail/interface/trait/wrong_syntax.stderr
@@ -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()
+  |              ^^^^
diff --git a/tests/codegen/fail/object/attr_field_double_underscored.rs b/tests/codegen/fail/object/attr_field_double_underscored.rs
index ff854d0a..b00e7488 100644
--- a/tests/codegen/fail/object/attr_field_double_underscored.rs
+++ b/tests/codegen/fail/object/attr_field_double_underscored.rs
@@ -3,7 +3,7 @@ use juniper::graphql_object;
 struct ObjA;
 
 #[graphql_object]
-impl Character for ObjA {
+impl ObjA {
     fn __id(&self) -> &str {
         "funA"
     }
diff --git a/tests/codegen/fail/object/attr_field_double_underscored.stderr b/tests/codegen/fail/object/attr_field_double_underscored.stderr
index 48c6275a..db2d9dc1 100644
--- a/tests/codegen/fail/object/attr_field_double_underscored.stderr
+++ b/tests/codegen/fail/object/attr_field_double_underscored.stderr
@@ -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 {
+  |        ^^^^
diff --git a/tests/codegen/fail/object/attr_fields_duplicate.stderr b/tests/codegen/fail/object/attr_fields_duplicate.stderr
index 49716590..d34fbecc 100644
--- a/tests/codegen/fail/object/attr_fields_duplicate.stderr
+++ b/tests/codegen/fail/object/attr_fields_duplicate.stderr
@@ -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")]
+   |       ^^^^^^^
diff --git a/tests/codegen/fail/object/attr_wrong_syntax.rs b/tests/codegen/fail/object/attr_wrong_syntax.rs
new file mode 100644
index 00000000..695feef5
--- /dev/null
+++ b/tests/codegen/fail/object/attr_wrong_syntax.rs
@@ -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() {}
diff --git a/tests/codegen/fail/object/attr_wrong_syntax.stderr b/tests/codegen/fail/object/attr_wrong_syntax.stderr
new file mode 100644
index 00000000..b7002d3e
--- /dev/null
+++ b/tests/codegen/fail/object/attr_wrong_syntax.stderr
@@ -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`
diff --git a/tests/codegen/fail/subscription/argument_double_underscored.rs b/tests/codegen/fail/subscription/argument_double_underscored.rs
index fe01fc69..aaaec96d 100644
--- a/tests/codegen/fail/subscription/argument_double_underscored.rs
+++ b/tests/codegen/fail/subscription/argument_double_underscored.rs
@@ -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")))
     }
 }
diff --git a/tests/codegen/fail/subscription/argument_double_underscored.stderr b/tests/codegen/fail/subscription/argument_double_underscored.stderr
index a79c53eb..8866daaa 100644
--- a/tests/codegen/fail/subscription/argument_double_underscored.stderr
+++ b/tests/codegen/fail/subscription/argument_double_underscored.stderr
@@ -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> {
    |                        ^^^^^
diff --git a/tests/codegen/fail/subscription/argument_non_input_type.rs b/tests/codegen/fail/subscription/argument_non_input_type.rs
index 90b24bb2..0bcd0560 100644
--- a/tests/codegen/fail/subscription/argument_non_input_type.rs
+++ b/tests/codegen/fail/subscription/argument_non_input_type.rs
@@ -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 {
@@ -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")))
     }
 }
diff --git a/tests/codegen/fail/subscription/argument_non_input_type.stderr b/tests/codegen/fail/subscription/argument_non_input_type.stderr
index baded3fd..3022c6b4 100644
--- a/tests/codegen/fail/subscription/argument_non_input_type.stderr
+++ b/tests/codegen/fail/subscription/argument_non_input_type.stderr
@@ -1,7 +1,7 @@
 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
@@ -9,7 +9,7 @@ warning: unused variable: `obj`
 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>`:
@@ -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>`:
@@ -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>`:
diff --git a/tests/codegen/fail/subscription/argument_wrong_default_array.rs b/tests/codegen/fail/subscription/argument_wrong_default_array.rs
index c0ec6863..4db4caea8 100644
--- a/tests/codegen/fail/subscription/argument_wrong_default_array.rs
+++ b/tests/codegen/fail/subscription/argument_wrong_default_array.rs
@@ -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;
 
@@ -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])))
     }
 }
diff --git a/tests/codegen/fail/subscription/field_double_underscored.rs b/tests/codegen/fail/subscription/field_double_underscored.rs
index 686dccc2..cbc40783 100644
--- a/tests/codegen/fail/subscription/field_double_underscored.rs
+++ b/tests/codegen/fail/subscription/field_double_underscored.rs
@@ -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")))
     }
 }
diff --git a/tests/codegen/fail/subscription/field_double_underscored.stderr b/tests/codegen/fail/subscription/field_double_underscored.stderr
index c22772ab..271f188a 100644
--- a/tests/codegen/fail/subscription/field_double_underscored.stderr
+++ b/tests/codegen/fail/subscription/field_double_underscored.stderr
@@ -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> {
+   |              ^^^^
diff --git a/tests/codegen/fail/subscription/field_non_output_return_type.rs b/tests/codegen/fail/subscription/field_non_output_return_type.rs
index 8cc91854..071ea61b 100644
--- a/tests/codegen/fail/subscription/field_non_output_return_type.rs
+++ b/tests/codegen/fail/subscription/field_non_output_return_type.rs
@@ -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 {
@@ -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 })))
     }
 }
diff --git a/tests/codegen/fail/subscription/field_not_async.rs b/tests/codegen/fail/subscription/field_not_async.rs
index f9cfb5b5..9e1312a1 100644
--- a/tests/codegen/fail/subscription/field_not_async.rs
+++ b/tests/codegen/fail/subscription/field_not_async.rs
@@ -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 ObjA {
-    fn id(&self) -> Stream<'static, bool> {
+    fn id(&self) -> BoxStream<'static, bool> {
         Box::pin(stream::once(future::ready(true)))
     }
 }
diff --git a/tests/codegen/fail/subscription/field_not_async.stderr b/tests/codegen/fail/subscription/field_not_async.stderr
index d1c63681..45cb105f 100644
--- a/tests/codegen/fail/subscription/field_not_async.stderr
+++ b/tests/codegen/fail/subscription/field_not_async.stderr
@@ -1,7 +1,7 @@
 error: GraphQL object synchronous resolvers are not supported
        · note: https://spec.graphql.org/October2021#sec-Objects
        · note: Specify that this function is async: `async fn foo()`
-  --> fail/subscription/field_not_async.rs:11:5
+  --> fail/subscription/field_not_async.rs:12:5
    |
-11 |     fn id(&self) -> Stream<'static, bool> {
+12 |     fn id(&self) -> BoxStream<'static, bool> {
    |     ^^
diff --git a/tests/codegen/fail/subscription/fields_duplicate.rs b/tests/codegen/fail/subscription/fields_duplicate.rs
index 22ccc977..ad9c01de 100644
--- a/tests/codegen/fail/subscription/fields_duplicate.rs
+++ b/tests/codegen/fail/subscription/fields_duplicate.rs
@@ -1,19 +1,20 @@
-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 ObjA {
-    async fn id(&self) -> Stream<'static, &'static str> {
+    async fn id(&self) -> BoxStream<'static, &'static str> {
         Box::pin(stream::once(future::ready("funA")))
     }
 
     #[graphql(name = "id")]
-    async fn id2(&self) -> Stream<'static, &'static str> {
+    async fn id2(&self) -> BoxStream<'static, &'static str> {
         Box::pin(stream::once(future::ready("funB")))
     }
 }
diff --git a/tests/codegen/fail/subscription/fields_duplicate.stderr b/tests/codegen/fail/subscription/fields_duplicate.stderr
index c3c43abd..f40d76fa 100644
--- a/tests/codegen/fail/subscription/fields_duplicate.stderr
+++ b/tests/codegen/fail/subscription/fields_duplicate.stderr
@@ -1,6 +1,12 @@
 error: GraphQL object must have a different name for each field
        · note: https://spec.graphql.org/October2021#sec-Objects
-  --> fail/subscription/fields_duplicate.rs:10:6
+  --> fail/subscription/fields_duplicate.rs:11:6
    |
-10 | impl ObjA {
+11 | impl ObjA {
    |      ^^^^
+
+error: cannot find attribute `graphql` in this scope
+  --> fail/subscription/fields_duplicate.rs:16:7
+   |
+16 |     #[graphql(name = "id")]
+   |       ^^^^^^^
diff --git a/tests/codegen/fail/subscription/name_double_underscored.rs b/tests/codegen/fail/subscription/name_double_underscored.rs
index d9911148..a26874fb 100644
--- a/tests/codegen/fail/subscription/name_double_underscored.rs
+++ b/tests/codegen/fail/subscription/name_double_underscored.rs
@@ -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 {
-    fn id(&self) -> Stream<'static, &'static str> {
+    fn id(&self) -> BoxStream<'static, &'static str> {
         Box::pin(stream::once(future::ready("funA")))
     }
 }
diff --git a/tests/codegen/fail/subscription/name_double_underscored.stderr b/tests/codegen/fail/subscription/name_double_underscored.stderr
index 607414ec..d7a0ba65 100644
--- a/tests/codegen/fail/subscription/name_double_underscored.stderr
+++ b/tests/codegen/fail/subscription/name_double_underscored.stderr
@@ -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/name_double_underscored.rs:10:6
+  --> fail/subscription/name_double_underscored.rs:11:6
    |
-10 | impl __Obj {
+11 | impl __Obj {
    |      ^^^^^
diff --git a/tests/codegen/fail/subscription/wrong_syntax.rs b/tests/codegen/fail/subscription/wrong_syntax.rs
new file mode 100644
index 00000000..f99d2697
--- /dev/null
+++ b/tests/codegen/fail/subscription/wrong_syntax.rs
@@ -0,0 +1,19 @@
+use std::{future, pin::Pin};
+
+use futures::{stream, Stream};
+use juniper::graphql_subscription;
+
+type BoxStream<'a, I> = Pin<Box<dyn Stream<Item = I> + Send + 'a>>;
+
+struct ObjA {
+    field: bool
+}
+
+#[graphql_subscription]
+impl ObjA {
+    fn id(&self) -> BoxStream<'static, bool> {
+        Box::pin(stream::once(future::ready(self.self.field)))
+    }
+}
+
+fn main() {}
diff --git a/tests/codegen/fail/subscription/wrong_syntax.stderr b/tests/codegen/fail/subscription/wrong_syntax.stderr
new file mode 100644
index 00000000..7994ddc9
--- /dev/null
+++ b/tests/codegen/fail/subscription/wrong_syntax.stderr
@@ -0,0 +1,15 @@
+error: #[graphql_subscription] attribute is applicable to non-trait `impl` blocks only
+  --> fail/subscription/wrong_syntax.rs:12:1
+   |
+12 | #[graphql_subscription]
+   | ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: this error originates in the attribute macro `graphql_subscription` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error[E0609]: no field `self` on type `&ObjA`
+  --> fail/subscription/wrong_syntax.rs:15:50
+   |
+15 |         Box::pin(stream::once(future::ready(self.self.field)))
+   |                                                  ^^^^ unknown field
+   |
+   = note: available fields are: `field`
diff --git a/tests/codegen/fail/union/trait_with_attr_on_method.stderr b/tests/codegen/fail/union/trait_with_attr_on_method.stderr
index 9341e9bb..b207dced 100644
--- a/tests/codegen/fail/union/trait_with_attr_on_method.stderr
+++ b/tests/codegen/fail/union/trait_with_attr_on_method.stderr
@@ -5,3 +5,9 @@ error: GraphQL union cannot use #[graphql(with = ...)] attribute on a trait meth
   |
 5 |     #[graphql(with = something)]
   |               ^^^^
+
+error: cannot find attribute `graphql` in this scope
+ --> fail/union/trait_with_attr_on_method.rs:5:7
+  |
+5 |     #[graphql(with = something)]
+  |       ^^^^^^^