Skip to main content

lisette_semantics/cache/
types.rs

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