1use rustc_hash::FxHashMap as HashMap;
2
3use ecow::EcoString;
4use serde::{Deserialize, Serialize};
5use syntax::ast::{
6 Annotation, AttributeArg, Generic, Span, StructKind, Visibility as FieldVisibility,
7};
8use syntax::program::{
9 Attributes, Definition, DefinitionBody, Interface, MethodSignatures, Visibility,
10};
11use syntax::types::Type;
12
13#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
17pub struct CachedSpan {
18 pub file_index: u32,
19 pub byte_offset: u32,
20 pub byte_length: u32,
21}
22
23impl CachedSpan {
24 pub fn from_span(span: &Span, file_id_to_index: &HashMap<u32, u32>) -> Self {
25 Self {
26 file_index: *file_id_to_index.get(&span.file_id).unwrap_or(&0),
27 byte_offset: span.byte_offset,
28 byte_length: span.byte_length,
29 }
30 }
31
32 pub fn to_span(&self, file_ids: &[u32]) -> Span {
33 Span {
34 file_id: file_ids.get(self.file_index as usize).copied().unwrap_or(0),
35 byte_offset: self.byte_offset,
36 byte_length: self.byte_length,
37 }
38 }
39}
40
41#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
42pub struct CachedGeneric {
43 pub name: String,
44 pub bounds: Vec<Annotation>,
45 pub span: CachedSpan,
46}
47
48impl CachedGeneric {
49 pub fn from_generic(generic: &Generic, file_id_to_index: &HashMap<u32, u32>) -> Self {
50 Self {
51 name: generic.name.to_string(),
52 bounds: generic.bounds.clone(),
53 span: CachedSpan::from_span(&generic.span, file_id_to_index),
54 }
55 }
56
57 pub fn to_generic(&self, file_ids: &[u32]) -> Generic {
58 Generic {
59 name: EcoString::from(self.name.as_str()),
60 bounds: self.bounds.clone(),
61 span: self.span.to_span(file_ids),
62 }
63 }
64}
65
66#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
67pub enum CachedLiteral {
68 Integer { value: u64, text: Option<String> },
69 Float { value: f64, text: Option<String> },
70 Boolean(bool),
71 String(String),
72 Char(String),
73}
74
75impl CachedLiteral {
76 pub fn from_literal(lit: &syntax::ast::Literal) -> Self {
77 use syntax::ast::Literal;
78 match lit {
79 Literal::Integer { value, text } => CachedLiteral::Integer {
80 value: *value,
81 text: text.clone(),
82 },
83 Literal::Float { value, text } => CachedLiteral::Float {
84 value: *value,
85 text: text.clone(),
86 },
87 Literal::Boolean(v) => CachedLiteral::Boolean(*v),
88 Literal::String { value, raw } => {
89 debug_assert!(
90 !raw,
91 "cached const literals are canonicalized to non-raw strings"
92 );
93 CachedLiteral::String(value.clone())
94 }
95 Literal::Char(v) => CachedLiteral::Char(v.clone()),
96 Literal::Imaginary(_) | Literal::FormatString(_) | Literal::Slice(_) => {
98 CachedLiteral::Integer {
99 value: 0,
100 text: None,
101 }
102 }
103 }
104 }
105
106 pub fn to_literal(&self) -> syntax::ast::Literal {
107 use syntax::ast::Literal;
108 match self {
109 CachedLiteral::Integer { value, text } => Literal::Integer {
110 value: *value,
111 text: text.clone(),
112 },
113 CachedLiteral::Float { value, text } => Literal::Float {
114 value: *value,
115 text: text.clone(),
116 },
117 CachedLiteral::Boolean(v) => Literal::Boolean(*v),
118 CachedLiteral::String(v) => Literal::String {
119 value: v.clone(),
120 raw: false,
121 },
122 CachedLiteral::Char(v) => Literal::Char(v.clone()),
123 }
124 }
125}
126
127#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
128pub struct CachedAttribute {
129 pub name: String,
130 pub args: Vec<AttributeArg>,
131}
132
133impl CachedAttribute {
134 pub fn from_attribute(attribute: &syntax::ast::Attribute) -> Self {
135 Self {
136 name: attribute.name.clone(),
137 args: attribute.args.clone(),
138 }
139 }
140
141 pub fn to_attribute(&self) -> syntax::ast::Attribute {
142 syntax::ast::Attribute {
143 name: self.name.clone(),
144 args: self.args.clone(),
145 span: Span::dummy(),
146 }
147 }
148}
149
150#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
151pub struct CachedStructField {
152 pub name: String,
153 pub name_span: CachedSpan,
154 pub ty: Type,
155 pub visibility: FieldVisibility,
156 pub attributes: Vec<CachedAttribute>,
157 pub doc: Option<String>,
158 pub embedded: bool,
159}
160
161impl CachedStructField {
162 pub fn from_field(
163 field: &syntax::ast::StructFieldDefinition,
164 file_id_to_index: &HashMap<u32, u32>,
165 ) -> Self {
166 Self {
167 name: field.name.to_string(),
168 name_span: CachedSpan::from_span(&field.name_span, file_id_to_index),
169 ty: Clone::clone(&field.ty),
170 visibility: field.visibility,
171 attributes: field
172 .attributes
173 .iter()
174 .map(CachedAttribute::from_attribute)
175 .collect(),
176 doc: field.doc.clone(),
177 embedded: field.embedded,
178 }
179 }
180
181 pub fn to_field(&self, file_ids: &[u32]) -> syntax::ast::StructFieldDefinition {
182 syntax::ast::StructFieldDefinition {
183 doc: self.doc.clone(),
184 name: self.name.clone().into(),
185 name_span: self.name_span.to_span(file_ids),
186 ty: self.ty.clone(),
187 visibility: self.visibility,
188 attributes: self.attributes.iter().map(|a| a.to_attribute()).collect(),
189 annotation: Annotation::Unknown,
190 embedded: self.embedded,
191 }
192 }
193}
194
195#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
196pub struct CachedEnumVariant {
197 pub name: String,
198 pub name_span: CachedSpan,
199 pub fields: CachedVariantFields,
200 pub doc: Option<String>,
201}
202
203#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
204pub enum CachedVariantFields {
205 Unit,
206 Tuple(Vec<CachedEnumField>),
207 Struct(Vec<CachedEnumField>),
208}
209
210impl CachedVariantFields {
211 pub fn from_variant_fields(fields: &syntax::ast::VariantFields) -> Self {
212 match fields {
213 syntax::ast::VariantFields::Unit => CachedVariantFields::Unit,
214 syntax::ast::VariantFields::Tuple(fs) => {
215 CachedVariantFields::Tuple(fs.iter().map(CachedEnumField::from_field).collect())
216 }
217 syntax::ast::VariantFields::Struct(fs) => {
218 CachedVariantFields::Struct(fs.iter().map(CachedEnumField::from_field).collect())
219 }
220 }
221 }
222
223 pub fn to_variant_fields(&self) -> syntax::ast::VariantFields {
224 match self {
225 CachedVariantFields::Unit => syntax::ast::VariantFields::Unit,
226 CachedVariantFields::Tuple(fs) => {
227 syntax::ast::VariantFields::Tuple(fs.iter().map(|f| f.to_field()).collect())
228 }
229 CachedVariantFields::Struct(fs) => {
230 syntax::ast::VariantFields::Struct(fs.iter().map(|f| f.to_field()).collect())
231 }
232 }
233 }
234}
235
236impl CachedEnumVariant {
237 pub fn from_variant(
238 variant: &syntax::ast::EnumVariant,
239 file_id_to_index: &HashMap<u32, u32>,
240 ) -> Self {
241 Self {
242 name: variant.name.to_string(),
243 name_span: CachedSpan::from_span(&variant.name_span, file_id_to_index),
244 fields: CachedVariantFields::from_variant_fields(&variant.fields),
245 doc: variant.doc.clone(),
246 }
247 }
248
249 pub fn to_variant(&self, file_ids: &[u32]) -> syntax::ast::EnumVariant {
250 syntax::ast::EnumVariant {
251 doc: self.doc.clone(),
252 name: self.name.clone().into(),
253 name_span: self.name_span.to_span(file_ids),
254 fields: self.fields.to_variant_fields(),
255 }
256 }
257}
258
259#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
260pub struct CachedEnumField {
261 pub name: String,
262 pub ty: Type,
263}
264
265impl CachedEnumField {
266 pub fn from_field(field: &syntax::ast::EnumFieldDefinition) -> Self {
267 Self {
268 name: field.name.to_string(),
269 ty: Clone::clone(&field.ty),
270 }
271 }
272
273 pub fn to_field(&self) -> syntax::ast::EnumFieldDefinition {
274 syntax::ast::EnumFieldDefinition {
275 name: self.name.clone().into(),
276 name_span: Span::dummy(),
277 ty: self.ty.clone(),
278 annotation: Annotation::Unknown,
279 }
280 }
281}
282
283#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
284pub struct CachedInterface {
285 pub name: String,
286 pub generics: Vec<CachedGeneric>,
287 pub parents: Vec<Type>,
288 pub methods: HashMap<String, Type>,
289}
290
291impl CachedInterface {
292 pub fn from_interface(iface: &Interface, file_id_to_index: &HashMap<u32, u32>) -> Self {
293 Self {
294 name: iface.name.to_string(),
295 generics: iface
296 .generics
297 .iter()
298 .map(|g| CachedGeneric::from_generic(g, file_id_to_index))
299 .collect(),
300 parents: iface.parents.iter().map(Clone::clone).collect(),
301 methods: iface
302 .methods
303 .iter()
304 .map(|(k, v)| (k.to_string(), Clone::clone(v)))
305 .collect(),
306 }
307 }
308
309 pub fn to_interface(&self, file_ids: &[u32]) -> Interface {
310 Interface {
311 name: EcoString::from(self.name.as_str()),
312 generics: self
313 .generics
314 .iter()
315 .map(|g| g.to_generic(file_ids))
316 .collect(),
317 parents: self.parents.to_vec(),
318 methods: self
319 .methods
320 .iter()
321 .map(|(k, v)| (EcoString::from(k.as_str()), v.clone()))
322 .collect(),
323 }
324 }
325}
326
327#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
331pub struct CachedDefinition {
332 pub ty: Type,
333 pub name: Option<String>,
334 pub name_span: Option<CachedSpan>,
335 pub doc: Option<String>,
336 pub body: CachedDefinitionBody,
337}
338
339#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
340pub enum CachedDefinitionBody {
341 TypeAlias {
342 generics: Vec<CachedGeneric>,
343 methods: HashMap<String, Type>,
344 is_opaque: bool,
345 },
346 Enum {
347 generics: Vec<CachedGeneric>,
348 variants: Vec<CachedEnumVariant>,
349 methods: HashMap<String, Type>,
350 attributes: Attributes,
351 },
352 Struct {
353 generics: Vec<CachedGeneric>,
354 fields: Vec<CachedStructField>,
355 kind: StructKind,
356 methods: HashMap<String, Type>,
357 constructor: Option<Type>,
358 attributes: Attributes,
359 },
360 Interface {
361 definition: CachedInterface,
362 },
363 Value {
364 allowed_lints: Vec<String>,
365 go_hints: Vec<String>,
366 go_name: Option<String>,
367 const_value: Option<CachedLiteral>,
368 },
369}
370
371impl CachedDefinition {
372 pub fn from_definition(definition: &Definition, file_id_to_index: &HashMap<u32, u32>) -> Self {
375 let Definition {
376 ty,
377 name,
378 name_span,
379 doc,
380 body,
381 ..
382 } = definition;
383 let body = match body {
384 DefinitionBody::TypeAlias {
385 generics,
386 annotation,
387 methods,
388 } => CachedDefinitionBody::TypeAlias {
389 generics: generics
390 .iter()
391 .map(|g| CachedGeneric::from_generic(g, file_id_to_index))
392 .collect(),
393 methods: Self::convert_methods(methods),
394 is_opaque: annotation.is_opaque(),
395 },
396 DefinitionBody::Enum {
397 generics,
398 variants,
399 methods,
400 attributes,
401 } => CachedDefinitionBody::Enum {
402 generics: generics
403 .iter()
404 .map(|g| CachedGeneric::from_generic(g, file_id_to_index))
405 .collect(),
406 variants: variants
407 .iter()
408 .map(|v| CachedEnumVariant::from_variant(v, file_id_to_index))
409 .collect(),
410 methods: Self::convert_methods(methods),
411 attributes: attributes.clone(),
412 },
413 DefinitionBody::Struct {
414 generics,
415 fields,
416 kind,
417 methods,
418 constructor,
419 attributes,
420 } => CachedDefinitionBody::Struct {
421 generics: generics
422 .iter()
423 .map(|g| CachedGeneric::from_generic(g, file_id_to_index))
424 .collect(),
425 fields: fields
426 .iter()
427 .map(|f| CachedStructField::from_field(f, file_id_to_index))
428 .collect(),
429 kind: *kind,
430 methods: Self::convert_methods(methods),
431 constructor: constructor.clone(),
432 attributes: attributes.clone(),
433 },
434 DefinitionBody::Interface { definition } => CachedDefinitionBody::Interface {
435 definition: CachedInterface::from_interface(definition, file_id_to_index),
436 },
437 DefinitionBody::Value {
438 allowed_lints,
439 go_hints,
440 go_name,
441 const_value,
442 } => CachedDefinitionBody::Value {
443 allowed_lints: allowed_lints.clone(),
444 go_hints: go_hints.clone(),
445 go_name: go_name.clone(),
446 const_value: const_value.as_ref().map(CachedLiteral::from_literal),
447 },
448 };
449 CachedDefinition {
450 ty: ty.clone(),
451 name: name.as_ref().map(|n| n.to_string()),
452 name_span: name_span.map(|s| CachedSpan::from_span(&s, file_id_to_index)),
453 doc: doc.clone(),
454 body,
455 }
456 }
457
458 fn convert_methods(methods: &MethodSignatures) -> HashMap<String, Type> {
459 methods
460 .iter()
461 .map(|(k, v)| (k.to_string(), Clone::clone(v)))
462 .collect()
463 }
464
465 fn restore_methods(methods: &HashMap<String, Type>) -> MethodSignatures {
466 methods
467 .iter()
468 .map(|(k, v)| (EcoString::from(k.as_str()), v.clone()))
469 .collect()
470 }
471
472 pub fn to_definition(&self, file_ids: &[u32]) -> Definition {
473 let body = match &self.body {
474 CachedDefinitionBody::TypeAlias {
475 generics,
476 methods,
477 is_opaque,
478 } => DefinitionBody::TypeAlias {
479 generics: generics.iter().map(|g| g.to_generic(file_ids)).collect(),
480 annotation: if *is_opaque {
481 Annotation::Opaque {
482 span: Span::dummy(),
483 }
484 } else {
485 Annotation::Unknown
486 },
487 methods: Self::restore_methods(methods),
488 },
489 CachedDefinitionBody::Enum {
490 generics,
491 variants,
492 methods,
493 attributes,
494 } => DefinitionBody::Enum {
495 generics: generics.iter().map(|g| g.to_generic(file_ids)).collect(),
496 variants: variants.iter().map(|v| v.to_variant(file_ids)).collect(),
497 methods: Self::restore_methods(methods),
498 attributes: attributes.clone(),
499 },
500 CachedDefinitionBody::Struct {
501 generics,
502 fields,
503 kind,
504 methods,
505 constructor,
506 attributes,
507 } => DefinitionBody::Struct {
508 generics: generics.iter().map(|g| g.to_generic(file_ids)).collect(),
509 fields: fields.iter().map(|f| f.to_field(file_ids)).collect(),
510 kind: *kind,
511 methods: Self::restore_methods(methods),
512 constructor: constructor.clone(),
513 attributes: attributes.clone(),
514 },
515 CachedDefinitionBody::Interface { definition } => DefinitionBody::Interface {
516 definition: definition.to_interface(file_ids),
517 },
518 CachedDefinitionBody::Value {
519 allowed_lints,
520 go_hints,
521 go_name,
522 const_value,
523 } => DefinitionBody::Value {
524 allowed_lints: allowed_lints.clone(),
525 go_hints: go_hints.clone(),
526 go_name: go_name.clone(),
527 const_value: const_value.as_ref().map(CachedLiteral::to_literal),
528 },
529 };
530 Definition {
531 visibility: Visibility::Public,
532 ty: self.ty.clone(),
533 name: self.name.as_ref().map(|n| EcoString::from(n.as_str())),
534 name_span: self.name_span.as_ref().map(|s| s.to_span(file_ids)),
535 doc: self.doc.clone(),
536 body,
537 }
538 }
539}