Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve debugging experience for enums on windows-msvc #85292

Merged
merged 10 commits into from
Jun 3, 2021
251 changes: 149 additions & 102 deletions compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion compiler/rustc_codegen_llvm/src/llvm/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2038,7 +2038,7 @@ extern "C" {

pub fn LLVMRustDIBuilderCreateUnionType(
Builder: &DIBuilder<'a>,
Scope: &'a DIScope,
Scope: Option<&'a DIScope>,
Name: *const c_char,
NameLen: size_t,
File: &'a DIFile,
Expand Down
59 changes: 56 additions & 3 deletions compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
use rustc_data_structures::fx::FxHashSet;
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_middle::ty::{self, subst::SubstsRef, Ty, TyCtxt};
use rustc_middle::ty::{self, subst::SubstsRef, AdtDef, Ty, TyCtxt};
use rustc_target::abi::{TagEncoding, Variants};

use std::fmt::Write;

Expand Down Expand Up @@ -45,8 +46,12 @@ pub fn push_debuginfo_type_name<'tcx>(
ty::Float(float_ty) => output.push_str(float_ty.name_str()),
ty::Foreign(def_id) => push_item_name(tcx, def_id, qualified, output),
ty::Adt(def, substs) => {
push_item_name(tcx, def.did, qualified, output);
push_type_params(tcx, substs, output, visited);
if def.is_enum() && cpp_like_names {
msvc_enum_fallback(tcx, t, def, substs, output, visited);
} else {
push_item_name(tcx, def.did, qualified, output);
push_type_params(tcx, substs, output, visited);
}
}
ty::Tuple(component_types) => {
if cpp_like_names {
Expand Down Expand Up @@ -233,6 +238,54 @@ pub fn push_debuginfo_type_name<'tcx>(
}
}

/// MSVC names enums differently than other platforms so that the debugging visualization
// format (natvis) is able to understand enums and render the active variant correctly in the
// debugger. For more information, look in `src/etc/natvis/intrinsic.natvis` and
// `EnumMemberDescriptionFactor::create_member_descriptions`.
fn msvc_enum_fallback(
wesleywiser marked this conversation as resolved.
Show resolved Hide resolved
tcx: TyCtxt<'tcx>,
ty: Ty<'tcx>,
def: &AdtDef,
substs: SubstsRef<'tcx>,
output: &mut String,
visited: &mut FxHashSet<Ty<'tcx>>,
) {
let layout = tcx.layout_of(tcx.param_env(def.did).and(ty)).expect("layout error");

if let Variants::Multiple {
tag_encoding: TagEncoding::Niche { dataful_variant, .. },
tag,
variants,
..
} = &layout.variants
{
let dataful_variant_layout = &variants[*dataful_variant];

// calculate the range of values for the dataful variant
let dataful_discriminant_range =
&dataful_variant_layout.largest_niche.as_ref().unwrap().scalar.valid_range;

let min = dataful_discriminant_range.start();
let min = tag.value.size(&tcx).truncate(*min);

let max = dataful_discriminant_range.end();
let max = tag.value.size(&tcx).truncate(*max);

output.push_str("enum$<");
push_item_name(tcx, def.did, true, output);
push_type_params(tcx, substs, output, visited);

let dataful_variant_name = def.variants[*dataful_variant].ident.as_str();

output.push_str(&format!(", {}, {}, {}>", min, max, dataful_variant_name));
} else {
output.push_str("enum$<");
push_item_name(tcx, def.did, true, output);
push_type_params(tcx, substs, output, visited);
output.push('>');
}
}

fn push_item_name(tcx: TyCtxt<'tcx>, def_id: DefId, qualified: bool, output: &mut String) {
if qualified {
output.push_str(&tcx.crate_name(def_id.krate).as_str());
Expand Down
53 changes: 53 additions & 0 deletions src/etc/natvis/intrinsic.natvis
Original file line number Diff line number Diff line change
Expand Up @@ -149,4 +149,57 @@
<Synthetic Name="[...]"><DisplayString>...</DisplayString></Synthetic>
</Expand>
</Type>
<Type Name="enum$&lt;*&gt;">
<Intrinsic Name="tag" Expression="variant0.variant$" />
<DisplayString Condition="tag() == 0">{tag(),en}</DisplayString>
<DisplayString Condition="tag() == 1" Optional="true">{tag(),en}</DisplayString>
<DisplayString Condition="tag() == 2" Optional="true">{tag(),en}</DisplayString>
<DisplayString Condition="tag() == 3" Optional="true">{tag(),en}</DisplayString>
<DisplayString Condition="tag() == 4" Optional="true">{tag(),en}</DisplayString>
<DisplayString Condition="tag() == 5" Optional="true">{tag(),en}</DisplayString>
<DisplayString Condition="tag() == 6" Optional="true">{tag(),en}</DisplayString>
<DisplayString Condition="tag() == 7" Optional="true">{tag(),en}</DisplayString>
<DisplayString Condition="tag() == 8" Optional="true">{tag(),en}</DisplayString>
<DisplayString Condition="tag() == 9" Optional="true">{tag(),en}</DisplayString>
<DisplayString Condition="tag() == 10" Optional="true">{tag(),en}</DisplayString>
<DisplayString Condition="tag() == 11" Optional="true">{tag(),en}</DisplayString>
<DisplayString Condition="tag() == 12" Optional="true">{tag(),en}</DisplayString>
<DisplayString Condition="tag() == 13" Optional="true">{tag(),en}</DisplayString>
<DisplayString Condition="tag() == 14" Optional="true">{tag(),en}</DisplayString>
<DisplayString Condition="tag() == 15" Optional="true">{tag(),en}</DisplayString>

<Expand>
<ExpandedItem Condition="tag() == 0">variant0</ExpandedItem>
<ExpandedItem Condition="tag() == 1" Optional="true">variant1</ExpandedItem>
<ExpandedItem Condition="tag() == 2" Optional="true">variant2</ExpandedItem>
<ExpandedItem Condition="tag() == 3" Optional="true">variant3</ExpandedItem>
<ExpandedItem Condition="tag() == 4" Optional="true">variant4</ExpandedItem>
<ExpandedItem Condition="tag() == 5" Optional="true">variant5</ExpandedItem>
<ExpandedItem Condition="tag() == 6" Optional="true">variant6</ExpandedItem>
<ExpandedItem Condition="tag() == 7" Optional="true">variant7</ExpandedItem>
<ExpandedItem Condition="tag() == 8" Optional="true">variant8</ExpandedItem>
<ExpandedItem Condition="tag() == 9" Optional="true">variant9</ExpandedItem>
<ExpandedItem Condition="tag() == 10" Optional="true">variant10</ExpandedItem>
<ExpandedItem Condition="tag() == 11" Optional="true">variant11</ExpandedItem>
<ExpandedItem Condition="tag() == 12" Optional="true">variant12</ExpandedItem>
<ExpandedItem Condition="tag() == 13" Optional="true">variant13</ExpandedItem>
<ExpandedItem Condition="tag() == 14" Optional="true">variant14</ExpandedItem>
<ExpandedItem Condition="tag() == 15" Optional="true">variant15</ExpandedItem>
</Expand>
</Type>

<!-- $T1 is the name of the enum, $T2 is the low value of the dataful variant tag,
$T3 is the high value of the dataful variant tag, $T4 is the name of the dataful variant -->
<Type Name="enum$&lt;*, *, *, *&gt;">
<Intrinsic Name="tag" Expression="discriminant" />
<Intrinsic Name="is_dataful" Expression="tag() &gt;= $T2 &amp;&amp; tag() &lt;= $T3" />
<DisplayString Condition="is_dataful()">{"$T4",sb}({dataful_variant})</DisplayString>
<DisplayString Condition="!is_dataful()">{discriminant,en}</DisplayString>
<Expand>
<ExpandedItem Condition="is_dataful()">dataful_variant</ExpandedItem>
<Synthetic Condition="is_dataful()" Name="[variant]">
<DisplayString>{"$T4",sb}</DisplayString>
</Synthetic>
</Expand>
</Type>
</AutoVisualizer>
17 changes: 0 additions & 17 deletions src/etc/natvis/libcore.natvis
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,6 @@
</Expand>
</Type>

<Type Name="core::option::Option&lt;*&gt;">
<DisplayString Condition="RUST$ENUM$DISR == 0x0">None</DisplayString>
<DisplayString Condition="RUST$ENUM$DISR == 0x1">Some({__0})</DisplayString>
<Expand>
<Item Name="[value]" ExcludeView="simple" Condition="RUST$ENUM$DISR == 1">__0</Item>
</Expand>
</Type>

<Type Name="core::option::Option&lt;*&gt;" Priority="MediumLow">
<DisplayString Condition="*(void**)this == nullptr">None</DisplayString>
<DisplayString>Some({($T1 *)this})</DisplayString>
Expand All @@ -30,15 +22,6 @@
</Expand>
</Type>

<Type Name="core::result::Result&lt;*&gt;">
<DisplayString Condition="RUST$ENUM$DISR == 0x0">Ok({__0})</DisplayString>
<DisplayString Condition="RUST$ENUM$DISR == 0x1">Err({(*($T2*) &amp;__0)})</DisplayString>
<Expand>
<Item Name="[value]" Condition="RUST$ENUM$DISR == 0x0">__0</Item>
<Item Name="[value]" Condition="RUST$ENUM$DISR == 0x1">(*($T2*) &amp;__0)</Item>
</Expand>
</Type>

<Type Name="core::ptr::non_null::NonNull&lt;*&gt;">
<DisplayString>{(void*) pointer}</DisplayString>
<Expand>
Expand Down
16 changes: 8 additions & 8 deletions src/test/codegen/async-fn-debug-msvc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,33 +17,33 @@ async fn async_fn_test() {
// FIXME: No way to reliably check the filename.

// CHECK-DAG: [[ASYNC_FN:!.*]] = !DINamespace(name: "async_fn_test"
// CHECK-DAG: [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_union_type, name: "generator-0", scope: [[ASYNC_FN]]
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, scope: [[GEN]],
// CHECK-DAG: [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_union_type, name: "generator-0"
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "variant0", scope: [[GEN]],
// For brevity, we only check the struct name and members of the last variant.
// CHECK-SAME: file: [[FILE:![0-9]*]], line: 11,
// CHECK-NOT: flags: DIFlagArtificial
// CHECK-SAME: )
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, scope: [[GEN]],
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "variant1", scope: [[GEN]],
// CHECK-SAME: file: [[FILE]], line: 15,
// CHECK-NOT: flags: DIFlagArtificial
// CHECK-SAME: )
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, scope: [[GEN]],
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "variant2", scope: [[GEN]],
// CHECK-SAME: file: [[FILE]], line: 15,
// CHECK-NOT: flags: DIFlagArtificial
// CHECK-SAME: )
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, scope: [[GEN]],
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "variant3", scope: [[GEN]],
// CHECK-SAME: file: [[FILE]], line: 12,
// CHECK-NOT: flags: DIFlagArtificial
// CHECK-SAME: )
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, scope: [[GEN]],
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "variant4", scope: [[GEN]],
// CHECK-SAME: file: [[FILE]], line: 14,
// CHECK-SAME: baseType: [[VARIANT:![0-9]*]]
// CHECK-NOT: flags: DIFlagArtificial
// CHECK-SAME: )
// CHECK: [[S1:!.*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Suspend1", scope: [[ASYNC_FN]],
// CHECK: [[S1:!.*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Suspend1", scope: [[GEN]],
// CHECK-NOT: flags: DIFlagArtificial
// CHECK-SAME: )
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "RUST$ENUM$DISR", scope: [[S1]],
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "variant$", scope: [[S1]],
// CHECK-SAME: flags: DIFlagArtificial
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "s", scope: [[S1]]
// CHECK-NOT: flags: DIFlagArtificial
Expand Down
16 changes: 8 additions & 8 deletions src/test/codegen/generator-debug-msvc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,33 +21,33 @@ fn generator_test() -> impl Generator<Yield = i32, Return = ()> {
// FIXME: No way to reliably check the filename.

// CHECK-DAG: [[GEN_FN:!.*]] = !DINamespace(name: "generator_test"
// CHECK-DAG: [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_union_type, name: "generator-0", scope: [[GEN_FN]]
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, scope: [[GEN]],
// CHECK-DAG: [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_union_type, name: "generator-0"
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "variant0", scope: [[GEN]],
// For brevity, we only check the struct name and members of the last variant.
// CHECK-SAME: file: [[FILE:![0-9]*]], line: 14,
// CHECK-NOT: flags: DIFlagArtificial
// CHECK-SAME: )
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, scope: [[GEN]],
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "variant1", scope: [[GEN]],
// CHECK-SAME: file: [[FILE]], line: 18,
// CHECK-NOT: flags: DIFlagArtificial
// CHECK-SAME: )
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, scope: [[GEN]],
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "variant2", scope: [[GEN]],
// CHECK-SAME: file: [[FILE]], line: 18,
// CHECK-NOT: flags: DIFlagArtificial
// CHECK-SAME: )
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, scope: [[GEN]],
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "variant3", scope: [[GEN]],
// CHECK-SAME: file: [[FILE]], line: 15,
// CHECK-NOT: flags: DIFlagArtificial
// CHECK-SAME: )
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, scope: [[GEN]],
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "variant4", scope: [[GEN]],
// CHECK-SAME: file: [[FILE]], line: 17,
// CHECK-SAME: baseType: [[VARIANT:![0-9]*]]
// CHECK-NOT: flags: DIFlagArtificial
// CHECK-SAME: )
// CHECK: [[S1:!.*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Suspend1", scope: [[GEN_FN]],
// CHECK: [[S1:!.*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Suspend1", scope: [[GEN]],
// CHECK-NOT: flags: DIFlagArtificial
// CHECK-SAME: )
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "RUST$ENUM$DISR", scope: [[S1]],
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "variant$", scope: [[S1]],
// CHECK-SAME: flags: DIFlagArtificial
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "s", scope: [[S1]]
// CHECK-NOT: flags: DIFlagArtificial
Expand Down
97 changes: 97 additions & 0 deletions src/test/debuginfo/msvc-pretty-enums.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// only-cdb
// ignore-tidy-linelength
// compile-flags:-g

// cdb-command: g

// Note: The natvis used to visualize niche-layout enums don't work correctly in cdb
// so the best we can do is to make sure we are generating the right debuginfo

// cdb-command: dx -r2 a,!
// cdb-check:a,! [Type: enum$<core::option::Option<enum$<msvc_pretty_enums::CStyleEnum>>, 2, 16, Some>]
// cdb-check: [+0x000] dataful_variant [Type: enum$<core::option::Option<enum$<msvc_pretty_enums::CStyleEnum>>, 2, 16, Some>::Some]
// cdb-check: [+0x000] __0 : Low (0x2) [Type: msvc_pretty_enums::CStyleEnum]
// cdb-check: [+0x000] discriminant : 0x2 [Type: enum$<core::option::Option<enum$<msvc_pretty_enums::CStyleEnum>>, 2, 16, Some>::Discriminant$]

// cdb-command: dx -r2 b,!
// cdb-check:b,! [Type: enum$<core::option::Option<enum$<msvc_pretty_enums::CStyleEnum>>, 2, 16, Some>]
// cdb-check: [+0x000] dataful_variant [Type: enum$<core::option::Option<enum$<msvc_pretty_enums::CStyleEnum>>, 2, 16, Some>::Some]
// cdb-check: [+0x000] __0 : 0x11 [Type: msvc_pretty_enums::CStyleEnum]
// cdb-check: [+0x000] discriminant : None (0x11) [Type: enum$<core::option::Option<enum$<msvc_pretty_enums::CStyleEnum>>, 2, 16, Some>::Discriminant$]

// cdb-command: dx -r2 c,!
// cdb-check:c,! [Type: enum$<msvc_pretty_enums::NicheLayoutEnum, 2, 16, Data>]
// cdb-check: [+0x000] dataful_variant [Type: enum$<msvc_pretty_enums::NicheLayoutEnum, 2, 16, Data>::Data]
// cdb-check: [+0x000] my_data : 0x11 [Type: msvc_pretty_enums::CStyleEnum]
// cdb-check: [+0x000] discriminant : Tag1 (0x11) [Type: enum$<msvc_pretty_enums::NicheLayoutEnum, 2, 16, Data>::Discriminant$]

// cdb-command: dx -r2 d,!
// cdb-check:d,! [Type: enum$<msvc_pretty_enums::NicheLayoutEnum, 2, 16, Data>]
// cdb-check: [+0x000] dataful_variant [Type: enum$<msvc_pretty_enums::NicheLayoutEnum, 2, 16, Data>::Data]
// cdb-check: [+0x000] my_data : High (0x10) [Type: msvc_pretty_enums::CStyleEnum]
// cdb-check: [+0x000] discriminant : 0x10 [Type: enum$<msvc_pretty_enums::NicheLayoutEnum, 2, 16, Data>::Discriminant$]

// cdb-command: dx -r2 e,!
// cdb-check:e,! [Type: enum$<msvc_pretty_enums::NicheLayoutEnum, 2, 16, Data>]
// cdb-check: [+0x000] dataful_variant [Type: enum$<msvc_pretty_enums::NicheLayoutEnum, 2, 16, Data>::Data]
// cdb-check: [+0x000] my_data : 0x13 [Type: msvc_pretty_enums::CStyleEnum]
// cdb-check: [+0x000] discriminant : Tag2 (0x13) [Type: enum$<msvc_pretty_enums::NicheLayoutEnum, 2, 16, Data>::Discriminant$]

// cdb-command: dx -r2 f,!
// cdb-check:f,! [Type: enum$<core::option::Option<u32*>, 1, [...], Some>]
// cdb-check: [+0x000] dataful_variant [Type: enum$<core::option::Option<u32*>, 1, [...], Some>::Some]
// cdb-check: [+0x000] __0 : 0x[...] : 0x1 [Type: unsigned int *]
// cdb-check: [+0x000] discriminant : 0x[...] [Type: enum$<core::option::Option<u32*>, 1, [...], Some>::Discriminant$]

// cdb-command: dx -r2 g,!
// cdb-check:g,! [Type: enum$<core::option::Option<u32*>, 1, [...], Some>]
// cdb-check: [+0x000] dataful_variant [Type: enum$<core::option::Option<u32*>, 1, [...], Some>::Some]
// cdb-check: [+0x000] __0 : 0x0 [Type: unsigned int *]
// cdb-check: [+0x000] discriminant : None (0x0) [Type: enum$<core::option::Option<u32*>, 1, [...], Some>::Discriminant$]

// cdb-command: dx h
// cdb-check:h : Some [Type: enum$<core::option::Option<u32>>]
// cdb-check: [+0x000] variant$ : Some (0x1) [Type: core::option::Option]
// cdb-check: [+0x004] __0 : 0xc [Type: unsigned int]

// cdb-command: dx i
// cdb-check:i : None [Type: enum$<core::option::Option<u32>>]
// cdb-check: [+0x000] variant$ : None (0x0) [Type: core::option::Option]

// cdb-command: dx j
// cdb-check:j : High (0x10) [Type: msvc_pretty_enums::CStyleEnum]

// cdb-command: dx -r2 k,!
// cdb-check:k,! [Type: enum$<core::option::Option<alloc::string::String>, 1, [...], Some>]
// cdb-check: [+0x000] dataful_variant [Type: enum$<core::option::Option<alloc::string::String>, 1, [...], Some>::Some]
// cdb-check: [+0x000] __0 [Type: alloc::string::String]
// cdb-check: [+0x000] discriminant : 0x[...] [Type: enum$<core::option::Option<alloc::string::String>, 1, [...], Some>::Discriminant$]

pub enum CStyleEnum {
Low = 2,
High = 16,
}

pub enum NicheLayoutEnum {
Tag1,
Data { my_data: CStyleEnum },
Tag2,
}

fn main() {
let a = Some(CStyleEnum::Low);
let b = Option::<CStyleEnum>::None;
let c = NicheLayoutEnum::Tag1;
let d = NicheLayoutEnum::Data { my_data: CStyleEnum::High };
let e = NicheLayoutEnum::Tag2;
let f = Some(&1u32);
let g = Option::<&'static u32>::None;
let h = Some(12u32);
let i = Option::<u32>::None;
let j = CStyleEnum::High;
let k = Some("IAMA optional string!".to_string());

zzz(); // #break
}

fn zzz() { () }
7 changes: 4 additions & 3 deletions src/test/debuginfo/pretty-std.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// ignore-freebsd: gdb package too new
// only-cdb // "Temporarily" ignored on GDB/LLDB due to debuginfo tests being disabled, see PR 47155
// ignore-android: FIXME(#10381)
// ignore-tidy-linelength
// compile-flags:-g
// min-gdb-version: 7.7
// min-lldb-version: 310
Expand Down Expand Up @@ -111,11 +112,11 @@
// NOTE: OsString doesn't have a .natvis entry yet.

// cdb-command: dx some
// cdb-check:some : Some(8) [Type: [...]::Option<i16>]
// cdb-check:some : Some [Type: enum$<core::option::Option<i16>>]
// cdb-command: dx none
// cdb-check:none : None [Type: [...]::Option<i64>]
// cdb-check:none : None [Type: enum$<core::option::Option<i64>>]
// cdb-command: dx some_string
// cdb-check:some_string : Some("IAMA optional string!") [[...]::Option<[...]::String>]
// cdb-check:some_string [Type: enum$<core::option::Option<alloc::string::String>, 1, [...], Some>]

#![allow(unused_variables)]
use std::ffi::OsString;
Expand Down