Skip to content

Commit

Permalink
Analyzer for providing too many arguments in [ArgumentsAttribute(...)] (
Browse files Browse the repository at this point in the history
  • Loading branch information
thomhurst authored Feb 1, 2025
1 parent b10ca8e commit 42c9d8e
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 16 deletions.
23 changes: 23 additions & 0 deletions TUnit.Analyzers.Tests/DataDrivenTestArgumentsAnalyzerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -215,4 +215,27 @@ public void Int_To_Short(short value)
"""
);
}

[Test]
public async Task Error_When_Too_Many_Arguments()
{
await Verifier
.VerifyAnalyzerAsync(
"""
using System.Threading.Tasks;
using TUnit.Core;
public class MyClass
{
[Test]
[{|#0:Arguments(null, "")|}]
public void Create_Unit_Test(string? something)
{
}
}
""",
Verifier.Diagnostic(Rules.TooManyArguments)
.WithLocation(0)
);
}
}
27 changes: 27 additions & 0 deletions TUnit.Analyzers/Resources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions TUnit.Analyzers/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -459,4 +459,13 @@
<data name="TUnit0049Title" xml:space="preserve">
<value>[MatrixDataSourceAttribute] is required</value>
</data>
<data name="TUnit0050Description" xml:space="preserve">
<value>Too many arguments provided.</value>
</data>
<data name="TUnit0050MessageFormat" xml:space="preserve">
<value>Too many arguments provided.</value>
</data>
<data name="TUnit0050Title" xml:space="preserve">
<value>Too many arguments provided</value>
</data>
</root>
3 changes: 3 additions & 0 deletions TUnit.Analyzers/Rules.cs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,9 @@ public static class Rules
public static DiagnosticDescriptor MatrixDataSourceAttributeRequired =
CreateDescriptor("TUnit0049", UsageCategory, DiagnosticSeverity.Error);

public static readonly DiagnosticDescriptor TooManyArguments =
CreateDescriptor("TUnit0050", UsageCategory, DiagnosticSeverity.Error);

private static DiagnosticDescriptor CreateDescriptor(string diagnosticId, string category, DiagnosticSeverity severity)
{
return new DiagnosticDescriptor(
Expand Down
44 changes: 28 additions & 16 deletions TUnit.Analyzers/TestDataAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ public class TestDataAnalyzer : ConcurrentDiagnosticAnalyzer
Rules.PropertyRequiredNotSet,
Rules.MustHavePropertySetter,
Rules.ReturnFunc,
Rules.MatrixDataSourceAttributeRequired);
Rules.MatrixDataSourceAttributeRequired,
Rules.TooManyArguments);

protected override void InitializeInternal(AnalysisContext context)
{
Expand Down Expand Up @@ -126,7 +127,7 @@ private void Analyze(SymbolAnalysisContext context,
if (SymbolEqualityComparer.Default.Equals(attribute.AttributeClass,
context.Compilation.GetTypeByMetadataName(WellKnown.AttributeFullyQualifiedClasses.Arguments.WithoutGlobalPrefix)))
{
CheckArguments(context, attribute, parameters);
CheckArguments(context, attribute, parameters, propertySymbol);
}

if (SymbolEqualityComparer.Default.Equals(attribute.AttributeClass,
Expand Down Expand Up @@ -175,7 +176,7 @@ private ImmutableArray<ITypeSymbol> GetTypes(ImmutableArray<IParameterSymbol> pa
}

private void CheckArguments(SymbolAnalysisContext context, AttributeData argumentsAttribute,
ImmutableArray<IParameterSymbol> parameters)
ImmutableArray<IParameterSymbol> parameters, IPropertySymbol? propertySymbol)
{
if (argumentsAttribute.ConstructorArguments.IsDefaultOrEmpty)
{
Expand All @@ -192,25 +193,36 @@ private void CheckArguments(SymbolAnalysisContext context, AttributeData argumen

var cancellationTokenType = context.Compilation.GetTypeByMetadataName(typeof(CancellationToken).FullName!);

for (var i = 0; i < parameters.Length; i++)
for (var i = 0; i < Math.Max(parameters.Length, arguments.Length); i++)
{
var parameter = parameters[i];
var parameter = parameters.ElementAtOrDefault(i);

if (parameter is null && propertySymbol is null)
{
context.ReportDiagnostic(
Diagnostic.Create(Rules.TooManyArguments, argumentsAttribute.GetLocation())
);
break;
}

var argumentExists = i + 1 <= arguments.Length;
var methodParameterType = parameter.Type;

var typeSymbol = parameter?.Type ?? propertySymbol!.Type;

var argument = arguments.ElementAtOrDefault(i);

if (parameter.Type.IsCollectionType(context.Compilation, out var innerType)
if (typeSymbol.IsCollectionType(context.Compilation, out var innerType)
&& arguments.Skip(i).Select(x => x.Type).All(x => SymbolEqualityComparer.Default.Equals(x, innerType)))
{
break;
}

if (SymbolEqualityComparer.Default.Equals(methodParameterType, cancellationTokenType))
if (SymbolEqualityComparer.Default.Equals(typeSymbol, cancellationTokenType))
{
continue;
}

if (!argumentExists && parameter.IsOptional)
if (!argumentExists && parameter?.IsOptional is true)
{
continue;
}
Expand All @@ -221,32 +233,32 @@ private void CheckArguments(SymbolAnalysisContext context, AttributeData argumen
Diagnostic.Create(Rules.WrongArgumentTypeTestData,
argumentsAttribute.GetLocation(),
"null",
methodParameterType.ToDisplayString())
typeSymbol.ToDisplayString())
);
return;
}

if (argument.IsNull && methodParameterType.NullableAnnotation == NullableAnnotation.NotAnnotated)
if (argument.IsNull && typeSymbol.NullableAnnotation == NullableAnnotation.NotAnnotated)
{
context.ReportDiagnostic(
Diagnostic.Create(Rules.MethodParameterBadNullability,
parameters[i].Locations.FirstOrDefault(),
parameters[i].Name)
parameter?.Locations.FirstOrDefault() ?? propertySymbol!.Locations.FirstOrDefault(),
parameter?.Name ?? propertySymbol!.Name)
);
}

if (IsEnumAndInteger(methodParameterType, argument.Type))
if (IsEnumAndInteger(typeSymbol, argument.Type))
{
continue;
}

if (!argument.IsNull && !CanConvert(context, argument, methodParameterType))
if (!argument.IsNull && !CanConvert(context, argument, typeSymbol))
{
context.ReportDiagnostic(
Diagnostic.Create(Rules.WrongArgumentTypeTestData,
argumentsAttribute.GetLocation(),
argument.Type?.ToDisplayString(),
methodParameterType.ToDisplayString())
typeSymbol.ToDisplayString())
);
return;
}
Expand Down

0 comments on commit 42c9d8e

Please sign in to comment.