From 7790f146e4bea9442e87568c2c69eec39523477f Mon Sep 17 00:00:00 2001 From: Koen Bekkenutte <2912652+kbekkenutte@users.noreply.github.com> Date: Fri, 26 Nov 2021 00:00:47 +0800 Subject: [PATCH] Fully qualify Type names where possible --- .../DeclarationSyntaxRewriter.cs | 26 +++++++++++++++ .../ExpressionSyntaxRewriter.cs | 9 +++++ ...sts.SelectProjectableProperty.verified.txt | 10 ++++++ ...Tests.cs => TypeNameQualificationTests.cs} | 26 +++++---------- ...NamesAreGettingFullyQualified.verified.txt | 18 ++++++++++ ...gnoreSupport_IsBeingRewritten.verified.txt | 4 +-- ...writeSupport_IsBeingRewritten.verified.txt | 4 +-- ...writeSupport_IsBeingRewritten.verified.txt | 4 +-- ...ts.ProjectableExtensionMethod.verified.txt | 4 +-- ...ropertyToNavigationalProperty.verified.txt | 2 +- .../ProjectionExpressionGeneratorTests.cs | 33 +++++++++++++++++++ 11 files changed, 114 insertions(+), 26 deletions(-) create mode 100644 tests/EntityFrameworkCore.Projectables.FunctionalTests/TypeNameQualificationTests.SelectProjectableProperty.verified.txt rename tests/EntityFrameworkCore.Projectables.FunctionalTests/{StatelessPropertyTests.cs => TypeNameQualificationTests.cs} (59%) create mode 100644 tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.DeclarationTypeNamesAreGettingFullyQualified.verified.txt diff --git a/src/EntityFrameworkCore.Projectables.Generator/DeclarationSyntaxRewriter.cs b/src/EntityFrameworkCore.Projectables.Generator/DeclarationSyntaxRewriter.cs index 1dd5875..b2c3708 100644 --- a/src/EntityFrameworkCore.Projectables.Generator/DeclarationSyntaxRewriter.cs +++ b/src/EntityFrameworkCore.Projectables.Generator/DeclarationSyntaxRewriter.cs @@ -45,5 +45,31 @@ namespace EntityFrameworkCore.Projectables.Generator return base.VisitNullableType(node); } + + public override SyntaxNode? VisitIdentifierName(IdentifierNameSyntax node) + { + var typeInfo = _semanticModel.GetTypeInfo(node); + if (typeInfo.Type is not null) + { + return SyntaxFactory.ParseTypeName( + typeInfo.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + ).WithTriviaFrom(node); + } + + return base.VisitIdentifierName(node); + } + + public override SyntaxNode? VisitQualifiedName(QualifiedNameSyntax node) + { + var typeInfo = _semanticModel.GetTypeInfo(node); + if (typeInfo.Type is not null) + { + return SyntaxFactory.ParseTypeName( + typeInfo.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + ).WithTriviaFrom(node); + } + + return base.VisitQualifiedName(node); + } } } diff --git a/src/EntityFrameworkCore.Projectables.Generator/ExpressionSyntaxRewriter.cs b/src/EntityFrameworkCore.Projectables.Generator/ExpressionSyntaxRewriter.cs index 74b0660..524a14d 100644 --- a/src/EntityFrameworkCore.Projectables.Generator/ExpressionSyntaxRewriter.cs +++ b/src/EntityFrameworkCore.Projectables.Generator/ExpressionSyntaxRewriter.cs @@ -130,6 +130,15 @@ namespace EntityFrameworkCore.Projectables.Generator } else if (symbolInfo.Symbol.Kind is SymbolKind.Property or SymbolKind.Method or SymbolKind.Field && SymbolEqualityComparer.Default.Equals(symbolInfo.Symbol.ContainingType, _targetTypeSymbol)) { + if (node.Parent is MemberAccessExpressionSyntax parentMemberAccessNode) + { + var targetSymbolInfo = _semanticModel.GetSymbolInfo(parentMemberAccessNode.Expression); + if (targetSymbolInfo.Symbol is { Kind: SymbolKind.Parameter }) + { + return base.VisitIdentifierName(node); + } + } + return SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.IdentifierName("@this"), node diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/TypeNameQualificationTests.SelectProjectableProperty.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/TypeNameQualificationTests.SelectProjectableProperty.verified.txt new file mode 100644 index 0000000..f9bccba --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/TypeNameQualificationTests.SelectProjectableProperty.verified.txt @@ -0,0 +1,10 @@ +SELECT [t0].[Id], [t0].[EntityId], [t0].[ParentId] +FROM [Entity] AS [e] +LEFT JOIN ( + SELECT [t].[Id], [t].[EntityId], [t].[ParentId] + FROM ( + SELECT [e0].[Id], [e0].[EntityId], [e0].[ParentId], ROW_NUMBER() OVER(PARTITION BY [e0].[EntityId] ORDER BY [e0].[Id]) AS [row] + FROM [Entity] AS [e0] + ) AS [t] + WHERE [t].[row] <= 1 +) AS [t0] ON [e].[Id] = [t0].[EntityId] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/StatelessPropertyTests.cs b/tests/EntityFrameworkCore.Projectables.FunctionalTests/TypeNameQualificationTests.cs similarity index 59% rename from tests/EntityFrameworkCore.Projectables.FunctionalTests/StatelessPropertyTests.cs rename to tests/EntityFrameworkCore.Projectables.FunctionalTests/TypeNameQualificationTests.cs index 62d3070..efc030f 100644 --- a/tests/EntityFrameworkCore.Projectables.FunctionalTests/StatelessPropertyTests.cs +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/TypeNameQualificationTests.cs @@ -1,37 +1,29 @@ using System; using System.Collections.Generic; -using System.Data.SqlTypes; using System.Linq; using System.Text; using System.Threading.Tasks; using EntityFrameworkCore.Projectables.FunctionalTests.Helpers; using Microsoft.EntityFrameworkCore; -using ScenarioTests; using VerifyXunit; using Xunit; namespace EntityFrameworkCore.Projectables.FunctionalTests { [UsesVerify] - public class StatelessPropertyTests + public class TypeNameQualificationTests { public record Entity { public int Id { get; set; } - + + public int? ParentId { get; set; } + + public ICollection Children { get; } = new List(); + [Projectable] - public int Computed => 0; - } - - [Fact] - public Task FilterOnProjectableProperty() - { - using var dbContext = new SampleDbContext(); - - var query = dbContext.Set().AsQueryable() - .Where(x => x.Computed == 1); - - return Verifier.Verify(query.ToQueryString()); + public Entity? FirstChild => + Children.OrderBy(x => x.Id).FirstOrDefault(); } [Fact] @@ -40,7 +32,7 @@ namespace EntityFrameworkCore.Projectables.FunctionalTests using var dbContext = new SampleDbContext(); var query = dbContext.Set() - .Select(x => x.Computed); + .Select(x => x.FirstChild); return Verifier.Verify(query.ToQueryString()); } diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.DeclarationTypeNamesAreGettingFullyQualified.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.DeclarationTypeNamesAreGettingFullyQualified.verified.txt new file mode 100644 index 0000000..b11d6d6 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.DeclarationTypeNamesAreGettingFullyQualified.verified.txt @@ -0,0 +1,18 @@ +using System; +using System.Linq; +using System.Collections.Generic; +using EntityFrameworkCore.Projectables; +using Foo; + +namespace EntityFrameworkCore.Projectables.Generated +#nullable disable +{ + public static class Foo_EntityExtensions_Entity_Something + { + public static System.Linq.Expressions.Expression> Expression() + { + return (global::Foo.EntityExtensions.Entity entity) => + entity; + } + } +} \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.NullableElementAndMemberBinding_WithIgnoreSupport_IsBeingRewritten.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.NullableElementAndMemberBinding_WithIgnoreSupport_IsBeingRewritten.verified.txt index 4085a48..ddb0e7b 100644 --- a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.NullableElementAndMemberBinding_WithIgnoreSupport_IsBeingRewritten.verified.txt +++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.NullableElementAndMemberBinding_WithIgnoreSupport_IsBeingRewritten.verified.txt @@ -9,9 +9,9 @@ namespace EntityFrameworkCore.Projectables.Generated { public static class Foo_EntityExtensions_GetFirstRelatedIgnoreNulls { - public static System.Linq.Expressions.Expression> Expression() + public static System.Linq.Expressions.Expression> Expression() { - return (Entity entity) => + return (global::Foo.EntityExtensions.Entity entity) => entity.RelatedEntities[0]; } } diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.NullableElementAndMemberBinding_WithRewriteSupport_IsBeingRewritten.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.NullableElementAndMemberBinding_WithRewriteSupport_IsBeingRewritten.verified.txt index 4ff2555..f6d11bc 100644 --- a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.NullableElementAndMemberBinding_WithRewriteSupport_IsBeingRewritten.verified.txt +++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.NullableElementAndMemberBinding_WithRewriteSupport_IsBeingRewritten.verified.txt @@ -9,9 +9,9 @@ namespace EntityFrameworkCore.Projectables.Generated { public static class Foo_EntityExtensions_GetFirstRelatedIgnoreNulls { - public static System.Linq.Expressions.Expression> Expression() + public static System.Linq.Expressions.Expression> Expression() { - return (Entity entity) => + return (global::Foo.EntityExtensions.Entity entity) => entity != null ? (entity.RelatedEntities != null ? (entity.RelatedEntities[0]) : null) : null; } } diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.NullableParameters_WithRewriteSupport_IsBeingRewritten.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.NullableParameters_WithRewriteSupport_IsBeingRewritten.verified.txt index a72ac27..c8e1981 100644 --- a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.NullableParameters_WithRewriteSupport_IsBeingRewritten.verified.txt +++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.NullableParameters_WithRewriteSupport_IsBeingRewritten.verified.txt @@ -9,9 +9,9 @@ namespace EntityFrameworkCore.Projectables.Generated { public static class Foo_EntityExtensions_GetFirstName { - public static System.Linq.Expressions.Expression> Expression() + public static System.Linq.Expressions.Expression> Expression() { - return (Entity entity) => + return (global::Foo.EntityExtensions.Entity entity) => entity.FullName != null ? (entity.FullName.Substring(entity.FullName != null ? (entity.FullName.IndexOf(' ') ) : null?? 0)) : null; } } diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.ProjectableExtensionMethod.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.ProjectableExtensionMethod.verified.txt index 75226c2..3226e6f 100644 --- a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.ProjectableExtensionMethod.verified.txt +++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.ProjectableExtensionMethod.verified.txt @@ -8,9 +8,9 @@ namespace EntityFrameworkCore.Projectables.Generated { public static class Foo_C_Foo { - public static System.Linq.Expressions.Expression> Expression() + public static System.Linq.Expressions.Expression> Expression() { - return (D d) => + return (global::Foo.D d) => 1; } } diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.ProjectablePropertyToNavigationalProperty.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.ProjectablePropertyToNavigationalProperty.verified.txt index ff27e37..df7b8d4 100644 --- a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.ProjectablePropertyToNavigationalProperty.verified.txt +++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.ProjectablePropertyToNavigationalProperty.verified.txt @@ -8,7 +8,7 @@ namespace EntityFrameworkCore.Projectables.Generated { public static class Foo_C_Foo { - public static System.Linq.Expressions.Expression> Expression() + public static System.Linq.Expressions.Expression> Expression() { return (global::Foo.C @this) => @this.Dees.First(); diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.cs b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.cs index c277987..e6fa9b2 100644 --- a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.cs +++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.cs @@ -867,6 +867,39 @@ namespace Foo { return Verifier.Verify(result.GeneratedTrees[0].ToString()); } + [Fact] + public Task DeclarationTypeNamesAreGettingFullyQualified() + { + 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] + public static Entity Something(Entity entity) + => entity; + } + } +} +"); + + var result = RunGenerator(compilation); + + Assert.Empty(result.Diagnostics); + Assert.Single(result.GeneratedTrees); + + return Verifier.Verify(result.GeneratedTrees[0].ToString()); + } + #region Helpers Compilation CreateCompilation(string source, bool expectedToCompile = true)