diff --git a/include/minja/minja.hpp b/include/minja/minja.hpp index 604e613..dd0ae6c 100644 --- a/include/minja/minja.hpp +++ b/include/minja/minja.hpp @@ -1270,6 +1270,11 @@ class BinaryOpExpr : public Expression { } auto r = right->evaluate(context); + if (op != Op::Eq && op != Op::Ne) { + if (r.is_null() || (l.is_null() && (op != Op::In && op != Op::NotIn))) { + throw std::runtime_error("unsupported operand type(s)"); + } + } switch (op) { case Op::StrConcat: return l.to_str() + r.to_str(); case Op::Add: return l + r; @@ -2147,11 +2152,11 @@ class Parser { } std::runtime_error unexpected(const TemplateToken & token) const { - return std::runtime_error("Unexpected " + TemplateToken::typeToString(token.type) + return std::runtime_error("Encountered unknown tag '" + TemplateToken::typeToString(token.type) + "'" + error_location_suffix(*template_str, token.location.pos)); } std::runtime_error unterminated(const TemplateToken & token) const { - return std::runtime_error("Unterminated " + TemplateToken::typeToString(token.type) + return std::runtime_error("Unexpected end of template. Jinja was looking for the following tags: '" + TemplateToken::typeToString(token.type) + "'" + error_location_suffix(*template_str, token.location.pos)); } diff --git a/tests/test-syntax.cpp b/tests/test-syntax.cpp index 54088b8..db1787d 100644 --- a/tests/test-syntax.cpp +++ b/tests/test-syntax.cpp @@ -484,28 +484,33 @@ TEST(SyntaxTest, SimpleCases) { "", render("{% if 1 %}{% elif 1 %}{% else %}{% endif %}", {}, {})); + EXPECT_THAT([]() { render(R"({{ 'a' + None }})", {}, {}); }, testing::Throws()); + EXPECT_THAT([]() { render(R"({{ None + 'b' }})", {}, {}); }, testing::Throws()); + EXPECT_THAT([]() { render(R"({{ 'a' in None }})", {}, {}); }, testing::Throws()); + EXPECT_EQ( + "False,True,False", + render(R"({{ None in [] }},{{ None == None }},{{ None != None }})", {}, {})); if (!getenv("USE_JINJA2")) { // TODO: capture stderr from jinja2 and test these. - EXPECT_THAT([]() { render("{%- set _ = [].pop() -%}", {}, {}); }, ThrowsWithSubstr("pop from empty list")); EXPECT_THAT([]() { render("{%- set _ = {}.pop() -%}", {}, {}); }, ThrowsWithSubstr("pop")); EXPECT_THAT([]() { render("{%- set _ = {}.pop('foooo') -%}", {}, {}); }, ThrowsWithSubstr("foooo")); - EXPECT_THAT([]() { render("{% else %}", {}, {}); }, ThrowsWithSubstr("Unexpected else")); + EXPECT_THAT([]() { render("{% else %}", {}, {}); }, ThrowsWithSubstr("Encountered unknown tag 'else'")); - EXPECT_THAT([]() { render("{% else %}", {}, {}); }, ThrowsWithSubstr("Unexpected else")); - EXPECT_THAT([]() { render("{% endif %}", {}, {}); }, ThrowsWithSubstr("Unexpected endif")); - EXPECT_THAT([]() { render("{% elif 1 %}", {}, {}); }, ThrowsWithSubstr("Unexpected elif")); - EXPECT_THAT([]() { render("{% endfor %}", {}, {}); }, ThrowsWithSubstr("Unexpected endfor")); - EXPECT_THAT([]() { render("{% endfilter %}", {}, {}); }, ThrowsWithSubstr("Unexpected endfilter")); + EXPECT_THAT([]() { render("{% else %}", {}, {}); }, ThrowsWithSubstr("Encountered unknown tag 'else'")); + EXPECT_THAT([]() { render("{% endif %}", {}, {}); }, ThrowsWithSubstr("Encountered unknown tag 'endif'")); + EXPECT_THAT([]() { render("{% elif 1 %}", {}, {}); }, ThrowsWithSubstr("Encountered unknown tag 'elif'")); + EXPECT_THAT([]() { render("{% endfor %}", {}, {}); }, ThrowsWithSubstr("Encountered unknown tag 'endfor'")); + EXPECT_THAT([]() { render("{% endfilter %}", {}, {}); }, ThrowsWithSubstr("Encountered unknown tag 'endfilter'")); - EXPECT_THAT([]() { render("{% if 1 %}", {}, {}); }, ThrowsWithSubstr("Unterminated if")); - EXPECT_THAT([]() { render("{% for x in 1 %}", {}, {}); }, ThrowsWithSubstr("Unterminated for")); - EXPECT_THAT([]() { render("{% generation %}", {}, {}); }, ThrowsWithSubstr("Unterminated generation")); - EXPECT_THAT([]() { render("{% if 1 %}{% else %}", {}, {}); }, ThrowsWithSubstr("Unterminated if")); - EXPECT_THAT([]() { render("{% if 1 %}{% else %}{% elif 1 %}{% endif %}", {}, {}); }, ThrowsWithSubstr("Unterminated if")); - EXPECT_THAT([]() { render("{% filter trim %}", {}, {}); }, ThrowsWithSubstr("Unterminated filter")); + EXPECT_THAT([]() { render("{% if 1 %}", {}, {}); }, ThrowsWithSubstr("Unexpected end of template. Jinja was looking for the following tags: 'if'")); + EXPECT_THAT([]() { render("{% for x in 1 %}", {}, {}); }, ThrowsWithSubstr("Unexpected end of template. Jinja was looking for the following tags: 'for'")); + EXPECT_THAT([]() { render("{% generation %}", {}, {}); }, ThrowsWithSubstr("Unexpected end of template. Jinja was looking for the following tags: 'generation'")); + EXPECT_THAT([]() { render("{% if 1 %}{% else %}", {}, {}); }, ThrowsWithSubstr("Unexpected end of template. Jinja was looking for the following tags: 'if'")); + EXPECT_THAT([]() { render("{% if 1 %}{% else %}{% elif 1 %}{% endif %}", {}, {}); }, ThrowsWithSubstr("Unexpected end of template. Jinja was looking for the following tags: 'if'")); + EXPECT_THAT([]() { render("{% filter trim %}", {}, {}); }, ThrowsWithSubstr("Unexpected end of template. Jinja was looking for the following tags: 'filter'")); } EXPECT_EQ(