decycle_impl/
process_module.rs1use proc_macro2::Span;
2use proc_macro2::TokenStream;
3use proc_macro_error::*;
4use std::collections::HashSet;
5use syn::*;
6use template_quote::quote;
7
8fn check_submodule(module: &ItemMod) {
9 use syn::visit::Visit;
10 struct Visitor;
11 impl<'ast> Visit<'ast> for Visitor {
12 fn visit_attribute(&mut self, i: &'ast syn::Attribute) {
13 if crate::is_decycle_attribute(i) {
14 abort!(&i, "#[decycle] is not supported in nested modules")
15 }
16 syn::visit::visit_attribute(self, i);
17 }
18 }
19 Visitor.visit_item_mod(module);
20}
21
22fn process_trait_path(item: &Item) -> Vec<Path> {
23 match item {
24 Item::Trait(ItemTrait { ident, .. }) | Item::TraitAlias(ItemTraitAlias { ident, .. }) => {
25 vec![crate::ident_to_path(ident)]
26 }
27 Item::Use(ItemUse { tree, .. }) => {
28 fn process_use_tree(tree: &UseTree) -> Vec<Path> {
29 match tree {
30 UseTree::Path(UsePath { tree, .. }) => process_use_tree(tree),
31 UseTree::Name(UseName { ident })
32 | UseTree::Rename(UseRename { rename: ident, .. }) => {
33 vec![crate::ident_to_path(ident)]
34 }
35 UseTree::Glob(use_glob) => {
36 abort!(use_glob, "glob is not supported in #[decycle] use")
37 }
38 UseTree::Group(UseGroup { items, .. }) => {
39 items.iter().flat_map(process_use_tree).collect()
40 }
41 }
42 }
43 process_use_tree(tree)
44 }
45 _ => unreachable!(),
46 }
47}
48
49pub fn process_module(
50 mut module: ItemMod,
51 decycle: &Path,
52 recurse_level: usize,
53 support_infinite_cycle: bool,
54) -> TokenStream {
55 let contents = &mut module
56 .content
57 .as_mut()
58 .unwrap_or_else(|| abort!(&module.semi, "needs content"))
59 .1;
60 let (traits, working_list): (Vec<_>, Vec<_>) = contents.iter_mut().fold(
61 Default::default(),
62 |(mut traits, mut working_list), item| {
63 match item {
64 Item::Trait(ItemTrait { attrs, .. })
65 | Item::TraitAlias(ItemTraitAlias { attrs, .. })
66 | Item::Use(ItemUse { attrs, .. }) => {
67 let mut old_attrs = std::mem::take(attrs).into_iter();
69 let mut flag = false;
70 attrs.extend((&mut old_attrs).take_while(|attr| {
71 if crate::is_decycle_attribute(attr) {
72 flag = true;
73 }
74 !flag
75 }));
76 attrs.extend(old_attrs);
77 if flag {
78 if let Item::Trait(item_trait) = item {
79 traits.push(item_trait.clone());
80 } else {
81 working_list.extend(process_trait_path(item));
82 }
83 }
84 }
85 _ => (),
86 }
87 (traits, working_list)
88 },
89 );
90 proc_macro_error::set_dummy(
91 quote! { #{&module.ident} {#(for content in contents.clone()) { #content }} },
92 );
93 for item in contents.iter() {
94 match item {
95 Item::Mod(item_mod) => {
96 check_submodule(item_mod);
97 }
98 Item::Macro(_) => abort!(&item, "macro is not supported in #[decycle] module"),
99 _ => (),
100 }
101 }
102 if traits.is_empty() && working_list.is_empty() {
103 abort!(
104 Span::call_site(),
105 "cannot detect traits nor `use` statement annotated witn #[decycle]"
106 )
107 }
108 let all_traits: HashSet<Ident> = working_list
109 .iter()
110 .filter_map(|path| path.segments.last().map(|seg| seg.ident.clone()))
111 .chain(traits.iter().map(|ItemTrait { ident, .. }| ident.clone()))
112 .collect();
113 let (raw_contents, contents): (Vec<_>, Vec<_>) = contents
114 .iter()
115 .map(|content| {
116 if let Item::Impl(
117 item_impl @ ItemImpl {
118 trait_: Some((_, trait_path, _)),
119 ..
120 },
121 ) = content
122 {
123 if trait_path.segments.len() == 1 {
125 if let Some(seg) = trait_path.segments.first() {
126 if all_traits.contains(&seg.ident) {
127 return (None, Some(item_impl.clone()));
129 }
130 }
131 }
132 }
133 (Some(content), None)
134 })
135 .fold(
136 Default::default(),
137 |(mut raw_contents, mut contents), (raw_content, content)| {
138 raw_contents.extend(raw_content);
139 contents.extend(content);
140 (raw_contents, contents)
141 },
142 );
143 let first_path = working_list.first().cloned();
144 let mut args = crate::finalize::FinalizeArgs {
145 working_list,
146 traits,
147 contents,
148 recurse_level,
149 support_infinite_cycle,
150 };
151 args.working_list.push(parse_quote!(#decycle::__finalize));
152 quote! {
153 #(for attr in &module.attrs) { #attr }
154 #{&module.vis} #{&module.unsafety} #{&module.mod_token} #{&module.ident} {
155
156 #(for raw_content in raw_contents) { #raw_content }
157
158 #(if let Some(first_path) = first_path) {
159 #first_path! { #args }
160 }
161 #(else) {
162 #{ crate::finalize::finalize(args) }
163 }
164 }
165 }
166}