Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ dotnet add package AlephMapper
Using `PackageReference`:

```xml
<PackageReference Include="AlephMapper" Version="0.7.3">
<PackageReference Include="AlephMapper" Version="0.7.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
Expand All @@ -82,7 +82,7 @@ With Central Package Management:

```xml
<!-- Directory.Packages.props -->
<PackageVersion Include="AlephMapper" Version="0.7.3" />
<PackageVersion Include="AlephMapper" Version="0.7.4" />

<!-- Project file -->
<PackageReference Include="AlephMapper">
Expand All @@ -103,7 +103,7 @@ When referencing the generator directly from source:

### Compiler compatibility

AlephMapper 0.7.3 requires a Roslyn compiler host compatible with `Microsoft.CodeAnalysis` 4.14 or later. This version uses Roslyn's embedded-marker support so its generated configuration attributes remain private to the consuming assembly, including when `InternalsVisibleTo` is used.
AlephMapper 0.7.4 requires a Roslyn compiler host compatible with `Microsoft.CodeAnalysis` 4.14 or later. This version uses Roslyn's embedded-marker support so its generated configuration attributes remain private to the consuming assembly, including when `InternalsVisibleTo` is used.

### Migration: `[Expressive]` to `[Projectable]`

Expand Down
2 changes: 1 addition & 1 deletion source/AlephMapper.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageProjectUrl>https://github.com/Raffinert/AlephMapper</PackageProjectUrl>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageVersion>0.7.3</PackageVersion>
<PackageVersion>0.7.4</PackageVersion>
<BuildNumber Condition="'$(BuildNumber)' == ''">0</BuildNumber>
<Version>$(PackageVersion)</Version>
<AssemblyVersion>$(PackageVersion).$(BuildNumber)</AssemblyVersion>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ internal sealed partial class InliningResolver
// Only handle null-coalescing specially; otherwise defer to default behavior
if (!node.OperatorToken.IsKind(SyntaxKind.QuestionQuestionToken))
{
return base.VisitBinaryExpression(node);
var rewritten = (BinaryExpressionSyntax?)base.VisitBinaryExpression(node);
return node.IsKind(SyntaxKind.AsExpression) && rewritten != null
? rewritten.WithRight(GetFullyQualifiedTypeSyntax((TypeSyntax)node.Right, (TypeSyntax)rewritten.Right))
: rewritten;
}

var rightOriginal = node.Right;
Expand Down Expand Up @@ -116,35 +119,45 @@ private ExpressionSyntax RewriteCollectionExpression(SyntaxNode collectionExpres
try
{
// First, try to get the type from the semantic model of the collection expression itself
var typeInfo = model.GetTypeInfo(collectionExpression);
if (typeInfo.Type != null && typeInfo.Type.TypeKind != TypeKind.Error)
if (CanQuerySemanticModel(collectionExpression))
{
return typeInfo.Type;
var typeInfo = model.GetTypeInfo(collectionExpression);
if (typeInfo.Type != null && typeInfo.Type.TypeKind != TypeKind.Error)
return typeInfo.Type;
}

// If that fails, try to infer from the context
if (contextNode != null)
{
// For binary expressions (null coalescing), use the left side type
var leftTypeInfo = model.GetTypeInfo(contextNode);
if (leftTypeInfo.Type != null)
return leftTypeInfo.Type;
if (CanQuerySemanticModel(contextNode))
{
var leftTypeInfo = model.GetTypeInfo(contextNode);
if (leftTypeInfo.Type != null)
return leftTypeInfo.Type;
}

// For assignments, get the type of the left side
if (contextNode.Parent is AssignmentExpressionSyntax assignment)
{
var leftType = model.GetTypeInfo(assignment.Left);
if (leftType.Type != null)
return leftType.Type;
if (CanQuerySemanticModel(assignment.Left))
{
var leftType = model.GetTypeInfo(assignment.Left);
if (leftType.Type != null)
return leftType.Type;
}
}

// For property initializers, look at the property type
if (contextNode.Parent is EqualsValueClauseSyntax equalsValue &&
equalsValue.Parent is PropertyDeclarationSyntax property)
{
var propType = model.GetTypeInfo(property.Type);
if (propType.Type != null)
return propType.Type;
if (CanQuerySemanticModel(property.Type))
{
var propType = model.GetTypeInfo(property.Type);
if (propType.Type != null)
return propType.Type;
}
}
}

Expand All @@ -157,19 +170,25 @@ private ExpressionSyntax RewriteCollectionExpression(SyntaxNode collectionExpres
if (currentNode is AssignmentExpressionSyntax assignmentExpr &&
IsDescendantOf(collectionExpression, assignmentExpr.Right))
{
var leftSideType = model.GetTypeInfo(assignmentExpr.Left);
if (leftSideType.Type != null)
return leftSideType.Type;
if (CanQuerySemanticModel(assignmentExpr.Left))
{
var leftSideType = model.GetTypeInfo(assignmentExpr.Left);
if (leftSideType.Type != null)
return leftSideType.Type;
}
}

// Look for binary expressions (null coalescing) where we're on the right side
if (currentNode is BinaryExpressionSyntax binaryExpr &&
binaryExpr.OperatorToken.IsKind(SyntaxKind.QuestionQuestionToken) &&
IsDescendantOf(collectionExpression, binaryExpr.Right))
{
var leftSideType = model.GetTypeInfo(binaryExpr.Left);
if (leftSideType.Type != null)
return leftSideType.Type;
if (CanQuerySemanticModel(binaryExpr.Left))
{
var leftSideType = model.GetTypeInfo(binaryExpr.Left);
if (leftSideType.Type != null)
return leftSideType.Type;
}
}

currentNode = currentNode.Parent;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ internal sealed partial class InliningResolver
public override SyntaxNode VisitObjectCreationExpression(ObjectCreationExpressionSyntax node)
{
var rewritten = (ObjectCreationExpressionSyntax)base.VisitObjectCreationExpression(node)!;
if (model.GetTypeInfo(node).Type is { } typeSymbol)
if (CanQuerySemanticModel(node) && model.GetTypeInfo(node).Type is { } typeSymbol)
{
var typeName = AlephMapper.Helpers.TypeDisplay.ForSymbol(
typeSymbol,
Expand All @@ -26,7 +26,9 @@ public override SyntaxNode VisitObjectCreationExpression(ObjectCreationExpressio

public override SyntaxNode VisitImplicitObjectCreationExpression(ImplicitObjectCreationExpressionSyntax implicitNew)
{
var typeSymbol = model.GetTypeInfo(implicitNew).Type;
var typeSymbol = CanQuerySemanticModel(implicitNew)
? model.GetTypeInfo(implicitNew).Type
: null;
var type = typeSymbol == null
? null
: AlephMapper.Helpers.TypeDisplay.ForSymbol(
Expand Down Expand Up @@ -60,6 +62,7 @@ public override SyntaxNode VisitImplicitObjectCreationExpression(ImplicitObjectC
private bool IsAnnotatedReturnCreation(ExpressionSyntax expression)
{
return returnTypeToAnnotate != null &&
CanQuerySemanticModel(expression) &&
SymbolEqualityComparer.Default.Equals(
model.GetTypeInfo(expression).Type ?? model.GetTypeInfo(expression).ConvertedType,
returnTypeToAnnotate);
Expand Down
14 changes: 13 additions & 1 deletion source/SyntaxRewriters/InliningResolver.InvocationRewriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,21 @@ internal sealed partial class InliningResolver(

private IMethodSymbol? ResolveMethodGroupSymbol(ExpressionSyntax expr)
{
if (!CanQuerySemanticModel(expr))
{
return null;
}

var si = model.GetSymbolInfo(expr);
if (si.Symbol is IMethodSymbol ms) return ms;
return null;
}

private bool CanQuerySemanticModel(SyntaxNode node)
{
return node.SyntaxTree == model.SyntaxTree;
}

private static IMethodSymbol? TryGetDelegateInvoke(IMethodSymbol invokedMethod, int argIndex)
{
if (argIndex < 0 || argIndex >= invokedMethod.Parameters.Length) return null;
Expand All @@ -63,7 +73,9 @@ private void RecordCircularReference(IMethodSymbol method)

public override SyntaxNode? VisitInvocationExpression(InvocationExpressionSyntax node)
{
if (node.Parent == null || model.GetSymbolInfo(node.Expression).Symbol is not IMethodSymbol invokedMethod)
if (node.Parent == null ||
!CanQuerySemanticModel(node) ||
model.GetSymbolInfo(node.Expression).Symbol is not IMethodSymbol invokedMethod)
{
return base.VisitInvocationExpression(node);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ internal partial class InliningResolver

if (rewriteSupport is NullConditionalRewrite.Rewrite)
{
if (!CanQuerySemanticModel(node))
{
return base.VisitConditionalAccessExpression(node);
}

var typeInfo = model.GetTypeInfo(node);
var convertedType = typeInfo.ConvertedType ?? typeInfo.Type;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#nullable enable

using AlephMapper.Helpers;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;

namespace AlephMapper.SyntaxRewriters;

internal sealed partial class InliningResolver
{
public override SyntaxNode? VisitMemberAccessExpression(MemberAccessExpressionSyntax node)
{
var rewritten = (MemberAccessExpressionSyntax?)base.VisitMemberAccessExpression(node);
if (rewritten == null ||
!CanQuerySemanticModel(node) ||
model.GetSymbolInfo(node).Symbol is not ISymbol member ||
!member.IsStatic ||
member.ContainingType is not ITypeSymbol containingType)
{
return rewritten;
}

var typeName = TypeDisplay.ForSymbol(
containingType,
containingType.NullableAnnotation,
nullablePolicy);
var typeSyntax = ParseTypeName(typeName).WithTriviaFrom(rewritten.Expression);
return MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression,
typeSyntax,
rewritten.Name)
.WithTriviaFrom(rewritten);
}
}

#nullable restore
52 changes: 52 additions & 0 deletions source/SyntaxRewriters/InliningResolver.TypeReferenceRewriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

using AlephMapper.Helpers;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System.Linq;

namespace AlephMapper.SyntaxRewriters;

Expand All @@ -26,8 +28,58 @@ internal sealed partial class InliningResolver
return rewritten?.WithType(GetFullyQualifiedTypeSyntax(node.Type, rewritten.Type));
}

public override SyntaxNode? VisitDeclarationPattern(DeclarationPatternSyntax node)
{
var rewritten = (DeclarationPatternSyntax?)base.VisitDeclarationPattern(node);
return rewritten?.WithType(GetFullyQualifiedTypeSyntax(node.Type, rewritten.Type));
}

public override SyntaxNode? VisitRecursivePattern(RecursivePatternSyntax node)
{
var rewritten = (RecursivePatternSyntax?)base.VisitRecursivePattern(node);
return rewritten?.WithType(
rewritten.Type == null
? null
: GetFullyQualifiedTypeSyntax(node.Type!, rewritten.Type));
}

public override SyntaxNode? VisitArrayCreationExpression(ArrayCreationExpressionSyntax node)
{
var rewritten = (ArrayCreationExpressionSyntax?)base.VisitArrayCreationExpression(node);
return rewritten?.WithType(
rewritten.Type.WithElementType(
GetFullyQualifiedTypeSyntax(node.Type.ElementType, rewritten.Type.ElementType)));
}

public override SyntaxNode? VisitSizeOfExpression(SizeOfExpressionSyntax node)
{
var rewritten = (SizeOfExpressionSyntax?)base.VisitSizeOfExpression(node);
return rewritten?.WithType(GetFullyQualifiedTypeSyntax(node.Type, rewritten.Type));
}

public override SyntaxNode? VisitGenericName(GenericNameSyntax node)
{
var rewritten = (GenericNameSyntax?)base.VisitGenericName(node);
if (rewritten == null)
{
return null;
}

var typeArguments = node.TypeArgumentList.Arguments
.Zip(rewritten.TypeArgumentList.Arguments, GetFullyQualifiedTypeSyntax)
.ToList();
return rewritten.WithTypeArgumentList(
rewritten.TypeArgumentList.WithArguments(
Microsoft.CodeAnalysis.CSharp.SyntaxFactory.SeparatedList(typeArguments)));
}

private TypeSyntax GetFullyQualifiedTypeSyntax(TypeSyntax originalType, TypeSyntax rewrittenType)
{
if (!CanQuerySemanticModel(originalType))
{
return rewrittenType;
}

var type = model.GetTypeInfo(originalType).Type;
if (type is null || type.TypeKind == TypeKind.Error)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

namespace Tests;

[global::System.CodeDom.Compiler.GeneratedCode("AlephMapper", "0.7.3.0")]
[global::System.CodeDom.Compiler.GeneratedCode("AlephMapper", "0.7.4.0")]
partial class PersonMapper
{
/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

namespace Tests;

[global::System.CodeDom.Compiler.GeneratedCode("AlephMapper", "0.7.3.0")]
[global::System.CodeDom.Compiler.GeneratedCode("AlephMapper", "0.7.4.0")]
partial class PersonMapper
{
/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

namespace Tests;

[global::System.CodeDom.Compiler.GeneratedCode("AlephMapper", "0.7.3.0")]
[global::System.CodeDom.Compiler.GeneratedCode("AlephMapper", "0.7.4.0")]
partial class AdaptUpdateMapper
{
/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

namespace AlephMapper.Tests;

[global::System.CodeDom.Compiler.GeneratedCode("AlephMapper", "0.7.3.0")]
[global::System.CodeDom.Compiler.GeneratedCode("AlephMapper", "0.7.4.0")]
partial class CircularMapper
{
/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

namespace AlephMapper.Tests;

[global::System.CodeDom.Compiler.GeneratedCode("AlephMapper", "0.7.3.0")]
[global::System.CodeDom.Compiler.GeneratedCode("AlephMapper", "0.7.4.0")]
partial class CircularPropertyMapper
{
/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

namespace AlephMapper.Tests;

[global::System.CodeDom.Compiler.GeneratedCode("AlephMapper", "0.7.3.0")]
[global::System.CodeDom.Compiler.GeneratedCode("AlephMapper", "0.7.4.0")]
partial class CollectionMapper
{
/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

namespace AlephMapper.Tests;

[global::System.CodeDom.Compiler.GeneratedCode("AlephMapper", "0.7.3.0")]
[global::System.CodeDom.Compiler.GeneratedCode("AlephMapper", "0.7.4.0")]
partial class ConditionalPatternMapper
{
/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

namespace AlephMapper.Tests;

[global::System.CodeDom.Compiler.GeneratedCode("AlephMapper", "0.7.3.0")]
[global::System.CodeDom.Compiler.GeneratedCode("AlephMapper", "0.7.4.0")]
partial class EfCoreIgnoreMapper
{
/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

namespace AlephMapper.Tests;

[global::System.CodeDom.Compiler.GeneratedCode("AlephMapper", "0.7.3.0")]
[global::System.CodeDom.Compiler.GeneratedCode("AlephMapper", "0.7.4.0")]
partial class EfCoreMapper
{
/// <summary>
Expand Down
Loading
Loading