Undefined method 'types' for Crystal::Nop

I try to use some methods for the ast module in crystal. Although I am running into some problems probably likely because the ast is a macro.

When I run the following code p node.args[0].not_nil!.types.

I get this error:
Error: undefined method 'types' for Crystal::Nop (compile-time type is Crystal::ASTNode+)

Does any one know a way around this?

I think the error is pretty clear in this case. The type you’re calling #types on doesn’t have a method called #types. Basically the same error you’d get if you tried to do like p 1.types.

Can you share the actual code that reproduces this/what you’re wanting to do?

I have this code

  private def handle_visit_describe_call(node : Crystal::Call)
    p node.args[0].not_nil!.types
    @breadcrumbs << node.args[0].not_nil!.as(Crystal::StringLiteral).value 
    accept(node.block.not_nil!)
  end

It is used to convert an ast like: ABC to stringLiteral.

But when the code gets a path like Class1::Class2 it can’t convert it to stringLiteral.
I want a way to be able to do that conversion anyway.

Oh so you’re customizing the compiler? That’s a bit out of my area of expertise.

What are you making that you’re needing to do that? IMO feels like a bit much unless you’re working on some static analysis or LSP tool or something…

I am contributor for exercism.

It is a learning platform for a bunch of languages including crystal.

I am at the moment trying to fix a bug with our test-runner, which you can find here: exercism/crystal-test-runner (github.com).

And since I didn’t write the test-runner I am a bit unsure how the person before me thought out the design. And there is an exercise on the platform which has a test case which is describe class1::class2 and the test-runner flips out when it sees it. My plan was to apply an “easy” fix but it may be more complicated than that.

2 Likes

You should probably use a case expression over the different AST node types.

Something like this:

case arg = node.args[0]
when StringLiteral
  arg.value
when Path
  arg.to_s
end

Path#to_s should turn Class1::Class2 into its string representation.
I suppose you could call to_s on any node type to turn it into a string. Replacing when Path by else would do that and you’d only have special handling for StringLiteral.

I get an error saying: Error: undefined constant StringLiteral

updating it to Crystal::StringLiteral, fixes it. Does that have the same meaning?

Okay I changed it to

Crystal::StringLiteral
Crystal::Path

And it works.
Thanks for all of your help!