Index: lib/StaticAnalyzer/Core/BugReporterVisitors.cpp =================================================================== --- lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -61,7 +61,9 @@ return U->getSubExpr()->IgnoreParenCasts(); } else if (const MemberExpr *ME = dyn_cast(E)) { - if (ME->isArrow() || isDeclRefExprToReference(ME->getBase())) { + if (ME->isImplicitAccess()) { + return ME; + } else if (ME->isArrow() || isDeclRefExprToReference(ME->getBase())) { return ME->getBase()->IgnoreParenCasts(); } else { // If we have a member expr with a dot, the base must have been @@ -73,9 +75,9 @@ return IvarRef->getBase()->IgnoreParenCasts(); } else if (const ArraySubscriptExpr *AE = dyn_cast(E)) { - return AE->getBase(); + return getDerefExpr(AE->getBase()); } - else if (isDeclRefExprToReference(E)) { + else if (isa(E)) { return E; } break; @@ -974,14 +976,11 @@ // This code interacts heavily with this hack; otherwise the value // would not be null at all for most fields, so we'd be unable to track it. if (const auto *Op = dyn_cast(Ex)) - if (Op->getOpcode() == UO_AddrOf && Op->getSubExpr()->isLValue()) { - Ex = Op->getSubExpr()->IgnoreParenCasts(); - while (const auto *ME = dyn_cast(Ex)) { - Ex = ME->getBase()->IgnoreParenCasts(); - } - } + if (Op->getOpcode() == UO_AddrOf && Op->getSubExpr()->isLValue()) + if (const Expr *DerefEx = getDerefExpr(Op->getSubExpr())) + Ex = DerefEx; - if (ExplodedGraph::isInterestingLValueExpr(Ex) || CallEvent::isCallStmt(Ex)) + if (Ex && (ExplodedGraph::isInterestingLValueExpr(Ex) || CallEvent::isCallStmt(Ex))) Inner = Ex; } Index: lib/StaticAnalyzer/Core/RegionStore.cpp =================================================================== --- lib/StaticAnalyzer/Core/RegionStore.cpp +++ lib/StaticAnalyzer/Core/RegionStore.cpp @@ -1338,6 +1338,9 @@ /// the array). This is called by ExprEngine when evaluating casts /// from arrays to pointers. SVal RegionStoreManager::ArrayToPointer(Loc Array, QualType T) { + if (Array.getAs()) + return Array; + if (!Array.getAs()) return UnknownVal(); Index: test/Analysis/null-deref-offsets.c =================================================================== --- test/Analysis/null-deref-offsets.c +++ test/Analysis/null-deref-offsets.c @@ -7,7 +7,7 @@ int z[2]; }; -void testOffsets(struct S *s) { +void testOffsets(struct S *s, int coin) { if (s != 0) return; @@ -21,14 +21,17 @@ // FIXME: These should ideally be true. clang_analyzer_eval(&(s->y) == 4); // expected-warning{{FALSE}} - clang_analyzer_eval(&(s->z[0]) == 8); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(&(s->z[1]) == 12); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(&(s->z[0]) == 8); // expected-warning{{FALSE}} + clang_analyzer_eval(&(s->z[1]) == 12); // expected-warning{{FALSE}} // FIXME: These should ideally be false. clang_analyzer_eval(&(s->y) == 0); // expected-warning{{TRUE}} - clang_analyzer_eval(&(s->z[0]) == 0); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(&(s->z[1]) == 0); // expected-warning{{UNKNOWN}} - - // But this should still be a null dereference. - s->y = 5; // expected-warning{{Access to field 'y' results in a dereference of a null pointer (loaded from variable 's')}} + clang_analyzer_eval(&(s->z[0]) == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(&(s->z[1]) == 0); // expected-warning{{TRUE}} + + // But these should still be reported as null dereferences. + if (coin) + s->y = 5; // expected-warning{{Access to field 'y' results in a dereference of a null pointer (loaded from variable 's')}} + else + s->z[1] = 6; // expected-warning{{Array access (via field 'z') results in a null pointer dereference}} }