NeVeSpl / NTypewriter

File/code generator using Scriban text templates populated with C# code metadata from Roslyn API.
https://nevespl.github.io/NTypewriter/
MIT License
126 stars 25 forks source link

Action.ReturnType has is broken with introduction of TypedConstant. #89

Closed gregveres closed 1 year ago

gregveres commented 1 year ago

For a script that generates my controller typescript code, I do something like this for each controller:

{{for method in controller.Methods }}
    {{- ImportType (method | Action.ReturnType )}}
    {{-for param in method | Action.Parameters}}
        {{- ImportType param.Type}}
    {{-end}}
{{-end}}

In 0.4.1, this used to work great. But now Action.ReturnType is returning a null type for all controller actions that use the ResponseType() attribute.

Here is how a particular controller action is defined that produced a null type from Action.ReturnType

        [HttpGet]
        [Route("Org/{orgId}")]
        [ResponseType(typeof(List<ApiAnnouncementsBaseViewModel.Announcement>))]
        public async Task<IHttpActionResult> GetAllAnnouncements(int orgId)
        {
            var vm = new ApiOrgAnnouncementsViewModel();
            var result = await vm.GetOrgAnnouncements(orgId, GetCurrentUser(), IsUserSiteAdmin(), SystemClock.Instance, getDb());
            return ParseResult(result.status, result.message, result.announcements);
        }

I am using Debug.WriteLine to print out the type that is returned from Actino.ReturnType and it is always null in these situations. This breaks my entire generation because all of my controllers are defined this way.

gregveres commented 1 year ago

Given the above, this is the output from a Debug.WriteLine type placed just inside the ImportType scriban function.

12:24:32.392 INFO: 4Sc4Hl5 Task<IHttpActionResult> AnnouncementsController.GetAllAnnouncements(int orgId)
12:24:32.392 INFO: 4Sc4Hl5 in ImportType for
12:24:32.392 INFO: 4Sc4Hl5 
12:24:32.392 INFO: 4Sc4Hl5 in ImportType for
12:24:32.392 INFO: 4Sc4Hl5 int

The first line of this output is from a Debug.WriteLine method that is placed just inside the for loop.

gregveres commented 1 year ago

I am pretty sure I have figured it out. In the ReturnType function, you have the same issue that I had when you introduced the TypedConstant for the attribute arguments.

This line needs to be updated to take the TypedConstant into consideration.

gregveres commented 1 year ago

I thought I could make a copy of the ReturnType in my custom functions and then fix it there, but it is not as straight forward as expected. I have changed Action.ReturnType to Custom.ReturnType to call my function, but I am running into a type mismatch I think.

Here is the code I am trying:

        public static IType ReturnType(IMethod method)
        {
            var result = method.ReturnType;

            var responseTypeAttribute = method.Attributes.FirstOrDefault(a => a.Name == "ResponseType" || a.Name == "ProducesResponseType");
            var responseTypeArgument = responseTypeAttribute?.Arguments.FirstOrDefault(x => x.Name == "responseType" || x.Name == "Type");
            if (responseTypeArgument?.Value is ITypedConstant type) return type.Value as IType;

            while (result != null && (result.BareName == "Task" || result.BareName == "ActionResult"))
            {
                if (result.IsGeneric)
                {
                    result = result.TypeArguments.FirstOrDefault();
                }
                else
                {
                    result = null;
                }
            }
            return result;
        }

The issue I am running into is that the TypedConstant.Value isn't an IType so the "as IType" that used to be there still fails. The Value field is of the type: `` Microsoft.CodeAnalysis.CSharp.Symbols.PublicModel.NonErrorNamedTypeSymbol``` casting this type to an IType causes the return value to be null.

gregveres commented 1 year ago

I have run out of ideas on how to extract the IType from responseTypeArgument.Value.Value. I have to roll back until there is a fix for this, unfortunately.

gregveres commented 1 year ago

Ugg, there doesn't seem to be a way to roll back.

NeVeSpl commented 1 year ago

That is too many breaking changes, previous behaviour will return in 0.4.4.2, ITypedConstant will be only used for enums.