Skip to content

Commit b4076be

Browse files
[RGen] Add a validator that will check properties or fields. (#23650)
Both properties and fields are represented by the same struct. We add a validator that based on the struct properties will validate a property of a field. The tests have been refactored so that we can share them with the new vlaidator and the individual ones.
2 parents 79940b9 + a5a5fb3 commit b4076be

File tree

5 files changed

+552
-326
lines changed

5 files changed

+552
-326
lines changed
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
using System.Collections.Generic;
5+
using System.Collections.Immutable;
6+
using System.Linq;
7+
using Microsoft.CodeAnalysis;
8+
using Microsoft.Macios.Generator.Context;
9+
using Microsoft.Macios.Generator.DataModel;
10+
11+
namespace Microsoft.Macios.Bindings.Analyzer.Validators;
12+
13+
/// <summary>
14+
/// Validator that validates both properties and fields using the appropriate inner validator based on the property type.
15+
/// </summary>
16+
class PropertyOrFieldValidator : Validator<Property> {
17+
18+
readonly FieldValidator fieldValidator = new ();
19+
readonly PropertyValidator propertyValidator = new ();
20+
21+
/// <summary>
22+
/// Validates a property or field by delegating to the appropriate validator based on whether it's a field or property.
23+
/// </summary>
24+
/// <param name="property">The property to validate.</param>
25+
/// <param name="context">The root context containing compilation information.</param>
26+
/// <param name="diagnostics">The collection of diagnostics produced during validation.</param>
27+
/// <param name="location">The optional location for diagnostic reporting.</param>
28+
/// <returns>True if validation passes, false otherwise.</returns>
29+
bool ValidatePropertyOrField (Property property, RootContext context,
30+
out ImmutableArray<Diagnostic> diagnostics,
31+
Location? location = null)
32+
{
33+
diagnostics = ImmutableArray<Diagnostic>.Empty;
34+
Dictionary<string, List<Diagnostic>> errors = property.IsField
35+
? fieldValidator.ValidateAll (property, context)
36+
: propertyValidator.ValidateAll (property, context);
37+
if (errors.Count == 0)
38+
return true;
39+
// select all the errors from the inner validator
40+
diagnostics = [.. errors.SelectMany (x => x.Value)];
41+
return false;
42+
}
43+
44+
/// <summary>
45+
/// Initializes a new instance of the <see cref="PropertyOrFieldValidator"/> class.
46+
/// Sets up validation strategies that delegate to field or property validators based on the property type.
47+
/// </summary>
48+
public PropertyOrFieldValidator () : base (p => p.Location)
49+
{
50+
// add a global strategy based on the type of property (IsField or IsProperty) will call the appropriate strategy
51+
var errors = fieldValidator.Descriptors.AddRange (propertyValidator.Descriptors);
52+
AddGlobalStrategy (errors, ValidatePropertyOrField);
53+
}
54+
}
Lines changed: 9 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -1,71 +1,19 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT License.
33
#pragma warning disable APL0003
4-
using System.Collections.Generic;
5-
using System.Collections.Immutable;
6-
using System.Linq;
7-
using Microsoft.CodeAnalysis;
8-
using Microsoft.CodeAnalysis.CSharp;
4+
95
using Microsoft.Macios.Bindings.Analyzer.Validators;
10-
using Microsoft.Macios.Generator.Attributes;
11-
using Microsoft.Macios.Generator.Availability;
12-
using Microsoft.Macios.Generator.Context;
13-
using Microsoft.Macios.Generator.DataModel;
146
using Xunit;
15-
using static Microsoft.Macios.Generator.Tests.TestDataFactory;
167

178
namespace Microsoft.Macios.Bindings.Analyzer.Tests.Validators;
189

1910
public class FieldValidatorTests {
2011

21-
readonly RootContext context;
12+
readonly PropertyValidatorTestLogic testLogic;
2213

2314
public FieldValidatorTests ()
2415
{
25-
// Create a dummy compilation to get a semantic model and RootContext
26-
var syntaxTree = CSharpSyntaxTree.ParseText ("namespace Test { }");
27-
var compilation = CSharpCompilation.Create (
28-
"TestAssembly",
29-
[syntaxTree],
30-
references: [],
31-
options: new CSharpCompilationOptions (OutputKind.DynamicallyLinkedLibrary)
32-
);
33-
var semanticModel = compilation.GetSemanticModel (syntaxTree);
34-
context = new RootContext (semanticModel);
35-
}
36-
37-
Property CreateFieldProperty (string name = "TestProperty",
38-
bool isStatic = true,
39-
bool isPartial = true,
40-
ObjCBindings.Property flags = ObjCBindings.Property.Default,
41-
string? selector = "testSelector")
42-
{
43-
var modifiers = ImmutableArray.CreateBuilder<SyntaxToken> ();
44-
modifiers.Add (SyntaxFactory.Token (SyntaxKind.PublicKeyword));
45-
if (isStatic)
46-
modifiers.Add (SyntaxFactory.Token (SyntaxKind.StaticKeyword));
47-
if (isPartial)
48-
modifiers.Add (SyntaxFactory.Token (SyntaxKind.PartialKeyword));
49-
50-
var exportFieldData = new FieldInfo<ObjCBindings.Property> (
51-
new FieldData<ObjCBindings.Property> (selector!, flags), "Test");
52-
53-
var availability = new SymbolAvailability.Builder ();
54-
availability.Add (new SupportedOSPlatformData ("ios"));
55-
availability.Add (new SupportedOSPlatformData ("tvos"));
56-
availability.Add (new SupportedOSPlatformData ("macos"));
57-
availability.Add (new SupportedOSPlatformData ("maccatalyst"));
58-
59-
return new Property (
60-
name: name,
61-
returnType: ReturnTypeForString (),
62-
symbolAvailability: availability.ToImmutable (),
63-
attributes: [],
64-
modifiers: modifiers.ToImmutable (),
65-
accessors: []
66-
) {
67-
ExportFieldData = exportFieldData,
68-
};
16+
testLogic = new PropertyValidatorTestLogic (new FieldValidator ());
6917
}
7018

7119
[Theory]
@@ -74,18 +22,7 @@ Property CreateFieldProperty (string name = "TestProperty",
7422
[InlineData (true, false, 1)] // Invalid: not partial
7523
[InlineData (false, false, 2)] // Invalid: neither static nor partial
7624
public void ValidateFieldModifiersTests (bool isStatic, bool isPartial, int expectedDiagnosticsCount)
77-
{
78-
// Arrange
79-
var validator = new FieldValidator ();
80-
var property = CreateFieldProperty (isStatic: isStatic, isPartial: isPartial);
81-
82-
// Act
83-
var result = validator.ValidateAll (property, context);
84-
85-
// Assert
86-
var totalDiagnostics = result.Values.Sum (x => x.Count);
87-
Assert.Equal (expectedDiagnosticsCount, totalDiagnostics);
88-
}
25+
=> testLogic.ValidateFieldModifiersTestsImpl (isStatic, isPartial, expectedDiagnosticsCount);
8926

9027
[Theory]
9128
[InlineData (ObjCBindings.Property.Default, 0)]
@@ -105,52 +42,18 @@ public void ValidateFieldModifiersTests (bool isStatic, bool isPartial, int expe
10542
[InlineData (ObjCBindings.Property.Optional, 1)]
10643
[InlineData (ObjCBindings.Property.CreateEvents, 1)]
10744
public void ValidateIgnoredFlagsTests (ObjCBindings.Property flags, int expectedDiagnosticsCount)
108-
{
109-
var validator = new FieldValidator ();
110-
var property = CreateFieldProperty (flags: flags);
111-
112-
var result = new List<Diagnostic> ();
113-
foreach (var (_, value) in validator.ValidateAll (property, context)) {
114-
result.AddRange (value);
115-
}
116-
117-
if (expectedDiagnosticsCount > 0) {
118-
//Assert.True (result.ContainsKey ("FlagsAreValid"));
119-
Assert.Equal (expectedDiagnosticsCount, result.Count);
120-
Assert.All (result, d => Assert.Equal ("RBI0028", d.Id));
121-
} else {
122-
Assert.Empty (result);
123-
}
124-
}
45+
=> testLogic.ValidateIgnoredFlagsTestsImpl (flags, expectedDiagnosticsCount);
12546

12647
[Theory]
12748
[InlineData (ObjCBindings.Property.IsThreadStatic | ObjCBindings.Property.MarshalNativeExceptions, 2)]
12849
[InlineData (ObjCBindings.Property.CustomMarshalDirective | ObjCBindings.Property.DisableZeroCopy | ObjCBindings.Property.IsThreadSafe, 3)]
12950
[InlineData (ObjCBindings.Property.Transient | ObjCBindings.Property.PlainString | ObjCBindings.Property.CoreImageFilterProperty | ObjCBindings.Property.AutoRelease, 4)]
13051
public void ValidateMultipleIgnoredFlagsTests (ObjCBindings.Property flags, int expectedDiagnosticsCount)
131-
{
132-
var validator = new FieldValidator ();
133-
var property = CreateFieldProperty (flags: flags);
134-
135-
var result = new List<Diagnostic> ();
136-
foreach (var (_, value) in validator.ValidateAll (property, context)) {
137-
result.AddRange (value);
138-
}
139-
140-
Assert.NotEmpty (result);
141-
Assert.Equal (expectedDiagnosticsCount, result.Count);
142-
Assert.All (result, d => Assert.Equal ("RBI0028", d.Id));
143-
}
52+
=> testLogic.ValidateMultipleIgnoredFlagsTestsImpl (flags, expectedDiagnosticsCount);
14453

14554
[Fact]
14655
public void ValidateValidFieldPropertyTests ()
147-
{
148-
var validator = new FieldValidator ();
149-
var property = CreateFieldProperty (); // Default: static, partial, no invalid flags
150-
151-
var result = validator.ValidateAll (property, context);
152-
Assert.Empty (result);
153-
}
56+
=> testLogic.ValidateValidFieldPropertyTestsImpl ();
15457

15558
[Theory]
15659
[InlineData ("validSelector", true, 0)]
@@ -159,20 +62,7 @@ public void ValidateValidFieldPropertyTests ()
15962
[InlineData ("another_valid_selector", true, 0)]
16063
[InlineData ("valid123", true, 0)]
16164
public void SelectorIsNotNullOrEmptyTests (string? selector, bool expectedResult, int expectedDiagnosticsCount)
162-
{
163-
var validator = new FieldValidator ();
164-
var property = CreateFieldProperty (selector: selector);
165-
166-
var result = validator.ValidateAll (property, context);
167-
168-
var diagnostics = result.Values.SelectMany (x => x).ToList ();
169-
var selectorDiagnostics = diagnostics.Where (d => d.Id == "RBI0018").ToList ();
170-
171-
Assert.Equal (expectedDiagnosticsCount, selectorDiagnostics.Count);
172-
if (!expectedResult) {
173-
Assert.All (selectorDiagnostics, d => Assert.Equal ("RBI0018", d.Id));
174-
}
175-
}
65+
=> testLogic.FieldSelectorIsNotNullOrEmptyTestsImpl (selector, expectedResult, expectedDiagnosticsCount);
17666

17767
[Theory]
17868
[InlineData ("validSelector", true, 0)]
@@ -185,18 +75,5 @@ public void SelectorIsNotNullOrEmptyTests (string? selector, bool expectedResult
18575
[InlineData (" leadingSpace", false, 1)]
18676
[InlineData ("trailingSpace ", false, 1)]
18777
public void SelectorHasNoWhitespaceTests (string selector, bool expectedResult, int expectedDiagnosticsCount)
188-
{
189-
var validator = new FieldValidator ();
190-
var property = CreateFieldProperty (selector: selector);
191-
192-
var result = validator.ValidateAll (property, context);
193-
194-
var diagnostics = result.Values.SelectMany (x => x).ToList ();
195-
var whitespaceDiagnostics = diagnostics.Where (d => d.Id == "RBI0019").ToList ();
196-
197-
Assert.Equal (expectedDiagnosticsCount, whitespaceDiagnostics.Count);
198-
if (!expectedResult) {
199-
Assert.All (whitespaceDiagnostics, d => Assert.Equal ("RBI0019", d.Id));
200-
}
201-
}
78+
=> testLogic.FieldSelectorHasNoWhitespaceTestsImpl (selector, expectedResult, expectedDiagnosticsCount);
20279
}

0 commit comments

Comments
 (0)