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 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 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}