Skip to content

Commit

Permalink
Follow Invocation -> Parser -> Emitter Pattern for Pretend
Browse files Browse the repository at this point in the history
  • Loading branch information
justindbaur committed Dec 2, 2023
1 parent bcaac87 commit 7670f69
Show file tree
Hide file tree
Showing 19 changed files with 104 additions and 228 deletions.
127 changes: 0 additions & 127 deletions src/Pretender.SourceGenerator/CreateEntrypoint.cs

This file was deleted.

14 changes: 14 additions & 0 deletions src/Pretender.SourceGenerator/Emitter/GrandEmitter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace Pretender.SourceGenerator.Emitter
{
internal class GrandEmitter
{
public GrandEmitter()
{

}
}
}
Original file line number Diff line number Diff line change
@@ -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<Diagnostic> Diagnostics { get; } = new List<Diagnostic>();
public ITypeSymbol PretendType => _pretendType;

public CompilationUnitSyntax GetCompilationUnit(CancellationToken token)
public CompilationUnitSyntax Emit(CancellationToken token)
{
var pretendFieldAssignment = ExpressionStatement(
AssignmentExpression(SyntaxKind.SimpleAssignmentExpression,
Expand All @@ -53,7 +32,7 @@ public CompilationUnitSyntax GetCompilationUnit(CancellationToken token)

token.ThrowIfCancellationRequested();

var typeMembers = TypeToPretend.GetMembers();
var typeMembers = _pretendType.GetMembers();

foreach (var member in typeMembers)
{
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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)));
}
Expand Down Expand Up @@ -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)
Expand All @@ -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})"))))))));
}

Expand All @@ -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?[]
Expand Down Expand Up @@ -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)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
using Pretender.SourceGenerator.SetupArguments;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;

namespace Pretender.SourceGenerator
namespace Pretender.SourceGenerator.Emitter
{
internal class SetupActionEmitter
{
Expand Down
6 changes: 1 addition & 5 deletions src/Pretender.SourceGenerator/InterceptsLocationInfo.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
using Microsoft.CodeAnalysis.Operations;
using Pretender.SourceGenerator.Parser;

namespace Pretender.SourceGenerator
namespace Pretender.SourceGenerator.Invocation
{
internal class CreateInvocation
{
Expand Down Expand Up @@ -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;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
using Microsoft.CodeAnalysis.Operations;
using Pretender.SourceGenerator.Parser;

namespace Pretender.SourceGenerator
namespace Pretender.SourceGenerator.Invocation
{
internal class PretendInvocation
{
Expand Down Expand Up @@ -51,6 +51,7 @@ public static bool IsCandidateSyntaxNode(SyntaxNode node)
cancellationToken.ThrowIfCancellationRequested();
return CreateFromGeneric(invocation);
}

// TODO: Support attribute

return null;
Expand All @@ -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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
using Microsoft.CodeAnalysis.Operations;
using Pretender.SourceGenerator.Parser;

namespace Pretender.SourceGenerator
namespace Pretender.SourceGenerator.Invocation
{
internal class SetupInvocation
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 2 additions & 5 deletions src/Pretender.SourceGenerator/OperationExtensions.cs
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -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)
Expand Down
1 change: 1 addition & 0 deletions src/Pretender.SourceGenerator/Parser/CreateParser.cs
Original file line number Diff line number Diff line change
@@ -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
Expand Down
Loading

0 comments on commit 7670f69

Please sign in to comment.