]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs
New upstream version 1.55.0+dfsg1
[rustc.git] / compiler / rustc_codegen_ssa / src / debuginfo / type_names.rs
CommitLineData
d9579d0f
AL
1// Type Names for Debug Info.
2
136023e0
XL
3// Notes on targetting MSVC:
4// In general, MSVC's debugger attempts to parse all arguments as C++ expressions,
5// even if the argument is explicitly a symbol name.
6// As such, there are many things that cause parsing issues:
7// * `#` is treated as a special character for macros.
8// * `{` or `<` at the beginning of a name is treated as an operator.
9// * `>>` is always treated as a right-shift.
10// * `[` in a name is treated like a regex bracket expression (match any char
11// within the brackets).
12// * `"` is treated as the start of a string.
13
532ac7d7 14use rustc_data_structures::fx::FxHashSet;
136023e0 15use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
dfeec247
XL
16use rustc_hir as hir;
17use rustc_hir::def_id::DefId;
136023e0
XL
18use rustc_hir::definitions::{DefPathData, DefPathDataName, DisambiguatedDefPathData};
19use rustc_middle::ich::NodeIdHashingMode;
20use rustc_middle::ty::layout::IntegerExt;
21use rustc_middle::ty::subst::{GenericArgKind, SubstsRef};
22use rustc_middle::ty::{self, AdtDef, ExistentialProjection, Ty, TyCtxt};
23use rustc_target::abi::{Integer, TagEncoding, Variants};
24use smallvec::SmallVec;
d9579d0f 25
1b1a35ee
XL
26use std::fmt::Write;
27
d9579d0f 28// Compute the name of the type as it should be stored in debuginfo. Does not do
0731742a 29// any caching, i.e., calling the function twice with the same type will also do
d9579d0f 30// the work twice. The `qualified` parameter only affects the first level of the
0731742a 31// type name, further levels (i.e., type parameters) are always fully qualified.
dc9dc135
XL
32pub fn compute_debuginfo_type_name<'tcx>(
33 tcx: TyCtxt<'tcx>,
34 t: Ty<'tcx>,
35 qualified: bool,
36) -> String {
136023e0
XL
37 let _prof = tcx.prof.generic_activity("compute_debuginfo_type_name");
38
d9579d0f 39 let mut result = String::with_capacity(64);
532ac7d7 40 let mut visited = FxHashSet::default();
48663c56 41 push_debuginfo_type_name(tcx, t, qualified, &mut result, &mut visited);
d9579d0f
AL
42 result
43}
44
45// Pushes the name of the type as it should be stored in debuginfo on the
46// `output` String. See also compute_debuginfo_type_name().
136023e0 47fn push_debuginfo_type_name<'tcx>(
dc9dc135
XL
48 tcx: TyCtxt<'tcx>,
49 t: Ty<'tcx>,
50 qualified: bool,
51 output: &mut String,
52 visited: &mut FxHashSet<Ty<'tcx>>,
53) {
3b2f2976
XL
54 // When targeting MSVC, emit C++ style type names for compatibility with
55 // .natvis visualizers (and perhaps other existing native debuggers?)
136023e0 56 let cpp_like_names = cpp_like_names(tcx);
3b2f2976 57
1b1a35ee 58 match *t.kind() {
b7449926
XL
59 ty::Bool => output.push_str("bool"),
60 ty::Char => output.push_str("char"),
61 ty::Str => output.push_str("str"),
136023e0
XL
62 ty::Never => {
63 if cpp_like_names {
64 output.push_str("never$");
65 } else {
66 output.push('!');
67 }
68 }
60c5eb7d
XL
69 ty::Int(int_ty) => output.push_str(int_ty.name_str()),
70 ty::Uint(uint_ty) => output.push_str(uint_ty.name_str()),
71 ty::Float(float_ty) => output.push_str(float_ty.name_str()),
48663c56 72 ty::Foreign(def_id) => push_item_name(tcx, def_id, qualified, output),
b7449926 73 ty::Adt(def, substs) => {
17df50a5
XL
74 if def.is_enum() && cpp_like_names {
75 msvc_enum_fallback(tcx, t, def, substs, output, visited);
76 } else {
77 push_item_name(tcx, def.did, qualified, output);
136023e0 78 push_generic_params_internal(tcx, substs, output, visited);
17df50a5 79 }
dfeec247 80 }
b7449926 81 ty::Tuple(component_types) => {
f035d41b 82 if cpp_like_names {
136023e0 83 output.push_str("tuple$<");
f035d41b
XL
84 } else {
85 output.push('(');
86 }
87
f9f354fc 88 for component_type in component_types {
48663c56 89 push_debuginfo_type_name(tcx, component_type.expect_ty(), true, output, visited);
136023e0 90 push_arg_separator(cpp_like_names, output);
d9579d0f
AL
91 }
92 if !component_types.is_empty() {
136023e0 93 pop_arg_separator(output);
d9579d0f 94 }
f035d41b
XL
95
96 if cpp_like_names {
136023e0 97 push_close_angle_bracket(cpp_like_names, output);
f035d41b
XL
98 } else {
99 output.push(')');
100 }
dfeec247
XL
101 }
102 ty::RawPtr(ty::TypeAndMut { ty: inner_type, mutbl }) => {
136023e0
XL
103 if cpp_like_names {
104 match mutbl {
105 hir::Mutability::Not => output.push_str("ptr_const$<"),
106 hir::Mutability::Mut => output.push_str("ptr_mut$<"),
107 }
108 } else {
3b2f2976 109 output.push('*');
136023e0
XL
110 match mutbl {
111 hir::Mutability::Not => output.push_str("const "),
112 hir::Mutability::Mut => output.push_str("mut "),
113 }
d9579d0f
AL
114 }
115
136023e0 116 push_debuginfo_type_name(tcx, inner_type, qualified, output, visited);
3b2f2976
XL
117
118 if cpp_like_names {
136023e0 119 push_close_angle_bracket(cpp_like_names, output);
3b2f2976 120 }
dfeec247 121 }
b7449926 122 ty::Ref(_, inner_type, mutbl) => {
136023e0
XL
123 // Slices and `&str` are treated like C++ pointers when computing debug
124 // info for MSVC debugger. However, wrapping these types' names in a synthetic type
125 // causes the .natvis engine for WinDbg to fail to display their data, so we opt these
126 // types out to aid debugging in MSVC.
127 let is_slice_or_str = match *inner_type.kind() {
128 ty::Slice(_) | ty::Str => true,
129 _ => false,
130 };
131
3b2f2976
XL
132 if !cpp_like_names {
133 output.push('&');
136023e0
XL
134 output.push_str(mutbl.prefix_str());
135 } else if !is_slice_or_str {
136 match mutbl {
137 hir::Mutability::Not => output.push_str("ref$<"),
138 hir::Mutability::Mut => output.push_str("ref_mut$<"),
139 }
3b2f2976 140 }
d9579d0f 141
136023e0 142 push_debuginfo_type_name(tcx, inner_type, qualified, output, visited);
3b2f2976 143
136023e0
XL
144 if cpp_like_names && !is_slice_or_str {
145 push_close_angle_bracket(cpp_like_names, output);
3b2f2976 146 }
dfeec247 147 }
b7449926 148 ty::Array(inner_type, len) => {
136023e0
XL
149 if cpp_like_names {
150 output.push_str("array$<");
151 push_debuginfo_type_name(tcx, inner_type, true, output, visited);
152 match len.val {
153 ty::ConstKind::Param(param) => write!(output, ",{}>", param.name).unwrap(),
154 _ => write!(output, ",{}>", len.eval_usize(tcx, ty::ParamEnv::reveal_all()))
155 .unwrap(),
156 }
157 } else {
158 output.push('[');
159 push_debuginfo_type_name(tcx, inner_type, true, output, visited);
160 match len.val {
161 ty::ConstKind::Param(param) => write!(output, "; {}]", param.name).unwrap(),
162 _ => write!(output, "; {}]", len.eval_usize(tcx, ty::ParamEnv::reveal_all()))
163 .unwrap(),
164 }
165 }
dfeec247 166 }
b7449926 167 ty::Slice(inner_type) => {
3b2f2976 168 if cpp_like_names {
136023e0 169 output.push_str("slice$<");
3b2f2976
XL
170 } else {
171 output.push('[');
172 }
173
48663c56 174 push_debuginfo_type_name(tcx, inner_type, true, output, visited);
3b2f2976
XL
175
176 if cpp_like_names {
136023e0 177 push_close_angle_bracket(cpp_like_names, output);
3b2f2976
XL
178 } else {
179 output.push(']');
180 }
dfeec247 181 }
b7449926 182 ty::Dynamic(ref trait_data, ..) => {
136023e0
XL
183 let auto_traits: SmallVec<[DefId; 4]> = trait_data.auto_traits().collect();
184
185 let has_enclosing_parens = if cpp_like_names {
186 output.push_str("dyn$<");
187 false
188 } else {
189 if trait_data.len() > 1 && auto_traits.len() != 0 {
190 // We need enclosing parens because there is more than one trait
191 output.push_str("(dyn ");
192 true
193 } else {
194 output.push_str("dyn ");
195 false
196 }
197 };
198
0731742a 199 if let Some(principal) = trait_data.principal() {
fc512014
XL
200 let principal =
201 tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), principal);
136023e0
XL
202 push_item_name(tcx, principal.def_id, qualified, output);
203 let principal_has_generic_params =
204 push_generic_params_internal(tcx, principal.substs, output, visited);
205
206 let projection_bounds: SmallVec<[_; 4]> = trait_data
207 .projection_bounds()
208 .map(|bound| {
209 let ExistentialProjection { item_def_id, ty, .. } = bound.skip_binder();
210 (item_def_id, ty)
211 })
212 .collect();
213
214 if projection_bounds.len() != 0 {
215 if principal_has_generic_params {
216 // push_generic_params_internal() above added a `>` but we actually
217 // want to add more items to that list, so remove that again.
218 pop_close_angle_bracket(output);
219 }
220
221 for (item_def_id, ty) in projection_bounds {
222 push_arg_separator(cpp_like_names, output);
223
224 if cpp_like_names {
225 output.push_str("assoc$<");
226 push_item_name(tcx, item_def_id, false, output);
227 push_arg_separator(cpp_like_names, output);
228 push_debuginfo_type_name(tcx, ty, true, output, visited);
229 push_close_angle_bracket(cpp_like_names, output);
230 } else {
231 push_item_name(tcx, item_def_id, false, output);
232 output.push('=');
233 push_debuginfo_type_name(tcx, ty, true, output, visited);
234 }
235 }
236
237 push_close_angle_bracket(cpp_like_names, output);
238 }
239
240 if auto_traits.len() != 0 {
241 push_auto_trait_separator(cpp_like_names, output);
242 }
243 }
244
245 if auto_traits.len() != 0 {
246 let mut auto_traits: SmallVec<[String; 4]> = auto_traits
247 .into_iter()
248 .map(|def_id| {
249 let mut name = String::with_capacity(20);
250 push_item_name(tcx, def_id, true, &mut name);
251 name
252 })
253 .collect();
254 auto_traits.sort_unstable();
255
256 for auto_trait in auto_traits {
257 output.push_str(&auto_trait);
258 push_auto_trait_separator(cpp_like_names, output);
259 }
260
261 pop_auto_trait_separator(output);
262 }
263
264 if cpp_like_names {
265 push_close_angle_bracket(cpp_like_names, output);
266 } else if has_enclosing_parens {
267 output.push(')');
0731742a 268 }
dfeec247 269 }
b7449926 270 ty::FnDef(..) | ty::FnPtr(_) => {
532ac7d7
XL
271 // We've encountered a weird 'recursive type'
272 // Currently, the only way to generate such a type
273 // is by using 'impl trait':
274 //
275 // fn foo() -> impl Copy { foo }
276 //
277 // There's not really a sensible name we can generate,
278 // since we don't include 'impl trait' types (e.g. ty::Opaque)
279 // in the output
280 //
281 // Since we need to generate *something*, we just
282 // use a dummy string that should make it clear
283 // that something unusual is going on
284 if !visited.insert(t) {
136023e0
XL
285 output.push_str(if cpp_like_names {
286 "recursive_type$"
287 } else {
288 "<recursive_type>"
289 });
532ac7d7
XL
290 return;
291 }
292
136023e0
XL
293 let sig =
294 tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), t.fn_sig(tcx));
d9579d0f 295
136023e0
XL
296 if cpp_like_names {
297 // Format as a C++ function pointer: return_type (*)(params...)
298 if sig.output().is_unit() {
299 output.push_str("void");
300 } else {
301 push_debuginfo_type_name(tcx, sig.output(), true, output, visited);
302 }
303 output.push_str(" (*)(");
304 } else {
305 output.push_str(sig.unsafety.prefix_str());
306
307 if sig.abi != rustc_target::spec::abi::Abi::Rust {
308 output.push_str("extern \"");
309 output.push_str(sig.abi.name());
310 output.push_str("\" ");
311 }
d9579d0f 312
136023e0
XL
313 output.push_str("fn(");
314 }
d9579d0f 315
476ff2be
SL
316 if !sig.inputs().is_empty() {
317 for &parameter_type in sig.inputs() {
48663c56 318 push_debuginfo_type_name(tcx, parameter_type, true, output, visited);
136023e0 319 push_arg_separator(cpp_like_names, output);
d9579d0f 320 }
136023e0 321 pop_arg_separator(output);
d9579d0f
AL
322 }
323
532ac7d7 324 if sig.c_variadic {
476ff2be 325 if !sig.inputs().is_empty() {
d9579d0f
AL
326 output.push_str(", ...");
327 } else {
328 output.push_str("...");
329 }
330 }
331
332 output.push(')');
333
136023e0 334 if !cpp_like_names && !sig.output().is_unit() {
5bcae85e 335 output.push_str(" -> ");
48663c56 336 push_debuginfo_type_name(tcx, sig.output(), true, output, visited);
d9579d0f 337 }
532ac7d7 338
532ac7d7
XL
339 // We only keep the type in 'visited'
340 // for the duration of the body of this method.
341 // It's fine for a particular function type
342 // to show up multiple times in one overall type
343 // (e.g. MyType<fn() -> u8, fn() -> u8>
344 //
345 // We only care about avoiding recursing
346 // directly back to the type we're currently
347 // processing
348 visited.remove(t);
dfeec247 349 }
136023e0
XL
350 ty::Closure(def_id, ..) | ty::Generator(def_id, ..) => {
351 let key = tcx.def_key(def_id);
352 if qualified {
353 let parent_def_id = DefId { index: key.parent.unwrap(), ..def_id };
354 push_item_name(tcx, parent_def_id, true, output);
355 output.push_str("::");
356 }
357 push_unqualified_item_name(tcx, def_id, key.disambiguated_data, output);
ea8adc8c 358 }
3dfed10e
XL
359 // Type parameters from polymorphized functions.
360 ty::Param(_) => {
361 output.push_str(&format!("{:?}", t));
362 }
f035d41b 363 ty::Error(_)
dfeec247
XL
364 | ty::Infer(_)
365 | ty::Placeholder(..)
dfeec247
XL
366 | ty::Projection(..)
367 | ty::Bound(..)
368 | ty::Opaque(..)
3dfed10e 369 | ty::GeneratorWitness(..) => {
dfeec247
XL
370 bug!(
371 "debuginfo: Trying to create type name for \
372 unexpected type: {:?}",
373 t
374 );
d9579d0f
AL
375 }
376 }
377
17df50a5
XL
378 /// MSVC names enums differently than other platforms so that the debugging visualization
379 // format (natvis) is able to understand enums and render the active variant correctly in the
380 // debugger. For more information, look in `src/etc/natvis/intrinsic.natvis` and
381 // `EnumMemberDescriptionFactor::create_member_descriptions`.
382 fn msvc_enum_fallback(
383 tcx: TyCtxt<'tcx>,
384 ty: Ty<'tcx>,
385 def: &AdtDef,
386 substs: SubstsRef<'tcx>,
387 output: &mut String,
388 visited: &mut FxHashSet<Ty<'tcx>>,
389 ) {
390 let layout = tcx.layout_of(tcx.param_env(def.did).and(ty)).expect("layout error");
391
136023e0
XL
392 output.push_str("enum$<");
393 push_item_name(tcx, def.did, true, output);
394 push_generic_params_internal(tcx, substs, output, visited);
395
17df50a5
XL
396 if let Variants::Multiple {
397 tag_encoding: TagEncoding::Niche { dataful_variant, .. },
398 tag,
399 variants,
400 ..
401 } = &layout.variants
402 {
403 let dataful_variant_layout = &variants[*dataful_variant];
404
405 // calculate the range of values for the dataful variant
406 let dataful_discriminant_range =
407 &dataful_variant_layout.largest_niche.as_ref().unwrap().scalar.valid_range;
408
409 let min = dataful_discriminant_range.start();
410 let min = tag.value.size(&tcx).truncate(*min);
411
412 let max = dataful_discriminant_range.end();
413 let max = tag.value.size(&tcx).truncate(*max);
414
17df50a5
XL
415 let dataful_variant_name = def.variants[*dataful_variant].ident.as_str();
416
136023e0
XL
417 output.push_str(&format!(", {}, {}, {}", min, max, dataful_variant_name));
418 } else if let Variants::Single { index: variant_idx } = &layout.variants {
419 // Uninhabited enums can't be constructed and should never need to be visualized so
420 // skip this step for them.
421 if def.variants.len() != 0 {
422 let variant = def.variants[*variant_idx].ident.as_str();
423
424 output.push_str(&format!(", {}", variant));
425 }
426 }
427 push_close_angle_bracket(true, output);
428 }
429
430 const NON_CPP_AUTO_TRAIT_SEPARATOR: &str = " + ";
431
432 fn push_auto_trait_separator(cpp_like_names: bool, output: &mut String) {
433 if cpp_like_names {
434 push_arg_separator(cpp_like_names, output);
17df50a5 435 } else {
136023e0 436 output.push_str(NON_CPP_AUTO_TRAIT_SEPARATOR);
17df50a5
XL
437 }
438 }
439
136023e0
XL
440 fn pop_auto_trait_separator(output: &mut String) {
441 if output.ends_with(NON_CPP_AUTO_TRAIT_SEPARATOR) {
442 output.truncate(output.len() - NON_CPP_AUTO_TRAIT_SEPARATOR.len());
443 } else {
444 pop_arg_separator(output);
445 }
446 }
447}
448
449pub fn push_item_name(tcx: TyCtxt<'tcx>, def_id: DefId, qualified: bool, output: &mut String) {
450 let def_key = tcx.def_key(def_id);
451 if qualified {
452 if let Some(parent) = def_key.parent {
453 push_item_name(tcx, DefId { krate: def_id.krate, index: parent }, true, output);
454 output.push_str("::");
455 }
456 }
457
458 push_unqualified_item_name(tcx, def_id, def_key.disambiguated_data, output);
459}
460
461fn push_unqualified_item_name(
462 tcx: TyCtxt<'tcx>,
463 def_id: DefId,
464 disambiguated_data: DisambiguatedDefPathData,
465 output: &mut String,
466) {
467 match disambiguated_data.data {
468 DefPathData::CrateRoot => {
48663c56 469 output.push_str(&tcx.crate_name(def_id.krate).as_str());
136023e0
XL
470 }
471 DefPathData::ClosureExpr if tcx.generator_kind(def_id).is_some() => {
472 // Generators look like closures, but we want to treat them differently
473 // in the debug info.
474 if cpp_like_names(tcx) {
475 write!(output, "generator${}", disambiguated_data.disambiguator).unwrap();
476 } else {
477 write!(output, "{{generator#{}}}", disambiguated_data.disambiguator).unwrap();
d9579d0f 478 }
54a0048b 479 }
136023e0
XL
480 _ => match disambiguated_data.data.name() {
481 DefPathDataName::Named(name) => {
482 output.push_str(&name.as_str());
483 }
484 DefPathDataName::Anon { namespace } => {
485 if cpp_like_names(tcx) {
486 write!(output, "{}${}", namespace, disambiguated_data.disambiguator).unwrap();
487 } else {
488 write!(output, "{{{}#{}}}", namespace, disambiguated_data.disambiguator)
489 .unwrap();
490 }
491 }
492 },
493 };
494}
495
496// Pushes the generic parameters in the given `InternalSubsts` to the output string.
497// This ignores region parameters, since they can't reliably be
498// reconstructed for items from non-local crates. For local crates, this
499// would be possible but with inlining and LTO we have to use the least
500// common denominator - otherwise we would run into conflicts.
501fn push_generic_params_internal<'tcx>(
502 tcx: TyCtxt<'tcx>,
503 substs: SubstsRef<'tcx>,
504 output: &mut String,
505 visited: &mut FxHashSet<Ty<'tcx>>,
506) -> bool {
507 if substs.non_erasable_generics().next().is_none() {
508 return false;
d9579d0f
AL
509 }
510
136023e0
XL
511 debug_assert_eq!(substs, tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), substs));
512
513 let cpp_like_names = cpp_like_names(tcx);
514
515 output.push('<');
516
517 for type_parameter in substs.non_erasable_generics() {
518 match type_parameter {
519 GenericArgKind::Type(type_parameter) => {
520 push_debuginfo_type_name(tcx, type_parameter, true, output, visited);
521 }
522 GenericArgKind::Const(ct) => {
523 push_const_param(tcx, ct, output);
524 }
525 other => bug!("Unexpected non-erasable generic: {:?}", other),
d9579d0f
AL
526 }
527
136023e0
XL
528 push_arg_separator(cpp_like_names, output);
529 }
530 pop_arg_separator(output);
531 push_close_angle_bracket(cpp_like_names, output);
532
533 true
534}
d9579d0f 535
136023e0
XL
536fn push_const_param<'tcx>(tcx: TyCtxt<'tcx>, ct: &'tcx ty::Const<'tcx>, output: &mut String) {
537 match ct.val {
538 ty::ConstKind::Param(param) => {
539 write!(output, "{}", param.name)
d9579d0f 540 }
136023e0
XL
541 _ => match ct.ty.kind() {
542 ty::Int(ity) => {
543 let bits = ct.eval_bits(tcx, ty::ParamEnv::reveal_all(), ct.ty);
544 let val = Integer::from_int_ty(&tcx, *ity).size().sign_extend(bits) as i128;
545 write!(output, "{}", val)
546 }
547 ty::Uint(_) => {
548 let val = ct.eval_bits(tcx, ty::ParamEnv::reveal_all(), ct.ty);
549 write!(output, "{}", val)
550 }
551 ty::Bool => {
552 let val = ct.try_eval_bool(tcx, ty::ParamEnv::reveal_all()).unwrap();
553 write!(output, "{}", val)
554 }
555 _ => {
556 // If we cannot evaluate the constant to a known type, we fall back
557 // to emitting a stable hash value of the constant. This isn't very pretty
558 // but we get a deterministic, virtually unique value for the constant.
559 let hcx = &mut tcx.create_stable_hashing_context();
560 let mut hasher = StableHasher::new();
561 hcx.while_hashing_spans(false, |hcx| {
562 hcx.with_node_id_hashing_mode(NodeIdHashingMode::HashDefPath, |hcx| {
563 ct.val.hash_stable(hcx, &mut hasher);
564 });
565 });
566 // Let's only emit 64 bits of the hash value. That should be plenty for
567 // avoiding collisions and will make the emitted type names shorter.
568 let hash: u64 = hasher.finish();
569
570 if cpp_like_names(tcx) {
571 write!(output, "CONST${:x}", hash)
572 } else {
573 write!(output, "{{CONST#{:x}}}", hash)
574 }
575 }
576 },
577 }
578 .unwrap();
579}
d9579d0f 580
136023e0
XL
581pub fn push_generic_params<'tcx>(tcx: TyCtxt<'tcx>, substs: SubstsRef<'tcx>, output: &mut String) {
582 let _prof = tcx.prof.generic_activity("compute_debuginfo_type_name");
583 let mut visited = FxHashSet::default();
584 push_generic_params_internal(tcx, substs, output, &mut visited);
585}
586
587fn push_close_angle_bracket(cpp_like_names: bool, output: &mut String) {
588 // MSVC debugger always treats `>>` as a shift, even when parsing templates,
589 // so add a space to avoid confusion.
590 if cpp_like_names && output.ends_with('>') {
591 output.push(' ')
592 };
593
594 output.push('>');
595}
596
597fn pop_close_angle_bracket(output: &mut String) {
598 assert!(output.ends_with('>'), "'output' does not end with '>': {}", output);
599 output.pop();
600 if output.ends_with(' ') {
d9579d0f 601 output.pop();
136023e0
XL
602 }
603}
d9579d0f 604
136023e0
XL
605fn push_arg_separator(cpp_like_names: bool, output: &mut String) {
606 // Natvis does not always like having spaces between parts of the type name
607 // and this causes issues when we need to write a typename in natvis, for example
608 // as part of a cast like the `HashMap` visualizer does.
609 if cpp_like_names {
610 output.push(',');
611 } else {
612 output.push_str(", ");
613 };
614}
615
616fn pop_arg_separator(output: &mut String) {
617 if output.ends_with(' ') {
618 output.pop();
d9579d0f 619 }
136023e0
XL
620
621 assert!(output.ends_with(','));
622
623 output.pop();
624}
625
626fn cpp_like_names(tcx: TyCtxt<'_>) -> bool {
627 tcx.sess.target.is_like_msvc
d9579d0f 628}