Skip to content

Fix rewriter when the sync method is in the same extensions class#110

Draft
0xced wants to merge 1 commit intozompinc:masterfrom
0xced:fix-sync-in-same-extension
Draft

Fix rewriter when the sync method is in the same extensions class#110
0xced wants to merge 1 commit intozompinc:masterfrom
0xced:fix-sync-in-same-extension

Conversation

@0xced
Copy link
Contributor

@0xced 0xced commented Dec 16, 2025

Pull request #108 made sure that EF Core async extensions (such as AnyAsync are properly translated to the System.Linq.Queryable.Any extension method).

Unfortunately, this introduced a regression. When async and sync methods are in the same extension class (such as ExecuteDeleteAsync and ExecuteDelete) the generated sync method would be wrong. I.e., generated in System.Linq.Queryable instead of Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.

This pull request tries to address this issue. Unfortunately, I'm again facing discrepancies between the GenerationSandbox.Tests and Generator.Tests projects. This is why this pull request is opened a draft.

When used in the GenerationSandbox.Tests project, the generated code is correct.

// <auto-generated/>
#nullable enable
namespace GenerationSandbox.Tests
{
    public partial class EntityFrameworkQueryableExtensions
    {
        /// <summary>
        /// Test method.
        /// </summary>
        /// <param name="dbContext">The db context.</param>
        /// <returns>The result.</returns>
        public int QueryableExtension(global::Microsoft.EntityFrameworkCore.DbContext dbContext)
        {
            var dbSet = dbContext.Set<object>();
            if (global::System.Linq.Queryable.Any(dbSet))
            {
                return global::Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ExecuteDelete(dbSet);
            }

            return 0;
        }
    }
}

When generated in the Generator.Tests project, the generated code is incorrect. Note how the
Any and ExecuteDelete methods are not generated with their fully qualified names.

//HintName: Zomp.SyncMethodGenerator.IntegrationTests.EntityFrameworkQueryableExtensions.QueryableExtensionAsync.g.cs
// <auto-generated/>
#nullable enable
namespace Zomp.SyncMethodGenerator.IntegrationTests
{
    public partial class EntityFrameworkQueryableExtensions
    {
        public int QueryableExtension(global::Microsoft.EntityFrameworkCore.DbContext dbContext)
        {
            var dbSet = dbContext.Set<object>();
            if (dbSet.Any())
            {
                return dbSet.ExecuteDelete();
            }
            
            return 0;
        }
    }
}

I don't understand why the generated code is different. I have identified that var symbol = GetSymbol(node) returns null for InvocationExpressionSyntax InvocationExpression dbSet.AnyAsync(cancellationToken) in Generator.Tests and not null in GenerationSandbox.Tests but I don't understand why.

@0xced 0xced marked this pull request as draft December 16, 2025 16:44
@0xced 0xced force-pushed the fix-sync-in-same-extension branch from 3a210c4 to 48e1da9 Compare January 28, 2026 15:35
@0xced
Copy link
Contributor Author

0xced commented Jan 28, 2026

@virzak Would love to have your eyes on this when you get some spare time. No rush, I just wanted to make sure it didn’t slip through the cracks.

@virzak
Copy link
Contributor

virzak commented Jan 28, 2026

Will look at it this weekend probably

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants