Skip to content

Commit f506dfb

Browse files
committed
Validate new ClassDataAttribute<T> in v3 3.0
1 parent 7b461c7 commit f506dfb

File tree

10 files changed

+111
-25
lines changed

10 files changed

+111
-25
lines changed

src/Directory.Build.props

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,8 @@
7878
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
7979
<PackageReference Include="NSubstitute" Version="5.3.0" />
8080
<PackageReference Include="System.ValueTuple" Version="4.6.1" />
81-
<PackageReference Include="xunit.v3.assert.source" Version="3.0.0-pre.14" />
82-
<PackageReference Include="xunit.v3.core" Version="3.0.0-pre.14" />
81+
<PackageReference Include="xunit.v3.assert.source" Version="3.0.0-pre.40" />
82+
<PackageReference Include="xunit.v3.core" Version="3.0.0-pre.40" />
8383
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.1" />
8484
</ItemGroup>
8585

src/xunit.analyzers.tests/Analyzers/X1000/ClassDataAttributeMustPointAtValidClassTests.cs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,67 @@ public void TestMethod(int n) { }
227227
await Verify.VerifyAnalyzerV2(LanguageVersion.CSharp7_1, source, expectedV2);
228228
await Verify.VerifyAnalyzerV3(LanguageVersion.CSharp7_1, source, expectedV3);
229229
}
230+
231+
#if ROSLYN_LATEST && !NETFRAMEWORK
232+
233+
[Fact]
234+
public async ValueTask v3_only()
235+
{
236+
var source = /* lang=c#-test */ """
237+
using System.Collections;
238+
using System.Collections.Generic;
239+
using Xunit;
240+
241+
class DataClass_Enumerable_Object: IEnumerable<object> {
242+
public IEnumerator<object> GetEnumerator() => null;
243+
IEnumerator IEnumerable.GetEnumerator() => null;
244+
}
245+
246+
abstract class DataClass_Abstract: IEnumerable<object[]> {
247+
public IEnumerator<object[]> GetEnumerator() => null;
248+
IEnumerator IEnumerable.GetEnumerator() => null;
249+
}
250+
251+
class DataClass_NoParameterlessCtor: IEnumerable<object[]> {
252+
public DataClass_NoParameterlessCtor(string parameter) { }
253+
public IEnumerator<object[]> GetEnumerator() => null;
254+
IEnumerator IEnumerable.GetEnumerator() => null;
255+
}
256+
257+
class DataClass_InternalCtor: IEnumerable<object[]> {
258+
internal DataClass_InternalCtor() { }
259+
public IEnumerator<object[]> GetEnumerator() => null;
260+
IEnumerator IEnumerable.GetEnumerator() => null;
261+
}
262+
263+
class DataClass_PrivateCtor: IEnumerable<object[]> {
264+
internal DataClass_PrivateCtor() { }
265+
public IEnumerator<object[]> GetEnumerator() => null;
266+
IEnumerator IEnumerable.GetEnumerator() => null;
267+
}
268+
269+
public class TestClass {
270+
[Theory]
271+
[{|#0:ClassData<DataClass_Enumerable_Object>|}]
272+
[{|#1:ClassData<DataClass_Abstract>|}]
273+
[{|#2:ClassData<DataClass_NoParameterlessCtor>|}]
274+
[{|#3:ClassData<DataClass_InternalCtor>|}]
275+
[{|#4:ClassData<DataClass_PrivateCtor>|}]
276+
public void TestMethod(int n) { }
277+
}
278+
""";
279+
var expectedV3 = new[] {
280+
Verify.Diagnostic("xUnit1007").WithLocation(0).WithArguments("DataClass_Enumerable_Object", SupportedV3),
281+
Verify.Diagnostic("xUnit1007").WithLocation(1).WithArguments("DataClass_Abstract", SupportedV3),
282+
Verify.Diagnostic("xUnit1007").WithLocation(2).WithArguments("DataClass_NoParameterlessCtor", SupportedV3),
283+
Verify.Diagnostic("xUnit1007").WithLocation(3).WithArguments("DataClass_InternalCtor", SupportedV3),
284+
Verify.Diagnostic("xUnit1007").WithLocation(4).WithArguments("DataClass_PrivateCtor", SupportedV3),
285+
};
286+
287+
await Verify.VerifyAnalyzerV3(LanguageVersion.CSharp11, source, expectedV3);
288+
}
289+
290+
#endif // ROSLYN_LATEST && !NETFRAMEWORK
230291
}
231292

232293
public class X1037_TheoryDataTypeArgumentsMustMatchTestMethodParameters_TooFewTypeParameters

src/xunit.analyzers.tests/Utility/CodeAnalyzerHelper.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,10 @@ static CodeAnalyzerHelper()
6161
new PackageIdentity("Microsoft.Extensions.Primitives", "8.0.0"),
6262
new PackageIdentity("System.Threading.Tasks.Extensions", "4.5.4"),
6363
new PackageIdentity("System.Text.Json", "8.0.0"),
64-
new PackageIdentity("xunit.v3.assert", "3.0.0-pre.14"),
65-
new PackageIdentity("xunit.v3.common", "3.0.0-pre.14"),
66-
new PackageIdentity("xunit.v3.extensibility.core", "3.0.0-pre.14"),
67-
new PackageIdentity("xunit.v3.runner.common", "3.0.0-pre.14")
64+
new PackageIdentity("xunit.v3.assert", "3.0.0-pre.40"),
65+
new PackageIdentity("xunit.v3.common", "3.0.0-pre.40"),
66+
new PackageIdentity("xunit.v3.extensibility.core", "3.0.0-pre.40"),
67+
new PackageIdentity("xunit.v3.runner.common", "3.0.0-pre.40")
6868
)
6969
);
7070

@@ -74,8 +74,8 @@ static CodeAnalyzerHelper()
7474
new PackageIdentity("Microsoft.Extensions.Primitives", "8.0.0"),
7575
new PackageIdentity("System.Threading.Tasks.Extensions", "4.5.4"),
7676
new PackageIdentity("System.Text.Json", "8.0.0"),
77-
new PackageIdentity("xunit.v3.common", "3.0.0-pre.14"),
78-
new PackageIdentity("xunit.v3.runner.utility", "3.0.0-pre.14")
77+
new PackageIdentity("xunit.v3.common", "3.0.0-pre.40"),
78+
new PackageIdentity("xunit.v3.runner.utility", "3.0.0-pre.40")
7979
)
8080
);
8181
}

src/xunit.analyzers.tests/xunit.analyzers.tests.csproj

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@
2323
<PackageDownload Include="xunit.extensibility.execution" Version="[2.9.3-pre.4]" />
2424
<PackageDownload Include="xunit.runner.utility" Version="[2.9.3-pre.4]" />
2525

26-
<PackageDownload Include="xunit.v3.assert" Version="[3.0.0-pre.14]" />
27-
<PackageDownload Include="xunit.v3.common" Version="[3.0.0-pre.14]" />
28-
<PackageDownload Include="xunit.v3.extensibility.core" Version="[3.0.0-pre.14]" />
29-
<PackageDownload Include="xunit.v3.runner.common" Version="[3.0.0-pre.14]" />
30-
<PackageDownload Include="xunit.v3.runner.utility" Version="[3.0.0-pre.14]" />
26+
<PackageDownload Include="xunit.v3.assert" Version="[3.0.0-pre.40]" />
27+
<PackageDownload Include="xunit.v3.common" Version="[3.0.0-pre.40]" />
28+
<PackageDownload Include="xunit.v3.extensibility.core" Version="[3.0.0-pre.40]" />
29+
<PackageDownload Include="xunit.v3.runner.common" Version="[3.0.0-pre.40]" />
30+
<PackageDownload Include="xunit.v3.runner.utility" Version="[3.0.0-pre.40]" />
3131

3232
<!-- Download packages referenced by CodeAnalysisNetAnalyzers -->
3333
<PackageDownload Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="[9.0.0-preview.24454.1]" />

src/xunit.analyzers/Utility/Constants.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ public static class Xunit
131131
public const string AssemblyFixtureAttribute_V3 = "Xunit.AssemblyFixtureAttribute";
132132
public const string Assert = "Xunit.Assert";
133133
public const string ClassDataAttribute = "Xunit.ClassDataAttribute";
134+
public const string ClassDataAttributeOfT_V3 = "Xunit.ClassDataAttribute`1";
134135
public const string CollectionAttribute = "Xunit.CollectionAttribute";
135136
public const string CollectionAttributeOfT_V3 = "Xunit.CollectionAttribute`1";
136137
public const string CollectionDefinitionAttribute = "Xunit.CollectionDefinitionAttribute";

src/xunit.analyzers/Utility/TypeSymbolFactory.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ public static class TypeSymbolFactory
4242
public static INamedTypeSymbol? ClassDataAttribute(Compilation compilation) =>
4343
Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.ClassDataAttribute);
4444

45+
public static INamedTypeSymbol? ClassDataAttributeOfT_V3(Compilation compilation) =>
46+
Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.ClassDataAttributeOfT_V3);
47+
4548
public static INamedTypeSymbol? CollectionAttribute(Compilation compilation) =>
4649
Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.CollectionAttribute);
4750

src/xunit.analyzers/Utility/V3CoreContext.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ public class V3CoreContext : ICoreContext
88
{
99
readonly Lazy<INamedTypeSymbol?> lazyAssemblyFixtureAttributeType;
1010
readonly Lazy<INamedTypeSymbol?> lazyClassDataAttributeType;
11+
readonly Lazy<INamedTypeSymbol?> lazyClassDataAttributeOfTType;
1112
readonly Lazy<INamedTypeSymbol?> lazyCollectionAttributeType;
1213
readonly Lazy<INamedTypeSymbol?> lazyCollectionAttributeOfTType;
1314
readonly Lazy<INamedTypeSymbol?> lazyCollectionDefinitionAttributeType;
@@ -30,6 +31,7 @@ public class V3CoreContext : ICoreContext
3031

3132
lazyAssemblyFixtureAttributeType = new(() => TypeSymbolFactory.AssemblyFixtureAttribute_V3(compilation));
3233
lazyClassDataAttributeType = new(() => TypeSymbolFactory.ClassDataAttribute(compilation));
34+
lazyClassDataAttributeOfTType = new(() => TypeSymbolFactory.ClassDataAttributeOfT_V3(compilation));
3335
lazyCollectionAttributeType = new(() => TypeSymbolFactory.CollectionAttribute(compilation));
3436
lazyCollectionAttributeOfTType = new(() => TypeSymbolFactory.CollectionAttributeOfT_V3(compilation));
3537
lazyCollectionDefinitionAttributeType = new(() => TypeSymbolFactory.CollectionDefinitionAttribute(compilation));
@@ -55,6 +57,12 @@ public class V3CoreContext : ICoreContext
5557
public INamedTypeSymbol? ClassDataAttributeType =>
5658
lazyClassDataAttributeType.Value;
5759

60+
/// <summary>
61+
/// Gets a reference to type <c>ClassDataAttribute&lt;T&gt;</c>, if available.
62+
/// </summary>
63+
public INamedTypeSymbol? ClassDataAttributeOfTType =>
64+
lazyClassDataAttributeOfTType.Value;
65+
5866
/// <inheritdoc/>
5967
public INamedTypeSymbol? CollectionAttributeType =>
6068
lazyCollectionAttributeType.Value;

src/xunit.analyzers/X1000/ClassDataAttributeMustPointAtValidClass.cs

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -52,18 +52,31 @@ public override void AnalyzeCompilation(
5252
{
5353
context.CancellationToken.ThrowIfCancellationRequested();
5454

55-
// Only work against ClassDataAttribute
56-
if (!SymbolEqualityComparer.Default.Equals(semanticModel.GetTypeInfo(attributeSyntax).Type, xunitContext.Core.ClassDataAttributeType))
55+
var attributeType = semanticModel.GetTypeInfo(attributeSyntax).Type as INamedTypeSymbol;
56+
if (attributeType is null)
5757
continue;
5858

59-
// Need the referenced type to do anything
60-
if (attributeSyntax.ArgumentList is null)
61-
continue;
62-
if (attributeSyntax.ArgumentList.Arguments[0].Expression is not TypeOfExpressionSyntax typeOfExpression)
63-
continue;
64-
if (semanticModel.GetTypeInfo(typeOfExpression.Type).Type is not INamedTypeSymbol classType)
65-
continue;
66-
if (classType.Kind == SymbolKind.ErrorType)
59+
var classType = default(INamedTypeSymbol);
60+
61+
// [ClassData(typeof(...))]
62+
if (SymbolEqualityComparer.Default.Equals(attributeType, xunitContext.Core.ClassDataAttributeType))
63+
{
64+
if (attributeSyntax.ArgumentList is null)
65+
continue;
66+
if (attributeSyntax.ArgumentList.Arguments[0].Expression is not TypeOfExpressionSyntax typeOfExpression)
67+
continue;
68+
69+
classType = semanticModel.GetTypeInfo(typeOfExpression.Type).Type as INamedTypeSymbol;
70+
}
71+
// [ClassData<...>]
72+
else if (attributeType.IsGenericType)
73+
{
74+
var classDataOfTType = xunitContext.V3Core?.ClassDataAttributeOfTType?.ConstructUnboundGenericType();
75+
if (classDataOfTType is not null && SymbolEqualityComparer.Default.Equals(attributeType.ConstructUnboundGenericType(), classDataOfTType))
76+
classType = attributeType.TypeArguments[0] as INamedTypeSymbol;
77+
}
78+
79+
if (classType is null || classType.Kind == SymbolKind.ErrorType)
6780
continue;
6881

6982
// Make sure the class implements a compatible interface

tools/builder/build.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
</ItemGroup>
1717

1818
<ItemGroup>
19-
<PackageDownload Include="xunit.v3.runner.console" Version="[3.0.0-pre.14]" />
19+
<PackageDownload Include="xunit.v3.runner.console" Version="[3.0.0-pre.40]" />
2020
</ItemGroup>
2121

2222
</Project>

tools/builder/models/BuildContext.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ public partial IReadOnlyList<string> GetSkippedAnalysisFolders() =>
1616

1717
partial void Initialize()
1818
{
19-
consoleRunner = Path.Combine(NuGetPackageCachePath, "xunit.v3.runner.console", "3.0.0-pre.14", "tools", "net472", "xunit.v3.runner.console.exe");
19+
consoleRunner = Path.Combine(NuGetPackageCachePath, "xunit.v3.runner.console", "3.0.0-pre.40", "tools", "net472", "xunit.v3.runner.console.exe");
2020
if (!File.Exists(consoleRunner))
2121
throw new InvalidOperationException($"Cannot find console runner at '{consoleRunner}'");
2222
}

0 commit comments

Comments
 (0)