Skip to content

fix #5591: Add check for meaningless comma operator in if statement, misplaced ')' #7406

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

clock999
Copy link
Contributor

https://trac.cppcheck.net/ticket/5591

Description:
int f2(int, int = 0) {....}

void f()
{
if(f2(0), 0) //actually mean f2(0, 0)
{
....
}
}

@clock999 clock999 force-pushed the wy_dev_5591 branch 9 times, most recently from 53083da to 1687100 Compare March 26, 2025 14:04
@chrchr-github
Copy link
Collaborator

Thanks for your contribution. Note that these issues seem to be diagnosed already by current compilers.

@clock999
Copy link
Contributor Author

clock999 commented Mar 27, 2025

Hi CHR, many thanks for your comments, and I have updated the commit. But there is a failed case tested on the github for the commit, that seems not the problem of the committed code. Seems the network traffic‌ caused a overtime. But I am not sure. Do you know how to trigger the checks to run again? Thanks a lot!

@chrchr-github
Copy link
Collaborator

The macos runners can be very slow sometimes. I have restarted the failing run.

Makefile Outdated
@@ -204,6 +204,7 @@ LIBOBJ = $(libcppdir)/valueflow.o \
$(libcppdir)/checkleakautovar.o \
$(libcppdir)/checkmemoryleak.o \
$(libcppdir)/checknullpointer.o \
$(libcppdir)/checkoperatorcomma.o \
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't want that we have files with just 1 checker. With this filename it's hard to see that more checkers would be added later. I suggest that your checker is added in checkother instead.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or make the name more generic so more checkers can be added in it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi Danmar, yes, it is not a common check, so it is better to add the checker to checkother.cpp.

const Token * parent = tok->astParent();
if (parent && (Token::simpleMatch(parent->previous(), "if (") ||
Token::simpleMatch(parent->previous(), "while ("))) {
assertWithSuspiciousCommaError(tok);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it looks strange to me that assertWithSuspiciousComma is reported when there is no assert.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi Danmar, I will use the word warn instead of using assert.

parent = parent->astParent();
if (Token::simpleMatch(parent->previous(), "for (")) {
if (tok->index() < tok->astParent()->index()) {
assertWithSuspiciousCommaError(tok);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have the feeling this could be noisy. Maybe you want to increment two variables in the third expression and then a comma seems natural.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi Danmar, this is to check the condition judgment part of the 'for' statement, and I can make it more straightforward. But if we make the checker focusing on a very specific case, maybe it is not needed for a 'for' statement.

@danmar
Copy link
Owner

danmar commented Apr 4, 2025

the PR description says that you want to detect:

if(f2(0), 0) //actually mean f2(0, 0)

well yes that looks like a possible bug. there seems to be misplaced parenthesis

but I fear your checker is much more verbose. you warn whenever there is a comma in a if/while. no matter if there are function calls.

If the condition says:

    if (x,y)

I agree the code looks weird.. but it's not a likely misplaced parenthesis and I am not convinced that the comma is a bug. It looks intentional.

@danmar
Copy link
Owner

danmar commented Apr 4, 2025

I also want to point out that misra has a rule that forbids comma operators. But I still feel sympathy for this checker if we make it more specific and it can detect actual bugs and not just warn about random comma operators.

@clock999
Copy link
Contributor Author

clock999 commented Apr 17, 2025

I also want to point out that misra has a rule that forbids comma operators. But I still feel sympathy for this checker if we make it more specific and it can detect actual bugs and not just warn about random comma operators.

Hi Danmer, according to the description of the bug 5591, seems the user just want the cppcheck to help to give some warning to avoid unintended spelling mistakes which are grammatically correct. But it is a logical error during running. So maybe it is a little difficult to completely decide if the code is intended or not by a static analysis. Maybe we can find out the most likely possibility and provide a warning. The case described on the bug usually happened when using a function with default arguments, as the default arguments might be ignored and misplaced outside the parentheses. So is it ok that we just focus on the functions with default args called in an if or while condition statement?

@clock999 clock999 force-pushed the wy_dev_5591 branch 3 times, most recently from 7219b70 to 74a9515 Compare April 17, 2025 04:48
@danmar
Copy link
Owner

danmar commented Apr 17, 2025

So maybe it is a little difficult to completely decide if the code is intended or not by a static analysis.

yeah.. and when you say "a little difficult" you probably mean "impossible" right ? :-)

our approach is to make the checker "passive" then and warn only about the most obvious bugs. and I think your suggestion resonate well with that.

The case described on the bug usually happened when using a function with default arguments, as the default arguments might be ignored and misplaced outside the parentheses. So is it ok that we just focus on the functions with default args called in an if or while condition statement?

Yes that sounds much better to me. If such function appear immediately before the comma operator then I would think it seems fair to write a warning.

Copy link
Owner

@danmar danmar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • please add some tests also in the testother.cpp
  • update CheckOther::getErrorMessages
  • update classInfo in checkother.h

Comment on lines 4381 to 4382
if (parent && (Token::simpleMatch(parent->previous(), "if (") ||
Token::simpleMatch(parent->previous(), "while ("))) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can write Token::Match(parent->previous(), "if|while (") instead

const Token * parent = tok->astParent();
if (parent && (Token::simpleMatch(parent->previous(), "if (") ||
Token::simpleMatch(parent->previous(), "while ("))) {
if (tok->previous()->str() == ")" && tok->previous()->link()->str() == "(") {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like overall to be over-explicit. but this && tok->previous()->link()->str() == "(" is too redundant please remove that.

const Function * func = tok->previous()->link()->previous()->function();
if (func && func->initArgCount > 0) {
const Token * r_op = tok->astOperand2();
if (r_op && r_op->hasKnownValue()) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

explain why there must be a known value?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi Danmar, sorry, that code is thoughtless. I will update the commit. I make a sample to explain my thinking.

int func(int a, int b = 100) {
    return 1;
}

int main() {
    int x = 200;
    int y = 100;
    if (func(100), x) {} //case 1, report warning
    if (func(100), true) {} //case 2, report warning
    if (func(100), 100) {} //case 3, report warning
    
    if (func(100), func(100)) {} //case 4, not report warning
    if (func(100), x + y) {} //case 5, not report warning
    return 100;
}

For case 4 and case 5, seems the programmer is more likely intended to do that. But I am not quite sure about that.

@danmar
Copy link
Owner

danmar commented Apr 17, 2025

you don't have to do it in this PR but I think this warning could be more generic and written for other statements also. Example:

     x = 2 * (3 + foo(4,5),6) + 7;

It should check that the comma is inside parentheses.

@chrchr-github
Copy link
Collaborator

lib/checkother.cpp:4384:50: style: Call to 'Token::previous()' followed by 'Token::link()' can be simplified. [redundantNextPrevious]
                    const Function * func = tok->previous()->link()->previous()->function();

Use tok->linkAt(-1)->previous()->function() instead.

@clock999
Copy link
Contributor Author

lib/checkother.cpp:4384:50: style: Call to 'Token::previous()' followed by 'Token::link()' can be simplified. [redundantNextPrevious]
                    const Function * func = tok->previous()->link()->previous()->function();

Use tok->linkAt(-1)->previous()->function() instead.

Hi CHR, thanks a lot for your help! With the latest submit, the github report test failures. It is caused by running the testrunner. But all the tests can be passed when I run the testrunner locally on my pc.
When running the test case 'suspiciousComma' in the testother.cpp, on github, the actual output of the test case is different from the one on my local pc.
On github:

Expected: 
[test.cpp:3]: (style) There is an suspicious comma expression used as a condition in an if/while condition statement.\n

Actual: 
[test.cpp:3:15]: (style) There is an suspicious comma expression used as a condition in an if/while condition statement. [warnSuspiciousComma]\n

Local:

Actual: 
[test.cpp:3]: (style) There is an suspicious comma expression used as a condition in an if/while condition statement.\n

Do you know what's the reason? Thanks!

@chrchr-github
Copy link
Collaborator

Do you know what's the reason? Thanks!

Maybe you need to rebase onto main? I think there were some recent changes involving the column number.

@clock999 clock999 force-pushed the wy_dev_5591 branch 2 times, most recently from 68c021b to 35ed502 Compare April 25, 2025 05:34
@chrchr-github
Copy link
Collaborator

lib/checkother.cpp:4399:26: style: Call to 'Token::tokAt()' followed by 'Token::str()' can be simplified. [redundantNextPrevious]
                if (tok->tokAt(-1)->str() == ")") {
                         ^

should be tok->strAt(-1).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants