Skip to main content

react_compiler_hir/
print.rs

1//! Shared formatting utilities for HIR debug printing.
2//!
3//! This module provides `PrintFormatter` — a stateful formatter that both
4//! `react_compiler::debug_print` (HIR printer) and
5//! `react_compiler_reactive_scopes::print_reactive_function` (reactive printer)
6//! delegate to for shared formatting logic.
7//!
8//! It also exports standalone formatting functions (format_loc, format_primitive, etc.)
9//! that require no state.
10
11use rustc_hash::FxHashSet;
12
13use react_compiler_diagnostics::CompilerError;
14use react_compiler_diagnostics::CompilerErrorOrDiagnostic;
15use react_compiler_diagnostics::SourceLocation;
16
17use crate::AliasingEffect;
18use crate::HirFunction;
19use crate::IdentifierId;
20use crate::IdentifierName;
21use crate::InstructionValue;
22use crate::LValue;
23use crate::MutationReason;
24use crate::Pattern;
25use crate::Place;
26use crate::PlaceOrSpreadOrHole;
27use crate::ScopeId;
28use crate::Type;
29use crate::environment::Environment;
30use crate::type_config::ValueKind;
31use crate::type_config::ValueReason;
32
33// =============================================================================
34// Standalone formatting functions (no state needed)
35// =============================================================================
36
37pub fn format_loc(loc: &Option<SourceLocation>) -> String {
38    match loc {
39        Some(l) => format_loc_value(l),
40        None => "generated".to_string(),
41    }
42}
43
44pub fn format_loc_value(loc: &SourceLocation) -> String {
45    format!(
46        "{}:{}-{}:{}",
47        loc.start.line, loc.start.column, loc.end.line, loc.end.column
48    )
49}
50
51/// Format a string like JS `JSON.stringify`: escape control chars and quotes
52/// but preserve non-ASCII unicode (e.g. U+00A0 nbsp) as literal characters.
53pub fn format_js_string(s: &str) -> String {
54    let mut result = String::with_capacity(s.len() + 2);
55    result.push('"');
56    for c in s.chars() {
57        match c {
58            '"' => result.push_str("\\\""),
59            '\\' => result.push_str("\\\\"),
60            '\n' => result.push_str("\\n"),
61            '\r' => result.push_str("\\r"),
62            '\t' => result.push_str("\\t"),
63            '\u{0008}' => result.push_str("\\b"),
64            '\u{000c}' => result.push_str("\\f"),
65            // Only escape C0 control chars (U+0000–U+001F), matching JS JSON.stringify.
66            // Do NOT escape C1 controls (U+0080–U+009F) — JS outputs those as literal chars.
67            c if (c as u32) <= 0x1F => {
68                result.push_str(&format!("\\u{:04x}", c as u32));
69            }
70            c => result.push(c),
71        }
72    }
73    result.push('"');
74    result
75}
76
77pub fn format_primitive(prim: &crate::PrimitiveValue) -> String {
78    match prim {
79        crate::PrimitiveValue::Null => "null".to_string(),
80        crate::PrimitiveValue::Undefined => "undefined".to_string(),
81        crate::PrimitiveValue::Boolean(b) => format!("{}", b),
82        crate::PrimitiveValue::Number(n) => crate::format_js_number(n.value()),
83        crate::PrimitiveValue::String(s) => match s.as_str() {
84            Some(utf8) => format_js_string(utf8),
85            // Ill-formed strings: escape the well-formed segments exactly like
86            // format_js_string and render each unpaired surrogate as \uXXXX,
87            // matching what TS's JSON.stringify-based printer emits.
88            None => {
89                let mut result = String::new();
90                result.push('"');
91                let mut units = s.code_units().into_iter().peekable();
92                while let Some(unit) = units.next() {
93                    let is_lead = (0xD800..=0xDBFF).contains(&unit);
94                    let is_trail = (0xDC00..=0xDFFF).contains(&unit);
95                    if is_lead {
96                        if let Some(&next) = units.peek() {
97                            if (0xDC00..=0xDFFF).contains(&next) {
98                                units.next();
99                                let cp = 0x10000
100                                    + ((unit as u32 - 0xD800) << 10)
101                                    + (next as u32 - 0xDC00);
102                                result.push(char::from_u32(cp).expect("valid supplementary"));
103                                continue;
104                            }
105                        }
106                    }
107                    if is_lead || is_trail {
108                        result.push_str(&format!("\\u{unit:04x}"));
109                        continue;
110                    }
111                    let c = char::from_u32(unit as u32).expect("BMP non-surrogate is a char");
112                    match c {
113                        '"' => result.push_str("\\\""),
114                        '\\' => result.push_str("\\\\"),
115                        '\n' => result.push_str("\\n"),
116                        '\r' => result.push_str("\\r"),
117                        '\t' => result.push_str("\\t"),
118                        '\u{0008}' => result.push_str("\\b"),
119                        '\u{000c}' => result.push_str("\\f"),
120                        c if (c as u32) <= 0x1F => {
121                            result.push_str(&format!("\\u{:04x}", c as u32));
122                        }
123                        c => result.push(c),
124                    }
125                }
126                result.push('"');
127                result
128            }
129        },
130    }
131}
132
133pub fn format_property_literal(prop: &crate::PropertyLiteral) -> String {
134    match prop {
135        crate::PropertyLiteral::String(s) => s.clone(),
136        crate::PropertyLiteral::Number(n) => crate::format_js_number(n.value()),
137    }
138}
139
140pub fn format_object_property_key(key: &crate::ObjectPropertyKey) -> String {
141    match key {
142        crate::ObjectPropertyKey::String { name } => format!("String(\"{}\")", name),
143        crate::ObjectPropertyKey::Identifier { name } => {
144            format!("Identifier(\"{}\")", name)
145        }
146        crate::ObjectPropertyKey::Computed { name } => {
147            format!("Computed({})", name.identifier.0)
148        }
149        crate::ObjectPropertyKey::Number { name } => {
150            format!("Number({})", crate::format_js_number(name.value()))
151        }
152    }
153}
154
155pub fn format_non_local_binding(binding: &crate::NonLocalBinding) -> String {
156    match binding {
157        crate::NonLocalBinding::Global { name } => {
158            format!("Global {{ name: \"{}\" }}", name)
159        }
160        crate::NonLocalBinding::ModuleLocal { name } => {
161            format!("ModuleLocal {{ name: \"{}\" }}", name)
162        }
163        crate::NonLocalBinding::ImportDefault { name, module } => {
164            format!(
165                "ImportDefault {{ name: \"{}\", module: \"{}\" }}",
166                name, module
167            )
168        }
169        crate::NonLocalBinding::ImportNamespace { name, module } => {
170            format!(
171                "ImportNamespace {{ name: \"{}\", module: \"{}\" }}",
172                name, module
173            )
174        }
175        crate::NonLocalBinding::ImportSpecifier {
176            name,
177            module,
178            imported,
179        } => {
180            format!(
181                "ImportSpecifier {{ name: \"{}\", module: \"{}\", imported: \"{}\" }}",
182                name, module, imported
183            )
184        }
185    }
186}
187
188pub fn format_value_kind(kind: ValueKind) -> &'static str {
189    match kind {
190        ValueKind::Mutable => "mutable",
191        ValueKind::Frozen => "frozen",
192        ValueKind::Primitive => "primitive",
193        ValueKind::MaybeFrozen => "maybe-frozen",
194        ValueKind::Global => "global",
195        ValueKind::Context => "context",
196    }
197}
198
199pub fn format_value_reason(reason: ValueReason) -> &'static str {
200    match reason {
201        ValueReason::KnownReturnSignature => "known-return-signature",
202        ValueReason::State => "state",
203        ValueReason::ReducerState => "reducer-state",
204        ValueReason::Context => "context",
205        ValueReason::Effect => "effect",
206        ValueReason::HookCaptured => "hook-captured",
207        ValueReason::HookReturn => "hook-return",
208        ValueReason::Global => "global",
209        ValueReason::JsxCaptured => "jsx-captured",
210        ValueReason::StoreLocal => "store-local",
211        ValueReason::ReactiveFunctionArgument => "reactive-function-argument",
212        ValueReason::Other => "other",
213    }
214}
215
216// =============================================================================
217// PrintFormatter — shared stateful formatter
218// =============================================================================
219
220/// Shared formatter state used by both HIR and reactive printers.
221///
222/// Both `DebugPrinter` structs delegate to this for formatting shared constructs
223/// like Places, Identifiers, Scopes, Types, InstructionValues, etc.
224pub struct PrintFormatter<'a> {
225    pub env: &'a Environment,
226    pub seen_identifiers: FxHashSet<IdentifierId>,
227    pub seen_scopes: FxHashSet<ScopeId>,
228    pub output: Vec<String>,
229    pub indent_level: usize,
230}
231
232impl<'a> PrintFormatter<'a> {
233    pub fn new(env: &'a Environment) -> Self {
234        Self {
235            env,
236            seen_identifiers: FxHashSet::default(),
237            seen_scopes: FxHashSet::default(),
238            output: Vec::new(),
239            indent_level: 0,
240        }
241    }
242
243    pub fn line(&mut self, text: &str) {
244        let indent = "  ".repeat(self.indent_level);
245        self.output.push(format!("{}{}", indent, text));
246    }
247
248    /// Write a line without adding indentation (used when copying pre-formatted output)
249    pub fn line_raw(&mut self, text: &str) {
250        self.output.push(text.to_string());
251    }
252
253    pub fn indent(&mut self) {
254        self.indent_level += 1;
255    }
256
257    pub fn dedent(&mut self) {
258        self.indent_level -= 1;
259    }
260
261    pub fn to_string_output(&self) -> String {
262        self.output.join("\n")
263    }
264
265    // =========================================================================
266    // AliasingEffect
267    // =========================================================================
268
269    pub fn format_effect(&self, effect: &AliasingEffect) -> String {
270        match effect {
271            AliasingEffect::Freeze { value, reason } => {
272                format!(
273                    "Freeze {{ value: {}, reason: {} }}",
274                    value.identifier.0,
275                    format_value_reason(*reason)
276                )
277            }
278            AliasingEffect::Mutate { value, reason } => match reason {
279                Some(MutationReason::AssignCurrentProperty) => {
280                    format!(
281                        "Mutate {{ value: {}, reason: AssignCurrentProperty }}",
282                        value.identifier.0
283                    )
284                }
285                None => format!("Mutate {{ value: {} }}", value.identifier.0),
286            },
287            AliasingEffect::MutateConditionally { value } => {
288                format!("MutateConditionally {{ value: {} }}", value.identifier.0)
289            }
290            AliasingEffect::MutateTransitive { value } => {
291                format!("MutateTransitive {{ value: {} }}", value.identifier.0)
292            }
293            AliasingEffect::MutateTransitiveConditionally { value } => {
294                format!(
295                    "MutateTransitiveConditionally {{ value: {} }}",
296                    value.identifier.0
297                )
298            }
299            AliasingEffect::Capture { from, into } => {
300                format!(
301                    "Capture {{ into: {}, from: {} }}",
302                    into.identifier.0, from.identifier.0
303                )
304            }
305            AliasingEffect::Alias { from, into } => {
306                format!(
307                    "Alias {{ into: {}, from: {} }}",
308                    into.identifier.0, from.identifier.0
309                )
310            }
311            AliasingEffect::MaybeAlias { from, into } => {
312                format!(
313                    "MaybeAlias {{ into: {}, from: {} }}",
314                    into.identifier.0, from.identifier.0
315                )
316            }
317            AliasingEffect::Assign { from, into } => {
318                format!(
319                    "Assign {{ into: {}, from: {} }}",
320                    into.identifier.0, from.identifier.0
321                )
322            }
323            AliasingEffect::Create {
324                into,
325                value,
326                reason,
327            } => {
328                format!(
329                    "Create {{ into: {}, value: {}, reason: {} }}",
330                    into.identifier.0,
331                    format_value_kind(*value),
332                    format_value_reason(*reason)
333                )
334            }
335            AliasingEffect::CreateFrom { from, into } => {
336                format!(
337                    "CreateFrom {{ into: {}, from: {} }}",
338                    into.identifier.0, from.identifier.0
339                )
340            }
341            AliasingEffect::ImmutableCapture { from, into } => {
342                format!(
343                    "ImmutableCapture {{ into: {}, from: {} }}",
344                    into.identifier.0, from.identifier.0
345                )
346            }
347            AliasingEffect::Apply {
348                receiver,
349                function,
350                mutates_function,
351                args,
352                into,
353                ..
354            } => {
355                let args_str: Vec<String> = args
356                    .iter()
357                    .map(|a| match a {
358                        PlaceOrSpreadOrHole::Hole => "hole".to_string(),
359                        PlaceOrSpreadOrHole::Place(p) => p.identifier.0.to_string(),
360                        PlaceOrSpreadOrHole::Spread(s) => format!("...{}", s.place.identifier.0),
361                    })
362                    .collect();
363                format!(
364                    "Apply {{ into: {}, receiver: {}, function: {}, mutatesFunction: {}, args: [{}] }}",
365                    into.identifier.0,
366                    receiver.identifier.0,
367                    function.identifier.0,
368                    mutates_function,
369                    args_str.join(", ")
370                )
371            }
372            AliasingEffect::CreateFunction {
373                captures,
374                function_id: _,
375                into,
376            } => {
377                let cap_str: Vec<String> = captures
378                    .iter()
379                    .map(|p| p.identifier.0.to_string())
380                    .collect();
381                format!(
382                    "CreateFunction {{ into: {}, captures: [{}] }}",
383                    into.identifier.0,
384                    cap_str.join(", ")
385                )
386            }
387            AliasingEffect::MutateFrozen { place, error } => {
388                format!(
389                    "MutateFrozen {{ place: {}, reason: {:?} }}",
390                    place.identifier.0, error.reason
391                )
392            }
393            AliasingEffect::MutateGlobal { place, error } => {
394                format!(
395                    "MutateGlobal {{ place: {}, reason: {:?} }}",
396                    place.identifier.0, error.reason
397                )
398            }
399            AliasingEffect::Impure { place, error } => {
400                format!(
401                    "Impure {{ place: {}, reason: {:?} }}",
402                    place.identifier.0, error.reason
403                )
404            }
405            AliasingEffect::Render { place } => {
406                format!("Render {{ place: {} }}", place.identifier.0)
407            }
408        }
409    }
410
411    // =========================================================================
412    // Place (with identifier deduplication)
413    // =========================================================================
414
415    pub fn format_place_field(&mut self, field_name: &str, place: &Place) {
416        let is_seen = self.seen_identifiers.contains(&place.identifier);
417        if is_seen {
418            self.line(&format!(
419                "{}: Place {{ identifier: Identifier({}), effect: {}, reactive: {}, loc: {} }}",
420                field_name,
421                place.identifier.0,
422                place.effect,
423                place.reactive,
424                format_loc(&place.loc)
425            ));
426        } else {
427            self.line(&format!("{}: Place {{", field_name));
428            self.indent();
429            self.line("identifier:");
430            self.indent();
431            self.format_identifier(place.identifier);
432            self.dedent();
433            self.line(&format!("effect: {}", place.effect));
434            self.line(&format!("reactive: {}", place.reactive));
435            self.line(&format!("loc: {}", format_loc(&place.loc)));
436            self.dedent();
437            self.line("}");
438        }
439    }
440
441    // =========================================================================
442    // Identifier (first-seen expansion)
443    // =========================================================================
444
445    pub fn format_identifier(&mut self, id: IdentifierId) {
446        self.seen_identifiers.insert(id);
447        let ident = &self.env.identifiers[id.0 as usize];
448        self.line("Identifier {");
449        self.indent();
450        self.line(&format!("id: {}", ident.id.0));
451        self.line(&format!("declarationId: {}", ident.declaration_id.0));
452        match &ident.name {
453            Some(name) => {
454                let (kind, value) = match name {
455                    IdentifierName::Named(n) => ("named", n.as_str()),
456                    IdentifierName::Promoted(n) => ("promoted", n.as_str()),
457                };
458                self.line(&format!(
459                    "name: {{ kind: \"{}\", value: \"{}\" }}",
460                    kind, value
461                ));
462            }
463            None => self.line("name: null"),
464        }
465        // Print the identifier's mutable_range directly, matching the TS
466        // DebugPrintHIR which prints `identifier.mutableRange`. In TS,
467        // InferReactiveScopeVariables sets identifier.mutableRange = scope.range
468        // (shared reference), and AlignReactiveScopesToBlockScopesHIR syncs them.
469        // After MergeOverlappingReactiveScopesHIR repoints scopes, the TS
470        // identifier.mutableRange still references the OLD scope's range (stale),
471        // so we match by using ident.mutable_range directly (which is synced
472        // at the AlignReactiveScopesToBlockScopesHIR step but not re-synced
473        // after scope repointing in merge passes).
474        self.line(&format!(
475            "mutableRange: [{}:{}]",
476            ident.mutable_range.start.0, ident.mutable_range.end.0
477        ));
478        match ident.scope {
479            Some(scope_id) => self.format_scope_field("scope", scope_id),
480            None => self.line("scope: null"),
481        }
482        self.line(&format!("type: {}", self.format_type(ident.type_)));
483        self.line(&format!("loc: {}", format_loc(&ident.loc)));
484        self.dedent();
485        self.line("}");
486    }
487
488    // =========================================================================
489    // Scope (with deduplication)
490    // =========================================================================
491
492    pub fn format_scope_field(&mut self, field_name: &str, scope_id: ScopeId) {
493        let is_seen = self.seen_scopes.contains(&scope_id);
494        if is_seen {
495            self.line(&format!("{}: Scope({})", field_name, scope_id.0));
496        } else {
497            self.seen_scopes.insert(scope_id);
498            if let Some(scope) = self.env.scopes.iter().find(|s| s.id == scope_id) {
499                let range_start = scope.range.start.0;
500                let range_end = scope.range.end.0;
501                let dependencies = scope.dependencies.clone();
502                let declarations = scope.declarations.clone();
503                let reassignments = scope.reassignments.clone();
504                let early_return_value = scope.early_return_value.clone();
505                let merged = scope.merged.clone();
506                let loc = scope.loc;
507
508                self.line(&format!("{}: Scope {{", field_name));
509                self.indent();
510                self.line(&format!("id: {}", scope_id.0));
511                self.line(&format!("range: [{}:{}]", range_start, range_end));
512
513                // dependencies
514                self.line("dependencies:");
515                self.indent();
516                for (i, dep) in dependencies.iter().enumerate() {
517                    let path_str: String = dep
518                        .path
519                        .iter()
520                        .map(|p| {
521                            let prop = match &p.property {
522                                crate::PropertyLiteral::String(s) => s.clone(),
523                                crate::PropertyLiteral::Number(n) => {
524                                    crate::format_js_number(n.value())
525                                }
526                            };
527                            format!("{}{}", if p.optional { "?." } else { "." }, prop)
528                        })
529                        .collect();
530                    self.line(&format!(
531                        "[{}] {{ identifier: {}, reactive: {}, path: \"{}\" }}",
532                        i, dep.identifier.0, dep.reactive, path_str
533                    ));
534                }
535                self.dedent();
536
537                // declarations
538                self.line("declarations:");
539                self.indent();
540                for (ident_id, decl) in &declarations {
541                    self.line(&format!(
542                        "{}: {{ identifier: {}, scope: {} }}",
543                        ident_id.0, decl.identifier.0, decl.scope.0
544                    ));
545                }
546                self.dedent();
547
548                // reassignments
549                self.line("reassignments:");
550                self.indent();
551                for ident_id in &reassignments {
552                    self.line(&format!("{}", ident_id.0));
553                }
554                self.dedent();
555
556                // earlyReturnValue
557                if let Some(early_return) = &early_return_value {
558                    self.line("earlyReturnValue:");
559                    self.indent();
560                    self.line(&format!("value: {}", early_return.value.0));
561                    self.line(&format!("loc: {}", format_loc(&early_return.loc)));
562                    self.line(&format!("label: bb{}", early_return.label.0));
563                    self.dedent();
564                } else {
565                    self.line("earlyReturnValue: null");
566                }
567
568                // merged
569                let merged_str: Vec<String> = merged.iter().map(|s| s.0.to_string()).collect();
570                self.line(&format!("merged: [{}]", merged_str.join(", ")));
571
572                // loc
573                self.line(&format!("loc: {}", format_loc(&loc)));
574
575                self.dedent();
576                self.line("}");
577            } else {
578                self.line(&format!("{}: Scope({})", field_name, scope_id.0));
579            }
580        }
581    }
582
583    // =========================================================================
584    // Type
585    // =========================================================================
586
587    pub fn format_type(&self, type_id: crate::TypeId) -> String {
588        if let Some(ty) = self.env.types.get(type_id.0 as usize) {
589            self.format_type_value(ty)
590        } else {
591            format!("Type({})", type_id.0)
592        }
593    }
594
595    pub fn format_type_value(&self, ty: &Type) -> String {
596        match ty {
597            Type::Primitive => "Primitive".to_string(),
598            Type::Function {
599                shape_id,
600                return_type,
601                is_constructor,
602            } => {
603                format!(
604                    "Function {{ shapeId: {}, return: {}, isConstructor: {} }}",
605                    match shape_id {
606                        Some(s) => format!("\"{}\"", s),
607                        None => "null".to_string(),
608                    },
609                    self.format_type_value(return_type),
610                    is_constructor
611                )
612            }
613            Type::Object { shape_id } => {
614                format!(
615                    "Object {{ shapeId: {} }}",
616                    match shape_id {
617                        Some(s) => format!("\"{}\"", s),
618                        None => "null".to_string(),
619                    }
620                )
621            }
622            Type::TypeVar { id } => format!("Type({})", id.0),
623            Type::Poly => "Poly".to_string(),
624            Type::Phi { operands } => {
625                let ops: Vec<String> = operands
626                    .iter()
627                    .map(|op| self.format_type_value(op))
628                    .collect();
629                format!("Phi {{ operands: [{}] }}", ops.join(", "))
630            }
631            Type::Property {
632                object_type,
633                object_name,
634                property_name,
635            } => {
636                let prop_str = match property_name {
637                    crate::PropertyNameKind::Literal { value } => {
638                        format!("\"{}\"", format_property_literal(value))
639                    }
640                    crate::PropertyNameKind::Computed { value } => {
641                        format!("computed({})", self.format_type_value(value))
642                    }
643                };
644                format!(
645                    "Property {{ objectType: {}, objectName: \"{}\", propertyName: {} }}",
646                    self.format_type_value(object_type),
647                    object_name,
648                    prop_str
649                )
650            }
651            Type::ObjectMethod => "ObjectMethod".to_string(),
652        }
653    }
654
655    // =========================================================================
656    // LValue
657    // =========================================================================
658
659    pub fn format_lvalue(&mut self, field_name: &str, lv: &LValue) {
660        self.line(&format!("{}:", field_name));
661        self.indent();
662        self.line(&format!("kind: {:?}", lv.kind));
663        self.format_place_field("place", &lv.place);
664        self.dedent();
665    }
666
667    // =========================================================================
668    // Pattern
669    // =========================================================================
670
671    pub fn format_pattern(&mut self, pattern: &Pattern) {
672        match pattern {
673            Pattern::Array(arr) => {
674                self.line("pattern: ArrayPattern {");
675                self.indent();
676                self.line("items:");
677                self.indent();
678                for (i, item) in arr.items.iter().enumerate() {
679                    match item {
680                        crate::ArrayPatternElement::Hole => {
681                            self.line(&format!("[{}] Hole", i));
682                        }
683                        crate::ArrayPatternElement::Place(p) => {
684                            self.format_place_field(&format!("[{}]", i), p);
685                        }
686                        crate::ArrayPatternElement::Spread(s) => {
687                            self.line(&format!("[{}] Spread:", i));
688                            self.indent();
689                            self.format_place_field("place", &s.place);
690                            self.dedent();
691                        }
692                    }
693                }
694                self.dedent();
695                self.line(&format!("loc: {}", format_loc(&arr.loc)));
696                self.dedent();
697                self.line("}");
698            }
699            Pattern::Object(obj) => {
700                self.line("pattern: ObjectPattern {");
701                self.indent();
702                self.line("properties:");
703                self.indent();
704                for (i, prop) in obj.properties.iter().enumerate() {
705                    match prop {
706                        crate::ObjectPropertyOrSpread::Property(p) => {
707                            self.line(&format!("[{}] ObjectProperty {{", i));
708                            self.indent();
709                            self.line(&format!("key: {}", format_object_property_key(&p.key)));
710                            self.line(&format!("type: \"{}\"", p.property_type));
711                            self.format_place_field("place", &p.place);
712                            self.dedent();
713                            self.line("}");
714                        }
715                        crate::ObjectPropertyOrSpread::Spread(s) => {
716                            self.line(&format!("[{}] Spread:", i));
717                            self.indent();
718                            self.format_place_field("place", &s.place);
719                            self.dedent();
720                        }
721                    }
722                }
723                self.dedent();
724                self.line(&format!("loc: {}", format_loc(&obj.loc)));
725                self.dedent();
726                self.line("}");
727            }
728        }
729    }
730
731    // =========================================================================
732    // Arguments
733    // =========================================================================
734
735    pub fn format_argument(&mut self, arg: &crate::PlaceOrSpread, index: usize) {
736        match arg {
737            crate::PlaceOrSpread::Place(p) => {
738                self.format_place_field(&format!("[{}]", index), p);
739            }
740            crate::PlaceOrSpread::Spread(s) => {
741                self.line(&format!("[{}] Spread:", index));
742                self.indent();
743                self.format_place_field("place", &s.place);
744                self.dedent();
745            }
746        }
747    }
748
749    // =========================================================================
750    // InstructionValue
751    // =========================================================================
752
753    /// Format an InstructionValue. The `inner_func_formatter` callback is invoked
754    /// for FunctionExpression/ObjectMethod to format the inner HirFunction. If None,
755    /// a placeholder is printed instead.
756    pub fn format_instruction_value(
757        &mut self,
758        value: &InstructionValue,
759        inner_func_formatter: Option<&dyn Fn(&mut PrintFormatter, &HirFunction)>,
760    ) {
761        match value {
762            InstructionValue::ArrayExpression { elements, loc } => {
763                self.line("ArrayExpression {");
764                self.indent();
765                self.line("elements:");
766                self.indent();
767                for (i, elem) in elements.iter().enumerate() {
768                    match elem {
769                        crate::ArrayElement::Place(p) => {
770                            self.format_place_field(&format!("[{}]", i), p);
771                        }
772                        crate::ArrayElement::Hole => {
773                            self.line(&format!("[{}] Hole", i));
774                        }
775                        crate::ArrayElement::Spread(s) => {
776                            self.line(&format!("[{}] Spread:", i));
777                            self.indent();
778                            self.format_place_field("place", &s.place);
779                            self.dedent();
780                        }
781                    }
782                }
783                self.dedent();
784                self.line(&format!("loc: {}", format_loc(loc)));
785                self.dedent();
786                self.line("}");
787            }
788            InstructionValue::ObjectExpression { properties, loc } => {
789                self.line("ObjectExpression {");
790                self.indent();
791                self.line("properties:");
792                self.indent();
793                for (i, prop) in properties.iter().enumerate() {
794                    match prop {
795                        crate::ObjectPropertyOrSpread::Property(p) => {
796                            self.line(&format!("[{}] ObjectProperty {{", i));
797                            self.indent();
798                            self.line(&format!("key: {}", format_object_property_key(&p.key)));
799                            self.line(&format!("type: \"{}\"", p.property_type));
800                            self.format_place_field("place", &p.place);
801                            self.dedent();
802                            self.line("}");
803                        }
804                        crate::ObjectPropertyOrSpread::Spread(s) => {
805                            self.line(&format!("[{}] Spread:", i));
806                            self.indent();
807                            self.format_place_field("place", &s.place);
808                            self.dedent();
809                        }
810                    }
811                }
812                self.dedent();
813                self.line(&format!("loc: {}", format_loc(loc)));
814                self.dedent();
815                self.line("}");
816            }
817            InstructionValue::UnaryExpression {
818                operator,
819                value: val,
820                loc,
821            } => {
822                self.line("UnaryExpression {");
823                self.indent();
824                self.line(&format!("operator: \"{}\"", operator));
825                self.format_place_field("value", val);
826                self.line(&format!("loc: {}", format_loc(loc)));
827                self.dedent();
828                self.line("}");
829            }
830            InstructionValue::BinaryExpression {
831                operator,
832                left,
833                right,
834                loc,
835            } => {
836                self.line("BinaryExpression {");
837                self.indent();
838                self.line(&format!("operator: \"{}\"", operator));
839                self.format_place_field("left", left);
840                self.format_place_field("right", right);
841                self.line(&format!("loc: {}", format_loc(loc)));
842                self.dedent();
843                self.line("}");
844            }
845            InstructionValue::NewExpression { callee, args, loc } => {
846                self.line("NewExpression {");
847                self.indent();
848                self.format_place_field("callee", callee);
849                self.line("args:");
850                self.indent();
851                for (i, arg) in args.iter().enumerate() {
852                    self.format_argument(arg, i);
853                }
854                self.dedent();
855                self.line(&format!("loc: {}", format_loc(loc)));
856                self.dedent();
857                self.line("}");
858            }
859            InstructionValue::CallExpression { callee, args, loc } => {
860                self.line("CallExpression {");
861                self.indent();
862                self.format_place_field("callee", callee);
863                self.line("args:");
864                self.indent();
865                for (i, arg) in args.iter().enumerate() {
866                    self.format_argument(arg, i);
867                }
868                self.dedent();
869                self.line(&format!("loc: {}", format_loc(loc)));
870                self.dedent();
871                self.line("}");
872            }
873            InstructionValue::MethodCall {
874                receiver,
875                property,
876                args,
877                loc,
878            } => {
879                self.line("MethodCall {");
880                self.indent();
881                self.format_place_field("receiver", receiver);
882                self.format_place_field("property", property);
883                self.line("args:");
884                self.indent();
885                for (i, arg) in args.iter().enumerate() {
886                    self.format_argument(arg, i);
887                }
888                self.dedent();
889                self.line(&format!("loc: {}", format_loc(loc)));
890                self.dedent();
891                self.line("}");
892            }
893            InstructionValue::JSXText { value: val, loc } => {
894                self.line(&format!(
895                    "JSXText {{ value: {}, loc: {} }}",
896                    format_js_string(val),
897                    format_loc(loc)
898                ));
899            }
900            InstructionValue::Primitive { value: prim, loc } => {
901                self.line(&format!(
902                    "Primitive {{ value: {}, loc: {} }}",
903                    format_primitive(prim),
904                    format_loc(loc)
905                ));
906            }
907            InstructionValue::TypeCastExpression {
908                value: val,
909                type_,
910                type_annotation_name,
911                type_annotation_kind,
912                type_annotation: _,
913                loc,
914            } => {
915                self.line("TypeCastExpression {");
916                self.indent();
917                self.format_place_field("value", val);
918                self.line(&format!("type: {}", self.format_type_value(type_)));
919                if let Some(annotation_name) = type_annotation_name {
920                    self.line(&format!("typeAnnotation: {}", annotation_name));
921                }
922                if let Some(annotation_kind) = type_annotation_kind {
923                    self.line(&format!("typeAnnotationKind: \"{}\"", annotation_kind));
924                }
925                self.line(&format!("loc: {}", format_loc(loc)));
926                self.dedent();
927                self.line("}");
928            }
929            InstructionValue::JsxExpression {
930                tag,
931                props,
932                children,
933                loc,
934                opening_loc,
935                closing_loc,
936            } => {
937                self.line("JsxExpression {");
938                self.indent();
939                match tag {
940                    crate::JsxTag::Place(p) => {
941                        self.format_place_field("tag", p);
942                    }
943                    crate::JsxTag::Builtin(b) => {
944                        self.line(&format!("tag: BuiltinTag(\"{}\")", b.name));
945                    }
946                }
947                self.line("props:");
948                self.indent();
949                for (i, prop) in props.iter().enumerate() {
950                    match prop {
951                        crate::JsxAttribute::Attribute { name, place } => {
952                            self.line(&format!("[{}] JsxAttribute {{", i));
953                            self.indent();
954                            self.line(&format!("name: \"{}\"", name));
955                            self.format_place_field("place", place);
956                            self.dedent();
957                            self.line("}");
958                        }
959                        crate::JsxAttribute::SpreadAttribute { argument } => {
960                            self.line(&format!("[{}] JsxSpreadAttribute:", i));
961                            self.indent();
962                            self.format_place_field("argument", argument);
963                            self.dedent();
964                        }
965                    }
966                }
967                self.dedent();
968                match children {
969                    Some(c) => {
970                        self.line("children:");
971                        self.indent();
972                        for (i, child) in c.iter().enumerate() {
973                            self.format_place_field(&format!("[{}]", i), child);
974                        }
975                        self.dedent();
976                    }
977                    None => self.line("children: null"),
978                }
979                self.line(&format!("openingLoc: {}", format_loc(opening_loc)));
980                self.line(&format!("closingLoc: {}", format_loc(closing_loc)));
981                self.line(&format!("loc: {}", format_loc(loc)));
982                self.dedent();
983                self.line("}");
984            }
985            InstructionValue::JsxFragment { children, loc } => {
986                self.line("JsxFragment {");
987                self.indent();
988                self.line("children:");
989                self.indent();
990                for (i, child) in children.iter().enumerate() {
991                    self.format_place_field(&format!("[{}]", i), child);
992                }
993                self.dedent();
994                self.line(&format!("loc: {}", format_loc(loc)));
995                self.dedent();
996                self.line("}");
997            }
998            InstructionValue::UnsupportedNode { node_type, loc, .. } => match node_type {
999                Some(t) => self.line(&format!(
1000                    "UnsupportedNode {{ type: {:?}, loc: {} }}",
1001                    t,
1002                    format_loc(loc)
1003                )),
1004                None => self.line(&format!("UnsupportedNode {{ loc: {} }}", format_loc(loc))),
1005            },
1006            InstructionValue::LoadLocal { place, loc } => {
1007                self.line("LoadLocal {");
1008                self.indent();
1009                self.format_place_field("place", place);
1010                self.line(&format!("loc: {}", format_loc(loc)));
1011                self.dedent();
1012                self.line("}");
1013            }
1014            InstructionValue::DeclareLocal {
1015                lvalue,
1016                type_annotation,
1017                loc,
1018            } => {
1019                self.line("DeclareLocal {");
1020                self.indent();
1021                self.format_lvalue("lvalue", lvalue);
1022                self.line(&format!(
1023                    "type: {}",
1024                    match type_annotation {
1025                        Some(t) => t.clone(),
1026                        None => "null".to_string(),
1027                    }
1028                ));
1029                self.line(&format!("loc: {}", format_loc(loc)));
1030                self.dedent();
1031                self.line("}");
1032            }
1033            InstructionValue::DeclareContext { lvalue, loc } => {
1034                self.line("DeclareContext {");
1035                self.indent();
1036                self.line("lvalue:");
1037                self.indent();
1038                self.line(&format!("kind: {:?}", lvalue.kind));
1039                self.format_place_field("place", &lvalue.place);
1040                self.dedent();
1041                self.line(&format!("loc: {}", format_loc(loc)));
1042                self.dedent();
1043                self.line("}");
1044            }
1045            InstructionValue::StoreLocal {
1046                lvalue,
1047                value: val,
1048                type_annotation,
1049                loc,
1050            } => {
1051                self.line("StoreLocal {");
1052                self.indent();
1053                self.format_lvalue("lvalue", lvalue);
1054                self.format_place_field("value", val);
1055                self.line(&format!(
1056                    "type: {}",
1057                    match type_annotation {
1058                        Some(t) => t.clone(),
1059                        None => "null".to_string(),
1060                    }
1061                ));
1062                self.line(&format!("loc: {}", format_loc(loc)));
1063                self.dedent();
1064                self.line("}");
1065            }
1066            InstructionValue::LoadContext { place, loc } => {
1067                self.line("LoadContext {");
1068                self.indent();
1069                self.format_place_field("place", place);
1070                self.line(&format!("loc: {}", format_loc(loc)));
1071                self.dedent();
1072                self.line("}");
1073            }
1074            InstructionValue::StoreContext {
1075                lvalue,
1076                value: val,
1077                loc,
1078            } => {
1079                self.line("StoreContext {");
1080                self.indent();
1081                self.line("lvalue:");
1082                self.indent();
1083                self.line(&format!("kind: {:?}", lvalue.kind));
1084                self.format_place_field("place", &lvalue.place);
1085                self.dedent();
1086                self.format_place_field("value", val);
1087                self.line(&format!("loc: {}", format_loc(loc)));
1088                self.dedent();
1089                self.line("}");
1090            }
1091            InstructionValue::Destructure {
1092                lvalue,
1093                value: val,
1094                loc,
1095            } => {
1096                self.line("Destructure {");
1097                self.indent();
1098                self.line("lvalue:");
1099                self.indent();
1100                self.line(&format!("kind: {:?}", lvalue.kind));
1101                self.format_pattern(&lvalue.pattern);
1102                self.dedent();
1103                self.format_place_field("value", val);
1104                self.line(&format!("loc: {}", format_loc(loc)));
1105                self.dedent();
1106                self.line("}");
1107            }
1108            InstructionValue::PropertyLoad {
1109                object,
1110                property,
1111                loc,
1112            } => {
1113                self.line("PropertyLoad {");
1114                self.indent();
1115                self.format_place_field("object", object);
1116                self.line(&format!(
1117                    "property: \"{}\"",
1118                    format_property_literal(property)
1119                ));
1120                self.line(&format!("loc: {}", format_loc(loc)));
1121                self.dedent();
1122                self.line("}");
1123            }
1124            InstructionValue::PropertyStore {
1125                object,
1126                property,
1127                value: val,
1128                loc,
1129            } => {
1130                self.line("PropertyStore {");
1131                self.indent();
1132                self.format_place_field("object", object);
1133                self.line(&format!(
1134                    "property: \"{}\"",
1135                    format_property_literal(property)
1136                ));
1137                self.format_place_field("value", val);
1138                self.line(&format!("loc: {}", format_loc(loc)));
1139                self.dedent();
1140                self.line("}");
1141            }
1142            InstructionValue::PropertyDelete {
1143                object,
1144                property,
1145                loc,
1146            } => {
1147                self.line("PropertyDelete {");
1148                self.indent();
1149                self.format_place_field("object", object);
1150                self.line(&format!(
1151                    "property: \"{}\"",
1152                    format_property_literal(property)
1153                ));
1154                self.line(&format!("loc: {}", format_loc(loc)));
1155                self.dedent();
1156                self.line("}");
1157            }
1158            InstructionValue::ComputedLoad {
1159                object,
1160                property,
1161                loc,
1162            } => {
1163                self.line("ComputedLoad {");
1164                self.indent();
1165                self.format_place_field("object", object);
1166                self.format_place_field("property", property);
1167                self.line(&format!("loc: {}", format_loc(loc)));
1168                self.dedent();
1169                self.line("}");
1170            }
1171            InstructionValue::ComputedStore {
1172                object,
1173                property,
1174                value: val,
1175                loc,
1176            } => {
1177                self.line("ComputedStore {");
1178                self.indent();
1179                self.format_place_field("object", object);
1180                self.format_place_field("property", property);
1181                self.format_place_field("value", val);
1182                self.line(&format!("loc: {}", format_loc(loc)));
1183                self.dedent();
1184                self.line("}");
1185            }
1186            InstructionValue::ComputedDelete {
1187                object,
1188                property,
1189                loc,
1190            } => {
1191                self.line("ComputedDelete {");
1192                self.indent();
1193                self.format_place_field("object", object);
1194                self.format_place_field("property", property);
1195                self.line(&format!("loc: {}", format_loc(loc)));
1196                self.dedent();
1197                self.line("}");
1198            }
1199            InstructionValue::LoadGlobal { binding, loc } => {
1200                self.line("LoadGlobal {");
1201                self.indent();
1202                self.line(&format!("binding: {}", format_non_local_binding(binding)));
1203                self.line(&format!("loc: {}", format_loc(loc)));
1204                self.dedent();
1205                self.line("}");
1206            }
1207            InstructionValue::StoreGlobal {
1208                name,
1209                value: val,
1210                loc,
1211            } => {
1212                self.line("StoreGlobal {");
1213                self.indent();
1214                self.line(&format!("name: \"{}\"", name));
1215                self.format_place_field("value", val);
1216                self.line(&format!("loc: {}", format_loc(loc)));
1217                self.dedent();
1218                self.line("}");
1219            }
1220            InstructionValue::FunctionExpression {
1221                name,
1222                name_hint,
1223                lowered_func,
1224                expr_type,
1225                loc,
1226            } => {
1227                self.line("FunctionExpression {");
1228                self.indent();
1229                self.line(&format!(
1230                    "name: {}",
1231                    match name {
1232                        Some(n) => format!("\"{}\"", n),
1233                        None => "null".to_string(),
1234                    }
1235                ));
1236                self.line(&format!(
1237                    "nameHint: {}",
1238                    match name_hint {
1239                        Some(h) => format!("\"{}\"", h),
1240                        None => "null".to_string(),
1241                    }
1242                ));
1243                self.line(&format!("type: \"{:?}\"", expr_type));
1244                self.line("loweredFunc:");
1245                let inner_func = &self.env.functions[lowered_func.func.0 as usize];
1246                if let Some(formatter) = inner_func_formatter {
1247                    formatter(self, inner_func);
1248                } else {
1249                    self.line(&format!("  <function {}>", lowered_func.func.0));
1250                }
1251                self.line(&format!("loc: {}", format_loc(loc)));
1252                self.dedent();
1253                self.line("}");
1254            }
1255            InstructionValue::ObjectMethod { loc, lowered_func } => {
1256                self.line("ObjectMethod {");
1257                self.indent();
1258                self.line("loweredFunc:");
1259                let inner_func = &self.env.functions[lowered_func.func.0 as usize];
1260                if let Some(formatter) = inner_func_formatter {
1261                    formatter(self, inner_func);
1262                } else {
1263                    self.line(&format!("  <function {}>", lowered_func.func.0));
1264                }
1265                self.line(&format!("loc: {}", format_loc(loc)));
1266                self.dedent();
1267                self.line("}");
1268            }
1269            InstructionValue::TaggedTemplateExpression {
1270                tag,
1271                value: val,
1272                loc,
1273            } => {
1274                self.line("TaggedTemplateExpression {");
1275                self.indent();
1276                self.format_place_field("tag", tag);
1277                self.line(&format!("raw: {}", format_js_string(&val.raw)));
1278                self.line(&format!(
1279                    "cooked: {}",
1280                    match &val.cooked {
1281                        Some(c) => format_js_string(c),
1282                        None => "undefined".to_string(),
1283                    }
1284                ));
1285                self.line(&format!("loc: {}", format_loc(loc)));
1286                self.dedent();
1287                self.line("}");
1288            }
1289            InstructionValue::TemplateLiteral {
1290                subexprs,
1291                quasis,
1292                loc,
1293            } => {
1294                self.line("TemplateLiteral {");
1295                self.indent();
1296                self.line("subexprs:");
1297                self.indent();
1298                for (i, sub) in subexprs.iter().enumerate() {
1299                    self.format_place_field(&format!("[{}]", i), sub);
1300                }
1301                self.dedent();
1302                self.line("quasis:");
1303                self.indent();
1304                for (i, q) in quasis.iter().enumerate() {
1305                    self.line(&format!(
1306                        "[{}] {{ raw: {}, cooked: {} }}",
1307                        i,
1308                        format_js_string(&q.raw),
1309                        match &q.cooked {
1310                            Some(c) => format_js_string(c),
1311                            None => "undefined".to_string(),
1312                        }
1313                    ));
1314                }
1315                self.dedent();
1316                self.line(&format!("loc: {}", format_loc(loc)));
1317                self.dedent();
1318                self.line("}");
1319            }
1320            InstructionValue::RegExpLiteral {
1321                pattern,
1322                flags,
1323                loc,
1324            } => {
1325                self.line(&format!(
1326                    "RegExpLiteral {{ pattern: \"{}\", flags: \"{}\", loc: {} }}",
1327                    pattern,
1328                    flags,
1329                    format_loc(loc)
1330                ));
1331            }
1332            InstructionValue::MetaProperty {
1333                meta,
1334                property,
1335                loc,
1336            } => {
1337                self.line(&format!(
1338                    "MetaProperty {{ meta: \"{}\", property: \"{}\", loc: {} }}",
1339                    meta,
1340                    property,
1341                    format_loc(loc)
1342                ));
1343            }
1344            InstructionValue::Await { value: val, loc } => {
1345                self.line("Await {");
1346                self.indent();
1347                self.format_place_field("value", val);
1348                self.line(&format!("loc: {}", format_loc(loc)));
1349                self.dedent();
1350                self.line("}");
1351            }
1352            InstructionValue::GetIterator { collection, loc } => {
1353                self.line("GetIterator {");
1354                self.indent();
1355                self.format_place_field("collection", collection);
1356                self.line(&format!("loc: {}", format_loc(loc)));
1357                self.dedent();
1358                self.line("}");
1359            }
1360            InstructionValue::IteratorNext {
1361                iterator,
1362                collection,
1363                loc,
1364            } => {
1365                self.line("IteratorNext {");
1366                self.indent();
1367                self.format_place_field("iterator", iterator);
1368                self.format_place_field("collection", collection);
1369                self.line(&format!("loc: {}", format_loc(loc)));
1370                self.dedent();
1371                self.line("}");
1372            }
1373            InstructionValue::NextPropertyOf { value: val, loc } => {
1374                self.line("NextPropertyOf {");
1375                self.indent();
1376                self.format_place_field("value", val);
1377                self.line(&format!("loc: {}", format_loc(loc)));
1378                self.dedent();
1379                self.line("}");
1380            }
1381            InstructionValue::Debugger { loc } => {
1382                self.line(&format!("Debugger {{ loc: {} }}", format_loc(loc)));
1383            }
1384            InstructionValue::PostfixUpdate {
1385                lvalue,
1386                operation,
1387                value: val,
1388                loc,
1389            } => {
1390                self.line("PostfixUpdate {");
1391                self.indent();
1392                self.format_place_field("lvalue", lvalue);
1393                self.line(&format!("operation: \"{}\"", operation));
1394                self.format_place_field("value", val);
1395                self.line(&format!("loc: {}", format_loc(loc)));
1396                self.dedent();
1397                self.line("}");
1398            }
1399            InstructionValue::PrefixUpdate {
1400                lvalue,
1401                operation,
1402                value: val,
1403                loc,
1404            } => {
1405                self.line("PrefixUpdate {");
1406                self.indent();
1407                self.format_place_field("lvalue", lvalue);
1408                self.line(&format!("operation: \"{}\"", operation));
1409                self.format_place_field("value", val);
1410                self.line(&format!("loc: {}", format_loc(loc)));
1411                self.dedent();
1412                self.line("}");
1413            }
1414            InstructionValue::StartMemoize {
1415                manual_memo_id,
1416                deps,
1417                deps_loc: _,
1418                has_invalid_deps: _,
1419                loc,
1420            } => {
1421                self.line("StartMemoize {");
1422                self.indent();
1423                self.line(&format!("manualMemoId: {}", manual_memo_id));
1424                match deps {
1425                    Some(d) => {
1426                        self.line("deps:");
1427                        self.indent();
1428                        for (i, dep) in d.iter().enumerate() {
1429                            let root_str = match &dep.root {
1430                                crate::ManualMemoDependencyRoot::Global { identifier_name } => {
1431                                    format!("Global(\"{}\")", identifier_name)
1432                                }
1433                                crate::ManualMemoDependencyRoot::NamedLocal {
1434                                    value: val,
1435                                    constant,
1436                                } => {
1437                                    format!(
1438                                        "NamedLocal({}, constant={})",
1439                                        val.identifier.0, constant
1440                                    )
1441                                }
1442                            };
1443                            let path_str: String = dep
1444                                .path
1445                                .iter()
1446                                .map(|p| {
1447                                    format!(
1448                                        "{}.{}",
1449                                        if p.optional { "?" } else { "" },
1450                                        format_property_literal(&p.property)
1451                                    )
1452                                })
1453                                .collect();
1454                            self.line(&format!("[{}] {}{}", i, root_str, path_str));
1455                        }
1456                        self.dedent();
1457                    }
1458                    None => self.line("deps: null"),
1459                }
1460                self.line(&format!("loc: {}", format_loc(loc)));
1461                self.dedent();
1462                self.line("}");
1463            }
1464            InstructionValue::FinishMemoize {
1465                manual_memo_id,
1466                decl,
1467                pruned,
1468                loc,
1469            } => {
1470                self.line("FinishMemoize {");
1471                self.indent();
1472                self.line(&format!("manualMemoId: {}", manual_memo_id));
1473                self.format_place_field("decl", decl);
1474                self.line(&format!("pruned: {}", pruned));
1475                self.line(&format!("loc: {}", format_loc(loc)));
1476                self.dedent();
1477                self.line("}");
1478            }
1479        }
1480    }
1481
1482    // =========================================================================
1483    // Errors
1484    // =========================================================================
1485
1486    pub fn format_errors(&mut self, error: &CompilerError) {
1487        if error.details.is_empty() {
1488            self.line("Errors: []");
1489            return;
1490        }
1491        self.line("Errors:");
1492        self.indent();
1493        for (i, detail) in error.details.iter().enumerate() {
1494            self.line(&format!("[{}] {{", i));
1495            self.indent();
1496            match detail {
1497                CompilerErrorOrDiagnostic::Diagnostic(d) => {
1498                    self.line(&format!("severity: {:?}", d.severity()));
1499                    self.line(&format!("reason: {:?}", d.reason));
1500                    self.line(&format!(
1501                        "description: {}",
1502                        match &d.description {
1503                            Some(desc) => format!("{:?}", desc),
1504                            None => "null".to_string(),
1505                        }
1506                    ));
1507                    self.line(&format!("category: {:?}", d.category));
1508                    let loc = d.primary_location();
1509                    self.line(&format!(
1510                        "loc: {}",
1511                        match loc {
1512                            Some(l) => format_loc_value(l),
1513                            None => "null".to_string(),
1514                        }
1515                    ));
1516                }
1517                CompilerErrorOrDiagnostic::ErrorDetail(d) => {
1518                    self.line(&format!("severity: {:?}", d.severity()));
1519                    self.line(&format!("reason: {:?}", d.reason));
1520                    self.line(&format!(
1521                        "description: {}",
1522                        match &d.description {
1523                            Some(desc) => format!("{:?}", desc),
1524                            None => "null".to_string(),
1525                        }
1526                    ));
1527                    self.line(&format!("category: {:?}", d.category));
1528                    self.line(&format!(
1529                        "loc: {}",
1530                        match &d.loc {
1531                            Some(l) => format_loc_value(l),
1532                            None => "null".to_string(),
1533                        }
1534                    ));
1535                }
1536            }
1537            self.dedent();
1538            self.line("}");
1539        }
1540        self.dedent();
1541    }
1542}