未验证 提交 24150b3a 编写于 作者: T Tarek Mahmoud Sayed 提交者: GitHub

Fix options source gen with non-accessible validation attributes (#88613)

上级 57f691af
......@@ -249,11 +249,11 @@ The diagnostic id values reserved for .NET Libraries analyzer warnings are `SYSL
| __`SYSLIB1212`__ | Options validation generator: Member potentially missing transitive validation. |
| __`SYSLIB1213`__ | Options validation generator: Member potentially missing enumerable validation. |
| __`SYSLIB1214`__ | Options validation generator: Can't validate constants, static fields or properties. |
| __`SYSLIB1215`__ | *_`SYSLIB1214`-`SYSLIB1219` reserved for Microsoft.Extensions.Options.SourceGeneration.* |
| __`SYSLIB1216`__ | *_`SYSLIB1214`-`SYSLIB1219` reserved for Microsoft.Extensions.Options.SourceGeneration.* |
| __`SYSLIB1217`__ | *_`SYSLIB1214`-`SYSLIB1219` reserved for Microsoft.Extensions.Options.SourceGeneration.* |
| __`SYSLIB1218`__ | *_`SYSLIB1214`-`SYSLIB1219` reserved for Microsoft.Extensions.Options.SourceGeneration.* |
| __`SYSLIB1219`__ | *_`SYSLIB1214`-`SYSLIB1219` reserved for Microsoft.Extensions.Options.SourceGeneration.* |
| __`SYSLIB1215`__ | Options validation generator: Validation attribute on the member is inaccessible from the validator type. |
| __`SYSLIB1216`__ | *_`SYSLIB1201`-`SYSLIB1219` reserved for Microsoft.Extensions.Options.SourceGeneration.* |
| __`SYSLIB1217`__ | *_`SYSLIB1201`-`SYSLIB1219` reserved for Microsoft.Extensions.Options.SourceGeneration.* |
| __`SYSLIB1218`__ | *_`SYSLIB1201`-`SYSLIB1219` reserved for Microsoft.Extensions.Options.SourceGeneration.* |
| __`SYSLIB1219`__ | *_`SYSLIB1201`-`SYSLIB1219` reserved for Microsoft.Extensions.Options.SourceGeneration.* |
| __`SYSLIB1220`__ | JsonSourceGenerator encountered a [JsonConverterAttribute] with an invalid type argument. |
| __`SYSLIB1221`__ | JsonSourceGenerator does not support this C# language version. |
| __`SYSLIB1222`__ | Constructor annotated with JsonConstructorAttribute is inaccessible. |
......
......@@ -98,5 +98,12 @@ internal sealed class DiagDescriptors : DiagDescriptorsBase
messageFormat: SR.CantValidateStaticOrConstMemberMessage,
category: Category,
defaultSeverity: DiagnosticSeverity.Warning);
public static DiagnosticDescriptor InaccessibleValidationAttribute { get; } = Make(
id: "SYSLIB1215",
title: SR.InaccessibleValidationAttributeTitle,
messageFormat: SR.InaccessibleValidationAttributeMessage,
category: Category,
defaultSeverity: DiagnosticSeverity.Info);
}
}
......@@ -97,7 +97,7 @@ public IReadOnlyList<ValidatorType> GetValidatorTypes(IEnumerable<(TypeDeclarati
? modelType.GetLocation()
: syntax.GetLocation();
var membersToValidate = GetMembersToValidate(modelType, true, lowerLocationInCompilation);
var membersToValidate = GetMembersToValidate(modelType, true, lowerLocationInCompilation, validatorType);
if (membersToValidate.Count == 0)
{
// this type lacks any eligible members
......@@ -243,7 +243,7 @@ private static bool HasOpenGenerics(ITypeSymbol type, out string genericType)
return null;
}
private List<ValidatedMember> GetMembersToValidate(ITypeSymbol modelType, bool speculate, Location lowerLocationInCompilation)
private List<ValidatedMember> GetMembersToValidate(ITypeSymbol modelType, bool speculate, Location lowerLocationInCompilation, ITypeSymbol validatorType)
{
// make a list of the most derived members in the model type
......@@ -271,7 +271,7 @@ private List<ValidatedMember> GetMembersToValidate(ITypeSymbol modelType, bool s
? member.GetLocation()
: lowerLocationInCompilation;
var memberInfo = GetMemberInfo(member, speculate, location);
var memberInfo = GetMemberInfo(member, speculate, location, validatorType);
if (memberInfo is not null)
{
if (member.DeclaredAccessibility != Accessibility.Public && member.DeclaredAccessibility != Accessibility.Internal)
......@@ -287,7 +287,7 @@ private List<ValidatedMember> GetMembersToValidate(ITypeSymbol modelType, bool s
return membersToValidate;
}
private ValidatedMember? GetMemberInfo(ISymbol member, bool speculate, Location location)
private ValidatedMember? GetMemberInfo(ISymbol member, bool speculate, Location location, ITypeSymbol validatorType)
{
ITypeSymbol memberType;
switch (member)
......@@ -370,7 +370,7 @@ private List<ValidatedMember> GetMembersToValidate(ITypeSymbol modelType, bool s
if (transValidatorTypeName == null)
{
transValidatorIsSynthetic = true;
transValidatorTypeName = AddSynthesizedValidator(memberType, member, location);
transValidatorTypeName = AddSynthesizedValidator(memberType, member, location, validatorType);
}
// pop the stack
......@@ -433,7 +433,7 @@ private List<ValidatedMember> GetMembersToValidate(ITypeSymbol modelType, bool s
if (enumerationValidatorTypeName == null)
{
enumerationValidatorIsSynthetic = true;
enumerationValidatorTypeName = AddSynthesizedValidator(enumeratedType, member, location);
enumerationValidatorTypeName = AddSynthesizedValidator(enumeratedType, member, location, validatorType);
}
// pop the stack
......@@ -441,6 +441,12 @@ private List<ValidatedMember> GetMembersToValidate(ITypeSymbol modelType, bool s
}
else if (ConvertTo(attributeType, _symbolHolder.ValidationAttributeSymbol))
{
if (!_compilation.IsSymbolAccessibleWithin(attributeType, validatorType))
{
Diag(DiagDescriptors.InaccessibleValidationAttribute, location, attributeType.Name, member.OriginalDefinition.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat), validatorType.Name);
continue;
}
var validationAttr = new ValidationAttributeInfo(attributeType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat));
validationAttrs.Add(validationAttr);
......@@ -475,7 +481,7 @@ private List<ValidatedMember> GetMembersToValidate(ITypeSymbol modelType, bool s
{
if (!HasOpenGenerics(memberType, out var genericType))
{
var membersToValidate = GetMembersToValidate(memberType, false, location);
var membersToValidate = GetMembersToValidate(memberType, false, location, validatorType);
if (membersToValidate.Count > 0)
{
Diag(DiagDescriptors.PotentiallyMissingTransitiveValidation, location, memberType.Name, member.Name);
......@@ -491,7 +497,7 @@ private List<ValidatedMember> GetMembersToValidate(ITypeSymbol modelType, bool s
{
if (!HasOpenGenerics(enumeratedType, out var genericType))
{
var membersToValidate = GetMembersToValidate(enumeratedType, false, location);
var membersToValidate = GetMembersToValidate(enumeratedType, false, location, validatorType);
if (membersToValidate.Count > 0)
{
Diag(DiagDescriptors.PotentiallyMissingEnumerableValidation, location, enumeratedType.Name, member.Name);
......@@ -519,7 +525,7 @@ private List<ValidatedMember> GetMembersToValidate(ITypeSymbol modelType, bool s
return null;
}
private string? AddSynthesizedValidator(ITypeSymbol modelType, ISymbol member, Location location)
private string? AddSynthesizedValidator(ITypeSymbol modelType, ISymbol member, Location location, ITypeSymbol validatorType)
{
var mt = modelType.WithNullableAnnotation(NullableAnnotation.None);
if (mt.OriginalDefinition.SpecialType == SpecialType.System_Nullable_T)
......@@ -533,7 +539,7 @@ private List<ValidatedMember> GetMembersToValidate(ITypeSymbol modelType, bool s
return "global::" + validator.Namespace + "." + validator.Name;
}
var membersToValidate = GetMembersToValidate(mt, true, location);
var membersToValidate = GetMembersToValidate(mt, true, location, validatorType);
if (membersToValidate.Count == 0)
{
// this type lacks any eligible members
......
......@@ -201,4 +201,10 @@
<data name="ValidatorsNeedSimpleConstructorTitle" xml:space="preserve">
<value>Validators used for transitive or enumerable validation must have a constructor with no parameters.</value>
</data>
<data name="InaccessibleValidationAttributeMessage" xml:space="preserve">
<value>Validation attribute '{0}' on the member '{1}' is inaccessible from the validator type '{2}'.</value>
</data>
<data name="InaccessibleValidationAttributeTitle" xml:space="preserve">
<value>Validation attribute on the member is inaccessible from the validator type..</value>
</data>
</root>
......@@ -62,6 +62,16 @@
<target state="translated">Typ anotovaný třídou OptionsValidatorAttribute neimplementuje nezbytné rozhraní.</target>
<note />
</trans-unit>
<trans-unit id="InaccessibleValidationAttributeMessage">
<source>Validation attribute '{0}' on the member '{1}' is inaccessible from the validator type '{2}'.</source>
<target state="new">Validation attribute '{0}' on the member '{1}' is inaccessible from the validator type '{2}'.</target>
<note />
</trans-unit>
<trans-unit id="InaccessibleValidationAttributeTitle">
<source>Validation attribute on the member is inaccessible from the validator type..</source>
<target state="new">Validation attribute on the member is inaccessible from the validator type..</target>
<note />
</trans-unit>
<trans-unit id="MemberIsInaccessibleMessage">
<source>Can't apply validation attributes to private field or property {0}.</source>
<target state="translated">Ověřovací atributy nelze použít u privátního pole nebo vlastnosti {0}.</target>
......
......@@ -62,6 +62,16 @@
<target state="translated">Ein mit "OptionsValidatorAttribute" versehener Typ implementiert nicht die erforderliche Schnittstelle.</target>
<note />
</trans-unit>
<trans-unit id="InaccessibleValidationAttributeMessage">
<source>Validation attribute '{0}' on the member '{1}' is inaccessible from the validator type '{2}'.</source>
<target state="new">Validation attribute '{0}' on the member '{1}' is inaccessible from the validator type '{2}'.</target>
<note />
</trans-unit>
<trans-unit id="InaccessibleValidationAttributeTitle">
<source>Validation attribute on the member is inaccessible from the validator type..</source>
<target state="new">Validation attribute on the member is inaccessible from the validator type..</target>
<note />
</trans-unit>
<trans-unit id="MemberIsInaccessibleMessage">
<source>Can't apply validation attributes to private field or property {0}.</source>
<target state="translated">Validierungsattribute können nicht auf private Felder oder Eigenschaften {0} angewendet werden.</target>
......
......@@ -62,6 +62,16 @@
<target state="translated">Un tipo anotado con “OptionsValidatorAttribute” no implementa la interfaz necesaria.</target>
<note />
</trans-unit>
<trans-unit id="InaccessibleValidationAttributeMessage">
<source>Validation attribute '{0}' on the member '{1}' is inaccessible from the validator type '{2}'.</source>
<target state="new">Validation attribute '{0}' on the member '{1}' is inaccessible from the validator type '{2}'.</target>
<note />
</trans-unit>
<trans-unit id="InaccessibleValidationAttributeTitle">
<source>Validation attribute on the member is inaccessible from the validator type..</source>
<target state="new">Validation attribute on the member is inaccessible from the validator type..</target>
<note />
</trans-unit>
<trans-unit id="MemberIsInaccessibleMessage">
<source>Can't apply validation attributes to private field or property {0}.</source>
<target state="translated">No se pueden aplicar atributos de validación a la propiedad o campo privado {0}.</target>
......
......@@ -62,6 +62,16 @@
<target state="translated">Un type annoté avec 'OptionsValidatorAttribute' n’implémente pas l’interface nécessaire.</target>
<note />
</trans-unit>
<trans-unit id="InaccessibleValidationAttributeMessage">
<source>Validation attribute '{0}' on the member '{1}' is inaccessible from the validator type '{2}'.</source>
<target state="new">Validation attribute '{0}' on the member '{1}' is inaccessible from the validator type '{2}'.</target>
<note />
</trans-unit>
<trans-unit id="InaccessibleValidationAttributeTitle">
<source>Validation attribute on the member is inaccessible from the validator type..</source>
<target state="new">Validation attribute on the member is inaccessible from the validator type..</target>
<note />
</trans-unit>
<trans-unit id="MemberIsInaccessibleMessage">
<source>Can't apply validation attributes to private field or property {0}.</source>
<target state="translated">Impossible d’appliquer les attributs de validation au champ privé ou à la propriété {0}.</target>
......
......@@ -62,6 +62,16 @@
<target state="translated">Un tipo annotato con 'OptionsValidatorAttribute' non implementa l'interfaccia necessaria.</target>
<note />
</trans-unit>
<trans-unit id="InaccessibleValidationAttributeMessage">
<source>Validation attribute '{0}' on the member '{1}' is inaccessible from the validator type '{2}'.</source>
<target state="new">Validation attribute '{0}' on the member '{1}' is inaccessible from the validator type '{2}'.</target>
<note />
</trans-unit>
<trans-unit id="InaccessibleValidationAttributeTitle">
<source>Validation attribute on the member is inaccessible from the validator type..</source>
<target state="new">Validation attribute on the member is inaccessible from the validator type..</target>
<note />
</trans-unit>
<trans-unit id="MemberIsInaccessibleMessage">
<source>Can't apply validation attributes to private field or property {0}.</source>
<target state="translated">Non è possibile applicare gli attributi di convalida al campo privato o alla proprietà {0}.</target>
......
......@@ -62,6 +62,16 @@
<target state="translated">'OptionsValidatorAttribute' の注釈が付けられた型が、必要とされるインターフェイスを実装していません。</target>
<note />
</trans-unit>
<trans-unit id="InaccessibleValidationAttributeMessage">
<source>Validation attribute '{0}' on the member '{1}' is inaccessible from the validator type '{2}'.</source>
<target state="new">Validation attribute '{0}' on the member '{1}' is inaccessible from the validator type '{2}'.</target>
<note />
</trans-unit>
<trans-unit id="InaccessibleValidationAttributeTitle">
<source>Validation attribute on the member is inaccessible from the validator type..</source>
<target state="new">Validation attribute on the member is inaccessible from the validator type..</target>
<note />
</trans-unit>
<trans-unit id="MemberIsInaccessibleMessage">
<source>Can't apply validation attributes to private field or property {0}.</source>
<target state="translated">プライベート フィールドまたはプロパティ {0} には検証属性を適用できません。</target>
......
......@@ -62,6 +62,16 @@
<target state="translated">'OptionsValidatorAttribute'로 주석이 추가된 형식은 필요한 인터페이스를 구현하지 않습니다.</target>
<note />
</trans-unit>
<trans-unit id="InaccessibleValidationAttributeMessage">
<source>Validation attribute '{0}' on the member '{1}' is inaccessible from the validator type '{2}'.</source>
<target state="new">Validation attribute '{0}' on the member '{1}' is inaccessible from the validator type '{2}'.</target>
<note />
</trans-unit>
<trans-unit id="InaccessibleValidationAttributeTitle">
<source>Validation attribute on the member is inaccessible from the validator type..</source>
<target state="new">Validation attribute on the member is inaccessible from the validator type..</target>
<note />
</trans-unit>
<trans-unit id="MemberIsInaccessibleMessage">
<source>Can't apply validation attributes to private field or property {0}.</source>
<target state="translated">프라이빗 필드 또는 속성 {0}에 유효성 검사 특성을 적용할 수 없습니다.</target>
......
......@@ -62,6 +62,16 @@
<target state="translated">Typ z adnotacją „OptionsValidatorAttribute” nie implementuje wymaganego interfejsu.</target>
<note />
</trans-unit>
<trans-unit id="InaccessibleValidationAttributeMessage">
<source>Validation attribute '{0}' on the member '{1}' is inaccessible from the validator type '{2}'.</source>
<target state="new">Validation attribute '{0}' on the member '{1}' is inaccessible from the validator type '{2}'.</target>
<note />
</trans-unit>
<trans-unit id="InaccessibleValidationAttributeTitle">
<source>Validation attribute on the member is inaccessible from the validator type..</source>
<target state="new">Validation attribute on the member is inaccessible from the validator type..</target>
<note />
</trans-unit>
<trans-unit id="MemberIsInaccessibleMessage">
<source>Can't apply validation attributes to private field or property {0}.</source>
<target state="translated">Nie można zastosować atrybutów weryfikacji do pola prywatnego lub właściwości {0}.</target>
......
......@@ -62,6 +62,16 @@
<target state="translated">Um tipo anotado com "OptionsValidatorAttribute" não implementa a interface necessária.</target>
<note />
</trans-unit>
<trans-unit id="InaccessibleValidationAttributeMessage">
<source>Validation attribute '{0}' on the member '{1}' is inaccessible from the validator type '{2}'.</source>
<target state="new">Validation attribute '{0}' on the member '{1}' is inaccessible from the validator type '{2}'.</target>
<note />
</trans-unit>
<trans-unit id="InaccessibleValidationAttributeTitle">
<source>Validation attribute on the member is inaccessible from the validator type..</source>
<target state="new">Validation attribute on the member is inaccessible from the validator type..</target>
<note />
</trans-unit>
<trans-unit id="MemberIsInaccessibleMessage">
<source>Can't apply validation attributes to private field or property {0}.</source>
<target state="translated">Não é possível aplicar atributos de validação a um campo privado ou propriedade {0}.</target>
......
......@@ -62,6 +62,16 @@
<target state="translated">Тип с аннотацией OptionsValidatorAttribute не реализует необходимый интерфейс.</target>
<note />
</trans-unit>
<trans-unit id="InaccessibleValidationAttributeMessage">
<source>Validation attribute '{0}' on the member '{1}' is inaccessible from the validator type '{2}'.</source>
<target state="new">Validation attribute '{0}' on the member '{1}' is inaccessible from the validator type '{2}'.</target>
<note />
</trans-unit>
<trans-unit id="InaccessibleValidationAttributeTitle">
<source>Validation attribute on the member is inaccessible from the validator type..</source>
<target state="new">Validation attribute on the member is inaccessible from the validator type..</target>
<note />
</trans-unit>
<trans-unit id="MemberIsInaccessibleMessage">
<source>Can't apply validation attributes to private field or property {0}.</source>
<target state="translated">Не удается применить атрибуты проверки к закрытому полю или свойству {0}.</target>
......
......@@ -62,6 +62,16 @@
<target state="translated">'OptionsValidatorAttribute' ile açıklama eklenmiş bir tür gerekli arabirimi uygulamıyor.</target>
<note />
</trans-unit>
<trans-unit id="InaccessibleValidationAttributeMessage">
<source>Validation attribute '{0}' on the member '{1}' is inaccessible from the validator type '{2}'.</source>
<target state="new">Validation attribute '{0}' on the member '{1}' is inaccessible from the validator type '{2}'.</target>
<note />
</trans-unit>
<trans-unit id="InaccessibleValidationAttributeTitle">
<source>Validation attribute on the member is inaccessible from the validator type..</source>
<target state="new">Validation attribute on the member is inaccessible from the validator type..</target>
<note />
</trans-unit>
<trans-unit id="MemberIsInaccessibleMessage">
<source>Can't apply validation attributes to private field or property {0}.</source>
<target state="translated">Doğrulama öznitelikleri, {0} özel alanına veya özelliğine uygulanamıyor.</target>
......
......@@ -62,6 +62,16 @@
<target state="translated">用 "OptionsValidatorAttribute" 批注的类型未实现必要的接口。</target>
<note />
</trans-unit>
<trans-unit id="InaccessibleValidationAttributeMessage">
<source>Validation attribute '{0}' on the member '{1}' is inaccessible from the validator type '{2}'.</source>
<target state="new">Validation attribute '{0}' on the member '{1}' is inaccessible from the validator type '{2}'.</target>
<note />
</trans-unit>
<trans-unit id="InaccessibleValidationAttributeTitle">
<source>Validation attribute on the member is inaccessible from the validator type..</source>
<target state="new">Validation attribute on the member is inaccessible from the validator type..</target>
<note />
</trans-unit>
<trans-unit id="MemberIsInaccessibleMessage">
<source>Can't apply validation attributes to private field or property {0}.</source>
<target state="translated">无法将验证属性应用于专用字段或属性 {0}。</target>
......
......@@ -62,6 +62,16 @@
<target state="translated">以 'OptionsValidatorAttribute' 附註的類型未實作必要的介面。</target>
<note />
</trans-unit>
<trans-unit id="InaccessibleValidationAttributeMessage">
<source>Validation attribute '{0}' on the member '{1}' is inaccessible from the validator type '{2}'.</source>
<target state="new">Validation attribute '{0}' on the member '{1}' is inaccessible from the validator type '{2}'.</target>
<note />
</trans-unit>
<trans-unit id="InaccessibleValidationAttributeTitle">
<source>Validation attribute on the member is inaccessible from the validator type..</source>
<target state="new">Validation attribute on the member is inaccessible from the validator type..</target>
<note />
</trans-unit>
<trans-unit id="MemberIsInaccessibleMessage">
<source>Can't apply validation attributes to private field or property {0}.</source>
<target state="translated">無法將驗證屬性套用至私用欄位或屬性 {0}。</target>
......
......@@ -1179,7 +1179,9 @@ public SecondValidator(int _)
Assert.Equal(DiagDescriptors.ValidatorsNeedSimpleConstructor.Id, diagnostics[0].Id);
}
[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))]
private static bool SupportRemoteExecutionAndNotInBrowser => RemoteExecutor.IsSupported && !PlatformDetection.IsBrowser;
[ConditionalFact(nameof(SupportRemoteExecutionAndNotInBrowser))]
public void ProduceDiagnosticFromOtherAssemblyTest()
{
string source = """
......@@ -1264,6 +1266,176 @@ public class MyOptions
File.Delete(assemblyPath); // cleanup
}
[ConditionalFact(nameof(SupportRemoteExecutionAndNotInBrowser))]
public async Task InaccessibleValidationAttributesTest()
{
string source = """
using System;
using System.ComponentModel.DataAnnotations;
#nullable enable
namespace ValidationTest;
public class BaseOptions
{
[Timeout] // internal attribute not visible outside the assembly
public int Prop1 { get; set; }
[Required]
public string Prop2 { get; set; }
}
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
internal sealed class TimeoutAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object? value, ValidationContext? validationContext)
{
return ValidationResult.Success!;
}
}
""";
string assemblyName = Path.GetRandomFileName();
string assemblyPath = Path.Combine(Path.GetTempPath(), assemblyName + ".dll");
var compilation = CSharpCompilation
.Create(assemblyName, options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary))
.AddReferences(MetadataReference.CreateFromFile(AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.GetName().Name == "System.Runtime").Location))
.AddReferences(MetadataReference.CreateFromFile(typeof(string).Assembly.Location))
.AddReferences(MetadataReference.CreateFromFile(typeof(RequiredAttribute).Assembly.Location))
.AddReferences(MetadataReference.CreateFromFile(typeof(OptionsValidatorAttribute).Assembly.Location))
.AddReferences(MetadataReference.CreateFromFile(typeof(IValidateOptions<object>).Assembly.Location))
.AddSyntaxTrees(CSharpSyntaxTree.ParseText(source));
EmitResult emitResult = compilation.Emit(assemblyPath);
Assert.True(emitResult.Success);
RemoteExecutor.Invoke(async (assemblyFullPath) => {
string source0 = """
using Microsoft.Extensions.Options;
""";
string source1 = """
using System.ComponentModel.DataAnnotations;
#nullable enable
#pragma warning disable CS1591
namespace ValidationTest
{
public class ExtOptions : BaseOptions
{
[Range(0, 10)]
public int Prop3 { get; set; }
}
}
""";
string source2 = """
namespace ValidationTest
{
[OptionsValidator]
internal sealed partial class ExtOptionsValidator : IValidateOptions<ExtOptions>
{
}
}
""";
Assembly assembly = Assembly.LoadFrom(assemblyFullPath);
var (diagnostics, generatedSources) = await RoslynTestUtils.RunGenerator(
new Generator(),
new[]
{
assembly,
Assembly.GetAssembly(typeof(RequiredAttribute)),
Assembly.GetAssembly(typeof(OptionsValidatorAttribute)),
Assembly.GetAssembly(typeof(IValidateOptions<object>)),
},
new List<string> { source0 + source1 + source2 })
.ConfigureAwait(false);
_ = Assert.Single(generatedSources);
Assert.Single(diagnostics);
Assert.Equal(DiagDescriptors.InaccessibleValidationAttribute.Id, diagnostics[0].Id);
string generatedSource = generatedSources[0].SourceText.ToString();
Assert.Contains("global::System.ComponentModel.DataAnnotations.RangeAttribute", generatedSource);
Assert.Contains("global::System.ComponentModel.DataAnnotations.RequiredAttribute", generatedSource);
Assert.DoesNotContain("Timeout", generatedSource);
// Ensure the generated source compiles
var compilation = CSharpCompilation
.Create(Path.GetRandomFileName()+".dll", options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary))
.AddReferences(MetadataReference.CreateFromFile(AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.GetName().Name == "System.Runtime").Location))
.AddReferences(MetadataReference.CreateFromFile(typeof(string).Assembly.Location))
.AddReferences(MetadataReference.CreateFromFile(typeof(RequiredAttribute).Assembly.Location))
.AddReferences(MetadataReference.CreateFromFile(typeof(OptionsValidatorAttribute).Assembly.Location))
.AddReferences(MetadataReference.CreateFromFile(typeof(IValidateOptions<object>).Assembly.Location))
.AddReferences(MetadataReference.CreateFromFile(typeof(System.CodeDom.Compiler.GeneratedCodeAttribute).Assembly.Location))
.AddReferences(MetadataReference.CreateFromFile(assemblyFullPath))
.AddSyntaxTrees(CSharpSyntaxTree.ParseText(source1 + Environment.NewLine + generatedSource));
MemoryStream ms = new();
EmitResult emitResult = compilation.Emit(ms);
Assert.True(emitResult.Success);
}, assemblyPath).Dispose();
File.Delete(assemblyPath); // cleanup
// Test private validation attribute in the same assembly
string source3 = """
using System;
using System.ComponentModel.DataAnnotations;
using Microsoft.Extensions.Options;
#nullable enable
namespace ValidationTest;
public class MyOptions
{
[Timeout] // private attribute
public int Prop1 { get; set; }
[Required]
public string Prop2 { get; set; }
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
private sealed class TimeoutAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object? value, ValidationContext? validationContext)
{
return ValidationResult.Success!;
}
}
}
[OptionsValidator]
public sealed partial class MyOptionsValidator : IValidateOptions<MyOptions>
{
}
""";
var (diagnostics, generatedSources) = await RoslynTestUtils.RunGenerator(
new Generator(),
new[]
{
Assembly.GetAssembly(typeof(RequiredAttribute)),
Assembly.GetAssembly(typeof(OptionsValidatorAttribute)),
Assembly.GetAssembly(typeof(IValidateOptions<object>)),
},
new List<string> { source3 })
.ConfigureAwait(false);
_ = Assert.Single(generatedSources);
Assert.Single(diagnostics);
Assert.Equal(DiagDescriptors.InaccessibleValidationAttribute.Id, diagnostics[0].Id);
string generatedSource = generatedSources[0].SourceText.ToString();
Assert.DoesNotContain("Timeout", generatedSource);
}
[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))]
public async Task CantValidateOpenGenericMembersInEnumeration()
{
......
......@@ -204,4 +204,10 @@
<data name="CantValidateStaticOrConstMemberTitle" xml:space="preserve">
<value>Can't validate constants, static fields or properties.</value>
</data>
<data name="InaccessibleValidationAttributeMessage" xml:space="preserve">
<value>Validation attribute '{0}' on the member '{1}' is inaccessible from the validator type '{2}'.</value>
</data>
<data name="InaccessibleValidationAttributeTitle" xml:space="preserve">
<value>Validation attribute on the member is inaccessible from the validator type..</value>
</data>
</root>
\ No newline at end of file
......@@ -204,4 +204,10 @@
<data name="CantValidateStaticOrConstMemberTitle" xml:space="preserve">
<value>Can't validate constants, static fields or properties.</value>
</data>
<data name="InaccessibleValidationAttributeMessage" xml:space="preserve">
<value>Validation attribute '{0}' on the member '{1}' is inaccessible from the validator type '{2}'.</value>
</data>
<data name="InaccessibleValidationAttributeTitle" xml:space="preserve">
<value>Validation attribute on the member is inaccessible from the validator type..</value>
</data>
</root>
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册