diff --git a/simplecpp.cpp b/simplecpp.cpp index 48a7ce9e..219a3079 100644 --- a/simplecpp.cpp +++ b/simplecpp.cpp @@ -206,11 +206,23 @@ static bool isAlternativeBinaryOp(const simplecpp::Token *tok, const std::string (isValue(tok->next) || tok->next->name || tok->next->op == '(')); } +static bool isAlternativeOpName(const simplecpp::Token *tok) +{ + static const std::set altop{"and","or","bitand","bitor","compl","not","not_eq","xor"}; + return tok->name && altop.find(tok->str()) != altop.end(); +} + static bool isAlternativeUnaryOp(const simplecpp::Token *tok, const std::string &alt) { - return ((tok->name && tok->str() == alt) && - (!tok->previous || tok->previous->op == '(') && - (tok->next && (tok->next->name || tok->next->number))); + if (!tok->name || tok->str() != alt) + return false; + // a unary operator comes first or after another operator, but not after an operand + const simplecpp::Token * const prev = tok->previous; + if (prev && (isValue(prev) || prev->op == ')' || (prev->name && !isAlternativeOpName(prev)))) + return false; + // it is followed by the start of an operand + const simplecpp::Token * const next = tok->next; + return next && (next->name || isValue(next) || next->isOneOf("(!~+-")); } static std::string replaceAll(std::string s, const std::string& from, const std::string& to) @@ -1318,7 +1330,7 @@ void simplecpp::TokenList::constFoldUnaryNotPosNeg(simplecpp::Token *tok1) tok->setstr(toString(~stringToLL(tok->next->str()))); deleteToken(tok->next); } else { - if (tok->previous && (isValue(tok->previous) || tok->previous->name)) + if (tok->previous && (isValue(tok->previous) || (tok->previous->name && !isAlternativeOpName(tok->previous)))) continue; if (!isValue(tok->next)) continue; @@ -2992,8 +3004,7 @@ static void simplifyName(simplecpp::TokenList &expr, const simplecpp::DUI &dui) tok->setstr(tok->str() == "true" ? "1" : "0"); continue; } - static const std::set altop{"and","or","bitand","bitor","compl","not","not_eq","xor"}; - if (altop.find(tok->str()) != altop.end()) { + if (isAlternativeOpName(tok)) { bool alt; if (tok->str() == "not" || tok->str() == "compl") { alt = isAlternativeUnaryOp(tok,tok->str()); diff --git a/test.cpp b/test.cpp index 866af490..9b30794b 100644 --- a/test.cpp +++ b/test.cpp @@ -2869,7 +2869,10 @@ static void ifShortCircuit() "defined(X) and X/0", "0 && 1 bitor (1/0)", "!(1 || (1/0) bitand 1)", - "0 and 1 xor (1/0)" + "0 and 1 xor (1/0)", + "0 && not (1/0)", + "0 && compl (1/0)", + "!(1 || not not (1/0))" }; for (const char * const expr : exprs) { const std::string code = std::string("#if ") + expr + "\n" @@ -2886,6 +2889,9 @@ static void ifShortCircuit() simplecpp::OutputList outputList; ASSERT_EQUALS("", preprocess("#if 1 && (1/0)\n#endif\n", &outputList)); ASSERT_EQUALS("file0,1,syntax_error,failed to evaluate #if condition, division/modulo by zero\n", toString(outputList)); + outputList.clear(); + ASSERT_EQUALS("", preprocess("#if not (1/0)\n#endif\n", &outputList)); + ASSERT_EQUALS("file0,1,syntax_error,failed to evaluate #if condition, division/modulo by zero\n", toString(outputList)); } static void ifZeroCondition() @@ -3022,6 +3028,45 @@ static void ifalt() // using "and", "or", etc ASSERT_EQUALS("\n1", preprocess(code)); } +static void ifAltUnaryOperators() // "not" and "compl" anywhere a unary operator is allowed +{ + const char * const exprs[] = { + // at the start of the expression + "not 0", + "(not 0)", + "not 0 && 1", + "compl 0", + // after a binary operator, "?" or ":" + "1 && not 0", + "1 and not 0", + "0 || not 0", + "1 == not 0", + "0 ? 0 : not 0", + "1 ? not 0 : 0", + // next to another unary operator + "not not 1", + "not -0", + "not ~-1", + "not !0 == 0", + "compl compl 1 == 1", + "compl -1 == 0", + "!(compl -1 + 1 == 0)", + "-compl 0 == 1", + // followed by "(" + "not (0)", + "not(0)", + "compl (0) == -1" + }; + for (const char * const expr : exprs) { + const std::string code = std::string("#if ") + expr + "\n" + "1\n" + "#else\n" + "2\n" + "#endif\n"; + ASSERT_EQUALS("\n1", preprocess(code.c_str())); + } +} + static void ifexpr() { const char code[] = "#define MACRO() (1)\n" @@ -5226,6 +5271,7 @@ static void runTests(int argc, char **argv, Input input) TEST_CASE(ifInvalidExpression); TEST_CASE(ifTrueFalse); TEST_CASE(ifalt); // using "and", "or", etc + TEST_CASE(ifAltUnaryOperators); TEST_CASE(ifexpr); TEST_CASE(ifUndefFuncStyleMacro);