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#[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 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#[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 },
375 ValueEnum {
376 variants: Vec<CachedValueEnumVariant>,
377 underlying_ty: Option<Type>,
378 methods: HashMap<String, Type>,
379 },
380 Struct {
381 generics: Vec<CachedGeneric>,
382 fields: Vec<CachedStructField>,
383 kind: StructKind,
384 methods: HashMap<String, Type>,
385 constructor: Option<Type>,
386 },
387 Interface {
388 definition: CachedInterface,
389 },
390 Value {
391 allowed_lints: Vec<String>,
392 go_hints: Vec<String>,
393 go_name: Option<String>,
394 },
395}
396
397impl CachedDefinition {
398 pub fn from_definition(definition: &Definition, file_id_to_index: &HashMap<u32, u32>) -> Self {
401 let Definition {
402 ty,
403 name,
404 name_span,
405 doc,
406 body,
407 ..
408 } = definition;
409 let body = match body {
410 DefinitionBody::TypeAlias {
411 generics,
412 annotation,
413 methods,
414 } => CachedDefinitionBody::TypeAlias {
415 generics: generics
416 .iter()
417 .map(|g| CachedGeneric::from_generic(g, file_id_to_index))
418 .collect(),
419 methods: Self::convert_methods(methods),
420 is_opaque: annotation.is_opaque(),
421 },
422 DefinitionBody::Enum {
423 generics,
424 variants,
425 methods,
426 } => CachedDefinitionBody::Enum {
427 generics: generics
428 .iter()
429 .map(|g| CachedGeneric::from_generic(g, file_id_to_index))
430 .collect(),
431 variants: variants
432 .iter()
433 .map(|v| CachedEnumVariant::from_variant(v, file_id_to_index))
434 .collect(),
435 methods: Self::convert_methods(methods),
436 },
437 DefinitionBody::ValueEnum {
438 variants,
439 underlying_ty,
440 methods,
441 } => CachedDefinitionBody::ValueEnum {
442 variants: variants
443 .iter()
444 .map(|v| CachedValueEnumVariant::from_variant(v, file_id_to_index))
445 .collect(),
446 underlying_ty: underlying_ty.clone(),
447 methods: Self::convert_methods(methods),
448 },
449 DefinitionBody::Struct {
450 generics,
451 fields,
452 kind,
453 methods,
454 constructor,
455 } => CachedDefinitionBody::Struct {
456 generics: generics
457 .iter()
458 .map(|g| CachedGeneric::from_generic(g, file_id_to_index))
459 .collect(),
460 fields: fields
461 .iter()
462 .map(|f| CachedStructField::from_field(f, file_id_to_index))
463 .collect(),
464 kind: *kind,
465 methods: Self::convert_methods(methods),
466 constructor: constructor.clone(),
467 },
468 DefinitionBody::Interface { definition } => CachedDefinitionBody::Interface {
469 definition: CachedInterface::from_interface(definition, file_id_to_index),
470 },
471 DefinitionBody::Value {
472 allowed_lints,
473 go_hints,
474 go_name,
475 } => CachedDefinitionBody::Value {
476 allowed_lints: allowed_lints.clone(),
477 go_hints: go_hints.clone(),
478 go_name: go_name.clone(),
479 },
480 };
481 CachedDefinition {
482 ty: ty.clone(),
483 name: name.as_ref().map(|n| n.to_string()),
484 name_span: name_span.map(|s| CachedSpan::from_span(&s, file_id_to_index)),
485 doc: doc.clone(),
486 body,
487 }
488 }
489
490 fn convert_methods(methods: &MethodSignatures) -> HashMap<String, Type> {
491 methods
492 .iter()
493 .map(|(k, v)| (k.to_string(), Clone::clone(v)))
494 .collect()
495 }
496
497 fn restore_methods(methods: &HashMap<String, Type>) -> MethodSignatures {
498 methods
499 .iter()
500 .map(|(k, v)| (EcoString::from(k.as_str()), v.clone()))
501 .collect()
502 }
503
504 pub fn to_definition(&self, file_ids: &[u32]) -> Definition {
505 let body = match &self.body {
506 CachedDefinitionBody::TypeAlias {
507 generics,
508 methods,
509 is_opaque,
510 } => DefinitionBody::TypeAlias {
511 generics: generics.iter().map(|g| g.to_generic(file_ids)).collect(),
512 annotation: if *is_opaque {
513 Annotation::Opaque {
514 span: Span::dummy(),
515 }
516 } else {
517 Annotation::Unknown
518 },
519 methods: Self::restore_methods(methods),
520 },
521 CachedDefinitionBody::Enum {
522 generics,
523 variants,
524 methods,
525 } => DefinitionBody::Enum {
526 generics: generics.iter().map(|g| g.to_generic(file_ids)).collect(),
527 variants: variants.iter().map(|v| v.to_variant(file_ids)).collect(),
528 methods: Self::restore_methods(methods),
529 },
530 CachedDefinitionBody::ValueEnum {
531 variants,
532 underlying_ty,
533 methods,
534 } => DefinitionBody::ValueEnum {
535 variants: variants.iter().map(|v| v.to_variant(file_ids)).collect(),
536 underlying_ty: underlying_ty.clone(),
537 methods: Self::restore_methods(methods),
538 },
539 CachedDefinitionBody::Struct {
540 generics,
541 fields,
542 kind,
543 methods,
544 constructor,
545 } => DefinitionBody::Struct {
546 generics: generics.iter().map(|g| g.to_generic(file_ids)).collect(),
547 fields: fields.iter().map(|f| f.to_field(file_ids)).collect(),
548 kind: *kind,
549 methods: Self::restore_methods(methods),
550 constructor: constructor.clone(),
551 },
552 CachedDefinitionBody::Interface { definition } => DefinitionBody::Interface {
553 definition: definition.to_interface(file_ids),
554 },
555 CachedDefinitionBody::Value {
556 allowed_lints,
557 go_hints,
558 go_name,
559 } => DefinitionBody::Value {
560 allowed_lints: allowed_lints.clone(),
561 go_hints: go_hints.clone(),
562 go_name: go_name.clone(),
563 },
564 };
565 Definition {
566 visibility: Visibility::Public,
567 ty: self.ty.clone(),
568 name: self.name.as_ref().map(|n| EcoString::from(n.as_str())),
569 name_span: self.name_span.as_ref().map(|s| s.to_span(file_ids)),
570 doc: self.doc.clone(),
571 body,
572 }
573 }
574}