rainerscript: warn on constant AND/OR operand at parse time

Why:
The AND/OR constant-operand warning (issue #1046) ran in the optimizer, after
constFoldCmp(). Legitimate constant comparisons - typically backtick-expanded
environment feature flags, e.g. `echo $FLAG` == "on" - fold to a constant
operand and were wrongly flagged. The warning also ran after yyparse(), so it
reported the last line of the master config instead of the offending expression.

Impact:
Comparisons that constant-fold to a constant no longer warn. The bare literal
mistake ($msg contains "a" or "b") still warns, now at the correct file and line.

Technical Overview:
Move the check from cnfexprOptimize_AND_OR() to cnfexprNew(), where the operand
is still the comparison node as written and cnfcurrfn/yylineno are accurate. A
backtick used directly as a bare boolean operand still warns, as intended: after
lexer expansion it is identical to a hand-written literal, exactly the pattern
the warning targets. Regression test extended with a negative constant-fold case.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Julien Thomas 2026-07-18 01:04:08 +02:00
parent e22428a2fb
commit 6d8e3b9db9
2 changed files with 32 additions and 12 deletions

View File

@ -1413,6 +1413,26 @@ void cnfobjPrint(struct cnfobj *o) {
struct cnfexpr *cnfexprNew(unsigned nodetype, struct cnfexpr *l, struct cnfexpr *r) { struct cnfexpr *cnfexprNew(unsigned nodetype, struct cnfexpr *l, struct cnfexpr *r) {
struct cnfexpr *expr; struct cnfexpr *expr;
/* Warn on bare constant AND/OR operands as written in the config, e.g.
* `$msg contains "a" or "b"` (issue #1046). This must happen here, at
* construction time, rather than in the optimizer: constFoldCmp() later
* reduces legitimate constant comparisons (typically backtick-expanded
* environment variables, e.g. `echo $FLAG` == "on") to constant operands,
* which must not warn. At this point such an operand is still a comparison
* node, and cnfcurrfn/yylineno still point at the offending expression, so
* the reported file and line are accurate.
*/
if (nodetype == AND || nodetype == OR) {
if (l != NULL && (l->nodetype == 'N' || l->nodetype == 'S')) {
parser_warnmsg("boolean operator '%s' has constant left operand; did you mean to repeat the comparison?",
tokenToString(nodetype));
}
if (r != NULL && (r->nodetype == 'N' || r->nodetype == 'S')) {
parser_warnmsg("boolean operator '%s' has constant right operand; did you mean to repeat the comparison?",
tokenToString(nodetype));
}
}
/* optimize some constructs during parsing */ /* optimize some constructs during parsing */
if (nodetype == 'M' && r->nodetype == 'N') { if (nodetype == 'M' && r->nodetype == 'N') {
((struct cnfnumval *)r)->val *= -1; ((struct cnfnumval *)r)->val *= -1;
@ -5627,15 +5647,9 @@ static struct cnfexpr *cnfexprOptimize_NOT(struct cnfexpr *expr) {
static struct cnfexpr *cnfexprOptimize_AND_OR(struct cnfexpr *expr) { static struct cnfexpr *cnfexprOptimize_AND_OR(struct cnfexpr *expr) {
struct cnffunc *funcl, *funcr; struct cnffunc *funcl, *funcr;
if (expr->l->nodetype == 'N' || expr->l->nodetype == 'S') { /* the constant-operand warning is emitted at construction time in
parser_warnmsg("boolean operator '%s' has constant left operand; did you mean to repeat the comparison?", * cnfexprNew(); see the comment there for why it cannot live here.
tokenToString(expr->nodetype)); */
}
if (expr->r->nodetype == 'N' || expr->r->nodetype == 'S') {
parser_warnmsg("boolean operator '%s' has constant right operand; did you mean to repeat the comparison?",
tokenToString(expr->nodetype));
}
if (expr->l->nodetype == 'F') { if (expr->l->nodetype == 'F') {
if (expr->r->nodetype == 'F') { if (expr->r->nodetype == 'F') {
funcl = (struct cnffunc *)expr->l; funcl = (struct cnffunc *)expr->l;

View File

@ -1,16 +1,21 @@
#!/bin/bash #!/bin/bash
# Verify that a common RainerScript boolean mistake logs a config warning: # Verify that a common RainerScript boolean mistake logs a config warning:
# `$msg contains "a" or "b"` keeps historical truthiness semantics, but the # `$msg contains "a" or "b"` keeps historical truthiness semantics, but the
# bare string literal is probably a missing repeated comparison. The oracle is # bare string literal is probably a missing repeated comparison. The warning
# rsyslogd config-check output because the warning is emitted while optimizing # must only fire on constants as written: comparisons that merely constant-fold
# the parsed configuration, before normal message routing is relevant. # to a constant (e.g. backtick-expanded environment variables) are legitimate
# and must stay silent. The oracle is rsyslogd config-check output.
. ${srcdir:=.}/diag.sh init . ${srcdir:=.}/diag.sh init
export MY_FEATURE="on"
generate_conf generate_conf
add_conf ' add_conf '
if $msg contains "alpha" or "beta" then { if $msg contains "alpha" or "beta" then {
action(type="omfile" file=`echo $RSYSLOG_OUT_LOG`) action(type="omfile" file=`echo $RSYSLOG_OUT_LOG`)
} }
if `echo $MY_FEATURE` == "on" and $msg contains "gamma" then {
action(type="omfile" file=`echo $RSYSLOG_OUT_LOG`)
}
' '
export RS_REDIR=">${RSYSLOG_DYNNAME}.rsyslog.log 2>&1" export RS_REDIR=">${RSYSLOG_DYNNAME}.rsyslog.log 2>&1"
@ -18,4 +23,5 @@ rsyslogd_config_check
unset RS_REDIR unset RS_REDIR
content_check "boolean operator 'OR' has constant right operand" "${RSYSLOG_DYNNAME}.rsyslog.log" content_check "boolean operator 'OR' has constant right operand" "${RSYSLOG_DYNNAME}.rsyslog.log"
check_not_present "boolean operator 'AND' has constant" "${RSYSLOG_DYNNAME}.rsyslog.log"
exit_test exit_test