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

[API Compat] CP0015 false positive for GeneratedCodeAttribute/GeneratedRegexAttribute #45954

Open
bitbonk opened this issue Jan 14, 2025 · 9 comments
Labels
Area-ApiCompat untriaged Request triage from a team member

Comments

@bitbonk
Copy link

bitbonk commented Jan 14, 2025

When I run the baseline package validation on a library that contains the following C# code that was not changed between the two versions

public abstract partial class IdentifierBase
{
    protected const string PermissiveNamePattern = @"^(?!\p{Z})[\p{L}\p{M}\p{N}\p{P}\p{S} ]{1,1024}(?<!\p{Z})$";

    // ...
    
    [GeneratedRegex(PermissiveNamePattern)]
    protected static partial Regex PermissiveNameRegex();
} 

I get the following error that I think shouldn't be getting:

C:\Program Files\dotnet\sdk\8.0.404\Sdks\Microsoft.NET.Sdk\targets\Microsoft.NET.ApiCompat.ValidatePackage.targets(39,5): error : API compatibility errors between 'lib/net8.0/MyLib.dll' (C:\Users\bitbonk\.nuget\packages\MyLib\1.1.0\MyLib.1.1.0.nupkg) and 'lib/net8.0/MyLib.dll' (C:\source\repos\MySolution\src\MyLib\bin\Release\MyLib.1.2.0.nupkg):  
C:\Program Files\dotnet\sdk\8.0.404\Sdks\Microsoft.NET.Sdk\targets\Microsoft.NET.ApiCompat.ValidatePackage.targets(39,5): error CP0015: Cannot change arguments of attribute 'System.CodeDom.Compiler.GeneratedCodeAttribute' on 'MyLib.IdentifierBase.PermissiveNameRegex()'.

When I look at the IL using ILDASM, I see no differences in the GeneratedCodeAttribute

Decompiled IL in MyLib.dll Version 1.1.0:

.method family hidebysig static class [System.Text.RegularExpressions]System.Text.RegularExpressions.Regex 
        PermissiveNameRegex() cil managed
{
  .custom instance void [System.Text.RegularExpressions]System.Text.RegularExpressions.GeneratedRegexAttribute::.ctor(string) = ( 01 00 39 5E 28 3F 21 5C 70 7B 5A 7D 29 5B 5C 70   // ..9^(?!\p{Z})[\p
                                                                                                                                  7B 4C 7D 5C 70 7B 4D 7D 5C 70 7B 4E 7D 5C 70 7B   // {L}\p{M}\p{N}\p{
                                                                                                                                  50 7D 5C 70 7B 53 7D 20 5D 7B 31 2C 31 30 32 34   // P}\p{S} ]{1,1024
                                                                                                                                  7D 28 3F 3C 21 5C 70 7B 5A 7D 29 24 00 00 )       // }(?<!\p{Z})$..
  .custom instance void [System.Runtime]System.CodeDom.Compiler.GeneratedCodeAttribute::.ctor(string,
                                                                                              string) = ( 01 00 28 53 79 73 74 65 6D 2E 54 65 78 74 2E 52   // ..(System.Text.R
                                                                                                          65 67 75 6C 61 72 45 78 70 72 65 73 73 69 6F 6E   // egularExpression
                                                                                                          73 2E 47 65 6E 65 72 61 74 6F 72 0C 38 2E 30 2E   // s.Generator.8.0.
                                                                                                          31 30 2E 33 36 36 31 32 00 00 )                   // 10.36612..
  // Code size       6 (0x6)
  .maxstack  8
  IL_0000:  ldsfld     class System.Text.RegularExpressions.Generated.'<RegexGenerator_g>F36B3EC1E1E4EB76C6E665E6A813463B838EF06AE647BC127D891E46EBC83B058__PermissiveNameRegex_5' System.Text.RegularExpressions.Generated.'<RegexGenerator_g>F36B3EC1E1E4EB76C6E665E6A813463B838EF06AE647BC127D891E46EBC83B058__PermissiveNameRegex_5'::Instance
  IL_0005:  ret
} // end of method IdentifierBase::PermissiveNameRegex

Decompiled IL in MyLib.dll Version 1.2.0:

.method family hidebysig static class [System.Text.RegularExpressions]System.Text.RegularExpressions.Regex 
        PermissiveNameRegex() cil managed
{
  .custom instance void [System.Text.RegularExpressions]System.Text.RegularExpressions.GeneratedRegexAttribute::.ctor(string) = ( 01 00 39 5E 28 3F 21 5C 70 7B 5A 7D 29 5B 5C 70   // ..9^(?!\p{Z})[\p
                                                                                                                                  7B 4C 7D 5C 70 7B 4D 7D 5C 70 7B 4E 7D 5C 70 7B   // {L}\p{M}\p{N}\p{
                                                                                                                                  50 7D 5C 70 7B 53 7D 20 5D 7B 31 2C 31 30 32 34   // P}\p{S} ]{1,1024
                                                                                                                                  7D 28 3F 3C 21 5C 70 7B 5A 7D 29 24 00 00 )       // }(?<!\p{Z})$..
  .custom instance void [System.Runtime]System.CodeDom.Compiler.GeneratedCodeAttribute::.ctor(string,
                                                                                              string) = ( 01 00 28 53 79 73 74 65 6D 2E 54 65 78 74 2E 52   // ..(System.Text.R
                                                                                                          65 67 75 6C 61 72 45 78 70 72 65 73 73 69 6F 6E   // egularExpression
                                                                                                          73 2E 47 65 6E 65 72 61 74 6F 72 0C 38 2E 30 2E   // s.Generator.8.0.
                                                                                                          31 30 2E 33 36 36 31 32 00 00 )                   // 10.36612..
  // Code size       6 (0x6)
  .maxstack  8
  IL_0000:  ldsfld     class System.Text.RegularExpressions.Generated.'<RegexGenerator_g>F6DAE5A16EF3D26D2EC7365C0E21EC5F0F0BC7D5BF6DD551DA7C930B1A38CDA6C__PermissiveNameRegex_5' System.Text.RegularExpressions.Generated.'<RegexGenerator_g>F6DAE5A16EF3D26D2EC7365C0E21EC5F0F0BC7D5BF6DD551DA7C930B1A38CDA6C__PermissiveNameRegex_5'::Instance
  IL_0005:  ret
} // end of method IdentifierBase::PermissiveNameRegex
@dotnet-issue-labeler dotnet-issue-labeler bot added the untriaged Request triage from a team member label Jan 14, 2025
Copy link

I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label.

1 similar comment
Copy link

I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label.

@bitbonk
Copy link
Author

bitbonk commented Jan 14, 2025

cc @ViktorHofer

@ViktorHofer
Copy link
Member

@ericstj any idea?

@bitbonk
Copy link
Author

bitbonk commented Jan 14, 2025

We get a similar error for all public WPF XAML User Controls:

C:\Program Files\dotnet\sdk\8.0.404\Sdks\Microsoft.NET.Sdk\targets\Microsoft.NET.ApiCompat.ValidatePackage.targets(39,5): error CP0015: Cannot change arguments of attribute 'System.CodeDom.Compiler.GeneratedCodeAttribute' on 'MyLib.UI.MyView.InitializeComponent()'.

The C# code generated by the WPF targets looks like this (decompiled with dotPeek):

 /// <summary>InitializeComponent</summary>
[DebuggerNonUserCode]
[GeneratedCode("PresentationBuildTasks", "8.0.8.0")]
public void InitializeComponent()
{
  if (this._contentLoaded)
     return;
  this._contentLoaded = true;
  Application.LoadComponent((object) this, new Uri("/MyLib.UI;component/systemdiagnosis/materials/myview.xaml", UriKind.Relative));
}

@ericstj
Copy link
Member

ericstj commented Jan 14, 2025

Probably folks should just suppress the GeneratedCodeAttribute from comparisons with exclude attributes -- looks like the string value contains version information of the generator, which would change depending on the SDK version used to generate the code.

The attribute rule is very noisy by default, it assumes rigid compatibility for attributes since every attribute has different rules about what's compatible and we haven't encoded that.

Ultimately #35542 would allow for per-attribute handling of attributes - and for GeneratedCodeAttribute we'd probably just ignore the parameters.

@bitbonk
Copy link
Author

bitbonk commented Jan 14, 2025

@ericstj I understand that GeneratedCodeAttribute might be noisy because of the version information but in the concrete example I gave I don't see any difference in IL of the attributes, only the method body is different, I also did not change the SDK version.
How come that the error occurred in my example?

Copy link
Contributor

@dotnet/area-infrastructure-libraries a new issue has been filed in the ApiCompat area, please triage

@ericstj
Copy link
Member

ericstj commented Jan 14, 2025

@ericstj I understand that GeneratedCodeAttribute might be noisy because of the version information but in the concrete example I gave I don't see any difference in IL of the attributes, only the method body is different, I also did not change the SDK version. How come that the error occurred in my example?

I couldn't reproduce this with the code snippet you shared, do you have a repro that demonstrates it?

FWIW - I think it's totally normal for folks to see a real difference here that should be ignored. Nearly every month we ship a new targeting pack with a new build of the source generators which will change the value here. That shouldn't be a breaking change.

Here's the relevant portion of APICompat that handles this:

public bool Equals(AttributeData? x, AttributeData? y)
{
if (x != null && y != null)
{
if (!symbolEqualityComparer.Equals(x.AttributeClass!, y.AttributeClass!))
{
return false;
}
if (!Enumerable.SequenceEqual(x.ConstructorArguments, y.ConstructorArguments, typedConstantEqualityComparer))
{
return false;
}
return Enumerable.SequenceEqual(x.NamedArguments, y.NamedArguments, new NamedArgumentComparer(typedConstantEqualityComparer));
}
return x == y;
}

And here's it "working correctly" under the debugger for the sample shared:
Image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-ApiCompat untriaged Request triage from a team member
Projects
None yet
Development

No branches or pull requests

4 participants