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!(
88 !raw,
89 "cached const literals are canonicalized to non-raw strings"
90 );
91 CachedLiteral::String(value.clone())
92 }
93 Literal::Char(v) => CachedLiteral::Char(v.clone()),
94 Literal::Imaginary(_) | Literal::FormatString(_) | Literal::Slice(_) => {
96 CachedLiteral::Integer {
97 value: 0,
98 text: None,
99 }
100 }
101 }
102 }
103
104 pub fn to_literal(&self) -> syntax::ast::Literal {
105 use syntax::ast::Literal;
106 match self {
107 CachedLiteral::Integer { value, text } => Literal::Integer {
108 value: *value,
109 text: text.clone(),
110 },
111 CachedLiteral::Float { value, text } => Literal::Float {
112 value: *value,
113 text: text.clone(),
114 },
115 CachedLiteral::Boolean(v) => Literal::Boolean(*v),
116 CachedLiteral::String(v) => Literal::String {
117 value: v.clone(),
118 raw: false,
119 },
120 CachedLiteral::Char(v) => Literal::Char(v.clone()),
121 }
122 }
123}
124
125#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
126pub struct CachedAttribute {
127 pub name: String,
128 pub args: Vec<AttributeArg>,
129}
130
131impl CachedAttribute {
132 pub fn from_attribute(attribute: &syntax::ast::Attribute) -> Self {
133 Self {
134 name: attribute.name.clone(),
135 args: attribute.args.clone(),
136 }
137 }
138
139 pub fn to_attribute(&self) -> syntax::ast::Attribute {
140 syntax::ast::Attribute {
141 name: self.name.clone(),
142 args: self.args.clone(),
143 span: Span::dummy(),
144 }
145 }
146}
147
148#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
149pub struct CachedStructField {
150 pub name: String,
151 pub name_span: CachedSpan,
152 pub ty: Type,
153 pub visibility: FieldVisibility,
154 pub attributes: Vec<CachedAttribute>,
155 pub doc: Option<String>,
156}
157
158impl CachedStructField {
159 pub fn from_field(
160 field: &syntax::ast::StructFieldDefinition,
161 file_id_to_index: &HashMap<u32, u32>,
162 ) -> Self {
163 Self {
164 name: field.name.to_string(),
165 name_span: CachedSpan::from_span(&field.name_span, file_id_to_index),
166 ty: Clone::clone(&field.ty),
167 visibility: field.visibility,
168 attributes: field
169 .attributes
170 .iter()
171 .map(CachedAttribute::from_attribute)
172 .collect(),
173 doc: field.doc.clone(),
174 }
175 }
176
177 pub fn to_field(&self, file_ids: &[u32]) -> syntax::ast::StructFieldDefinition {
178 syntax::ast::StructFieldDefinition {
179 doc: self.doc.clone(),
180 name: self.name.clone().into(),
181 name_span: self.name_span.to_span(file_ids),
182 ty: self.ty.clone(),
183 visibility: self.visibility,
184 attributes: self.attributes.iter().map(|a| a.to_attribute()).collect(),
185 annotation: Annotation::Unknown,
186 }
187 }
188}
189
190#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
191pub struct CachedEnumVariant {
192 pub name: String,
193 pub name_span: CachedSpan,
194 pub fields: CachedVariantFields,
195 pub doc: Option<String>,
196}
197
198#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
199pub enum CachedVariantFields {
200 Unit,
201 Tuple(Vec<CachedEnumField>),
202 Struct(Vec<CachedEnumField>),
203}
204
205impl CachedVariantFields {
206 pub fn from_variant_fields(fields: &syntax::ast::VariantFields) -> Self {
207 match fields {
208 syntax::ast::VariantFields::Unit => CachedVariantFields::Unit,
209 syntax::ast::VariantFields::Tuple(fs) => {
210 CachedVariantFields::Tuple(fs.iter().map(CachedEnumField::from_field).collect())
211 }
212 syntax::ast::VariantFields::Struct(fs) => {
213 CachedVariantFields::Struct(fs.iter().map(CachedEnumField::from_field).collect())
214 }
215 }
216 }
217
218 pub fn to_variant_fields(&self) -> syntax::ast::VariantFields {
219 match self {
220 CachedVariantFields::Unit => syntax::ast::VariantFields::Unit,
221 CachedVariantFields::Tuple(fs) => {
222 syntax::ast::VariantFields::Tuple(fs.iter().map(|f| f.to_field()).collect())
223 }
224 CachedVariantFields::Struct(fs) => {
225 syntax::ast::VariantFields::Struct(fs.iter().map(|f| f.to_field()).collect())
226 }
227 }
228 }
229}
230
231impl CachedEnumVariant {
232 pub fn from_variant(
233 variant: &syntax::ast::EnumVariant,
234 file_id_to_index: &HashMap<u32, u32>,
235 ) -> Self {
236 Self {
237 name: variant.name.to_string(),
238 name_span: CachedSpan::from_span(&variant.name_span, file_id_to_index),
239 fields: CachedVariantFields::from_variant_fields(&variant.fields),
240 doc: variant.doc.clone(),
241 }
242 }
243
244 pub fn to_variant(&self, file_ids: &[u32]) -> syntax::ast::EnumVariant {
245 syntax::ast::EnumVariant {
246 doc: self.doc.clone(),
247 name: self.name.clone().into(),
248 name_span: self.name_span.to_span(file_ids),
249 fields: self.fields.to_variant_fields(),
250 }
251 }
252}
253
254#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
255pub struct CachedEnumField {
256 pub name: String,
257 pub ty: Type,
258}
259
260impl CachedEnumField {
261 pub fn from_field(field: &syntax::ast::EnumFieldDefinition) -> Self {
262 Self {
263 name: field.name.to_string(),
264 ty: Clone::clone(&field.ty),
265 }
266 }
267
268 pub fn to_field(&self) -> syntax::ast::EnumFieldDefinition {
269 syntax::ast::EnumFieldDefinition {
270 name: self.name.clone().into(),
271 name_span: Span::dummy(),
272 ty: self.ty.clone(),
273 annotation: Annotation::Unknown,
274 }
275 }
276}
277
278#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
279pub struct CachedInterface {
280 pub name: String,
281 pub generics: Vec<CachedGeneric>,
282 pub parents: Vec<Type>,
283 pub methods: HashMap<String, Type>,
284}
285
286impl CachedInterface {
287 pub fn from_interface(iface: &Interface, file_id_to_index: &HashMap<u32, u32>) -> Self {
288 Self {
289 name: iface.name.to_string(),
290 generics: iface
291 .generics
292 .iter()
293 .map(|g| CachedGeneric::from_generic(g, file_id_to_index))
294 .collect(),
295 parents: iface.parents.iter().map(Clone::clone).collect(),
296 methods: iface
297 .methods
298 .iter()
299 .map(|(k, v)| (k.to_string(), Clone::clone(v)))
300 .collect(),
301 }
302 }
303
304 pub fn to_interface(&self, file_ids: &[u32]) -> Interface {
305 Interface {
306 name: EcoString::from(self.name.as_str()),
307 generics: self
308 .generics
309 .iter()
310 .map(|g| g.to_generic(file_ids))
311 .collect(),
312 parents: self.parents.to_vec(),
313 methods: self
314 .methods
315 .iter()
316 .map(|(k, v)| (EcoString::from(k.as_str()), v.clone()))
317 .collect(),
318 }
319 }
320}
321
322#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
326pub struct CachedDefinition {
327 pub ty: Type,
328 pub name: Option<String>,
329 pub name_span: Option<CachedSpan>,
330 pub doc: Option<String>,
331 pub body: CachedDefinitionBody,
332}
333
334#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
335pub enum CachedDefinitionBody {
336 TypeAlias {
337 generics: Vec<CachedGeneric>,
338 methods: HashMap<String, Type>,
339 is_opaque: bool,
340 },
341 Enum {
342 generics: Vec<CachedGeneric>,
343 variants: Vec<CachedEnumVariant>,
344 methods: HashMap<String, Type>,
345 display: bool,
346 },
347 Struct {
348 generics: Vec<CachedGeneric>,
349 fields: Vec<CachedStructField>,
350 kind: StructKind,
351 methods: HashMap<String, Type>,
352 constructor: Option<Type>,
353 display: bool,
354 closed_domain: bool,
355 },
356 Interface {
357 definition: CachedInterface,
358 },
359 Value {
360 allowed_lints: Vec<String>,
361 go_hints: Vec<String>,
362 go_name: Option<String>,
363 const_value: Option<CachedLiteral>,
364 },
365}
366
367impl CachedDefinition {
368 pub fn from_definition(definition: &Definition, file_id_to_index: &HashMap<u32, u32>) -> Self {
371 let Definition {
372 ty,
373 name,
374 name_span,
375 doc,
376 body,
377 ..
378 } = definition;
379 let body = match body {
380 DefinitionBody::TypeAlias {
381 generics,
382 annotation,
383 methods,
384 } => CachedDefinitionBody::TypeAlias {
385 generics: generics
386 .iter()
387 .map(|g| CachedGeneric::from_generic(g, file_id_to_index))
388 .collect(),
389 methods: Self::convert_methods(methods),
390 is_opaque: annotation.is_opaque(),
391 },
392 DefinitionBody::Enum {
393 generics,
394 variants,
395 methods,
396 display,
397 } => CachedDefinitionBody::Enum {
398 generics: generics
399 .iter()
400 .map(|g| CachedGeneric::from_generic(g, file_id_to_index))
401 .collect(),
402 variants: variants
403 .iter()
404 .map(|v| CachedEnumVariant::from_variant(v, file_id_to_index))
405 .collect(),
406 methods: Self::convert_methods(methods),
407 display: *display,
408 },
409 DefinitionBody::Struct {
410 generics,
411 fields,
412 kind,
413 methods,
414 constructor,
415 display,
416 closed_domain,
417 } => CachedDefinitionBody::Struct {
418 generics: generics
419 .iter()
420 .map(|g| CachedGeneric::from_generic(g, file_id_to_index))
421 .collect(),
422 fields: fields
423 .iter()
424 .map(|f| CachedStructField::from_field(f, file_id_to_index))
425 .collect(),
426 kind: *kind,
427 methods: Self::convert_methods(methods),
428 constructor: constructor.clone(),
429 display: *display,
430 closed_domain: *closed_domain,
431 },
432 DefinitionBody::Interface { definition } => CachedDefinitionBody::Interface {
433 definition: CachedInterface::from_interface(definition, file_id_to_index),
434 },
435 DefinitionBody::Value {
436 allowed_lints,
437 go_hints,
438 go_name,
439 const_value,
440 } => CachedDefinitionBody::Value {
441 allowed_lints: allowed_lints.clone(),
442 go_hints: go_hints.clone(),
443 go_name: go_name.clone(),
444 const_value: const_value.as_ref().map(CachedLiteral::from_literal),
445 },
446 };
447 CachedDefinition {
448 ty: ty.clone(),
449 name: name.as_ref().map(|n| n.to_string()),
450 name_span: name_span.map(|s| CachedSpan::from_span(&s, file_id_to_index)),
451 doc: doc.clone(),
452 body,
453 }
454 }
455
456 fn convert_methods(methods: &MethodSignatures) -> HashMap<String, Type> {
457 methods
458 .iter()
459 .map(|(k, v)| (k.to_string(), Clone::clone(v)))
460 .collect()
461 }
462
463 fn restore_methods(methods: &HashMap<String, Type>) -> MethodSignatures {
464 methods
465 .iter()
466 .map(|(k, v)| (EcoString::from(k.as_str()), v.clone()))
467 .collect()
468 }
469
470 pub fn to_definition(&self, file_ids: &[u32]) -> Definition {
471 let body = match &self.body {
472 CachedDefinitionBody::TypeAlias {
473 generics,
474 methods,
475 is_opaque,
476 } => DefinitionBody::TypeAlias {
477 generics: generics.iter().map(|g| g.to_generic(file_ids)).collect(),
478 annotation: if *is_opaque {
479 Annotation::Opaque {
480 span: Span::dummy(),
481 }
482 } else {
483 Annotation::Unknown
484 },
485 methods: Self::restore_methods(methods),
486 },
487 CachedDefinitionBody::Enum {
488 generics,
489 variants,
490 methods,
491 display,
492 } => DefinitionBody::Enum {
493 generics: generics.iter().map(|g| g.to_generic(file_ids)).collect(),
494 variants: variants.iter().map(|v| v.to_variant(file_ids)).collect(),
495 methods: Self::restore_methods(methods),
496 display: *display,
497 },
498 CachedDefinitionBody::Struct {
499 generics,
500 fields,
501 kind,
502 methods,
503 constructor,
504 display,
505 closed_domain,
506 } => DefinitionBody::Struct {
507 generics: generics.iter().map(|g| g.to_generic(file_ids)).collect(),
508 fields: fields.iter().map(|f| f.to_field(file_ids)).collect(),
509 kind: *kind,
510 methods: Self::restore_methods(methods),
511 constructor: constructor.clone(),
512 display: *display,
513 closed_domain: *closed_domain,
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}