CommunityToolkit / dotnet

.NET Community Toolkit is a collection of helpers and APIs that work for all .NET developers and are agnostic of any specific UI platform. The toolkit is maintained and published by Microsoft, and part of the .NET Foundation.
https://docs.microsoft.com/dotnet/communitytoolkit/?WT.mc_id=dotnet-0000-bramin
Other
2.99k stars 294 forks source link

[MVVM] Don't add default parameter in attribute constructor #778

Open hellow554 opened 11 months ago

hellow554 commented 11 months ago

Describe the bug

I have an Attribute, which constructor looks like this:

[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
public class CrossValidateAttribute : ValidationAttribute {
    public CrossValidateAttribute([CallerMemberName] string? member = null) { }
}

The idea is to get the properties name when this gets constructed. If I put this on a normal Property, e.g.

[CrossValidate]
public string Foo { ... }

it will work and I get Foo as content of member.

But, if I put this on an [ObservableProperty] the generator will output this:

[ObservableProperty]
[CrossValidate]
private string? bar;
/// <inheritdoc cref="bar"/>
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "8.2.0.0")]
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
[global::XXX.CrossValidateAttribute(null)]
public string? Bar { ... }

Here you can see the XXX.CrossValidateAttribute(null) call, which is not want I want.

Can this be done the way I want it, e.g. XXX.CrossValidateAttribute()?

IDE and version

VS 2022

IDE version

Version 17.7.6

Nuget packages

Nuget package version(s)

8.2.2

Additional context

No response

Help us help you

Yes, I'd like to be assigned to work on this item

JochenMader commented 11 months ago

Hi there

I did some research on this one and found following:

In ../src/CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/Models/AttributeInfo.cs:41

        // Get the constructor arguments
        foreach (TypedConstant typedConstant in attributeData.ConstructorArguments)
        {
            constructorArguments.Add(TypedConstantInfo.Create(typedConstant));
        }

For testing purposed I replaced this part with:

        // Get the constructor arguments
        for (int i = 0; i < attributeData.AttributeConstructor.Parameters.Count(); i++)
        {
            if (attributeData.AttributeConstructor.Parameters[i].HasExplicitDefaultValue) break;

            var typedConstant = attributeData.ConstructorArguments[i];
            var info = TypedConstantInfo.Create(typedConstant);
            constructorArguments.Add(info);
        }

Parameters with default value are skipped.

Maybe this (or somehow better) can be discussed as a solution.

best regards