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

test against empty results in compile error #10

Closed gregveres closed 3 years ago

gregveres commented 3 years ago

I am trying to test Type's ArrayType against empty to see if it is null or not. But the compile produced an error that the variable or function 'empty' was not found.

This might be the same problem I raised with the array builtin functions not being found.

10:43:05.108 INFO: Template loaded successfully
10:43:27.737 INFO: Rendering template
10:43:27.995 ERROR: <input>(31,28) The variable or function `empty` was not found
10:43:27.996 ERROR: System.Exception: Rendering template failed
   at NTypewriter.EditorForVisualStudio.CoreDomain.TemplateRenderer.<RenderTemplate>d__9.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at NTypewriter.EditorForVisualStudio.CoreDomain.TemplateRenderer.<RenderAsync>d__5.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd(Task task)
   at NTypewriter.EditorForVisualStudio.NTypewriterEditorForVisualStudioPackage.<RenderTemplateCommand_OnExecuted>d__11.MoveNext()

Here is the code:

{{- for class in data.Classes  | Symbols.ThatHaveAttribute "ExportToTypescript" -}}
{{- capture output -}}
  {{- for prop in class.Properties  }}
  {{type = prop.Type
    arrayType = "false"
    if prop.Type.ArrayType != empty
        type = prop.Type.ArrayType
        arrayType = "true"
    end
  }}
  {{prop.Name}}-{{type.Name}}-{{arrayType}}
  {{-end}}
{{-end}}

Here is what I am really trying to do:

I have a class in C# that is defined like this:

        [ExportToTypescript]
        public class ActivityInfo
        {
            public int Id { get; set; }
            public string name { get; set; }
            public List<ActivityPageMenuItem> menuItems { get; set; }
       }

I am trying to get the output to be:

import { ActivityPageMenuItem} from './ActivityPageMenuItem';

export interface ActivityInfo {
  id: number,
  name: string,
  menuItems: ActivityPageMenuItem[],
}

Notice that menuItems is defined as a List, so I need to pull out the type ActivityPageMenuItem and then I need to figure out that ActivityPageMenuItem has an attribute of ExportToTypescript on it so that I can emit that import statement.

I am assuming that the Type.ArrayType is ActivityPageMenuItem, but I can't just get to it because the other properties of the class have Type.ArrayType as null so when I try to access it for those members, I get a null pointer exception.

gregveres commented 3 years ago

I solved this a different way.

My first solution was to dump out all of the Is* flags on type and realize that List<> has IsGeneric true and that I could get the type from the type.TypeArguments.

Then I realized that Type.Unwrap exists for this purpose. So now I am using that instead of needing to test against empty.

However, I have a different use case where I think I need to test against empty...

I have the interface being dumped out properly and all the necessary imports happening, but the last piece of the puzzle is when the class is derived from another class. I need to extract the type from the BaseClass, extract the base class's type and import that. My first inclination is to test the BaseClass against null, but then empty doesn't exist.

gregveres commented 3 years ago

I am going to close this. I discovered that I missed the "null" entry in Scriban that I can use instead of empty. And this works just fine.

For the example above, I was able to use Class.HasBaseClass to avoid testing against null. But I did run up against a case where I needed to use the Type.ArrayType value when Type.IsEnumerable was true. It turns out that IsEnumerable is true in lots of cases where ArrayType is null and can't be used. So checking against null worked well there.

NeVeSpl commented 3 years ago

Why simply not use IsArray property? "empty" will be available in version 0.0.8

gregveres commented 3 years ago

I don't recall the exact case, but I was dumping out all the Is* flags and I am pretty sure that IsArray was true for some other case where ArrayType was still null.

But no matter because "null" as a Scriban keyword is available. I had missed it in the Scriban docs the first few times I was looking through their languages page. I am successfully testing against "null" now.