# How to resolve a Path to TypeNode in a macro inside a module

**URL:** <https://forum.crystal-lang.org/t/how-to-resolve-a-path-to-typenode-in-a-macro-inside-a-module/5259>\
**Category:** Help & Support\
**Created:** [January 14, 2023, 10:27am UTC](https://forum.crystal-lang.org/t/how-to-resolve-a-path-to-typenode-in-a-macro-inside-a-module/5259 "2023-01-14T10:27:38Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![intelligent-tester](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/intelligent-tester/32/1759_2.png) [@intelligent-tester](https://forum.crystal-lang.org/u/intelligent-tester)\
**Post date:** [January 14, 2023, 10:27am UTC](https://forum.crystal-lang.org/t/how-to-resolve-a-path-to-typenode-in-a-macro-inside-a-module/5259/1 "2023-01-14T10:27:38Z")

</div>

Code setup

```auto
macro emphasize_type(type)
  {% if type.resolve == Int32 %}
    " ***Int32***"
  {% else %}
  " ***No type***"
  {% end %}
end

class Foo
  macro emphasize_type(type)
    {% if type.resolve == Int32 %}
    	" ***Int32***"
    {% else %}
    	" ***No type***"
    {% end %}
  end
end

class Bar(T)
  def func1
	  puts emphasize_type(T)
  end

  def func2
	  puts Foo.emphasize_type(T)
  end
end

```

This works

```auto
Bar(Int32).new.func1

```

> [gshah@workstation Desktop]$ crystal delete.cr --error-trace  
> \*\*\*Int32\*\*\*

This does not

```auto
Bar(Int32).new.func2

```

> [gshah@workstation Desktop]$ crystal delete.cr --error-trace  
> Showing last frame. Use --error-trace for full trace.
> 
> In delete.cr:25:26
> 
> 25 | puts Foo.emphasize\_type(T)  
> ^  
> Error: undefined constant T

Why should defining a macro inside a class throw an error?

---

<div class="post-metadata">

**Author:** ![Blacksmoke16](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/blacksmoke16/32/1241_2.png) [@Blacksmoke16](https://forum.crystal-lang.org/u/Blacksmoke16)\
**Post date:** [January 14, 2023, 1:50pm UTC](https://forum.crystal-lang.org/t/how-to-resolve-a-path-to-typenode-in-a-macro-inside-a-module/5259/2 "2023-01-14T13:50:34Z")

</div>

I think this is a catch of macros in that `T` is being passed as a `Path` literal and not the `TypeNode` that path points to. When you then call `.resolve` it’s trying to resolve it in the context of `Foo` so it loses the reference to the actual `T`. This can be seen more clearly if you run with `--error-trace` that its failing on the `.resolve` method:

```auto
Error: instantiating 'Bar(Int32)#func2()'

In test.cr:25:14

 25 | puts Foo.emphasize_type(T)
               ^-------------
Error: expanding macro

In test.cr:11:16

 11 | {% if type.resolve == Int32 %}
                 ^------
Error: undefined constant T

In test.cr:25:29

 25 | puts Foo.emphasize_type(T)
                              ^
Error: undefined constant T

```

In order to pass the type it represents try `Foo.emphasize_type({{T}})` which then works.

---

<div class="post-metadata">

**Author:** ![intelligent-tester](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/intelligent-tester/32/1759_2.png) [@intelligent-tester](https://forum.crystal-lang.org/u/intelligent-tester)\
**Post date:** [January 15, 2023, 8:51am UTC](https://forum.crystal-lang.org/t/how-to-resolve-a-path-to-typenode-in-a-macro-inside-a-module/5259/3 "2023-01-15T08:51:29Z")

</div>

Thanks @Blacksmoke16.  
That solves it for simple types like **Int32** , but not for instances of generic classes like **Array(String)**.

For example, with this code set up

```auto
class Foo
  macro emphasize_type(type)
    {% if type.resolve == Int32 %}
    	" ***Int32***"
    {% else %}
    	" ***No type***"
    {% end %}
  end
end

class Bar(T)
  def func2
	  puts Foo.emphasize_type({{T}})
  end
end

```

as @Blacksmoke16 stated, this works now.

```auto
Bar(Int32).new.func2

```

> [gshah@workstation ~]$ crystal Desktop/delete.cr --error-trace  
> \*\*\*Int32\*\*\*

But this still does not work

```auto
Bar(Array(String)).new.func2

```

> [gshah@workstation ~]$ crystal Desktop/delete.cr --error-trace  
> Showing last frame. Use --error-trace for full trace.
> 
> There was a problem expanding macro ‘macro\_140386515094528’
> 
> Code in Desktop/delete.cr:25:28
> 
> 25 | puts Foo.emphasize\_type({{T}})  
> ^  
> Called macro defined in Desktop/delete.cr:25:28
> 
> 25 | puts Foo.emphasize\_type({{T}})
> 
> Which expanded to:
> 
> > 1 | Array(String)  
> > ^  
> > Error: Array(T).class is not a generic type, it’s a metaclass

Can it be made to work with **Array(String)**?

---

<div class="post-metadata">

**Author:** ![Blacksmoke16](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/blacksmoke16/32/1241_2.png) [@Blacksmoke16](https://forum.crystal-lang.org/u/Blacksmoke16)\
**Post date:** [January 15, 2023, 2:36pm UTC](https://forum.crystal-lang.org/t/how-to-resolve-a-path-to-typenode-in-a-macro-inside-a-module/5259/4 "2023-01-15T14:36:25Z")

</div>

I think this is essentially the same problem as before, but one that isn’t solved by `{{T}}`. Basically `Array(Int32)` is being passed where `Int32` actually is just the name of the generic var, essentially the same as `Array(T)`. Somewhat of a hack but you could use `parse_type` to parse it into a `TypeNode` which you could then use. Then check to see if its an `Array` type, and if so check its type vars to see if any of them are `Int32`. Something like:

```auto
macro emphasize_type(type)
  {% type = parse_type(type.stringify) %}
  {% if type.resolve <= Array && type.type_vars.any? &.resolve.==(Int32) %}
    " ***Int32***"
  {% else %}
    " ***No type***"
  {% end %}
end

```

But what exactly are you wanting to do that you need this for? There might be a better less brittle way to go about it that doesn’t involve you needing to handle all these diff cases.

---

<div class="post-metadata">

**Author:** ![asterite](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/asterite/32/60_2.png) [@asterite](https://forum.crystal-lang.org/u/asterite)\
**Post date:** [January 15, 2023, 3:27pm UTC](https://forum.crystal-lang.org/t/how-to-resolve-a-path-to-typenode-in-a-macro-inside-a-module/5259/5 "2023-01-15T15:27:22Z")

</div>

Is that code a simplified version of what you want/need, or exactly what you need? Because if so, you don’t need macros at all:

```crystal
def emphasize_type(type : Int32.class)
  " ***Int32***"
end

def emphasize_type(type : Class)
  " ***No type***"
end

class Foo
  def self.emphasize_type(type : Int32.class)
    " ***Int32***"
  end

  def self.emphasize_type(type : Class)
    " ***No type***"
  end
end

class Bar(T)
  def func1
    puts emphasize_type(T)
  end

  def func2
    puts Foo.emphasize_type(T)
  end
end

puts Bar(Int32).new.func1
puts Bar(Int32).new.func2

```

---

<div class="post-metadata">

**Author:** ![intelligent-tester](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/intelligent-tester/32/1759_2.png) [@intelligent-tester](https://forum.crystal-lang.org/u/intelligent-tester)\
**Post date:** [January 16, 2023, 4:58pm UTC](https://forum.crystal-lang.org/t/how-to-resolve-a-path-to-typenode-in-a-macro-inside-a-module/5259/6 "2023-01-16T16:58:27Z")

</div>

The code I shared is highly simplified version of what I am trying to do but Blacksmoke’s solution using `parse_type` works for me.

Thanks everyone for quick response and help with this.
