1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
use crate::predicate::analyze_generics;
use crate::visitor::ParamVisitor;
use crate::{param::Param, Derive};
use alloc::{collections::BTreeSet, vec::Vec};
use syn::visit::Visit;
use syn::Attribute;
use syn::{punctuated::Punctuated, Generics, Ident, Token, Variant, WherePredicate};
pub struct Enum {
pub ident: Ident,
pub variants: Punctuated<Variant, Token![,]>,
pub variants_attributes: Vec<Vec<Attribute>>,
pub attributes: Vec<Attribute>,
pub derives: Vec<Derive>,
pub generics: Generics,
}
impl Enum {
pub fn new(ident: Ident, attributes: Vec<Attribute>, derives: Vec<Derive>) -> Self {
Enum {
ident,
variants: Punctuated::new(),
variants_attributes: Vec::new(),
attributes,
derives,
generics: Generics {
lt_token: Some(syn::token::Lt::default()),
params: Punctuated::new(),
gt_token: Some(syn::token::Gt::default()),
where_clause: None,
},
}
}
pub fn compute_generics(&mut self, parent_generics: &Generics) {
// 1. Analyze constraints: Convert all inline bounds and where clauses
// into a list of PredicateDependency
let mut deps = analyze_generics(parent_generics);
// 2. Identify "Root" params: The generics explicitly used in the variants.
let mut visitor = ParamVisitor::new(parent_generics);
for variant in &self.variants {
visitor.visit_variant(variant);
}
let mut active_params: BTreeSet<Param> = visitor.found;
let mut active_predicates: Vec<WherePredicate> = Vec::new();
// 3. Repeatedly iterate through dependencies. If a predicate mentions
// ANY active param, we must keep that predicate AND activate
// any other params it mentions.
let mut changed = true;
while changed {
changed = false;
// We retain only the predicates we haven't matched yet.
deps.retain(|dep| {
// Check if this dependency touches any currently active param
let is_relevant = dep.used_params.iter().any(|p| active_params.contains(p));
if is_relevant {
// It is relevant: Keep the predicate
active_predicates.push(dep.predicate.clone());
// Activate all params used by this predicate
for p in &dep.used_params {
if active_params.insert(p.clone()) {
// If we added a NEW param, we must loop again
// to check for bounds dependent on this new param.
changed = true;
}
}
// Remove from `deps` so we don't process it again
return false;
}
true // Keep in `deps` for next pass
});
}
// 4. Construct the final Generics struct in-place
self.generics = Generics::default();
// A. Filter params and strip inline bounds
for param in &parent_generics.params {
let keep = match param {
syn::GenericParam::Type(t) => {
active_params.contains(&Param::Ident(t.ident.clone()))
}
syn::GenericParam::Lifetime(l) => {
active_params.contains(&Param::Lifetime(l.lifetime.clone()))
}
syn::GenericParam::Const(c) => {
active_params.contains(&Param::Ident(c.ident.clone()))
}
};
if keep {
let mut p = param.clone();
// CRITICAL: We clear inline bounds here because `analyze_generics`
// has already converted them into predicates. If we don't clear them,
// we will have duplicates (once in <> and once in where clause).
match &mut p {
syn::GenericParam::Type(t) => t.bounds.clear(),
syn::GenericParam::Lifetime(l) => l.bounds.clear(),
_ => {}
}
self.generics.params.push(p);
}
}
// B. Append the collected predicates to the where clause
if !active_predicates.is_empty() {
let where_clause = self.generics.make_where_clause();
where_clause.predicates.extend(active_predicates);
}
}
}