Skip to content
  •  
  •  
  •  
139 changes: 45 additions & 94 deletions src/Testing.AutoFixtures/AutoFixtureGenerator+StaticGenerator.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Text.RegularExpressions;

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
Expand Down Expand Up @@ -134,12 +135,10 @@ Compilation compilation
{
var isAbstract = parameterSymbol.IsAbstract;
var isInterface = parameterSymbol.Type.TypeKind == TypeKind.Interface;
var isValueType = parameterSymbol.Type.IsValueType;

var symbolName = $"_{parameterSymbol.Name}";
if (!isAbstract && !isInterface)
{
return FieldDeclaration(
return !isAbstract && !isInterface
? FieldDeclaration(
VariableDeclaration(
IdentifierName(
Identifier(
Expand Down Expand Up @@ -171,10 +170,8 @@ Compilation compilation
TokenList(
Token(SyntaxKind.PrivateKeyword)
)
);
}

return FieldDeclaration(
)
: FieldDeclaration(
VariableDeclaration(
IdentifierName(
Identifier(
Expand Down Expand Up @@ -233,7 +230,7 @@ private static MemberDeclarationSyntax BuildBuildMethod(
IEnumerable<IParameterSymbol> parameterSymbols
)
{
List<SyntaxNodeOrToken> list = new();
List<SyntaxNodeOrToken> list = [];
foreach (var parameterSymbol in parameterSymbols)
{
list.Add(Argument(IdentifierName($"_{parameterSymbol.Name}")));
Expand Down Expand Up @@ -379,17 +376,14 @@ private static MemberDeclarationSyntax WithPropertyMethod(IParameterSymbol const

SyntaxToken withTypeOrParameterName(IParameterSymbol parameterSymbol)
{
var primitiveName = $"{char.ToUpper(parameterSymbol.Name[0])}{parameterSymbol.Name.Substring(1, parameterSymbol.Name.Length - 1)}";
var primitiveName = $"{ char.ToUpper(parameterSymbol.Name[0]).ToString()}{parameterSymbol.Name[1..]}";
var splitLastCamel = useParameterName(parameterSymbol) ? primitiveName : SplitLastCamel(parameterSymbol);
return Identifier($"With{splitLastCamel}");
}

bool useParameterName(IParameterSymbol parameterSymbol)
{
return parameterSymbol.Type.TypeKind != TypeKind.Interface
|| parameterSymbol.Type.IsValueType
|| !parameterSymbol.Type.IsAbstract;
}
bool useParameterName(IParameterSymbol parameterSymbol) => parameterSymbol.Type.TypeKind != TypeKind.Interface
|| parameterSymbol.Type.IsValueType
|| !parameterSymbol.Type.IsAbstract;
}

private static MemberDeclarationSyntax BuildOperator(string className, string fixtureName) =>
Expand Down Expand Up @@ -495,9 +489,8 @@ private static InvocationExpressionSyntax GetFieldInvocation(Compilation compila
{
var fakeItEasy = compilation.GetTypeByMetadataName("FakeItEasy.Fake");

if (fakeItEasy is { })
{
return InvocationExpression(
return fakeItEasy is { }
? InvocationExpression(
MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression,
IdentifierName("A"),
Expand All @@ -508,10 +501,8 @@ private static InvocationExpressionSyntax GetFieldInvocation(Compilation compila
typeArgumentListSyntax(symbol)
)
)
);
}

return InvocationExpression(
)
: InvocationExpression(
MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression,
IdentifierName(
Expand All @@ -529,32 +520,11 @@ private static InvocationExpressionSyntax GetFieldInvocation(Compilation compila
)
)
);

TypeArgumentListSyntax typeArgumentListSyntax(IParameterSymbol parameterSymbol)
{
return TypeArgumentList(
SingletonSeparatedList<TypeSyntax>(
ParseName(parameterSymbol.Type.GetGenericDisplayName())
)
static TypeArgumentListSyntax typeArgumentListSyntax(IParameterSymbol parameterSymbol) => TypeArgumentList(
SingletonSeparatedList<TypeSyntax>(
ParseName(parameterSymbol.Type.GetGenericDisplayName())
)
);
}
}

private static void ReportDiagnostic(
SourceProductionContext productionContext,
DiagnosticDescriptor diagnosticDescriptor,
IEnumerable<Location> locations
)
{
ReportDiagnostic(productionContext, diagnosticDescriptor, locations.ToArray());
}

private static void ReportDiagnostic(SourceProductionContext productionContext, DiagnosticDescriptor diagnosticDescriptor, params Location[] locations)
{
foreach (var location in locations)
{
productionContext.ReportDiagnostic(Diagnostic.Create(diagnosticDescriptor, location));
}
}

private const string Fixture = nameof(Fixture);
Expand All @@ -563,57 +533,38 @@ private static void ReportDiagnostic(SourceProductionContext productionContext,
{
var targetSymbol = syntaxContext.TargetSymbol as INamedTypeSymbol;

if (syntaxContext.Attributes[0].ConstructorArguments.Length == 0)
{
return targetSymbol;
}

if (syntaxContext.Attributes[0].ConstructorArguments[0].Value is INamedTypeSymbol namedTypeSymbol)
{
return namedTypeSymbol;
}

return null;
}


private static bool ReportAutoFixture0001(INamedTypeSymbol classForFixture, SourceProductionContext productionContext)
{
if (classForFixture.Constructors.All(x => x.Parameters.IsDefaultOrEmpty))
{
ReportDiagnostic(productionContext, Diagnostics.AutoFixture0001, classForFixture.Locations);
return true;
}

return false;
return syntaxContext.Attributes[0].ConstructorArguments.Length == 0
? targetSymbol
: syntaxContext.Attributes[0].ConstructorArguments[0].Value is INamedTypeSymbol namedTypeSymbol ? namedTypeSymbol : null;
}

private static bool ReportAutoFixture0001(INamedTypeSymbol classForFixture, SourceProductionContext productionContext) => classForFixture.Constructors.All(methodSymbol => methodSymbol.Parameters.IsDefaultOrEmpty);

private static bool ReportAutoFixture0002(INamedTypeSymbol namedTypeSymbol, SourceProductionContext productionContext)
{
var reported = false;
foreach (var location in namedTypeSymbol
.Constructors
.SelectMany(methodSymbol => methodSymbol.Parameters)
.Distinct(ParameterReductionComparer.Default)
.Select(parameterSymbol => new { parameterSymbol, isArrayType = parameterSymbol.Type is IArrayTypeSymbol })
.Select(
tuple => new
{
tuple.isArrayType,
tuple.parameterSymbol,
hasParamsKeyWord = tuple.parameterSymbol.ToDisplayString().Contains("params"),
}
)
.Where(tuple => tuple.isArrayType && tuple.hasParamsKeyWord)
.SelectMany(tuple => tuple.parameterSymbol.Locations))
{
productionContext.ReportDiagnostic(Diagnostic.Create(Diagnostics.AutoFixture0002, location));
if (!reported)
{
reported = true;
}
}
const bool reported = false;
// foreach (var location in namedTypeSymbol
// .Constructors
// .SelectMany(methodSymbol => methodSymbol.Parameters)
// .Distinct(ParameterReductionComparer.Default)
// .Select(parameterSymbol => new { parameterSymbol, isArrayType = parameterSymbol.Type is IArrayTypeSymbol })
// .Select(
// tuple => new
// {
// tuple.isArrayType,
// tuple.parameterSymbol,
// hasParamsKeyWord = tuple.parameterSymbol.ToDisplayString().Contains("params"),
// }
// )
// .Where(tuple => tuple.isArrayType && tuple.hasParamsKeyWord)
// .SelectMany(tuple => tuple.parameterSymbol.Locations))
// {
// productionContext.ReportDiagnostic(Diagnostic.Create(Rsaf0002.Descriptor, location));
// if (!reported)
// {
// reported = true;
// }
// }

return reported;
}
Expand Down
54 changes: 10 additions & 44 deletions src/Testing.AutoFixtures/AutoFixtureGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
namespace Rocket.Surgery.Extensions.Testing.AutoFixtures;

[Generator]
public partial class AutoFixtureGenerator : IIncrementalGenerator //, ISourceGenerator
public partial class AutoFixtureGenerator : IIncrementalGenerator
{
private static void CurrentGenerator(
INamedTypeSymbol namedTypeSymbol,
Expand Down Expand Up @@ -51,14 +51,14 @@ Compilation compilation
);

var classDeclaration = BuildClassDeclaration(fixtureName)
.WithMembers(new(fullList));
.WithMembers([.. fullList]);

var namespaceDeclaration = BuildNamespace(targetSymbol)
.WithMembers(new(classDeclaration));

var usingDirectives = new HashSet<string>(
parameterSymbols
.Select(symbol => symbol.Type.ContainingNamespace?.ToDisplayString() ?? string.Empty)
.Select(symbol => symbol.Type.ContainingNamespace?.ToDisplayString() ?? "")
.Where(x => !string.IsNullOrWhiteSpace(x))
.Distinct()
)
Expand Down Expand Up @@ -112,20 +112,19 @@ public void Initialize(IncrementalGeneratorInitializationContext context)

context.RegisterSourceOutput(syntaxProvider, generateFixtureBuilder);

context.RegisterPostInitializationOutput(
initializationContext =>
{
initializationContext.AddSource("AutoFixtureAttribute.g.cs", Attribute.Source());
initializationContext.AddSource($"{nameof(AutoFixtureBase)}.g.cs", AutoFixtureBase.Source);
}
context.RegisterPostInitializationOutput(initializationContext =>
{
initializationContext.AddSource("AutoFixtureAttribute.g.cs", Attribute.Source());
initializationContext.AddSource($"{nameof(AutoFixtureBase)}.g.cs", AutoFixtureBase.Source);
}
);

void generateFixtureBuilder(
static void generateFixtureBuilder(
SourceProductionContext productionContext,
(GeneratorAttributeSyntaxContext context, Compilation compilation) valueTuple
)
{
( var syntaxContext, var compilation ) = valueTuple;
(var syntaxContext, var compilation) = valueTuple;

var classForFixture = GetClassForFixture(syntaxContext);

Expand All @@ -148,37 +147,4 @@ void generateFixtureBuilder(
CurrentGenerator(classForFixture, syntaxContext.TargetSymbol, productionContext, compilation);
}
}

internal class ParameterReductionComparer : IEqualityComparer<IParameterSymbol>
{
public static IEqualityComparer<IParameterSymbol> Default { get; } = new ParameterReductionComparer();

public bool Equals(IParameterSymbol x, IParameterSymbol y) =>
( x.Type.Equals(y.Type) && x.Name.Equals(y.Name) ) || SymbolEqualityComparer.Default.Equals(x, y);

public int GetHashCode(IParameterSymbol obj) => SymbolEqualityComparer.Default.GetHashCode(obj.Type) + obj.Type.GetHashCode() + obj.Name.GetHashCode();
}

internal class NamespaceComparer : IComparer<string>
{
public static NamespaceComparer Default { get; } = new();

public int Compare(string x, string y)
{
// Check if both namespaces start with "System"
var xIsSystem = x.StartsWith("System", StringComparison.Ordinal);
var yIsSystem = y.StartsWith("System", StringComparison.Ordinal);

return xIsSystem switch
{
// If only one of them starts with "System", prioritize it
true when !yIsSystem => -1,
false when yIsSystem => 1,
// If both start with "System" or neither does, compare them alphabetically
true when yIsSystem => string.Compare(x, y, StringComparison.Ordinal),
false when !yIsSystem => string.Compare(x, y, StringComparison.Ordinal),
_ => xIsSystem ? -1 : 1,
};
}
}
}
33 changes: 0 additions & 33 deletions src/Testing.AutoFixtures/Diagnostics.cs

This file was deleted.

Loading
Loading