Yet another situation where we cannot easily guess, by looking at the CFG, the target region for C++ constructor call.
Consider the following brace initialization:
struct A { A(); }; struct B : public A { int x; }; void foo() { B b = {}; }
AST in C++14:
`-VarDecl 0x7ff07884eab8 <col:3, col:10> col:5 b 'struct B' cinit `-CXXConstructExpr 0x7ff07887a940 <col:9, col:10> 'struct B' 'void (void) noexcept(false)' list zeroing
AST in C++17:
`-VarDecl 0x7fb1cf012b18 <col:3, col:10> col:5 b 'struct B' cinit `-InitListExpr 0x7fb1cf012f38 <col:9, col:10> 'struct B' |-CXXConstructExpr 0x7fb1cf012fd0 <col:10> 'struct A' 'void (void)' list `-ImplicitValueInitExpr 0x7fb1cf013000 <<invalid sloc>> 'int'
CFG in C++14:
[B1] 1: {} (CXXConstructExpr, struct B) 2: B b = {};
CFG in C++17:
[B1] 1: {} (CXXConstructExpr, struct A) 2: /*implicit*/(int)0 3: {} 4: B b = {};
So, essentially, in C++17 we don't have the trivial constructor call for B present anywhere at all, neither in AST, nor in CFG.
This causes a crash in the analyzer because he tries to find the target region for CK_NonVirtualBase constructor of A by looking at the parent stack frame, expecting to find the derived class constructor (for B) here, and then taking a base region within its this-object.
This fix is a quick-and-dirty suppression for the crash. It follows the tradition of "when unsure, make up a temporary and construct into that, then discard the result" - which is wrong, but at least it gets some invalidation done.
In our example it would be correct to construct into base{A, b}. There can also be situations when such initializer-list is instead lifetime-extended by a const& or && reference (see the tests), so in this case we'd need to construct into a respective sub-region of the temporary (but not into the temporary itself, of course).
Finally, from the perspective of checker development, i believe it would be useful to actually revive the constructor call, so that PreCall/PostCall event occured.
Could we try to make another lookup to see if we're initializing a variable of non-reference type? If so, we can make MemRegionManager use the region of the variable; otherwise, we can always fallback to temporary (it seems like sometimes this situation can happen out of DeclStmts). Or am I missing something?