Skip to content

Commit 520feeb

Browse files
authored
Add trim-safe ValidationContext constructor (#113426)
* Add trim-safe ValidationContext constructor * Avoid calling into trim-unfriendly constructors * Update options generator and RUC messages * Update RUC attributes in ref * Update netfx baselines * Bring back unformatted baseline
1 parent 6b264e8 commit 520feeb

17 files changed

+788
-10
lines changed

src/libraries/Microsoft.Extensions.Options/gen/Emitter.cs

+9-1
Original file line numberDiff line numberDiff line change
@@ -681,15 +681,23 @@ private void GenModelValidationMethod(
681681
if (_symbolHolder.UnconditionalSuppressMessageAttributeSymbol is not null)
682682
{
683683
// We disable the warning on `new ValidationContext(object)` usage as we use it in a safe way that not require executing the reflection code.
684-
// This is done by initializing the DisplayName in the context which is the part trigger reflection if it is not initialized.
684+
// This is done by initializing the DisplayName in the context which is the part trigger reflection if it is not initialized. For
685+
// projects targeting .NET 10 and above, we can avoid the suppression since we use the new trim-safe constructor.
686+
OutLn("#if !NET10_0_OR_GREATER");
685687
OutLn($"[System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage(\"Trimming\", \"IL2026:RequiresUnreferencedCode\",");
686688
OutLn($" Justification = \"The created ValidationContext object is used in a way that never call reflection\")]");
689+
OutLn("#endif");
687690
}
688691

689692
OutLn($"public {(makeStatic ? "static " : string.Empty)}global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, {modelToValidate.Name} options)");
690693
OutOpenBrace();
691694
OutLn($"global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;");
695+
OutLn("#if NET10_0_OR_GREATER");
696+
OutLn($"string displayName = string.IsNullOrEmpty(name) ? \"{modelToValidate.SimpleName}.Validate\" : $\"{{name}}.Validate\";");
697+
OutLn($"var context = new {StaticValidationContextType}(options, displayName, null, null);");
698+
OutLn("#else");
692699
OutLn($"var context = new {StaticValidationContextType}(options);");
700+
OutLn("#endif");
693701

694702
int capacity = modelToValidate.MembersToValidate.Count == 0 ? 0 : modelToValidate.MembersToValidate.Max(static vm => vm.ValidationAttributes.Count);
695703
if (capacity > 0)

src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/DataAnnotationAttributesWithParams.g.cs

+7
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,19 @@ partial class MyOptionsValidator
1313
/// <param name="options">The options instance.</param>
1414
/// <returns>Validation result.</returns>
1515
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
16+
#if !NET10_0_OR_GREATER
1617
[System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
1718
Justification = "The created ValidationContext object is used in a way that never call reflection")]
19+
#endif
1820
public global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::Test.MyOptions options)
1921
{
2022
global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
23+
#if NET10_0_OR_GREATER
24+
string displayName = string.IsNullOrEmpty(name) ? "MyOptions.Validate" : $"{name}.Validate";
25+
var context = new global::System.ComponentModel.DataAnnotations.ValidationContext(options, displayName, null, null);
26+
#else
2127
var context = new global::System.ComponentModel.DataAnnotations.ValidationContext(options);
28+
#endif
2229
var validationResults = new global::System.Collections.Generic.List<global::System.ComponentModel.DataAnnotations.ValidationResult>();
2330
var validationAttributes = new global::System.Collections.Generic.List<global::System.ComponentModel.DataAnnotations.ValidationAttribute>(1);
2431

src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/EmitterWithCustomValidator.netcore.g.cs

+7
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,19 @@ partial struct MyOptionsValidator
1313
/// <param name="options">The options instance.</param>
1414
/// <returns>Validation result.</returns>
1515
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
16+
#if !NET10_0_OR_GREATER
1617
[System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
1718
Justification = "The created ValidationContext object is used in a way that never call reflection")]
19+
#endif
1820
public global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::HelloWorld.MyOptions options)
1921
{
2022
global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
23+
#if NET10_0_OR_GREATER
24+
string displayName = string.IsNullOrEmpty(name) ? "MyOptions.Validate" : $"{name}.Validate";
25+
var context = new global::System.ComponentModel.DataAnnotations.ValidationContext(options, displayName, null, null);
26+
#else
2127
var context = new global::System.ComponentModel.DataAnnotations.ValidationContext(options);
28+
#endif
2229
var validationResults = new global::System.Collections.Generic.List<global::System.ComponentModel.DataAnnotations.ValidationResult>();
2330
var validationAttributes = new global::System.Collections.Generic.List<global::System.ComponentModel.DataAnnotations.ValidationAttribute>(1);
2431

src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/EmitterWithCustomValidator.netfx.g.cs

+5
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,12 @@ partial struct MyOptionsValidator
1616
public global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::HelloWorld.MyOptions options)
1717
{
1818
global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
19+
#if NET10_0_OR_GREATER
20+
string displayName = string.IsNullOrEmpty(name) ? "MyOptions.Validate" : $"{name}.Validate";
21+
var context = new global::System.ComponentModel.DataAnnotations.ValidationContext(options, displayName, null, null);
22+
#else
1923
var context = new global::System.ComponentModel.DataAnnotations.ValidationContext(options);
24+
#endif
2025
var validationResults = new global::System.Collections.Generic.List<global::System.ComponentModel.DataAnnotations.ValidationResult>();
2126
var validationAttributes = new global::System.Collections.Generic.List<global::System.ComponentModel.DataAnnotations.ValidationAttribute>(1);
2227

src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/GeneratedAttributesTest.netcore.lang10.g.cs

+7
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,19 @@ partial class OptionsUsingGeneratedAttributesValidator
1313
/// <param name="options">The options instance.</param>
1414
/// <returns>Validation result.</returns>
1515
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
16+
#if !NET10_0_OR_GREATER
1617
[System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
1718
Justification = "The created ValidationContext object is used in a way that never call reflection")]
19+
#endif
1820
public global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::ValidationTest.OptionsUsingGeneratedAttributes options)
1921
{
2022
global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
23+
#if NET10_0_OR_GREATER
24+
string displayName = string.IsNullOrEmpty(name) ? "OptionsUsingGeneratedAttributes.Validate" : $"{name}.Validate";
25+
var context = new global::System.ComponentModel.DataAnnotations.ValidationContext(options, displayName, null, null);
26+
#else
2127
var context = new global::System.ComponentModel.DataAnnotations.ValidationContext(options);
28+
#endif
2229
var validationResults = new global::System.Collections.Generic.List<global::System.ComponentModel.DataAnnotations.ValidationResult>();
2330
var validationAttributes = new global::System.Collections.Generic.List<global::System.ComponentModel.DataAnnotations.ValidationAttribute>(1);
2431

src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/GeneratedAttributesTest.netcore.lang11.g.cs

+7
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,19 @@ partial class OptionsUsingGeneratedAttributesValidator
1313
/// <param name="options">The options instance.</param>
1414
/// <returns>Validation result.</returns>
1515
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
16+
#if !NET10_0_OR_GREATER
1617
[System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
1718
Justification = "The created ValidationContext object is used in a way that never call reflection")]
19+
#endif
1820
public global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::ValidationTest.OptionsUsingGeneratedAttributes options)
1921
{
2022
global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
23+
#if NET10_0_OR_GREATER
24+
string displayName = string.IsNullOrEmpty(name) ? "OptionsUsingGeneratedAttributes.Validate" : $"{name}.Validate";
25+
var context = new global::System.ComponentModel.DataAnnotations.ValidationContext(options, displayName, null, null);
26+
#else
2127
var context = new global::System.ComponentModel.DataAnnotations.ValidationContext(options);
28+
#endif
2229
var validationResults = new global::System.Collections.Generic.List<global::System.ComponentModel.DataAnnotations.ValidationResult>();
2330
var validationAttributes = new global::System.Collections.Generic.List<global::System.ComponentModel.DataAnnotations.ValidationAttribute>(1);
2431

src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/GeneratedAttributesTest.netfx.lang10.g.cs

+5
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,12 @@ partial class OptionsUsingGeneratedAttributesValidator
1616
public global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::ValidationTest.OptionsUsingGeneratedAttributes options)
1717
{
1818
global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
19+
#if NET10_0_OR_GREATER
20+
string displayName = string.IsNullOrEmpty(name) ? "OptionsUsingGeneratedAttributes.Validate" : $"{name}.Validate";
21+
var context = new global::System.ComponentModel.DataAnnotations.ValidationContext(options, displayName, null, null);
22+
#else
1923
var context = new global::System.ComponentModel.DataAnnotations.ValidationContext(options);
24+
#endif
2025
var validationResults = new global::System.Collections.Generic.List<global::System.ComponentModel.DataAnnotations.ValidationResult>();
2126
var validationAttributes = new global::System.Collections.Generic.List<global::System.ComponentModel.DataAnnotations.ValidationAttribute>(1);
2227

src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/GeneratedAttributesTest.netfx.lang11.g.cs

+5
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,12 @@ partial class OptionsUsingGeneratedAttributesValidator
1616
public global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::ValidationTest.OptionsUsingGeneratedAttributes options)
1717
{
1818
global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
19+
#if NET10_0_OR_GREATER
20+
string displayName = string.IsNullOrEmpty(name) ? "OptionsUsingGeneratedAttributes.Validate" : $"{name}.Validate";
21+
var context = new global::System.ComponentModel.DataAnnotations.ValidationContext(options, displayName, null, null);
22+
#else
1923
var context = new global::System.ComponentModel.DataAnnotations.ValidationContext(options);
24+
#endif
2025
var validationResults = new global::System.Collections.Generic.List<global::System.ComponentModel.DataAnnotations.ValidationResult>();
2126
var validationAttributes = new global::System.Collections.Generic.List<global::System.ComponentModel.DataAnnotations.ValidationAttribute>(1);
2227

src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/OptionsExtendingSystemClassTest.netcore.g.cs

+21
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,19 @@ internal sealed partial class __EndPointsOptionsValidator__
1414
/// <param name="options">The options instance.</param>
1515
/// <returns>Validation result.</returns>
1616
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
17+
#if !NET10_0_OR_GREATER
1718
[System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
1819
Justification = "The created ValidationContext object is used in a way that never call reflection")]
20+
#endif
1921
public static global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::Test.EndPointsOptions options)
2022
{
2123
global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
24+
#if NET10_0_OR_GREATER
25+
string displayName = string.IsNullOrEmpty(name) ? "EndPointsOptions.Validate" : $"{name}.Validate";
26+
var context = new global::System.ComponentModel.DataAnnotations.ValidationContext(options, displayName, null, null);
27+
#else
2228
var context = new global::System.ComponentModel.DataAnnotations.ValidationContext(options);
29+
#endif
2330
var validationResults = new global::System.Collections.Generic.List<global::System.ComponentModel.DataAnnotations.ValidationResult>();
2431
var validationAttributes = new global::System.Collections.Generic.List<global::System.ComponentModel.DataAnnotations.ValidationAttribute>(2);
2532

@@ -58,12 +65,19 @@ internal sealed partial class __RedisClientOptionsValidator__
5865
/// <param name="options">The options instance.</param>
5966
/// <returns>Validation result.</returns>
6067
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
68+
#if !NET10_0_OR_GREATER
6169
[System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
6270
Justification = "The created ValidationContext object is used in a way that never call reflection")]
71+
#endif
6372
public static global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::Test.RedisClientOptions options)
6473
{
6574
global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
75+
#if NET10_0_OR_GREATER
76+
string displayName = string.IsNullOrEmpty(name) ? "RedisClientOptions.Validate" : $"{name}.Validate";
77+
var context = new global::System.ComponentModel.DataAnnotations.ValidationContext(options, displayName, null, null);
78+
#else
6679
var context = new global::System.ComponentModel.DataAnnotations.ValidationContext(options);
80+
#endif
6781
var validationResults = new global::System.Collections.Generic.List<global::System.ComponentModel.DataAnnotations.ValidationResult>();
6882
var validationAttributes = new global::System.Collections.Generic.List<global::System.ComponentModel.DataAnnotations.ValidationAttribute>(1);
6983

@@ -107,12 +121,19 @@ partial class RedisNamedClientOptions
107121
/// <param name="options">The options instance.</param>
108122
/// <returns>Validation result.</returns>
109123
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
124+
#if !NET10_0_OR_GREATER
110125
[System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
111126
Justification = "The created ValidationContext object is used in a way that never call reflection")]
127+
#endif
112128
public global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::Test.RedisNamedClientOptions options)
113129
{
114130
global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
131+
#if NET10_0_OR_GREATER
132+
string displayName = string.IsNullOrEmpty(name) ? "RedisNamedClientOptions.Validate" : $"{name}.Validate";
133+
var context = new global::System.ComponentModel.DataAnnotations.ValidationContext(options, displayName, null, null);
134+
#else
115135
var context = new global::System.ComponentModel.DataAnnotations.ValidationContext(options);
136+
#endif
116137
var validationResults = new global::System.Collections.Generic.List<global::System.ComponentModel.DataAnnotations.ValidationResult>();
117138
var validationAttributes = new global::System.Collections.Generic.List<global::System.ComponentModel.DataAnnotations.ValidationAttribute>(1);
118139

src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/OptionsExtendingSystemClassTest.netfx.g.cs

+15
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,12 @@ internal sealed partial class __EndPointsOptionsValidator__
1717
public static global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::Test.EndPointsOptions options)
1818
{
1919
global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
20+
#if NET10_0_OR_GREATER
21+
string displayName = string.IsNullOrEmpty(name) ? "EndPointsOptions.Validate" : $"{name}.Validate";
22+
var context = new global::System.ComponentModel.DataAnnotations.ValidationContext(options, displayName, null, null);
23+
#else
2024
var context = new global::System.ComponentModel.DataAnnotations.ValidationContext(options);
25+
#endif
2126
var validationResults = new global::System.Collections.Generic.List<global::System.ComponentModel.DataAnnotations.ValidationResult>();
2227
var validationAttributes = new global::System.Collections.Generic.List<global::System.ComponentModel.DataAnnotations.ValidationAttribute>(2);
2328

@@ -59,7 +64,12 @@ internal sealed partial class __RedisClientOptionsValidator__
5964
public static global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::Test.RedisClientOptions options)
6065
{
6166
global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
67+
#if NET10_0_OR_GREATER
68+
string displayName = string.IsNullOrEmpty(name) ? "RedisClientOptions.Validate" : $"{name}.Validate";
69+
var context = new global::System.ComponentModel.DataAnnotations.ValidationContext(options, displayName, null, null);
70+
#else
6271
var context = new global::System.ComponentModel.DataAnnotations.ValidationContext(options);
72+
#endif
6373
var validationResults = new global::System.Collections.Generic.List<global::System.ComponentModel.DataAnnotations.ValidationResult>();
6474
var validationAttributes = new global::System.Collections.Generic.List<global::System.ComponentModel.DataAnnotations.ValidationAttribute>(1);
6575

@@ -106,7 +116,12 @@ partial class RedisNamedClientOptions
106116
public global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::Test.RedisNamedClientOptions options)
107117
{
108118
global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
119+
#if NET10_0_OR_GREATER
120+
string displayName = string.IsNullOrEmpty(name) ? "RedisNamedClientOptions.Validate" : $"{name}.Validate";
121+
var context = new global::System.ComponentModel.DataAnnotations.ValidationContext(options, displayName, null, null);
122+
#else
109123
var context = new global::System.ComponentModel.DataAnnotations.ValidationContext(options);
124+
#endif
110125
var validationResults = new global::System.Collections.Generic.List<global::System.ComponentModel.DataAnnotations.ValidationResult>();
111126
var validationAttributes = new global::System.Collections.Generic.List<global::System.ComponentModel.DataAnnotations.ValidationAttribute>(1);
112127

0 commit comments

Comments
 (0)