From 7fad4481ddb6a6000c28c6d60f896e564e8bff6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Sat, 7 Nov 2026 13:40:41 +0100 Subject: [PATCH] Fix #731 (#if: an operator with a missing operand silently evaluates to false) Report "invalid expression" when the #if expression can not be folded to a single number. This also covers a missing operator ("1 2") and empty parentheses ("()"). Co-Authored-By: Claude Opus 5.5 --- simplecpp.cpp | 5 +++-- test.cpp | 46 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/simplecpp.cpp b/simplecpp.cpp index c9d762fa..48a7ce9e 100644 --- a/simplecpp.cpp +++ b/simplecpp.cpp @@ -3283,8 +3283,9 @@ static long long evaluate(simplecpp::TokenList &expr, const simplecpp::DUI &dui, simplifyName(expr, dui); simplifyNumbers(expr); expr.constFold(); - // TODO: handle invalid expressions - return expr.cfront() && expr.cfront() == expr.cback() && expr.cfront()->number ? stringToLL(expr.cfront()->str()) : 0LL; + if (!expr.cfront() || expr.cfront() != expr.cback() || !expr.cfront()->number) + throw std::runtime_error("invalid expression"); + return stringToLL(expr.cfront()->str()); } static const simplecpp::Token *gotoNextLine(const simplecpp::Token *tok) diff --git a/test.cpp b/test.cpp index 00a4a136..866af490 100644 --- a/test.cpp +++ b/test.cpp @@ -1842,7 +1842,8 @@ static void hashEscapeTrailingBackslash() "#endif\n"; simplecpp::OutputList outputList; preprocess(code, &outputList); - ASSERT_EQUALS(std::string("file0,2,portability_stringify,") + msg, toString(outputList)); + ASSERT_EQUALS(std::string("file0,2,portability_stringify,") + msg + + "file0,2,syntax_error,failed to evaluate #if condition, invalid expression\n", toString(outputList)); } { // even number of backslashes is fine @@ -2942,6 +2943,48 @@ static void ifUnaryOperators() } } +static void ifInvalidExpression() +{ + const char * const exprs[] = { + // missing operand + "1 +", + "(1 +)", + "(1 +) + 1", + "* 1", + "(* 1)", + "1 + * 2", + "1 <<", + "(1 ==)", + "1 &", + "(1 &&)", + "|| 1", + "!", + "(!)", + "-", + "~", + "(? 1 : 2)", + "(1 ? 2 :)", + "(1 ? 2 3)", + "(1 ? : 2)", + // missing operator + "1 2", + "(1 2)", + // empty parentheses + "()", + "1 + ()", + // syntax errors are reported also in operands that are not evaluated + "0 && (1 +)" + }; + for (const char * const expr : exprs) { + const std::string code = std::string("#if ") + expr + "\n" + "1\n" + "#endif\n"; + simplecpp::OutputList outputList; + ASSERT_EQUALS("", preprocess(code.c_str(), &outputList)); + ASSERT_EQUALS("file0,1,syntax_error,failed to evaluate #if condition, invalid expression\n", toString(outputList)); + } +} + static void ifTrueFalse() { const char code[] = "#if true && !false\n" @@ -5180,6 +5223,7 @@ static void runTests(int argc, char **argv, Input input) TEST_CASE(ifShortCircuit); TEST_CASE(ifZeroCondition); TEST_CASE(ifUnaryOperators); + TEST_CASE(ifInvalidExpression); TEST_CASE(ifTrueFalse); TEST_CASE(ifalt); // using "and", "or", etc TEST_CASE(ifexpr);