If the return statement is stored, we might as well allow querying against it.
Also fix the bug where the return statement is not stored if there is no return value.
And expose the return statement through getStatement helper function.
Details
Diff Detail
Event Timeline
lib/StaticAnalyzer/Core/CoreEngine.cpp | ||
---|---|---|
317 | All right, so this is not entirely NFC; it un-merges two ExplodedNodes during call exit when the state is otherwise identical - the CallExitBegin node itself and the "Bind Return Value"-tagged node. For example: 1 int coin(); 2 3 void foo() { 4 int x = coin(); 5 if (x > 0) 6 return; 7 else 8 return; 9 } 10 11 void bar() { 12 foo(); 13 } After binding the return value, removeDead is called to remove dead bindings for the entire callee context. It would therefore be called once before the patch and twice after the patch (but yielding just one node in both cases because the resulting node is identical). Here's a CallbackOrder test that shows that checkLiveSymbols is called twice: Note that checkDeadSymbols is not called at all because there are no dead symbols here to take care of. Even though dead symbols is the slowest thing ever in the analyzer, i believe that the overhead here of collecting dead symbols twice would be minimal because the situation when two returns have exactly identical states is relatively rare. If it isn't, then suddenly it might make sense to create a global (path-independent) cache for results of collecting dead symbols over a program state (sounds fun). |
@NoQ I would say the patch still makes sense. We would still get the same behavior with "return 0 / return 0" in two different locations.
IMO two states for two different locations should not be merged, also it indeed seems the situation is rare enough to seriously affect the performance.
Yep. Could you still add the test (ideally with some explanation of what it tests because i forgot to add it^^)?
Yep. Could you still add the test
Sure, what would be the testable observable behavior?
This test ensures that check::LiveSymbols is called as many times on the path through the second "return" as it is through the first "return" (three), and therefore the two paths were not merged prematurely before the respective return statement is evaluated. The paths would still be merged later, so we'd have only one post-call for foo(), but it is incorrect to merge them in the middle of evaluating two different statements.
All right, so this is not entirely NFC; it un-merges two ExplodedNodes during call exit when the state is otherwise identical - the CallExitBegin node itself and the "Bind Return Value"-tagged node.
For example:
After binding the return value, removeDead is called to remove dead bindings for the entire callee context. It would therefore be called once before the patch and twice after the patch (but yielding just one node in both cases because the resulting node is identical).
Here's a CallbackOrder test that shows that checkLiveSymbols is called twice:
Note that checkDeadSymbols is not called at all because there are no dead symbols here to take care of.
Even though dead symbols is the slowest thing ever in the analyzer, i believe that the overhead here of collecting dead symbols twice would be minimal because the situation when two returns have exactly identical states is relatively rare. If it isn't, then suddenly it might make sense to create a global (path-independent) cache for results of collecting dead symbols over a program state (sounds fun).