/*
* Concatenate child boxes, then cast to the desired type.
+ * Skip for an explicit `null` - concatenating would force
+ * a cast of FR_TYPE_NULL to the arg's declared type, which
+ * would either error or silently coerce to a zero-length
+ * value. Let the null box reach check_types intact.
*/
- if (concat) {
+ if (concat && !(fr_value_box_list_num_elements(list) == 1 && fr_type_is_null(vb->type))) {
if (fr_value_box_list_concat_in_place(ctx, vb, list, type, FR_VALUE_BOX_LIST_FREE, true, SIZE_MAX) < 0) {
RPEDEBUG("Function \"%s\" failed concatenating arguments to type %s", name, fr_type_to_str(type));
return XLAT_ACTION_FAIL;
goto check_types;
}
+ if (concat) goto check_types;
+
/*
* Only a single child box is valid here. Check there is
* just one, cast to the correct type
check_types:
if (!fr_type_is_leaf(arg->type)) goto check_non_leaf;
+ /*
+ * FR_TYPE_NULL is an explicit "no value" placeholder
+ * (the `null` keyword). Passing it through to the
+ * xlat body lets the implementation distinguish it
+ * from a zero-length value of the declared type; the
+ * author opted-in by writing `null` in the source.
+ * Casting it would paper over the distinction.
+ */
+ if (fr_type_is_null(vb->type)) return XLAT_ACTION_DONE;
+
/*
* Cast to the correct type if necessary.
*/
return 0;
}
+ /*
+ * An explicit `null` literal is preserved unchanged - the xlat
+ * body receives an FR_TYPE_NULL box in this arg slot and can
+ * decide what to do with it. Casting would collapse it into a
+ * zero-length value of the declared type and hide the intent.
+ */
+ if (fr_type_is_null(node->data.type)) return 0;
+
/*
* Cast (or parse) the input data to the expected argument data type.
*/
switch (src->type) {
/*
- * Explicit null casts to an empty string.
+ * An explicit `null` has no representation to cast from.
+ * Refuse rather than silently coerce to an empty string.
*/
case FR_TYPE_NULL:
- return fr_value_box_bstrndup(ctx, dst, dst_enumv, "", 0, src->tainted);
+ fr_strerror_const("Cannot cast null to a string");
+ return -1;
/*
* The presentation format of octets is hex
switch (src->type) {
/*
- * An explicit null (e.g. the `null` keyword in unlang)
- * casts to a zero-length octets box.
+ * An explicit `null` has no representation to cast from.
+ * Refuse rather than silently coerce to zero-length octets.
*/
case FR_TYPE_NULL:
- return fr_value_box_memdup(ctx, dst, dst_enumv, NULL, 0, src->tainted);
+ fr_strerror_const("Cannot cast null to octets");
+ return -1;
/*
* <string> (excluding terminating \0)