diff --git a/clang/lib/StaticAnalyzer/Core/Store.cpp b/clang/lib/StaticAnalyzer/Core/Store.cpp --- a/clang/lib/StaticAnalyzer/Core/Store.cpp +++ b/clang/lib/StaticAnalyzer/Core/Store.cpp @@ -442,6 +442,15 @@ SVal StoreManager::getLValueElement(QualType elementType, NonLoc Offset, SVal Base) { + + // Special case, if index is 0, return the same type as if + // this was not an array dereference. + if (Offset.isZeroConstant()) { + QualType BT = Base.getType(this->Ctx); + if (!BT.isNull() && BT->getPointeeType() == elementType) + return Base; + } + // If the base is an unknown or undefined value, just return it back. // FIXME: For absolute pointer addresses, we just return that value back as // well, although in reality we should return the offset added to that diff --git a/clang/test/Analysis/ptr-arith.c b/clang/test/Analysis/ptr-arith.c --- a/clang/test/Analysis/ptr-arith.c +++ b/clang/test/Analysis/ptr-arith.c @@ -2,6 +2,7 @@ // RUN: %clang_analyze_cc1 -analyzer-checker=alpha.core.FixedAddr,alpha.core.PointerArithm,alpha.core.PointerSub,debug.ExprInspection -analyzer-store=region -Wno-pointer-to-int-cast -verify -triple i686-apple-darwin9 -Wno-tautological-pointer-compare -analyzer-config eagerly-assume=false %s void clang_analyzer_eval(int); +void clang_analyzer_dump(int); void f1() { int a[10]; @@ -330,3 +331,59 @@ simd_float2 x = {0, 1}; return x[1]; // no-warning } + +struct s { + int v; +}; + +// These three expressions should produce the same sym vals. +void struct_pointer_canon(struct s *ps) { + struct s ss = *ps; + clang_analyzer_dump((*ps).v); + // expected-warning-re@-1{{reg_${{[[:digit:]]+}}}.v>}} + clang_analyzer_dump(ps[0].v); + // expected-warning-re@-1{{reg_${{[[:digit:]]+}}}.v>}} + clang_analyzer_dump(ps->v); + // expected-warning-re@-1{{reg_${{[[:digit:]]+}}}.v>}} + clang_analyzer_eval((*ps).v == ps[0].v); // expected-warning{{TRUE}} + clang_analyzer_eval((*ps).v == ps->v); // expected-warning{{TRUE}} + clang_analyzer_eval(ps[0].v == ps->v); // expected-warning{{TRUE}} +} + +void struct_pointer_canon_bidim(struct s **ps) { + struct s ss = **ps; + clang_analyzer_eval(&(ps[0][0].v) == &((*ps)->v)); // expected-warning{{TRUE}} +} + +typedef struct s T1; +typedef struct s T2; +void struct_pointer_canon_typedef(T1 *ps) { + T2 ss = *ps; + clang_analyzer_dump((*ps).v); + // expected-warning-re@-1{{reg_${{[[:digit:]]+}}}.v>}} + clang_analyzer_dump(ps[0].v); + // expected-warning-re@-1{{reg_${{[[:digit:]]+}}}.v>}} + clang_analyzer_dump(ps->v); + // expected-warning-re@-1{{reg_${{[[:digit:]]+}}}.v>}} + clang_analyzer_eval((*ps).v == ps[0].v); // expected-warning{{TRUE}} + clang_analyzer_eval((*ps).v == ps->v); // expected-warning{{TRUE}} + clang_analyzer_eval(ps[0].v == ps->v); // expected-warning{{TRUE}} +} + +void struct_pointer_canon_bidim_typedef(T1 **ps) { + T2 ss = **ps; + clang_analyzer_eval(&(ps[0][0].v) == &((*ps)->v)); // expected-warning{{TRUE}} +} + +void struct_pointer_canon_const(const struct s *ps) { + struct s ss = *ps; + clang_analyzer_dump((*ps).v); + // expected-warning-re@-1{{reg_${{[[:digit:]]+}}}.v>}} + clang_analyzer_dump(ps[0].v); + // expected-warning-re@-1{{reg_${{[[:digit:]]+}}}.v>}} + clang_analyzer_dump(ps->v); + // expected-warning-re@-1{{reg_${{[[:digit:]]+}}}.v>}} + clang_analyzer_eval((*ps).v == ps[0].v); // expected-warning{{TRUE}} + clang_analyzer_eval((*ps).v == ps->v); // expected-warning{{TRUE}} + clang_analyzer_eval(ps[0].v == ps->v); // expected-warning{{TRUE}} +}