diff --git a/src/Pretender.SourceGenerator/CreateEntrypoint.cs b/src/Pretender.SourceGenerator/CreateEntrypoint.cs deleted file mode 100644 index b61a1f1..0000000 --- a/src/Pretender.SourceGenerator/CreateEntrypoint.cs +++ /dev/null @@ -1,127 +0,0 @@ -using Microsoft.CodeAnalysis.CSharp.Syntax; -using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; -using Microsoft.CodeAnalysis.Operations; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using System.Collections.Immutable; - -namespace Pretender.SourceGenerator -{ - internal class CreateEntrypoint - { - public CreateEntrypoint(IInvocationOperation operation, ImmutableArray? typeArguments) - { - Operation = operation; - Location = new InterceptsLocationInfo(operation); - TypeArguments = typeArguments; - - // TODO: Do any Diagnostics? - } - - public InterceptsLocationInfo Location { get; } - public IInvocationOperation Operation { get; } - public ImmutableArray? TypeArguments { get; } - - public MethodDeclarationSyntax GetMethodDeclaration(int index) - { - var returnType = Operation.TargetMethod.ReturnType; - var returnTypeSyntax = returnType.AsUnknownTypeSyntax(); - - TypeParameterSyntax[] typeParameters; - ParameterSyntax[] methodParameters; - ArgumentSyntax[] constructorArguments; - - if (TypeArguments.HasValue) - { - typeParameters = new TypeParameterSyntax[TypeArguments.Value.Length]; - - // We always take the Pretend argument first as a this parameter - methodParameters = new ParameterSyntax[TypeArguments.Value.Length + 1]; - constructorArguments = new ArgumentSyntax[TypeArguments.Value.Length + 1]; - - for (var i = 0; i < TypeArguments.Value.Length; i++) - { - var typeName = $"T{i}"; - var argName = $"arg{i}"; - - typeParameters[i] = TypeParameter(typeName); - methodParameters[i + 1] = Parameter(Identifier(argName)) - .WithType(ParseTypeName(typeName)); - constructorArguments[i + 1] = Argument(IdentifierName(argName)); - } - } - else - { - typeParameters = []; - methodParameters = new ParameterSyntax[1]; - constructorArguments = new ArgumentSyntax[1]; - } - - methodParameters[0] = Parameter(Identifier("pretend")) - .WithType(GenericName("Pretend") - .AddTypeArgumentListArguments(returnTypeSyntax)) - .WithModifiers(TokenList(Token(SyntaxKind.ThisKeyword)) - ); - - constructorArguments[0] = Argument(IdentifierName("pretend")); - - var objectCreation = ObjectCreationExpression(ParseTypeName(returnType.ToPretendName())) - .WithArgumentList(ArgumentList(SeparatedList(constructorArguments))); - - var method = MethodDeclaration(returnTypeSyntax, $"Create{index}") - .WithBody(Block(ReturnStatement(objectCreation))) - .WithParameterList(ParameterList(SeparatedList(methodParameters))) - .WithModifiers(TokenList(Token(SyntaxKind.InternalKeyword), Token(SyntaxKind.StaticKeyword))); - - if (typeParameters.Length > 0) - { - return method - .WithTypeParameterList(TypeParameterList(SeparatedList(typeParameters))); - } - - return method; - } - } - - public class CreateEntryPointComparer : IEqualityComparer - { - - bool IEqualityComparer.Equals(CreateEntrypoint x, CreateEntrypoint y) - { - return SymbolEqualityComparer.Default.Equals(x.Operation.TargetMethod.ReturnType, y.Operation.TargetMethod.ReturnType) - && CompareTypeArguments(x.TypeArguments, y.TypeArguments); - } - - static bool CompareTypeArguments(ImmutableArray? x, ImmutableArray? y) - { - if (!x.HasValue) - { - return !y.HasValue; - } - - var xArray = x.Value; - var yArray = y!.Value; - - if (xArray.Length != yArray.Length) - { - return false; - } - - for (int i = 0; i < xArray.Length; i++) - { - if (!SymbolEqualityComparer.IncludeNullability.Equals(xArray[i], yArray[i])) - { - return false; - } - } - - return true; - } - - int IEqualityComparer.GetHashCode(CreateEntrypoint obj) - { - return SymbolEqualityComparer.Default.GetHashCode(obj.Operation.TargetMethod.ReturnType); - } - } - -} diff --git a/src/Pretender.SourceGenerator/Emitter/GrandEmitter.cs b/src/Pretender.SourceGenerator/Emitter/GrandEmitter.cs new file mode 100644 index 0000000..f2151e1 --- /dev/null +++ b/src/Pretender.SourceGenerator/Emitter/GrandEmitter.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Pretender.SourceGenerator.Emitter +{ + internal class GrandEmitter + { + public GrandEmitter() + { + + } + } +} diff --git a/src/Pretender.SourceGenerator/PretendEntrypoint.cs b/src/Pretender.SourceGenerator/Emitter/PretendEmitter.cs similarity index 86% rename from src/Pretender.SourceGenerator/PretendEntrypoint.cs rename to src/Pretender.SourceGenerator/Emitter/PretendEmitter.cs index e1c3456..dfaf851 100644 --- a/src/Pretender.SourceGenerator/PretendEntrypoint.cs +++ b/src/Pretender.SourceGenerator/Emitter/PretendEmitter.cs @@ -1,46 +1,25 @@ using System.Collections.Immutable; -using System.Diagnostics; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Operations; - using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; -namespace Pretender.SourceGenerator +namespace Pretender.SourceGenerator.Emitter { - internal class PretendEntrypoint + internal class PretendEmitter { - public static PretendEntrypoint FromMethodGeneric(IInvocationOperation operation) - { - Debug.Assert(operation.TargetMethod.TypeArguments.Length == 1, "This should have been asserted already"); - var genericLocation = ((GenericNameSyntax)((MemberAccessExpressionSyntax)((InvocationExpressionSyntax)operation.Syntax).Expression).Name).TypeArgumentList.Arguments[0].GetLocation(); - var typeArgument = operation.TargetMethod.TypeArguments[0]; - return new PretendEntrypoint(typeArgument, - genericLocation); - } + private readonly ITypeSymbol _pretendType; + private readonly bool _fillExisting; - public PretendEntrypoint(ITypeSymbol typeToPretend, Location invocationLocation) + public PretendEmitter(ITypeSymbol pretendType, bool fillExisting) { - TypeToPretend = typeToPretend; - - InvocationLocation = invocationLocation; - - // TODO: Do more diagnostics - if (TypeToPretend.IsSealed) - { - Diagnostics.Add(Diagnostic.Create( - DiagnosticDescriptors.UnableToPretendSealedType, - invocationLocation, - TypeToPretend)); - } + _pretendType = pretendType; + _fillExisting = fillExisting; } - public ITypeSymbol TypeToPretend { get; } - public Location InvocationLocation { get; } - public List Diagnostics { get; } = new List(); + public ITypeSymbol PretendType => _pretendType; - public CompilationUnitSyntax GetCompilationUnit(CancellationToken token) + public CompilationUnitSyntax Emit(CancellationToken token) { var pretendFieldAssignment = ExpressionStatement( AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, @@ -53,7 +32,7 @@ public CompilationUnitSyntax GetCompilationUnit(CancellationToken token) token.ThrowIfCancellationRequested(); - var typeMembers = TypeToPretend.GetMembers(); + var typeMembers = _pretendType.GetMembers(); foreach (var member in typeMembers) { @@ -139,7 +118,7 @@ public CompilationUnitSyntax GetCompilationUnit(CancellationToken token) methodInfoFields.Add(instanceField); - var classDeclaration = TypeToPretend.ScaffoldImplementation(new ScaffoldTypeOptions + var classDeclaration = _pretendType.ScaffoldImplementation(new ScaffoldTypeOptions { CustomFields = methodInfoFields.ToImmutableArray(), AddMethodBody = CreateMethodBody, @@ -186,7 +165,7 @@ private InvocationExpressionSyntax CreateSimpleMethodInfoGetter(string name, str return InvocationExpression(MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, - TypeOfExpression(ParseTypeName(TypeToPretend.ToFullDisplayString())), + TypeOfExpression(ParseTypeName(_pretendType.ToFullDisplayString())), IdentifierName(afterTypeOfMethod))) .AddArgumentListArguments(Argument(NameOfExpression(name))); } @@ -223,7 +202,7 @@ private ParameterSyntax CreateConstructorParameter() private TypeSyntax GetGenericPretendType() { return GenericName(Identifier("Pretend"), - TypeArgumentList(SingletonSeparatedList(ParseTypeName(TypeToPretend.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat))))); + TypeArgumentList(SingletonSeparatedList(ParseTypeName(_pretendType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat))))); } private FieldDeclarationSyntax GetStaticMethodCacheField(IMethodSymbol method, int index) @@ -233,7 +212,7 @@ private FieldDeclarationSyntax GetStaticMethodCacheField(IMethodSymbol method, i .AddModifiers(Token(SyntaxKind.PrivateKeyword), Token(SyntaxKind.StaticKeyword), Token(SyntaxKind.ReadOnlyKeyword)) .AddDeclarationVariables(VariableDeclarator(Identifier($"__methodInfo_{method.Name}_{index}")) .WithInitializer(EqualsValueClause( - InvocationExpression(MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, TypeOfExpression(ParseTypeName(TypeToPretend.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat))), IdentifierName("GetMethod"))) + InvocationExpression(MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, TypeOfExpression(ParseTypeName(_pretendType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat))), IdentifierName("GetMethod"))) .WithArgumentList(ArgumentList(SingletonSeparatedList(Argument(ParseExpression($"nameof({method.Name})")))))))); } @@ -244,7 +223,7 @@ private BlockSyntax CreateMethodBody(IMethodSymbol method) // This is using the new collection expression syntax in C# 12 // [arg1, arg2, arg3] var collectionExpression = CollectionExpression() - .AddElements(method.Parameters.Select(p + .AddElements(method.Parameters.Select(p => ExpressionElement(IdentifierName(p.Name))).ToArray()); // object?[] @@ -294,7 +273,7 @@ private BlockSyntax CreateMethodBody(IMethodSymbol method) var refAndOutParameters = method.Parameters .Where(p => p.RefKind == RefKind.Ref || p.RefKind == RefKind.Out); - + foreach (var p in refAndOutParameters) { diff --git a/src/Pretender.SourceGenerator/SetupActionEmitter.cs b/src/Pretender.SourceGenerator/Emitter/SetupActionEmitter.cs similarity index 99% rename from src/Pretender.SourceGenerator/SetupActionEmitter.cs rename to src/Pretender.SourceGenerator/Emitter/SetupActionEmitter.cs index d1f55d2..4f8458a 100644 --- a/src/Pretender.SourceGenerator/SetupActionEmitter.cs +++ b/src/Pretender.SourceGenerator/Emitter/SetupActionEmitter.cs @@ -6,7 +6,7 @@ using Pretender.SourceGenerator.SetupArguments; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; -namespace Pretender.SourceGenerator +namespace Pretender.SourceGenerator.Emitter { internal class SetupActionEmitter { diff --git a/src/Pretender.SourceGenerator/InterceptsLocationInfo.cs b/src/Pretender.SourceGenerator/InterceptsLocationInfo.cs index d9e7168..48e342c 100644 --- a/src/Pretender.SourceGenerator/InterceptsLocationInfo.cs +++ b/src/Pretender.SourceGenerator/InterceptsLocationInfo.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; - -using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Operations; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; diff --git a/src/Pretender.SourceGenerator/CreateInvocation.cs b/src/Pretender.SourceGenerator/Invocation/CreateInvocation.cs similarity index 98% rename from src/Pretender.SourceGenerator/CreateInvocation.cs rename to src/Pretender.SourceGenerator/Invocation/CreateInvocation.cs index e529e80..b295c09 100644 --- a/src/Pretender.SourceGenerator/CreateInvocation.cs +++ b/src/Pretender.SourceGenerator/Invocation/CreateInvocation.cs @@ -5,7 +5,7 @@ using Microsoft.CodeAnalysis.Operations; using Pretender.SourceGenerator.Parser; -namespace Pretender.SourceGenerator +namespace Pretender.SourceGenerator.Invocation { internal class CreateInvocation { @@ -36,7 +36,7 @@ public static bool IsCandidateSyntaxNode(SyntaxNode node) Debug.Assert(IsCandidateSyntaxNode(context.Node)); return context.SemanticModel.GetOperation(context.Node, cancellationToken) is IInvocationOperation operation && IsCreateOperation(operation, out var typeArguments) - ? new CreateInvocation(operation, typeArguments,new InterceptsLocationInfo(operation)) + ? new CreateInvocation(operation, typeArguments, new InterceptsLocationInfo(operation)) : null; } diff --git a/src/Pretender.SourceGenerator/PretendInvocation.cs b/src/Pretender.SourceGenerator/Invocation/PretendInvocation.cs similarity index 82% rename from src/Pretender.SourceGenerator/PretendInvocation.cs rename to src/Pretender.SourceGenerator/Invocation/PretendInvocation.cs index 5c6e022..fefe32a 100644 --- a/src/Pretender.SourceGenerator/PretendInvocation.cs +++ b/src/Pretender.SourceGenerator/Invocation/PretendInvocation.cs @@ -4,7 +4,7 @@ using Microsoft.CodeAnalysis.Operations; using Pretender.SourceGenerator.Parser; -namespace Pretender.SourceGenerator +namespace Pretender.SourceGenerator.Invocation { internal class PretendInvocation { @@ -51,6 +51,7 @@ public static bool IsCandidateSyntaxNode(SyntaxNode node) cancellationToken.ThrowIfCancellationRequested(); return CreateFromGeneric(invocation); } + // TODO: Support attribute return null; @@ -68,7 +69,16 @@ public static bool IsCandidateSyntaxNode(SyntaxNode node) return null; } - return new PretendInvocation(operation.TargetMethod.TypeArguments[0], operation.Syntax.GetLocation(), false); + return CreateFromTypeSymbol( + operation.TargetMethod.TypeArguments[0], + operation.Syntax.GetLocation(), + fillExisting: false); + } + + private static PretendInvocation? CreateFromTypeSymbol(ITypeSymbol typeSymbol, Location location, bool fillExisting) + { + // TODO: Maybe check that ITypeSymbol is INamedTypeSymbol? + return new PretendInvocation(typeSymbol, location, fillExisting); } } } diff --git a/src/Pretender.SourceGenerator/SetupInvocation.cs b/src/Pretender.SourceGenerator/Invocation/SetupInvocation.cs similarity index 97% rename from src/Pretender.SourceGenerator/SetupInvocation.cs rename to src/Pretender.SourceGenerator/Invocation/SetupInvocation.cs index 7d611dd..bfc8d81 100644 --- a/src/Pretender.SourceGenerator/SetupInvocation.cs +++ b/src/Pretender.SourceGenerator/Invocation/SetupInvocation.cs @@ -4,7 +4,7 @@ using Microsoft.CodeAnalysis.Operations; using Pretender.SourceGenerator.Parser; -namespace Pretender.SourceGenerator +namespace Pretender.SourceGenerator.Invocation { internal class SetupInvocation { diff --git a/src/Pretender.SourceGenerator/VerifyInvocation.cs b/src/Pretender.SourceGenerator/Invocation/VerifyInvocation.cs similarity index 98% rename from src/Pretender.SourceGenerator/VerifyInvocation.cs rename to src/Pretender.SourceGenerator/Invocation/VerifyInvocation.cs index 4c9015c..70cdf14 100644 --- a/src/Pretender.SourceGenerator/VerifyInvocation.cs +++ b/src/Pretender.SourceGenerator/Invocation/VerifyInvocation.cs @@ -4,7 +4,7 @@ using Microsoft.CodeAnalysis.Operations; using Pretender.SourceGenerator.Parser; -namespace Pretender.SourceGenerator +namespace Pretender.SourceGenerator.Invocation { // This should be a simple class just holding some information, deeper introspection to find diagnostics should // be done with a type cache diff --git a/src/Pretender.SourceGenerator/OperationExtensions.cs b/src/Pretender.SourceGenerator/OperationExtensions.cs index a554aa1..345818c 100644 --- a/src/Pretender.SourceGenerator/OperationExtensions.cs +++ b/src/Pretender.SourceGenerator/OperationExtensions.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; - -using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Operations; @@ -12,6 +8,7 @@ namespace Pretender.SourceGenerator { internal static class OperationExtensions { + // TODO: Take KnownTypeSymbols public static LiteralExpressionSyntax ToLiteralExpression(this ILiteralOperation operation) { if (operation.Type is null || !operation.ConstantValue.HasValue) diff --git a/src/Pretender.SourceGenerator/Parser/CreateParser.cs b/src/Pretender.SourceGenerator/Parser/CreateParser.cs index fedbdef..fb6c521 100644 --- a/src/Pretender.SourceGenerator/Parser/CreateParser.cs +++ b/src/Pretender.SourceGenerator/Parser/CreateParser.cs @@ -1,6 +1,7 @@ using System.Collections.Immutable; using Microsoft.CodeAnalysis; using Pretender.SourceGenerator.Emitter; +using Pretender.SourceGenerator.Invocation; using static Pretender.SourceGenerator.PretenderSourceGenerator; namespace Pretender.SourceGenerator.Parser diff --git a/src/Pretender.SourceGenerator/Parser/PretendParser.cs b/src/Pretender.SourceGenerator/Parser/PretendParser.cs index 4fd1444..672cfc1 100644 --- a/src/Pretender.SourceGenerator/Parser/PretendParser.cs +++ b/src/Pretender.SourceGenerator/Parser/PretendParser.cs @@ -1,5 +1,7 @@ using System.Collections.Immutable; using Microsoft.CodeAnalysis; +using Pretender.SourceGenerator.Emitter; +using Pretender.SourceGenerator.Invocation; using static Pretender.SourceGenerator.PretenderSourceGenerator; namespace Pretender.SourceGenerator.Parser @@ -14,10 +16,27 @@ public PretendParser(PretendInvocation pretendInvocation, CompilationData compil public PretendInvocation PretendInvocation { get; } - public (object? Emitter, ImmutableArray? Diagnostics) GetEmitter(CancellationToken cancellationToken) + public (PretendEmitter? Emitter, ImmutableArray? Diagnostics) Parse(CancellationToken cancellationToken) { + if (PretendInvocation.PretendType.IsSealed) + { + var sealedError = Diagnostic.Create( + DiagnosticDescriptors.UnableToPretendSealedType, + PretendInvocation.Location); + return (null, ImmutableArray.Create(sealedError)); + } - return (null, null); + // TODO: If we are filling an existing time, check that it is partial + + // TODO: Do more error diagnostics + + // TODO: Warn about well known good fakes + + // TODO: Do a larger amount of parsing + + cancellationToken.ThrowIfCancellationRequested(); + + return (new PretendEmitter(PretendInvocation.PretendType, PretendInvocation.FillExisting), null); } } } diff --git a/src/Pretender.SourceGenerator/Parser/SetupActionParser.cs b/src/Pretender.SourceGenerator/Parser/SetupActionParser.cs index 085f334..168cea8 100644 --- a/src/Pretender.SourceGenerator/Parser/SetupActionParser.cs +++ b/src/Pretender.SourceGenerator/Parser/SetupActionParser.cs @@ -2,6 +2,7 @@ using System.Diagnostics; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Operations; +using Pretender.SourceGenerator.Emitter; using Pretender.SourceGenerator.SetupArguments; namespace Pretender.SourceGenerator.Parser diff --git a/src/Pretender.SourceGenerator/Parser/SetupParser.cs b/src/Pretender.SourceGenerator/Parser/SetupParser.cs index 8d8002e..a5aed1c 100644 --- a/src/Pretender.SourceGenerator/Parser/SetupParser.cs +++ b/src/Pretender.SourceGenerator/Parser/SetupParser.cs @@ -1,6 +1,7 @@ using System.Collections.Immutable; using Microsoft.CodeAnalysis; using Pretender.SourceGenerator.Emitter; +using Pretender.SourceGenerator.Invocation; using static Pretender.SourceGenerator.PretenderSourceGenerator; namespace Pretender.SourceGenerator.Parser diff --git a/src/Pretender.SourceGenerator/Parser/VerifyParser.cs b/src/Pretender.SourceGenerator/Parser/VerifyParser.cs index 17f83d1..3460bd1 100644 --- a/src/Pretender.SourceGenerator/Parser/VerifyParser.cs +++ b/src/Pretender.SourceGenerator/Parser/VerifyParser.cs @@ -1,6 +1,7 @@ using System.Collections.Immutable; using Microsoft.CodeAnalysis; using Pretender.SourceGenerator.Emitter; +using Pretender.SourceGenerator.Invocation; using static Pretender.SourceGenerator.PretenderSourceGenerator; namespace Pretender.SourceGenerator.Parser diff --git a/src/Pretender.SourceGenerator/PretendEntrypointComparer.cs b/src/Pretender.SourceGenerator/PretendEntrypointComparer.cs deleted file mode 100644 index 4fd8a5a..0000000 --- a/src/Pretender.SourceGenerator/PretendEntrypointComparer.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Microsoft.CodeAnalysis; - -namespace Pretender.SourceGenerator -{ - internal class PretendEntrypointComparer : IEqualityComparer - { - public static readonly PretendEntrypointComparer TypeSymbol = new PretendEntrypointComparer(); - - public bool Equals(PretendEntrypoint x, PretendEntrypoint y) - { - return SymbolEqualityComparer.Default.Equals(x.TypeToPretend, y.TypeToPretend); - } - - public int GetHashCode(PretendEntrypoint obj) - { - return SymbolEqualityComparer.Default.GetHashCode(obj.TypeToPretend); - } - } -} diff --git a/src/Pretender.SourceGenerator/PretenderSourceGenerator.cs b/src/Pretender.SourceGenerator/PretenderSourceGenerator.cs index 14a9773..c326e43 100644 --- a/src/Pretender.SourceGenerator/PretenderSourceGenerator.cs +++ b/src/Pretender.SourceGenerator/PretenderSourceGenerator.cs @@ -4,6 +4,7 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Pretender.SourceGenerator.Emitter; +using Pretender.SourceGenerator.Invocation; using Pretender.SourceGenerator.Parser; namespace Pretender.SourceGenerator @@ -22,43 +23,40 @@ public void Initialize(IncrementalGeneratorInitializationContext context) : null); #region Pretend - IncrementalValuesProvider pretendsWithDiagnostics = + IncrementalValuesProvider<(PretendEmitter? Emitter, ImmutableArray? Diagnostics)> pretends = context.SyntaxProvider.CreateSyntaxProvider( predicate: (node, _) => PretendInvocation.IsCandidateSyntaxNode(node), - transform: static (context, token) => + transform: PretendInvocation.Create) + .Where(static p => p != null) + .Combine(compilationData) + .Select(static (tuple, cancellationToken) => { - var operation = context.SemanticModel.GetOperation(context.Node, token); - // TODO: I think this is where I need to filter out false positives - if (operation.IsInvocationOperation(out var invocationOperation)) + if (tuple.Right is not CompilationData compilationData) { - token.ThrowIfCancellationRequested(); - - return PretendEntrypoint.FromMethodGeneric(invocationOperation!); + return (null, null); } - // TODO: Check for constructor invocation operation - // and create the PretendEntrypoint with that information - return null; + // TODO: Create Parser + var parser = new PretendParser(tuple.Left!, compilationData); + return parser.Parse(cancellationToken); }) - .Where(static p => p != null) - .WithTrackingName("FindPretendGenerics")!; + .WithTrackingName("Pretend"); - context.RegisterSourceOutput(pretendsWithDiagnostics, static (context, pretend) => + context.RegisterSourceOutput(pretends, static (context, pretend) => { - foreach (var diagnostic in pretend!.Diagnostics) + if (pretend.Diagnostics is ImmutableArray diagnostics) { - context.ReportDiagnostic(diagnostic); + foreach (var diagnostic in diagnostics) + { + context.ReportDiagnostic(diagnostic); + } } - }); - var pretends = pretendsWithDiagnostics - .Where(p => p!.Diagnostics.Count == 0) - .GroupWith(s => s.InvocationLocation, PretendEntrypointComparer.TypeSymbol); - - context.RegisterSourceOutput(pretends, static (context, pretend) => - { - var compilationUnit = pretend.Source.GetCompilationUnit(context.CancellationToken); - context.AddSource($"Pretender.Type.{pretend.Source.TypeToPretend.ToPretendName()}.g.cs", compilationUnit.GetText(Encoding.UTF8)); + if (pretend.Emitter is PretendEmitter emitter) + { + var compilationUnit = emitter.Emit(context.CancellationToken); + context.AddSource($"Pretender.Type.{emitter.PretendType.ToPretendName()}.g.cs", compilationUnit.GetText(Encoding.UTF8)); + } }); #endregion diff --git a/src/Pretender.SourceGenerator/SymbolExtensions.cs b/src/Pretender.SourceGenerator/SymbolExtensions.cs index 6fd0eee..53964b7 100644 --- a/src/Pretender.SourceGenerator/SymbolExtensions.cs +++ b/src/Pretender.SourceGenerator/SymbolExtensions.cs @@ -9,6 +9,7 @@ namespace Pretender.SourceGenerator { internal static class SymbolExtensions { + // TODO: Replace many of these with KnownTypeSymbols public static bool EqualsByName(this ITypeSymbol type, string[] name) { var length = name.Length; @@ -52,11 +53,13 @@ public static TypeSyntax AsUnknownTypeSyntax(this ITypeSymbol type) public static ExpressionSyntax ToDefaultValueSyntax(this INamedTypeSymbol type, KnownTypeSymbols knownTypeSymbols) { // They have explicitly annotated this type as nullable, so return null - if (type.NullableAnnotation != NullableAnnotation.NotAnnotated) + if (type.NullableAnnotation == NullableAnnotation.Annotated) { return LiteralExpression(SyntaxKind.DefaultLiteralExpression); } + var nullableEnabled = type.NullableAnnotation != NullableAnnotation.None; + var comparer = SymbolEqualityComparer.Default; if (type.IsUnboundGenericType) diff --git a/test/SourceGeneratorTests/MainTests.cs b/test/SourceGeneratorTests/MainTests.cs index f85f2f4..db1404d 100644 --- a/test/SourceGeneratorTests/MainTests.cs +++ b/test/SourceGeneratorTests/MainTests.cs @@ -25,6 +25,8 @@ public async Task TaskOfTMethod() using System.Threading.Tasks; using Pretender; + namespace TaskOfTMethodNamespace; + public interface IMyInterface { Task MethodAsync(string str);