This patch continues work that was started in D32291.
Our bugreporter::getDerefExpr() API tries to find out what has been dereferenced. For example, if we have an lvalue expression x->y.z which causes a null dereference when dereferenced, the function returns lvalue x->y - the object from which the null pointer must have been loaded. Similarly, unwrapping lvalue x->y would result in x.
I believe i found a more correct way to implement it, namely to see where lvalue-to-rvalue casts are located in the expression. In our example, x->y is surrounded by an lvalue-to-rvalue cast, which indicates that we should not unwrap the expression further. And it is irrelevant whether the member expression is a dot or an arrow, or whether C++ this-> or ObjC self-> is written explicitly or assumed implicitly, or whether the expression or a sub-expression is a pointer or a reference (we used to look at these).
This patch refactors getDerefExpr() with this design in mind. Now the function must be much easier to understand, and also behave correctly.
Unwrapping of binary operators that caused the dereference (eg. *x = 2 -> *x) was removed from getDerefExpr() because it contradicts its purpose and seems to have never actually been used (we should be receiving *x in this function instead in all cases).
Current implementation has the benefit of not crashing on the newly added test case. The crash was caused by the fact that the old getDerefExpr() was thinking that self was dereferenced, even though in fact it wasn't.
I should probably have a look at what else might have changed and add more test cases, because the old code was quite strange.
An interesting aspect of this function is that sometimes it returns a sub-expression that represents the pointer rvalue and sometimes it returns a sub-expression that represents the lvalue whose location contains the pointer which is being dereferenced.
I believe the reason we need the lvalue is that trackNullOrUndef() tracks lvalues better than rvalues. (That function loads from the lvalue to get the symbol representing the dereferenced pointer value.)
This behavior is pretty confusing, so we should document it in the comment.