Bug 377046 - Bogus "No return, in function returning non-void" warning
Summary: Bogus "No return, in function returning non-void" warning
Status: NEW
Alias: None
Product: CDT
Classification: Tools
Component: cdt-codan (show other bugs)
Version: 8.0.2   Edit
Hardware: All All
: P3 normal (vote)
Target Milestone: ---   Edit
Assignee: CDT Codan Inbox CLA
QA Contact: Elena Laskavaia CLA
URL:
Whiteboard:
Keywords:
: 515463 550509 (view as bug list)
Depends on:
Blocks:
 
Reported: 2012-04-17 23:45 EDT by Sergey Prigogin CLA
Modified: 2019-08-28 22:36 EDT (History)
7 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Sergey Prigogin CLA 2012-04-17 23:45:31 EDT
The following function triggers a bogus "No return, in function returning non-void" warning:

int foo(bool p) {
  for (;;) {
    if (p) {
      return 0;
    }
  }
}
Comment 1 Doug Schaefer CLA 2012-04-18 13:16:00 EDT
Great example of why this stuff is hard ;) Solvable, but man, you got to really spend the time on it to get it right.
Comment 2 Tim Martin CLA 2015-08-15 10:47:07 EDT
So it looks like the cause of this is that we do ControlFlowGraphBuilder.isConstant(decision, 1) in order to decide whether the condition can ever terminate. The empty expression obviously isn't a constant expression, so this fails.

I think treating an empty expression as true within a for loop condition is something that doesn't occur anywhere else in the C standard, so for me it makes sense to fix this in ControlFlowGraphBuilder.createFor() rather than changing the logic in addOutgoing().

The naive solution is something like:

  ICPPNodeFactory nodeFactory = CPPNodeFactory.getDefault();

  CxxDecisionNode decision = 
     factory.createDecisionNode(forNode.getConditionExpression());

  if (decision.getData() == null) {
     decision.setData(
        nodeFactory.newLiteralExpression(
          IASTLiteralExpression.lk_integer_constant, "1"));
  }

Constructing a CPPNodeFactory here doesn't seem like it's the right thing to do (it spits a bunch of API warnings, apart from anything else). Is there a better way to do this?

Another problem with this approach is that it doesn't fix the bug that:

  int foo(bool p) {
     for(;42;) {
        if (p) {
           return 0;
        }
     }
  }

reports a warning; this is because the isConstant(decision, 1) check only checks for it to be equal to 1. I think this is actually a bug in addOutgoing().
Comment 3 Nathan Ridge CLA 2015-08-15 19:00:38 EDT
I agree that you probably don't want to be creating new AST nodes in ControlFlowGraphBuilder.

What if we simply had isConstant() treat a null IASTNode as constant? I don't think a null IASTNode would arise anywhere other than the condition of a for-loop.

To solve the second problem, we could change the second argument of isConstant() from long to boolean, and cast the numerical value of the expression to boolean before comparing it to the expected value.
Comment 4 Tim Martin CLA 2015-08-17 15:56:46 EDT
Digging a little more into isConstant(), it doesn't seem to work properly. If I try:

  int bar(bool p) {
     for (int x = 0; x < 10; x++) {
        if (p) {
           return 0;
        }
     }
  }

Then it doesn't report a warning. This seems to be because the "x < 10" evaluates as a constant according to isConstant(). The expression gets passed through to Value.evaluateBinaryExpression(), which returns 1 since the LHS returns a value of 0. It seems like the problem is that it thinks it has a value for the x variable without figuring out that it varies in this context.

I haven't traced this all the way through yet. My guess is that the best fix is to evaluate the expression without knowledge of any variable values, though I don't know how easy that is to do.
Comment 5 Nathan Ridge CLA 2015-08-17 16:43:15 EDT
Yikes!

When Value.evaluateBinding() handles an IVariable, it should probably only use getInitialValue() if the variable is constant; otherwise, it should consider its value to be Value.UNKNOWN.

You can check whether the variable is constant by getting its type, resolving any typedefs inside with 'SemanticUtil.getNestedType(type, TDEF)', and seeing if the resulting type is an IQualifierType with isConst() = true.
Comment 6 Tim Martin CLA 2015-08-20 16:28:01 EDT
Right, this works but causes a regression in ControlFlowGraphTest.test_deadbranch(), which has:

  foo() {
    int a=10,x=5;
    if (x<0)
      a++;
  }

The test requires that the else{} branch be a dead node.

I'm not sure of the importance of this test case. No other tests seem to be failing, so perhaps it's OK to change to a test case with a const-qualified variable type. This is technically a regression, but the ability to infer variable values seems quite limited anyway.

The other alternative is to fix just the original bug as reported and leave the "x < 10" case in the for loop to be dealt with as a separate issue.

I think it's probably worth making the "x < 10" for loop case work, because that's the most common pattern, and unless anything specific is dependent on the other case that's a price worth paying.
Comment 7 Nathan Ridge CLA 2015-08-20 16:44:12 EDT
I agree, we should just change that test case for now.

In the longer term we could do something smarter, where if a variable is not constant but has an initial value, we try to determine whether its value could have changed between the point of initialization and the point of access, and if we can prove that it couldn't have, then we use its initial value.
Comment 8 Eclipse Genie CLA 2015-08-22 03:48:33 EDT
New Gerrit change created: https://git.eclipse.org/r/54358
Comment 9 Tim Martin CLA 2015-08-22 10:01:26 EDT
OK, Hudson claims the build is failing. Looking at it, I can't immediately see how the failure is related to my change. If I'm reading it right, the codan tests are passing but the build is being abandoned in the managed builder tests?

https://hudson.eclipse.org/cdt/job/cdt-verify/3228/
Comment 10 Nathan Ridge CLA 2015-08-22 16:16:17 EDT
(In reply to Tim Martin from comment #9)
> OK, Hudson claims the build is failing. Looking at it, I can't immediately
> see how the failure is related to my change. If I'm reading it right, the
> codan tests are passing but the build is being abandoned in the managed
> builder tests?
> 
> https://hudson.eclipse.org/cdt/job/cdt-verify/3228/

Occasionally builds are aborted due to intermittent issues, I'm guessing with the test infrastructure.

I see Sergey has retriggered the tests [1], and this time they did complete, and there are a few failures which do look related to your patch.

[1] https://hudson.eclipse.org/cdt/job/cdt-verify/3229/
Comment 11 Tim Martin CLA 2015-08-25 11:42:09 EDT
For the record, I changed the remaining test cases in the same way, for the same reasoning as above, but Elena objected to changing these test cases. I guess the non-const type is an important part of these test cases, but that wasn't clear to me from looking at the test case code. If this behaviour is important, then we can't fix it this way.

I appreciate that the ideal is not to rely on const type to distinguish between cases where the initial variable value can be used or not. However, I think the level of inference required to solve the general case is going to be a lot more work than I was anticipating.

I'm going to leave this ticket for someone else who knows the Codan code better than I do.
Comment 12 Elena Laskavaia CLA 2015-08-25 13:54:19 EDT
Yim, to do proper value propagation we need DFA (data-flow analysis) engine which is above and beyond scope of this PR. But to do original sample few lines of fix that calculate value for "null" ast nodes should be sufficient, no? (Part of your patch)
Comment 13 Tim Martin CLA 2015-08-29 14:30:24 EDT
You're right, we can fix the original report easily and not worry about the for loop case for now. I've extracted the necessary lines and re-submitted the patch. I went with an overload of the isConstant() method as you suggested; the version that takes a long isn't used at the moment, so it seems unnecessary to me, but I suppose it could be used in the future.
Comment 14 Nobody - feel free to take it CLA 2016-09-23 08:21:07 EDT
How suppress this warning by using comment '@suppress("No return")'?
Or this suppression comment not work?
Comment 15 Nathan Ridge CLA 2016-10-13 17:08:26 EDT
(In reply to Alien Huker from comment #14)
> How suppress this warning by using comment '@suppress("No return")'?
> Or this suppression comment not work?

It works for me. For example:

int foo()  // @suppress("No return") 
{  
}

Note that the text of the suppression comment can be customized in Preferences -> C/C++ -> Code Analysis -> <select checker> -> Customize Selected.
Comment 16 Nathan Ridge CLA 2017-04-19 22:20:47 EDT
*** Bug 515463 has been marked as a duplicate of this bug. ***
Comment 17 Nathan Ridge CLA 2019-08-28 22:36:40 EDT
*** Bug 550509 has been marked as a duplicate of this bug. ***