sc_lint_attributes/
lib.rs1use proc_macro::TokenStream;
2use proc_macro2::TokenStream as TokenStream2;
3use sc_lint_directives::AttributeInput;
4use syn::Result;
5
6#[cfg(test)]
7use sc_lint_directives::Directive;
8
9fn expand_sc_lint(args: TokenStream2, item: TokenStream2) -> Result<TokenStream2> {
10 let _parsed = syn::parse2::<AttributeInput>(args)?;
11 Ok(item)
12}
13
14#[proc_macro_attribute]
15pub fn sc_lint(args: TokenStream, item: TokenStream) -> TokenStream {
16 let item_ts: TokenStream2 = item.clone().into();
17 let args_ts: TokenStream2 = args.into();
18 match expand_sc_lint(args_ts, item_ts) {
19 Ok(expanded) => TokenStream::from(expanded),
20 Err(error) => TokenStream::from(error.to_compile_error()),
21 }
22}
23
24#[cfg(test)]
25mod tests {
26 use super::AttributeInput;
27 use super::Directive;
28 use super::expand_sc_lint;
29 use quote::quote;
30
31 #[test]
32 fn parses_boundary_allow_rule() {
33 let parsed: AttributeInput =
34 syn::parse2(quote!(boundary.allow("cycle.type_method_self_loop"))).unwrap();
35 assert_eq!(
36 parsed.directives,
37 vec![Directive::Allow(vec![
38 "cycle.type_method_self_loop".to_string()
39 ])]
40 );
41 }
42
43 #[test]
44 fn parses_boundary_internal_only() {
45 let parsed: AttributeInput = syn::parse2(quote!(boundary.internal_only)).unwrap();
46 assert_eq!(parsed.directives, vec![Directive::InternalOnly]);
47 }
48
49 #[test]
50 fn parses_boundary_forbid_external_impls() {
51 let parsed: AttributeInput = syn::parse2(quote!(boundary.forbid_external_impls)).unwrap();
52 assert_eq!(parsed.directives, vec![Directive::ForbidExternalImpls]);
53 }
54
55 #[test]
56 fn parses_multiple_directives() {
57 let parsed: AttributeInput = syn::parse2(quote!(
58 boundary.internal_only,
59 boundary.forbid_external_impls,
60 boundary.allow("cycle.type_method_self_loop")
61 ))
62 .unwrap();
63 assert_eq!(
64 parsed.directives,
65 vec![
66 Directive::InternalOnly,
67 Directive::ForbidExternalImpls,
68 Directive::Allow(vec!["cycle.type_method_self_loop".to_string()]),
69 ]
70 );
71 }
72
73 #[test]
74 fn rejects_unknown_boundary_directive() {
75 let error = syn::parse2::<AttributeInput>(quote!(boundary.unknown)).unwrap_err();
76 assert!(error.to_string().contains("unsupported boundary directive"));
77 }
78
79 #[test]
80 fn expansion_is_noop_for_supported_directives() {
81 let expanded = expand_sc_lint(
82 quote!(
83 boundary.internal_only,
84 boundary.forbid_external_impls,
85 boundary.allow("cycle.type_method_self_loop")
86 ),
87 quote!(
88 pub struct Example;
89 ),
90 )
91 .unwrap();
92 assert_eq!(
93 expanded.to_string(),
94 quote!(
95 pub struct Example;
96 )
97 .to_string()
98 );
99 }
100}