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
Original file line number Diff line number Diff line change
Expand Up @@ -385,7 +385,63 @@ public ExpressionSyntaxRewriter(INamedTypeSymbol targetTypeSymbol, NullCondition

return base.VisitNullableType(node);
}


public override SyntaxNode? VisitInitializerExpression(InitializerExpressionSyntax node)
{
// Only handle object initializers that might contain indexer assignments
if (!node.IsKind(SyntaxKind.ObjectInitializerExpression))
{
return base.VisitInitializerExpression(node);
}

// Check if any expression is an indexer assignment (e.g., ["key"] = value)
var hasIndexerAssignment = node.Expressions.Any(e =>
e is AssignmentExpressionSyntax { Left: ImplicitElementAccessSyntax });

if (!hasIndexerAssignment)
{
return base.VisitInitializerExpression(node);
}

var newExpressions = new SeparatedSyntaxList<ExpressionSyntax>();

foreach (var expression in node.Expressions)
{
if (expression is AssignmentExpressionSyntax assignment &&
assignment.Left is ImplicitElementAccessSyntax implicitElementAccess)
{
// Transform ["key"] = value into { "key", value }
var arguments = new SeparatedSyntaxList<ExpressionSyntax>();

foreach (var argument in implicitElementAccess.ArgumentList.Arguments)
{
var visitedArgument = (ExpressionSyntax?)Visit(argument.Expression) ?? argument.Expression;
arguments = arguments.Add(visitedArgument);
}

var visitedValue = (ExpressionSyntax?)Visit(assignment.Right) ?? assignment.Right;
arguments = arguments.Add(visitedValue);

var complexElementInitializer = SyntaxFactory.InitializerExpression(
SyntaxKind.ComplexElementInitializerExpression,
arguments
);

newExpressions = newExpressions.Add(complexElementInitializer);
}
else
{
var visitedExpression = (ExpressionSyntax?)Visit(expression) ?? expression;
newExpressions = newExpressions.Add(visitedExpression);
}
}

return SyntaxFactory.InitializerExpression(
SyntaxKind.CollectionInitializerExpression,
newExpressions
).WithTriviaFrom(node);
}

private ExpressionSyntax ReplaceVariableWithCast(ExpressionSyntax expression, DeclarationPatternSyntax declaration, ExpressionSyntax governingExpression)
{
if (declaration.Designation is SingleVariableDesignationSyntax variableDesignation)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// <auto-generated/>
#nullable disable
using System;
using System.Linq;
using System.Collections.Generic;
using EntityFrameworkCore.Projectables;
using Foo;

namespace EntityFrameworkCore.Projectables.Generated
{
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
static class Foo_EntityExtensions_ToDictionary
{
static global::System.Linq.Expressions.Expression<global::System.Func<global::Foo.EntityExtensions.Entity, global::System.Collections.Generic.Dictionary<string, object>>> Expression()
{
return (global::Foo.EntityExtensions.Entity entity) => new Dictionary<string, object>
{
{
"FullName",
entity.FullName ?? "N/A"
},
{
"Id",
entity.Id.ToString()
}
};
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// <auto-generated/>
#nullable disable
using System;
using System.Linq;
using System.Collections.Generic;
using EntityFrameworkCore.Projectables;
using Foo;

namespace EntityFrameworkCore.Projectables.Generated
{
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
static class Foo_EntityExtensions_ToDictionary
{
static global::System.Linq.Expressions.Expression<global::System.Func<global::Foo.EntityExtensions.Entity, global::System.Collections.Generic.Dictionary<string, string>>> Expression()
{
return (global::Foo.EntityExtensions.Entity entity) => new Dictionary<string, string>
{
{
"FullName",
entity.FullName ?? "N/A"
}
};
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1901,6 +1901,81 @@ public Task GenericTypesWithConstraints()

return Verifier.Verify(result.GeneratedTrees[0].ToString());
}

[Fact]
public Task DictionaryIndexInitializer_IsBeingRewritten()
{
// lang=csharp
var compilation = CreateCompilation(@"
using System;
using System.Linq;
using System.Collections.Generic;
using EntityFrameworkCore.Projectables;

namespace Foo {
public static class EntityExtensions
{
public record Entity
{
public int Id { get; set; }
public string? FullName { get; set; }
}

[Projectable(NullConditionalRewriteSupport = NullConditionalRewriteSupport.Rewrite)]
public static Dictionary<string, object> ToDictionary(this Entity entity)
=> new Dictionary<string, object>
{
[""FullName""] = entity.FullName ?? ""N/A"",
[""Id""] = entity.Id.ToString(),
};
}
}
");

var result = RunGenerator(compilation);

Assert.Empty(result.Diagnostics);
Assert.Single(result.GeneratedTrees);

return Verifier.Verify(result.GeneratedTrees[0].ToString());
}

[Fact]
public Task DictionaryObjectInitializer_PreservesCollectionInitializerSyntax()
{
// lang=csharp
var compilation = CreateCompilation(@"
using System;
using System.Linq;
using System.Collections.Generic;
using EntityFrameworkCore.Projectables;

namespace Foo {
public static class EntityExtensions
{
public record Entity
{
public int Id { get; set; }
public string? FullName { get; set; }
}

[Projectable(NullConditionalRewriteSupport = NullConditionalRewriteSupport.Rewrite)]
public static Dictionary<string, string> ToDictionary(this Entity entity)
=> new Dictionary<string, string>
{
{ ""FullName"", entity.FullName ?? ""N/A"" }
};
}
}
");

var result = RunGenerator(compilation);

Assert.Empty(result.Diagnostics);
Assert.Single(result.GeneratedTrees);

return Verifier.Verify(result.GeneratedTrees[0].ToString());
}

#region Helpers

Expand Down