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 @@ -137,26 +137,48 @@ x is IPropertySymbol xProperty &&
descriptor.ClassTypeParameterList = descriptor.ClassTypeParameterList.AddParameters(
SyntaxFactory.TypeParameter(additionalClassTypeParameter.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat))
);


// See https://github.com/dotnet/roslyn/blob/d7e010bbe5b1d37837417fc5e79ecb2fd9b7b487/src/VisualStudio/CSharp/Impl/ObjectBrowser/DescriptionBuilder.cs#L340
if (!additionalClassTypeParameter.ConstraintTypes.IsDefaultOrEmpty)
{
descriptor.ClassConstraintClauses ??= SyntaxFactory.List<TypeParameterConstraintClauseSyntax>();

var parameters = new List<TypeConstraintSyntax>();

if (additionalClassTypeParameter.HasReferenceTypeConstraint)
{
parameters.Add(MakeTypeConstraint("class"));
}

if (additionalClassTypeParameter.HasValueTypeConstraint)
{
parameters.Add(MakeTypeConstraint("struct"));
}

if (additionalClassTypeParameter.HasNotNullConstraint)
{
parameters.Add(MakeTypeConstraint("notnull"));
}

parameters.AddRange(additionalClassTypeParameter
.ConstraintTypes
.Select(c =>
MakeTypeConstraint(c.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat))
)
);

if (additionalClassTypeParameter.HasConstructorConstraint)
{
parameters.Add(MakeTypeConstraint("new()"));
}

descriptor.ClassConstraintClauses = descriptor.ClassConstraintClauses.Value.Add(
SyntaxFactory.TypeParameterConstraintClause(
SyntaxFactory.IdentifierName(additionalClassTypeParameter.Name),
SyntaxFactory.SeparatedList<TypeParameterConstraintSyntax>(
additionalClassTypeParameter
.ConstraintTypes
.Select(c => SyntaxFactory.TypeConstraint(
SyntaxFactory.IdentifierName(c.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat))
))
)
SyntaxFactory.SeparatedList<TypeParameterConstraintSyntax>(parameters)
)
);
}

// todo: add additional type constraints
}
}

Expand Down Expand Up @@ -245,5 +267,7 @@ x is IPropertySymbol xProperty &&

return descriptor;
}

private static TypeConstraintSyntax MakeTypeConstraint(string constraint) => SyntaxFactory.TypeConstraint(SyntaxFactory.IdentifierName(constraint));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -111,4 +111,4 @@ PropertyInfo property when nodeExpression is not null
return base.VisitMember(node);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// <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_Entity_FullName<T, TEnum>
where T : global::Foo.TypedObject<TEnum> where TEnum : struct, global::System.Enum
{
static global::System.Linq.Expressions.Expression<global::System.Func<global::Foo.Entity<T, TEnum>, string>> Expression()
{
return (global::Foo.Entity<T, TEnum> @this) => $"{@this.FirstName} {@this.LastName} {@this.SomeSubobject.SomeProp}";
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// <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_Entity_FullName<T>
{
static global::System.Linq.Expressions.Expression<global::System.Func<global::Foo.Entity<T>, string>> Expression()
{
return (global::Foo.Entity<T> @this) => $"{@this.FirstName} {@this.LastName} {@this.SomeSubobject}";
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace EntityFrameworkCore.Projectables.Generated
{
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
static class _EntityBase_GetId<TId>
where TId : global::System.ICloneable
where TId : global::System.ICloneable, new()
{
static global::System.Linq.Expressions.Expression<global::System.Func<TId>> Expression()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -921,6 +921,72 @@ public static string EnforceString<T>(T value) where T : unmanaged
return Verifier.Verify(result.GeneratedTrees[0].ToString());
}

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

namespace Foo {
public class TypedObject<TEnum> where TEnum : struct, System.Enum
{
public TEnum SomeProp { get; set; }
}

public abstract class Entity<T, TEnum>where T : TypedObject<TEnum> where TEnum : struct, System.Enum
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public T SomeSubobject { get; set; }

[Projectable]
public string FullName => $""{FirstName} {LastName} {SomeSubobject.SomeProp}"";
}
}
");
var result = RunGenerator(compilation);

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

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

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

namespace Foo {
public abstract class Entity<T> where T : notnull
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public T SomeSubobject { get; set; }

[Projectable]
public string FullName => $""{FirstName} {LastName} {SomeSubobject}"";
}
}
");

var result = RunGenerator(compilation);

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

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

[Fact]
public Task DeclarationTypeNamesAreGettingFullyQualified()
{
Expand Down