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 attributes: Attributes,
346 },
347 Enum {
348 generics: Vec<CachedGeneric>,
349 variants: Vec<CachedEnumVariant>,
350 methods: HashMap<String, Type>,
351 attributes: Attributes,
352 },
353 Struct {
354 generics: Vec<CachedGeneric>,
355 fields: Vec<CachedStructField>,
356 kind: StructKind,
357 methods: HashMap<String, Type>,
358 constructor: Option<Type>,
359 attributes: Attributes,
360 },
361 Interface {
362 definition: CachedInterface,
363 },
364 Value {
365 allowed_lints: Vec<String>,
366 go_hints: Vec<String>,
367 go_name: Option<String>,
368 const_value: Option<CachedLiteral>,
369 },
370}
371
372impl CachedDefinition {
373 pub fn from_definition(definition: &Definition, file_id_to_index: &HashMap<u32, u32>) -> Self {
376 let Definition {
377 ty,
378 name,
379 name_span,
380 doc,
381 body,
382 ..
383 } = definition;
384 let body = match body {
385 DefinitionBody::TypeAlias {
386 generics,
387 annotation,
388 methods,
389 attributes,
390 } => CachedDefinitionBody::TypeAlias {
391 generics: generics
392 .iter()
393 .map(|g| CachedGeneric::from_generic(g, file_id_to_index))
394 .collect(),
395 methods: Self::convert_methods(methods),
396 is_opaque: annotation.is_opaque(),
397 attributes: attributes.clone(),
398 },
399 DefinitionBody::Enum {
400 generics,
401 variants,
402 methods,
403 attributes,
404 } => CachedDefinitionBody::Enum {
405 generics: generics
406 .iter()
407 .map(|g| CachedGeneric::from_generic(g, file_id_to_index))
408 .collect(),
409 variants: variants
410 .iter()
411 .map(|v| CachedEnumVariant::from_variant(v, file_id_to_index))
412 .collect(),
413 methods: Self::convert_methods(methods),
414 attributes: attributes.clone(),
415 },
416 DefinitionBody::Struct {
417 generics,
418 fields,
419 kind,
420 methods,
421 constructor,
422 attributes,
423 } => CachedDefinitionBody::Struct {
424 generics: generics
425 .iter()
426 .map(|g| CachedGeneric::from_generic(g, file_id_to_index))
427 .collect(),
428 fields: fields
429 .iter()
430 .map(|f| CachedStructField::from_field(f, file_id_to_index))
431 .collect(),
432 kind: *kind,
433 methods: Self::convert_methods(methods),
434 constructor: constructor.clone(),
435 attributes: attributes.clone(),
436 },
437 DefinitionBody::Interface { definition } => CachedDefinitionBody::Interface {
438 definition: CachedInterface::from_interface(definition, file_id_to_index),
439 },
440 DefinitionBody::Value {
441 allowed_lints,
442 go_hints,
443 go_name,
444 const_value,
445 } => CachedDefinitionBody::Value {
446 allowed_lints: allowed_lints.clone(),
447 go_hints: go_hints.clone(),
448 go_name: go_name.clone(),
449 const_value: const_value.as_ref().map(CachedLiteral::from_literal),
450 },
451 };
452 CachedDefinition {
453 ty: ty.clone(),
454 name: name.as_ref().map(|n| n.to_string()),
455 name_span: name_span.map(|s| CachedSpan::from_span(&s, file_id_to_index)),
456 doc: doc.clone(),
457 body,
458 }
459 }
460
461 fn convert_methods(methods: &MethodSignatures) -> HashMap<String, Type> {
462 methods
463 .iter()
464 .map(|(k, v)| (k.to_string(), Clone::clone(v)))
465 .collect()
466 }
467
468 fn restore_methods(methods: &HashMap<String, Type>) -> MethodSignatures {
469 methods
470 .iter()
471 .map(|(k, v)| (EcoString::from(k.as_str()), v.clone()))
472 .collect()
473 }
474
475 pub fn to_definition(&self, file_ids: &[u32]) -> Definition {
476 let body = match &self.body {
477 CachedDefinitionBody::TypeAlias {
478 generics,
479 methods,
480 is_opaque,
481 attributes,
482 } => DefinitionBody::TypeAlias {
483 generics: generics.iter().map(|g| g.to_generic(file_ids)).collect(),
484 annotation: if *is_opaque {
485 Annotation::Opaque {
486 span: Span::dummy(),
487 }
488 } else {
489 Annotation::Unknown
490 },
491 methods: Self::restore_methods(methods),
492 attributes: attributes.clone(),
493 },
494 CachedDefinitionBody::Enum {
495 generics,
496 variants,
497 methods,
498 attributes,
499 } => DefinitionBody::Enum {
500 generics: generics.iter().map(|g| g.to_generic(file_ids)).collect(),
501 variants: variants.iter().map(|v| v.to_variant(file_ids)).collect(),
502 methods: Self::restore_methods(methods),
503 attributes: attributes.clone(),
504 },
505 CachedDefinitionBody::Struct {
506 generics,
507 fields,
508 kind,
509 methods,
510 constructor,
511 attributes,
512 } => DefinitionBody::Struct {
513 generics: generics.iter().map(|g| g.to_generic(file_ids)).collect(),
514 fields: fields.iter().map(|f| f.to_field(file_ids)).collect(),
515 kind: *kind,
516 methods: Self::restore_methods(methods),
517 constructor: constructor.clone(),
518 attributes: attributes.clone(),
519 },
520 CachedDefinitionBody::Interface { definition } => DefinitionBody::Interface {
521 definition: definition.to_interface(file_ids),
522 },
523 CachedDefinitionBody::Value {
524 allowed_lints,
525 go_hints,
526 go_name,
527 const_value,
528 } => DefinitionBody::Value {
529 allowed_lints: allowed_lints.clone(),
530 go_hints: go_hints.clone(),
531 go_name: go_name.clone(),
532 const_value: const_value.as_ref().map(CachedLiteral::to_literal),
533 },
534 };
535 Definition {
536 visibility: Visibility::Public,
537 ty: self.ty.clone(),
538 name: self.name.as_ref().map(|n| EcoString::from(n.as_str())),
539 name_span: self.name_span.as_ref().map(|s| s.to_span(file_ids)),
540 doc: self.doc.clone(),
541 body,
542 }
543 }
544}