Skip to content

DebuggerDisplay: six rendering defects #51

Description

@hexbinoct

Built from source at 293d77f (0.1.17), net10.0, Windows x64, compared against netcoredbg 3.2.0-1092.

SharpDbg is the only one of the two that honours [DebuggerDisplay] at all, so all of this is ahead of netcoredbg, which renders {Ns.Type} for every annotated type. Six things in the rendering look wrong. Separate fixes, but one attribute, so one issue.

Types, one local of each:

[DebuggerDisplay("{Amount} {Currency,nq}")]
sealed class Money { public decimal Amount; public string Currency; }        // 12.5m, "USD"

[DebuggerDisplay("{Amount} {Currency}")]
sealed class QuotedMoney { public decimal Amount; public string Currency; }  // same values

[DebuggerDisplay("\\{{Value}\\}")]
sealed class Braced { public int Value = 3; }

[DebuggerDisplay("{NoSuchMember}")]
sealed class Missing { public int Value = 2; }

[DebuggerDisplay("{_name,nq} -> {Next._name,nq}")]
sealed class Link { string _name; public Link? Next; }                       // "head" -> "tail", tail.Next is null

[DebuggerDisplay("{Value}", Name = "renamed", Type = "RetypedThing")]
sealed class Labelled { public int Value = 11; }

[DebuggerDisplay("base {Tag,nq}")]
class Base { public string Tag = "b"; }
sealed class Derived : Base { public int Extra = 9; }                        // declares no display of its own
local SharpDbg expected
money 12.5 USD 12.5 USD
quoted 12.5 USD 12.5 "USD"
braced error: A '{' character may only be escaped by doubling {3}
missing error: The name 'NoSuchMember' does not exist... value keeps the parts that resolved
link head head -> tail head -> tail
link tail error: CIL execution failed ... NullReferenceException tail -> <error> in that fragment only
labelled renamed = 11 value 11, name column renamed, type column RetypedThing
derived {Display.Derived} base b
  1. A string fragment is never quoted. money and quoted render identically, so ,nq is a no-op and a quoted string cannot be asked for. ,nq means "no quotes", which implies quotes are the default.

  2. \{ and \} are not implemented. These are the documented escape for a literal brace. It looks like the display string is compiled as a C# interpolated string, so the backslash fails the whole value. {{ and }} are the interpolated-string escape, not the DebuggerDisplay one.

  3. A fragment that cannot be resolved replaces the whole value. Name={Name} Bad={Nope} loses the half that worked. The error belongs in that fragment's place.

  4. A null anywhere on a fragment's member path throws out of the IL interpreter. tail has Next == null, and the whole display becomes a NullReferenceException text. The head of the same list renders fine. A linked structure whose last node has a null link is ordinary, and the same fix as 3 covers it: put the error in the fragment, keep the rest.

  5. Name = is folded into the value and Type = is dropped. labelled renders as renamed = 11. Both name columns rather than the value: Name names the variable, Type the type column, and neither belongs in the value text.

  6. Attribute inheritance is not honoured, though DebuggerDisplayAttribute is Inherited = true. Derived declares no display and should get base b from Base. This one is only visible if you compare implementations, since a debugger that has no DebuggerDisplay at all produces the same output for it.

Happy to test a branch against these if useful, I have all eight shapes as locals in one program.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions