SteveDunn / Vogen

A semi-opinionated library which is a source generator and a code analyser. It Source generates Value Objects
Apache License 2.0
874 stars 45 forks source link

DebuggerDisplayAttribute breaks in Rider #181

Closed Blackclaws closed 2 years ago

Blackclaws commented 2 years ago

Describe the bug

When viewing the generated types in the debug window you get different messages depending on the type:

Int:

Underlying type: global::System.Int32, Value = Exception of type 'System.Security.VerificationException' was thrown: objRef is not object value

String:

Underlying type: System.String, Value = Failed to read field: Value does not fall within the expected range. The error code is E_INVALIDARG, orCOR_E_ARGUMENT, orWIN32_ERROR_INVALID_PARAMETER, or0x80070057.

What they seem to have in common is that the debugger breaks on stringifying the _value parameter.

It then doesn't even allow you to expand the objects as it doesn't get to the point of instantiating the proxy object.

If you remove the _value from

[global::System.Diagnostics.DebuggerDisplayAttribute("Underlying type: global::System.Int32, Value = { _value }")]

then you can expand the debug window. Unfortunately this then causes a stack overflow, because the DebugProxy has a reference to the ValueObject and it tries to recursively instantiate itself.

The solution is to take the important elements from the ValueObject in the DebugProxy and not keep a reference to the ValueObject in the DebugProxy.

I have no clue whether this is something that is contained to Rider's debugger or if this happens on other C# debuggers as well.

Steps to reproduce

  1. Create a class
    
    [ValueObject(typeof(string))]
    public partial class TestThing
    {

}

2. Create an instance

var test = TestThing.From("Test");

3. Do something with it

Console.WriteLine(test.Value);


4. Debug the line where you do something with it and look at the Variables section.

### Expected behaviour

I see the Debug Proxy without any issues and don't get exceptions.
Blackclaws commented 2 years ago

Ok after playing around with this a bit this seems a bit Rider specific.

This errors out:

[global::System.Diagnostics.DebuggerDisplayAttribute("The type { _value }")]
public class TestObject
{
    public TestObject(int a)
    {
        _value = a;
    }
    public int _value;
}

Accessing the field _value through a property works as expected.

Blackclaws commented 2 years ago

So definitely a Rider thing:

https://youtrack.jetbrains.com/issue/RIDER-79915 https://youtrack.jetbrains.com/issue/RIDER-79916

Probably not something you have to worry about as it should work. Not sure when it will get fixed however ...

Referencing properties instead of fields work fine by the way. So a solution for people running into this issue would be to change the code generation for _value to emit a Property instead.

SteveDunn commented 2 years ago

Thanks for the report. I've found a few issues with Rider and debug attributes. I documented them here: https://dunnhq.com/posts/2022/debugger-attributes/

Blackclaws commented 2 years ago

Yeah I've seen some more bug reports on behavior that is slightly weird for these attributes in the bugtracker. I'm not sure how common they are in general. Your code is the first time I've seen them used, though they appear insanely useful in some situations.

So I do hope they fix those two bugs eventually, they've at least confirmed them. Maybe let's wait a bit see if its just solved there, if not it might still be an easy fix to add a configuration option that introduces a backing property instead of a field.

CheloXL commented 2 years ago

@Blackclaws can you work with Rider/Vogen without issues? In projects where I use Vogen I simply can't use Rider, as every time I touch a file where a VO is referenced, hundred of errors start to show (probably because Rider is re-creating the VO source files and had problems with that). I always have to use VS (a thing I don't like to do, but the other option is simply not usable).

SteveDunn commented 2 years ago

That's not good. Perhaps I could #ifdef them out if you have something in your project properties. Something like NO_DEBUG_ATTRIBUTES?

CheloXL commented 2 years ago

@SteveDunn I don't think it is related to this attribute per-se. I asked @Blackclaws as it seems he's using Rider with Vogen.

I'm not sure what Rider does with source-generators, but before Vogen I used StronglyTypedId and had similar issues (but solved by simply closing/reopening the solution).

If I have to guess (based on my null knowledge of source generators), this seems to be related to how you hook into the pipeline.

SteveDunn commented 2 years ago

@CheloXL I don't very often use Rider, but I just tried it on a project where I use Vogen, and couldn't see any issues. Have you got a small repro project you could share?

Blackclaws commented 2 years ago

@Blackclaws can you work with Rider/Vogen without issues? In projects where I use Vogen I simply can't use Rider, as every time I touch a file where a VO is referenced, hundred of errors start to show (probably because Rider is re-creating the VO source files and had problems with that). I always have to use VS (a thing I don't like to do, but the other option is simply not usable).

Usually yes, it happens from time to time that Rider disables source generators after they threw an exception. Unloading and subsequently reloading the project in question fixes the issue. You can actually enable internal mode for Rider itself and view the project load log if you notice that this is something that always happens (https://rider-support.jetbrains.com/hc/en-us/articles/207327910 and then reload proejct and show logs).

Also make sure you're using the latest version, source generator functionality was improved a lot in the last major releases.

CheloXL commented 2 years ago

@SteveDunn Will try to create one. The project where I'm currently using it is quite big and of course I can't share it. @Blackclaws Thanks, will try to see what happens. Usually on load there are no errors and everything is fine. Things start to break apart when I edit code that has references to VOs. Sometimes even a "move this class to another file" breaks everything.

khalidabuhakmeh commented 2 years ago

👋 We are currently working to resolve this issue. You can follow the issue here. https://youtrack.jetbrains.com/issue/RIDER-79915/Rider-produces-an-exception-on-DebuggerDisplayAttribute-referencing-a-field

SteveDunn commented 2 years ago

Thank you @khalidabuhakmeh - much appreciated. On a related note: If JetBrains want to use this repo to improve its handling of reporting errors from source-generated code, then I'd be happy to participate by making any necessary changes to how the source is generated. Both Rider and R# suffer from the same issues, e.g. when creating a new Value Object, they both display that Value is not a property of the type, even though it's source generated in the background.

SteveDunn commented 2 years ago

I'm wondering if I should close this issue. Does the fact that having the debug attributes causes you to not be able to use Vogen? If so, I'll add something so that the can be turned off in Rider while its being fixed.

Blackclaws commented 2 years ago

I'm wondering if I should close this issue. Does the fact that having the debug attributes causes you to not be able to use Vogen? If so, I'll add something so that the can be turned off in Rider while its being fixed.

It should be fine to close it, I opened the issue because I thought it was a Vogen issue, but since its a rider issue we can close it now.

SteveDunn commented 1 year ago

I recently implemented an option that allows the user to omit the debug attributes. You won't get the useful functionality that VS shows, but at least it won't mess up the whole debugging experience in Rider.

You can achieve this by adding an assembly level attribute: [assembly: VogenDefaults(omitDebugAttributes: true)]