From acab82a6a6fe1409cb01294ed42ff6a463c2c077 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Sat, 7 Nov 2026 18:41:17 +0100 Subject: [PATCH] Fix #737 (Refactoring: separate the cppcheck-specific &= handling from TokenList::combineOperators()) Co-Authored-By: Claude Opus 5.5 --- simplecpp.cpp | 124 +++++++++++++++++++++++++++++++------------------- 1 file changed, 76 insertions(+), 48 deletions(-) diff --git a/simplecpp.cpp b/simplecpp.cpp index 9f23b04a..5ddd8d99 100644 --- a/simplecpp.cpp +++ b/simplecpp.cpp @@ -1220,26 +1220,83 @@ static bool isAlternativeAndBitandBitor(const simplecpp::Token* tok) return isAlternativeBinaryOp(tok, AND) || isAlternativeBinaryOp(tok, BITAND) || isAlternativeBinaryOp(tok, BITOR); } +/** + * Keep track of whether the code is in a function body. This is not part of + * preprocessing, it is used by the cppcheck-specific handling of &= in + * combineOperators(). + * @param executableScope one entry for each open {, true if it is a function + * body or a block in a function body + * @param tok the current token + * @return true if tok is { or } + */ +static bool updateExecutableScope(std::stack> &executableScope, const simplecpp::Token *tok) +{ + if (tok->op == '{') { + if (executableScope.top()) { + executableScope.push(true); + return true; + } + const simplecpp::Token *prev = tok->previous; + while (prev && prev->isOneOf(";{}(")) + prev = prev->previous; + executableScope.push(prev && prev->op == ')'); + return true; + } + if (tok->op == '}') { + if (executableScope.size() > 1) + executableScope.pop(); + return true; + } + return false; +} + +/** + * Is tok the & of an unnamed reference parameter with a default value in a + * function declaration, as in "void f(x&=2)"? Then "&" and "=" should not be + * combined into "&=". This is a cppcheck-specific heuristic, compilers + * tokenize "&=" as one operator. + * @param tok a & token that is directly followed by = + */ +static bool isAnonymousReferenceParameter(const simplecpp::Token *tok) +{ + int indentlevel = 0; + const simplecpp::Token *start = tok; + while (indentlevel >= 0 && start) { + if (start->op == ')') + ++indentlevel; + else if (start->op == '(') + --indentlevel; + else if (start->isOneOf(";{}")) + break; + start = start->previous; + } + if (indentlevel == -1 && start) { + const simplecpp::Token * const ftok = start; + bool isFuncDecl = ftok->name; + while (isFuncDecl) { + if (!start->name && start->str() != "::" && start->op != '*' && start->op != '&') + isFuncDecl = false; + if (!start->previous) + break; + if (start->previous->isOneOf(";{}:")) + break; + start = start->previous; + } + isFuncDecl &= start != ftok && start->name; + if (isFuncDecl) { + // TODO: we could loop through the parameters here and check if they are correct. + return true; + } + } + return false; +} + void simplecpp::TokenList::combineOperators() { std::stack> executableScope{{false}}; for (Token *tok = front(); tok; tok = tok->next) { - if (tok->op == '{') { - if (executableScope.top()) { - executableScope.push(true); - continue; - } - const Token *prev = tok->previous; - while (prev && prev->isOneOf(";{}(")) - prev = prev->previous; - executableScope.push(prev && prev->op == ')'); + if (updateExecutableScope(executableScope, tok)) continue; - } - if (tok->op == '}') { - if (executableScope.size() > 1) - executableScope.pop(); - continue; - } if (tok->op == '.') { // ellipsis ... @@ -1283,39 +1340,10 @@ void simplecpp::TokenList::combineOperators() continue; if (tok->next->op == '=' && tok->isOneOf("=!<>+-*/%&|^")) { - if (tok->op == '&' && !executableScope.top()) { - // don't combine &= if it is a anonymous reference parameter with default value: - // void f(x&=2) - int indentlevel = 0; - const Token *start = tok; - while (indentlevel >= 0 && start) { - if (start->op == ')') - ++indentlevel; - else if (start->op == '(') - --indentlevel; - else if (start->isOneOf(";{}")) - break; - start = start->previous; - } - if (indentlevel == -1 && start) { - const Token * const ftok = start; - bool isFuncDecl = ftok->name; - while (isFuncDecl) { - if (!start->name && start->str() != "::" && start->op != '*' && start->op != '&') - isFuncDecl = false; - if (!start->previous) - break; - if (start->previous->isOneOf(";{}:")) - break; - start = start->previous; - } - isFuncDecl &= start != ftok && start->name; - if (isFuncDecl) { - // TODO: we could loop through the parameters here and check if they are correct. - continue; - } - } - } + // don't combine &= if it is a anonymous reference parameter with default value: + // void f(x&=2) + if (tok->op == '&' && !executableScope.top() && isAnonymousReferenceParameter(tok)) + continue; tok->setstr(tok->str() + "="); deleteToken(tok->next); } else if ((tok->op == '|' || tok->op == '&') && tok->op == tok->next->op) {