1use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet};
2
3use ecow::EcoString;
4
5use crate::ast::{BindingId as AstBindingId, Pattern, RestPattern, Span};
6use crate::types::Type;
7
8use super::{Definition, File, ModuleInfo};
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
11struct ReceiverId(Span);
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum ReceiverCoercion {
15 AutoAddress,
17 AutoDeref,
19}
20
21#[derive(Debug, Clone, Default)]
22pub struct CoercionInfo {
23 receivers: HashMap<ReceiverId, ReceiverCoercion>,
24}
25
26impl CoercionInfo {
27 pub fn mark_coercion(&mut self, span: Span, coercion: ReceiverCoercion) {
28 self.receivers.insert(ReceiverId(span), coercion);
29 }
30
31 pub fn get_coercion(&self, span: Span) -> Option<ReceiverCoercion> {
32 self.receivers.get(&ReceiverId(span)).copied()
33 }
34}
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
37struct BindingId(Span);
38
39#[derive(Debug, Clone, Default)]
40pub struct UnusedInfo {
41 bindings: HashSet<BindingId>,
42 definitions: HashSet<BindingId>,
43 pub imports_by_module: HashMap<EcoString, HashSet<EcoString>>,
44}
45
46impl UnusedInfo {
47 pub fn mark_binding_unused(&mut self, span: Span) {
48 self.bindings.insert(BindingId(span));
49 }
50
51 pub fn is_unused_binding(&self, pattern: &Pattern) -> bool {
52 match pattern {
53 Pattern::Identifier { span, .. } => self.bindings.contains(&BindingId(*span)),
54 Pattern::AsBinding { span, name, .. } => {
55 let name_span = Span::new(
56 span.file_id,
57 span.byte_offset + span.byte_length - name.len() as u32,
58 name.len() as u32,
59 );
60 self.bindings.contains(&BindingId(name_span))
61 }
62 _ => false,
63 }
64 }
65
66 pub fn is_unused_rest_binding(&self, rest: &RestPattern) -> bool {
67 match rest {
68 RestPattern::Bind { span, .. } => self.bindings.contains(&BindingId(*span)),
69 _ => false,
70 }
71 }
72
73 pub fn mark_definition_unused(&mut self, span: Span) {
74 self.definitions.insert(BindingId(span));
75 }
76
77 pub fn is_unused_definition(&self, span: &Span) -> bool {
78 self.definitions.contains(&BindingId(*span))
79 }
80}
81
82#[derive(Debug, Clone, Default)]
83pub struct MutationInfo {
84 bindings: HashSet<AstBindingId>,
85}
86
87impl MutationInfo {
88 pub fn mark_binding_mutated(&mut self, id: AstBindingId) {
89 self.bindings.insert(id);
90 }
91
92 pub fn is_mutated(&self, id: AstBindingId) -> bool {
93 self.bindings.contains(&id)
94 }
95}
96
97#[derive(Debug, Clone, Copy, PartialEq, Eq)]
103pub enum DotAccessKind {
104 StructField { is_exported: bool },
106 TupleStructField { is_newtype: bool },
110 TupleElement,
112 ModuleMember,
114 ValueEnumVariant,
116 EnumVariant,
118 InstanceMethod { is_exported: bool },
120 InstanceMethodValue {
124 is_exported: bool,
125 is_pointer_receiver: bool,
126 },
127 StaticMethod { is_exported: bool },
129}
130
131#[derive(Debug, Clone, Copy, PartialEq, Eq)]
136pub enum NativeTypeKind {
137 Slice,
138 EnumeratedSlice,
139 Map,
140 Channel,
141 Sender,
142 Receiver,
143 String,
144}
145
146impl NativeTypeKind {
147 pub fn from_type(ty: &Type) -> Option<Self> {
148 let resolved = ty.resolve().strip_refs();
149 if let Type::Constructor { ref id, .. } = resolved
150 && (id.starts_with("@import/go:") || id.starts_with("go:"))
151 {
152 return None;
153 }
154 let name = resolved.get_name()?;
155 Self::from_name(name)
156 }
157
158 pub fn from_name(name: &str) -> Option<Self> {
159 match name {
160 "Slice" => Some(Self::Slice),
161 "EnumeratedSlice" => Some(Self::EnumeratedSlice),
162 "Map" => Some(Self::Map),
163 "Channel" => Some(Self::Channel),
164 "Sender" => Some(Self::Sender),
165 "Receiver" => Some(Self::Receiver),
166 "string" => Some(Self::String),
167 _ => None,
168 }
169 }
170}
171
172#[derive(Debug, Clone, Copy, PartialEq, Eq)]
176pub enum CallKind {
177 Regular,
179 TupleStructConstructor,
181 AssertType,
183 UfcsMethod,
185 NativeConstructor(NativeTypeKind),
187 NativeMethod(NativeTypeKind),
189 NativeMethodIdentifier(NativeTypeKind),
191 ReceiverMethodUfcs { is_public: bool },
193}
194
195#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
196struct ResolutionId(Span);
197
198#[derive(Debug, Clone, Default)]
203pub struct ResolutionInfo {
204 dot_accesses: HashMap<ResolutionId, DotAccessKind>,
205 calls: HashMap<ResolutionId, CallKind>,
206}
207
208impl ResolutionInfo {
209 pub fn mark_dot_access(&mut self, span: Span, kind: DotAccessKind) {
210 self.dot_accesses.insert(ResolutionId(span), kind);
211 }
212
213 pub fn get_dot_access(&self, span: Span) -> Option<DotAccessKind> {
214 self.dot_accesses.get(&ResolutionId(span)).copied()
215 }
216
217 pub fn mark_call(&mut self, span: Span, meta: CallKind) {
218 self.calls.insert(ResolutionId(span), meta);
219 }
220
221 pub fn get_call(&self, span: Span) -> Option<CallKind> {
222 self.calls.get(&ResolutionId(span)).copied()
223 }
224}
225
226pub struct EmitInput {
227 pub files: HashMap<u32, File>,
228 pub definitions: HashMap<EcoString, Definition>,
229 pub modules: HashMap<String, ModuleInfo>,
230 pub entry_module_id: String,
231 pub unused: UnusedInfo,
232 pub mutations: MutationInfo,
233 pub coercions: CoercionInfo,
234 pub resolutions: ResolutionInfo,
235 pub cached_modules: HashSet<String>,
236 pub ufcs_methods: HashSet<(String, String)>,
237 pub go_package_names: HashMap<String, String>,
238}