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

NUnit1032 throws an exception in a specific configuration #659

Closed
RenderMichael opened this issue Nov 29, 2023 · 4 comments · Fixed by #658
Closed

NUnit1032 throws an exception in a specific configuration #659

RenderMichael opened this issue Nov 29, 2023 · 4 comments · Fixed by #658
Assignees
Milestone

Comments

@RenderMichael
Copy link

The following code fails:

using NUnit.Framework;

using System.Diagnostics.CodeAnalysis;

namespace NUnitRepro;

[TestFixture]
public abstract class Test : IDataProvider
{
    private readonly DataReader DataReader;

    DataReader IDataProvider.DataReader
    {
        [RequiresUnreferencedCode("Reflection-based JSON")]
        get => DataReader;
    }

    [RequiresUnreferencedCode("Reflection-based JSON")]
    protected Test() => DataReader = new DataReader(this);

    [OneTimeSetUp] public void SetListener() => throw new NotImplementedException();
}

public interface IDataProvider
{
    DataReader DataReader
    {
        [RequiresUnreferencedCode("Reflection-based JSON")]
        get;
    }
}

[RequiresUnreferencedCode("Reflection-based JSON")]
public sealed class DataReader
{
    public DataReader(IDataProvider provider) => throw new NotImplementedException();
}

This gives the following error:

error AD0001: Analyzer 'NUnit.Analyzers.DisposeFieldsInTearDown.DisposeFieldsAndPropertiesInTearDownAnalyzer' threw an exception of type 'System.ArgumentException' with message 'An item with the same key has already been added. Key: DataReader'

If you remove abstract, or the attribute on the getter of the explicit implementation of IDataProvider.DataReader on Test, or the [OneTimeSetUp] attribute, this starts to work.

@RenderMichael RenderMichael changed the title NUnit1032 fails in a specific configuration NUnit1032 throws an exception in a specific configuration Nov 29, 2023
@RenderMichael
Copy link
Author

Visual Studio provides more information:
image

@manfred-brands manfred-brands self-assigned this Nov 30, 2023
@manfred-brands
Copy link
Member

Thanks @RenderMichael for your report.

The problem here is that your field and property have the same name and the analyzer doesn't expect this.
It would confuse people reading your code to see whether you are using the property or the field.
I'm not sure if it is worth the hassle of doubling the set of maintained dictionary/hashsets for this or if it even works.
As the analyzer works on the Roslyn Syntax level (aka Text), when checking for an assignment, it only sees: DataReader = . At that point it doesn't know if this is the field or the property (in your case it can't be the latter).

The optimization changes I did in #658 do fix your particular use-case as part of that change, read-only properties are not added to that dictionary as you cannot assign them, there is no need to check for them.

If you remove abstract, or the attribute on the getter of the explicit implementation of IDataProvider.DataReader on Test, or the [OneTimeSetUp] attribute, this starts to work.

Removing abstract doesn't change anything and it still fails on the current version.
Removing the attribute on the getter also does nothing, unless you change the code to remove the get and use an expression syntax: DataReader IDataProvider.DataReader => DataReader;. That case was already ignored in the current version.
Yes, removing the explicit implementation removes one of the symbols with the same name.
Removing the [OneTimeSetUp] result in this class not being considered a class with test related methods and it gets ignored.

What also fixes it is:

  • Rename the field to _DataReader

@RenderMichael
Copy link
Author

It would confuse people reading your code to see whether you are using the property or the field.

Since it’s an explicit implementation, you couldn’t use the property without a cast to the interface. That’s the reason I have it this way: It makes the property invisible to inheritors of the class while retaining DataReader’s functionality (not included in this minimal repro)

Is it possible to exclude explicit implementations from the analysis? That’s the only way I know of to get members/fields with the same name.

@manfred-brands
Copy link
Member

Is it possible to exclude explicit implementations from the analysis? That’s the only way I know of to get members/fields with the same name.

I added a PR to exclude explicit implementations.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants