jinja : handle empty expressions correctly (#20913)
* Reject empty computed member expressions before returning slices[0] from parse_member_expression_arguments().
* Treat empty computed member expressions with Jinja2 undefined semantics
Treat empty computed member expressions like `a[]` as undefined instead of
raising a parser error, to match Jinja2 behavior.
- return a noop expression for empty computed member arguments
- return undefined when a computed member key evaluates to undefined
- add Jinja tests covering `a[]|default('fallback')` and `a[] is undefined`
* Handle undefined computed member properties
Move undefined-property handling to the common member access path, and add a test covering `a[undefined] is undefined`.
* Use default undefined value in member access
Initialize val and then return it when property is undefined.
Co-authored-by: Sigbjørn Skjæret <sigbjorn.skjaeret@scala.com>
* empty statement parses to blank_expression instead of noop_statement
---------
Co-authored-by: Sigbjørn Skjæret <sigbjorn.skjaeret@scala.com>
This commit is contained in:
parent
64ac9ab66a
commit
ead417f01c
|
|
@ -539,6 +539,9 @@ private:
|
|||
statement_ptr step = slices.size() > 2 ? std::move(slices[2]) : nullptr;
|
||||
return mk_stmt<slice_expression>(start_pos, std::move(start), std::move(stop), std::move(step));
|
||||
}
|
||||
if (slices.empty()) {
|
||||
return mk_stmt<blank_expression>(start_pos);
|
||||
}
|
||||
return std::move(slices[0]);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -771,10 +771,15 @@ value member_expression::execute_impl(context & ctx) {
|
|||
}
|
||||
|
||||
JJ_DEBUG("Member expression on object type %s, property type %s", object->type().c_str(), property->type().c_str());
|
||||
ensure_key_type_allowed(property);
|
||||
|
||||
value val = mk_val<value_undefined>("object_property");
|
||||
|
||||
if (property->is_undefined()) {
|
||||
JJ_DEBUG("%s", "Member expression property is undefined, returning undefined");
|
||||
return val;
|
||||
}
|
||||
|
||||
ensure_key_type_allowed(property);
|
||||
|
||||
if (is_val<value_undefined>(object)) {
|
||||
JJ_DEBUG("%s", "Accessing property on undefined object, returning undefined");
|
||||
return val;
|
||||
|
|
|
|||
|
|
@ -263,6 +263,14 @@ struct comment_statement : public statement {
|
|||
|
||||
// Expressions
|
||||
|
||||
// Represents an omitted expression in a computed member, e.g. `a[]`.
|
||||
struct blank_expression : public expression {
|
||||
std::string type() const override { return "BlankExpression"; }
|
||||
value execute_impl(context &) override {
|
||||
return mk_val<value_undefined>();
|
||||
}
|
||||
};
|
||||
|
||||
struct member_expression : public expression {
|
||||
statement_ptr object;
|
||||
statement_ptr property;
|
||||
|
|
|
|||
|
|
@ -387,6 +387,24 @@ static void test_expressions(testing & t) {
|
|||
"Bob"
|
||||
);
|
||||
|
||||
test_template(t, "empty computed member defaults to undefined",
|
||||
"{{ a[]|default('fallback') }}",
|
||||
{{"a", {{"name", "Bob"}}}},
|
||||
"fallback"
|
||||
);
|
||||
|
||||
test_template(t, "empty computed member is undefined",
|
||||
"{{ a[] is undefined }}",
|
||||
{{"a", {{"name", "Bob"}}}},
|
||||
"True"
|
||||
);
|
||||
|
||||
test_template(t, "undefined computed member is undefined",
|
||||
"{{ a[undefined] is undefined }}",
|
||||
{{"a", {{"name", "Bob"}}}},
|
||||
"True"
|
||||
);
|
||||
|
||||
test_template(t, "array access",
|
||||
"{{ items[1] }}",
|
||||
{{"items", json::array({"a", "b", "c"})}},
|
||||
|
|
|
|||
Loading…
Reference in New Issue