mozilla / nunjucks

A powerful templating engine with inheritance, asynchronous control, and more (jinja2 inspired)
https://mozilla.github.io/nunjucks/
BSD 2-Clause "Simplified" License
8.52k stars 637 forks source link

call with custom tags fails #617

Open sorawee opened 8 years ago

sorawee commented 8 years ago
{% macro test() %}
  {{ caller() }}
{% endmacro %}

{% call test() %}
{% code %} 123123 {% endcode %}
{% endcall %}

produces

Template render error: (...)
  TypeError: Cannot read property 'typename' of null (In file '...')

where code is a custom tag (you can be sure that code is a custom tag because if it's not, the error would be unknown block tag: ... instead).

I tried changing code to other custom tags like math. They all fail.

Note that the following is completely okay:

{% code %} 123123 {% endcode %}

{% macro test() %}
  {{ caller() }}
{% endmacro %}

{% call test() %}
123123
{% endcall %}
sorawee commented 8 years ago

I investigate further a little bit and found the following:

The AST of

{% call test() %}
{% code %} 123 {% endcode %}
{% endcall %}

is:

{
    "lineno": 4,
    "colno": 1,
    "children": [
        {
            "lineno": 4,
            "colno": 10,
            "name": {
                "lineno": 4,
                "colno": 6,
                "value": "test"
            },
            "args": {
                "lineno": 4,
                "colno": 10,
                "children": [
                    {
                        "children": [
                            {
                                "lineno": 4,
                                "colno": 1,
                                "key": {
                                    "lineno": 4,
                                    "colno": 1,
                                    "value": "caller"
                                },
                                "value": {
                                    "lineno": 4,
                                    "colno": 1,
                                    "name": {
                                        "lineno": 4,
                                        "colno": 1,
                                        "value": "caller"
                                    },
                                    "args": {
                                        "children": []
                                    },
                                    "body": {
                                        "lineno": 0,
                                        "colno": 0,
                                        "children": [
                                            {
                                                "lineno": 4,
                                                "colno": 13,
                                                "children": [
                                                    {
                                                        "lineno": 4,
                                                        "colno": 13,
                                                        "value": "\n"
                                                    }
                                                ]
                                            },
                                            {
                                                "extName": "BlockcodeExtension",
                                                "prop": "run",
                                                "args": {
                                                    "lineno": 5,
                                                    "colno": 6,
                                                    "children": []
                                                },
                                                "contentArgs": [
                                                    {
                                                        "lineno": 0,
                                                        "colno": 0,
                                                        "children": [
                                                            {
                                                                "lineno": 5,
                                                                "colno": 6,
                                                                "children": [
                                                                    {
                                                                        "lineno": 5,
                                                                        "colno": 6,
                                                                        "value": " 123 "
                                                                    }
                                                                ]
                                                            }
                                                        ]
                                                    }
                                                ]
                                            },
                                            {
                                                "lineno": 5,
                                                "colno": 20,
                                                "children": [
                                                    {
                                                        "lineno": 5,
                                                        "colno": 20,
                                                        "value": "\n"
                                                    }
                                                ]
                                            }
                                        ]
                                    }
                                }
                            }
                        ]
                    }
                ]
            }
        }
    ]
}

And this node:

{
    "extName": "BlockcodeExtension",
    "prop": "run",
    "args": {
        "lineno": 5,
        "colno": 6,
        "children": []
    },
    "contentArgs": [
        {
            "lineno": 0,
            "colno": 0,
            "children": [
                {
                    "lineno": 5,
                    "colno": 6,
                    "children": [
                        {
                            "lineno": 5,
                            "colno": 6,
                            "value": " 123 "
                        }
                    ]
                }
            ]
        }
    ]
}

is categorized as a FilterAsync ((node instanceof nodes.Filter && lib.indexOf(asyncFilters, node.name.value) !== -1) || node instanceof nodes.CallExtensionAsync). Consequently, when we try to compile the node (in compileFilterAsync) and try to assertType, it will lookup and see if this is of any type. Because it is not, the compilation fails and it tries to report the error, but in error report it tries to lookup .typename which doesn't exist. That's the reason of the above exception.

sorawee commented 8 years ago

cc. @jlongster

carljm commented 8 years ago

Hi @sorawee - @jlongster is no longer active in nunjucks maintenance, I've been doing most of the day-to-day maintenance recently.

I'm not sure when I'll have time to dive into this deep enough to suggest a fix, but I'd be happy to review a pull request. It seems like you've got an analysis of the problem - do you have a suggested fix? And/or would you be willing to file a PR with at least a failing test case for this problem?

Thanks!