@@ -7,6 +7,7 @@ use proc_macro2::TokenStream;
7
7
use quote:: { quote, ToTokens } ;
8
8
use syn:: {
9
9
parse:: { Parse , ParseStream } ,
10
+ punctuated:: Punctuated ,
10
11
spanned:: Spanned as _,
11
12
token,
12
13
} ;
@@ -20,7 +21,7 @@ use crate::common::{parse::ParseBufferExt as _, SpanContainer};
20
21
/// [0]: https://spec.graphql.org/October2021#sec--deprecated
21
22
/// [1]: https://spec.graphql.org/October2021#sec-Language.Fields
22
23
/// [2]: https://spec.graphql.org/October2021#sec-Enum-Value
23
- #[ derive( Debug , Default ) ]
24
+ #[ derive( Debug , Default , Eq , PartialEq ) ]
24
25
pub ( crate ) struct Directive {
25
26
/// Optional [reason][1] attached to this [deprecation][0].
26
27
///
@@ -52,16 +53,16 @@ impl Directive {
52
53
attrs : & [ syn:: Attribute ] ,
53
54
) -> syn:: Result < Option < SpanContainer < Self > > > {
54
55
for attr in attrs {
55
- return Ok ( match attr. parse_meta ( ) {
56
- Ok ( syn:: Meta :: List ( ref list) ) if list. path . is_ident ( "deprecated" ) => {
56
+ return Ok ( match & attr. meta {
57
+ syn:: Meta :: List ( list) if list. path . is_ident ( "deprecated" ) => {
57
58
let directive = Self :: parse_from_deprecated_meta_list ( list) ?;
58
59
Some ( SpanContainer :: new (
59
60
list. path . span ( ) ,
60
61
directive. reason . as_ref ( ) . map ( |r| r. span ( ) ) ,
61
62
directive,
62
63
) )
63
64
}
64
- Ok ( syn:: Meta :: Path ( ref path) ) if path. is_ident ( "deprecated" ) => {
65
+ syn:: Meta :: Path ( path) if path. is_ident ( "deprecated" ) => {
65
66
Some ( SpanContainer :: new ( path. span ( ) , None , Self :: default ( ) ) )
66
67
}
67
68
_ => continue ,
@@ -77,20 +78,24 @@ impl Directive {
77
78
///
78
79
/// If the `#[deprecated(note = ...)]` attribute has incorrect format.
79
80
fn parse_from_deprecated_meta_list ( list : & syn:: MetaList ) -> syn:: Result < Self > {
80
- for meta in & list. nested {
81
- if let syn:: NestedMeta :: Meta ( syn :: Meta :: NameValue ( nv) ) = meta {
81
+ for meta in list. parse_args_with ( Punctuated :: < syn :: Meta , token :: Comma > :: parse_terminated ) ? {
82
+ if let syn:: Meta :: NameValue ( nv) = meta {
82
83
return if !nv. path . is_ident ( "note" ) {
83
84
Err ( syn:: Error :: new (
84
85
nv. path . span ( ) ,
85
86
"unrecognized setting on #[deprecated(..)] attribute" ,
86
87
) )
87
- } else if let syn:: Lit :: Str ( strlit) = & nv. lit {
88
+ } else if let syn:: Expr :: Lit ( syn:: ExprLit {
89
+ lit : syn:: Lit :: Str ( strlit) ,
90
+ ..
91
+ } ) = & nv. value
92
+ {
88
93
Ok ( Self {
89
94
reason : Some ( strlit. clone ( ) ) ,
90
95
} )
91
96
} else {
92
97
Err ( syn:: Error :: new (
93
- nv. lit . span ( ) ,
98
+ nv. value . span ( ) ,
94
99
"only strings are allowed for deprecation" ,
95
100
) )
96
101
} ;
@@ -112,3 +117,43 @@ impl ToTokens for Directive {
112
117
. to_tokens ( into) ;
113
118
}
114
119
}
120
+
121
+ #[ cfg( test) ]
122
+ mod parse_from_deprecated_attr_test {
123
+ use quote:: quote;
124
+ use syn:: parse_quote;
125
+
126
+ use super :: Directive ;
127
+
128
+ #[ test]
129
+ fn single ( ) {
130
+ let desc =
131
+ Directive :: parse_from_deprecated_attr ( & [ parse_quote ! { #[ deprecated( note = "foo" ) ] } ] )
132
+ . unwrap ( )
133
+ . unwrap ( )
134
+ . into_inner ( ) ;
135
+ assert_eq ! (
136
+ quote! { #desc } . to_string( ) ,
137
+ quote! { . deprecated( :: core:: option:: Option :: Some ( "foo" ) ) } . to_string( ) ,
138
+ ) ;
139
+ }
140
+
141
+ #[ test]
142
+ fn no_reason ( ) {
143
+ let desc = Directive :: parse_from_deprecated_attr ( & [ parse_quote ! { #[ deprecated] } ] )
144
+ . unwrap ( )
145
+ . unwrap ( )
146
+ . into_inner ( ) ;
147
+ assert_eq ! (
148
+ quote! { #desc } . to_string( ) ,
149
+ quote! { . deprecated( :: core:: option:: Option :: None ) } . to_string( ) ,
150
+ ) ;
151
+ }
152
+
153
+ #[ test]
154
+ fn not_deprecation ( ) {
155
+ let desc =
156
+ Directive :: parse_from_deprecated_attr ( & [ parse_quote ! { #[ blah = "foo" ] } ] ) . unwrap ( ) ;
157
+ assert_eq ! ( desc, None ) ;
158
+ }
159
+ }
0 commit comments