postsharp / Metalama

Metalama is a Roslyn-based meta-programming framework. Use this repo to report bugs or ask questions.
176 stars 5 forks source link

Feature request: allow an aspect to introduce a new type #128

Closed epsitec closed 1 month ago

epsitec commented 1 year ago

I'd like to be write an aspect to generate classes implementing data access interfaces. Currently (2023.1) my code won't work:

Example interface

interface IPerson
{
    string Name { get; }
    int Age { get; }
}

Example of the code I'd like my aspect to generate

class Person : SomeBaseClass, IPerson
{
    public Person(ISomeContext context, string name, int age) : base (context)
    {
        this.Name = name;
        this.Age = age;
    }
    public string Name { get; set; }
    public int Age { get; set; }
}

What's working, what's not working

  1. I cannot create a class from scratch (can be solved by requiring the user to write an empty class stub).
  2. I cannot create a constructor from scratch (can probably be solved by requiring the user to write an empty constructor stub).
  3. I can add the interface IPerson to Person in BuildAspect by calling builder.Advice.ImplementInterface().
  4. I can add properties Name and Age to the class by calling builder.Advice.IntroduceAutomaticProperty().
  5. I cannot make the properties show up in the interface (somehow, [InterfaceMember] should be applied dynamically).

The last point is a show-stopper for what I am intending to do. I'd love to see this feature be part of a coming release of Metalama.

PostSharpBot commented 1 year ago

Hello @epsitec, thank you for submitting this issue. We will try to get back to you as soon as possible. Note to the PostSharp team, this ticket is being tracked in our dashboard under ID TP-33174.

epsitec commented 1 year ago

Here is my current attempt:


public abstract class Entity<T>
    where T : class
{ }

public interface IPerson
{
    string Name { get; }
    int Age { get; }
}

[EntityImplementation]
public class Person : Entity<IPerson>
{ }

public sealed class EntityImplementationAttribute : TypeAspect
{
    public override void BuildAspect(IAspectBuilder<INamedType> builder)
    {
        _ = builder ?? throw new ArgumentNullException (nameof (builder));

        var typeName = builder.Target.Name;
        var baseType = builder.Target.BaseType
            ?? throw new InvalidCastException ("No base type");

        if (baseType.IsGeneric == false)
        {
            throw new InvalidOperationException ($"Base type of {typeName} should be generic");
        }
        if (baseType.TypeParameters.Count != 1)
        {
            throw new InvalidOperationException ($"Base type of {typeName} should have one generic parameter");
        }

        var interfaceType = (INamedType) baseType.TypeArguments[0];

        if (interfaceType.TypeKind != TypeKind.Interface)
        {
            throw new InvalidOperationException ($"The generic type parameter <{interfaceType.ToType ().Name}> of the base type of {typeName} should be an interface, not {interfaceType.TypeKind}");
        }

        var interfaceProperties = interfaceType.AllProperties;

        foreach (var interfaceProperty in interfaceProperties)
        {
            builder.Advice.IntroduceAutomaticProperty (
                builder.Target,
                interfaceProperty.Name,
                interfaceProperty.Type.ToType (),
                OverrideStrategy.Ignore,
                b => b.Accessibility = Accessibility.Public);
        }
        /*
        builder.Advice.ImplementInterface (
            builder.Target,
            interfaceType,
            OverrideStrategy.Ignore);
        */
    }
}
gfraiteur commented 1 year ago

To upvote a feature request please add a "me too" comment because the number of emoticon reactions is not aggregated by GitHUb.

YBAZAN commented 1 year ago

me too

CGuidi commented 1 year ago

me too

Skywalker13 commented 1 year ago

me too

WhitWaldo commented 1 year ago

me too

rvuistin commented 1 year ago

me too

sake402 commented 1 year ago

mee too

CaffeinatedCoder commented 11 months ago

me too

niklasstich commented 10 months ago

me too

gfraiteur commented 3 months ago

This feature is being worked on in 2024.2 and the first bits have been released in preview. See IAdviceFactory.IntroduceClass.

epsitec commented 3 weeks ago

Thank you. I could not wait for the feature to be implemented in Metalama, so I switched altogether to an incremental Roslyn Source Generator to produce the code I neded.

WhitWaldo commented 3 weeks ago

@epsitec This is an available feature in the latest stable version of Metalama.

prochan2 commented 3 weeks ago

https://doc.postsharp.net/metalama/conceptual/aspects/advising/introducing-types