return false;
}
+ bool isThisObject(const ElementRegion *R) {
+ if (const auto *Idx = R->getIndex().getAsInteger()) {
+ if (const auto *SR = R->getSuperRegion()->getAs<SymbolicRegion>()) {
+ QualType Ty = SR->getPointeeStaticType();
+ bool IsNotReinterpretCast = R->getValueType() == Ty;
+ if (Idx->isZero() && IsNotReinterpretCast)
+ return isThisObject(SR);
+ }
+ }
+ return false;
+ }
+
public:
SValExplainer(ASTContext &Ctx) : ACtx(Ctx) {}
// Add the relevant code once it does.
std::string VisitSymbolicRegion(const SymbolicRegion *R) {
- // Explain 'this' object here.
+ // Explain 'this' object here - if it's not wrapped by an ElementRegion.
// TODO: Explain CXXThisRegion itself, find a way to test it.
if (isThisObject(R))
return "'this' object";
std::string VisitElementRegion(const ElementRegion *R) {
std::string Str;
llvm::raw_string_ostream OS(Str);
+
+ // Explain 'this' object here.
+ // They are represented by a SymRegion wrapped by an ElementRegion; so
+ // match and handle it here.
+ if (isThisObject(R))
+ return "'this' object";
+
OS << "element of type '" << R->getElementType() << "' with index ";
// For concrete index: omit type of the index integer.
if (auto I = R->getIndex().getAs<nonloc::ConcreteInt>())
/// It might return null.
SymbolRef getSymbol() const { return sym; }
+ /// Gets the type of the wrapped symbol.
+ /// This type might not be accurate at all times - it's just our best guess.
+ /// Consider these cases:
+ /// void foo(void *data, char *str, base *obj) {...}
+ /// The type of the pointee of `data` is of course not `void`, yet that's our
+ /// best guess. `str` might point to any object and `obj` might point to some
+ /// derived instance. `TypedRegions` other hand are representing the cases
+ /// when we actually know their types.
+ QualType getPointeeStaticType() const {
+ return sym->getType()->getPointeeType();
+ }
+
bool isBoundable() const override { return true; }
void Profile(llvm::FoldingSetNodeID& ID) const override;
if (const auto *TVR = MR->getAs<TypedValueRegion>()) {
ElementTy = TVR->getValueType();
} else {
- ElementTy =
- MR->castAs<SymbolicRegion>()->getSymbol()->getType()->getPointeeType();
+ ElementTy = MR->castAs<SymbolicRegion>()->getPointeeStaticType();
}
assert(!ElementTy->isPointerType());
const MemRegion *Region = RegionSVal->getRegion();
if (CheckSuperRegion) {
- if (auto FieldReg = Region->getAs<FieldRegion>())
+ if (const SubRegion *FieldReg = Region->getAs<FieldRegion>()) {
+ if (const auto *ER = dyn_cast<ElementRegion>(FieldReg->getSuperRegion()))
+ FieldReg = ER;
return dyn_cast<SymbolicRegion>(FieldReg->getSuperRegion());
+ }
if (auto ElementReg = Region->getAs<ElementRegion>())
return dyn_cast<SymbolicRegion>(ElementReg->getSuperRegion());
}
// what is written inside the pointer.
bool CanDereference = true;
if (const auto *SR = L->getRegionAs<SymbolicRegion>()) {
- if (SR->getSymbol()->getType()->getPointeeType()->isVoidType())
+ if (SR->getPointeeStaticType()->isVoidType())
CanDereference = false;
} else if (L->getRegionAs<AllocaRegion>())
CanDereference = false;
SVal baseExprVal =
MR ? loc::MemRegionVal(MR) : state->getSVal(BaseExpr, LCtx);
+ // FIXME: Copied from RegionStoreManager::bind()
+ if (const auto *SR =
+ dyn_cast_or_null<SymbolicRegion>(baseExprVal.getAsRegion())) {
+ QualType T = SR->getPointeeStaticType();
+ baseExprVal =
+ loc::MemRegionVal(getStoreManager().GetElementZeroRegion(SR, T));
+ }
+
const auto *field = cast<FieldDecl>(Member);
SVal L = state->getLValue(field, baseExprVal);
// If our base region is symbolic, we don't know what type it really is.
// Pretend the type of the symbol is the true dynamic type.
// (This will at least be self-consistent for the life of the symbol.)
- Ty = SR->getSymbol()->getType()->getPointeeType();
+ Ty = SR->getPointeeStaticType();
RootIsSymbolic = true;
}
if (const TypedRegion *TR = dyn_cast<TypedRegion>(MR))
T = TR->getLocationType()->getPointeeType();
else if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(MR))
- T = SR->getSymbol()->getType()->getPointeeType();
+ T = SR->getPointeeStaticType();
}
assert(!T.isNull() && "Unable to auto-detect binding type!");
assert(!T->isVoidType() && "Attempting to dereference a void pointer!");
return bindAggregate(B, TR, V);
}
- if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R)) {
- // Binding directly to a symbolic region should be treated as binding
- // to element 0.
- QualType T = SR->getSymbol()->getType();
- if (T->isAnyPointerType() || T->isReferenceType())
- T = T->getPointeeType();
-
- R = GetElementZeroRegion(SR, T);
- }
+ // Binding directly to a symbolic region should be treated as binding
+ // to element 0.
+ if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R))
+ R = GetElementZeroRegion(SR, SR->getPointeeStaticType());
assert((!isa<CXXThisRegion>(R) || !B.lookup(R)) &&
"'this' pointer is not an l-value and is not assignable");
// Make sure that p4.x contains a symbol after copy.
if (p4.x > 0)
clang_analyzer_eval(p4.x > 0); // expected-warning{{TRUE}}
- // FIXME: Element region gets in the way, so these aren't the same symbols
- // as they should be.
- clang_analyzer_eval(pp.x == p4.x); // expected-warning{{UNKNOWN}}
+ clang_analyzer_eval(pp.x == p4.x); // expected-warning{{TRUE}}
PODWrapper w;
w.p.y = 1;
void test_field_dumps(struct S s, struct S *p) {
clang_analyzer_dump_pointer(&s.x); // expected-warning{{&s.x}}
- clang_analyzer_dump_pointer(&p->x); // expected-warning{{&SymRegion{reg_$1<struct S * p>}.x}}
+ clang_analyzer_dump_pointer(&p->x); // expected-warning{{&Element{SymRegion{reg_$1<struct S * p>},0 S64b,struct S}.x}}
clang_analyzer_dumpSvalType_pointer(&s.x); // expected-warning {{int *}}
}
}
void field_ptr(S *a) {
- clang_analyzer_dump(&a->f); // expected-warning {{SymRegion{reg_$0<S * a>}.f}}
+ clang_analyzer_dump(&a->f); // expected-warning {{Element{SymRegion{reg_$0<S * a>},0 S64b,struct S}.f}}
clang_analyzer_dumpExtent(&a->f); // expected-warning {{4 S64b}}
clang_analyzer_dumpElementCount(&a->f); // expected-warning {{1 S64b}}
}
void struct_pointer_canon(struct s *ps) {
struct s ss = *ps;
clang_analyzer_dump((*ps).v);
- // expected-warning-re@-1{{reg_${{[[:digit:]]+}}<int SymRegion{reg_${{[[:digit:]]+}}<struct s * ps>}.v>}}
+ // expected-warning-re@-1{{reg_${{[[:digit:]]+}}<int Element{SymRegion{reg_${{[[:digit:]]+}}<struct s * ps>},0 S64b,struct s}.v>}}
clang_analyzer_dump(ps[0].v);
- // expected-warning-re@-1{{reg_${{[[:digit:]]+}}<int SymRegion{reg_${{[[:digit:]]+}}<struct s * ps>}.v>}}
+ // expected-warning-re@-1{{reg_${{[[:digit:]]+}}<int Element{SymRegion{reg_${{[[:digit:]]+}}<struct s * ps>},0 S64b,struct s}.v>}}
clang_analyzer_dump(ps->v);
- // expected-warning-re@-1{{reg_${{[[:digit:]]+}}<int SymRegion{reg_${{[[:digit:]]+}}<struct s * ps>}.v>}}
+ // expected-warning-re@-1{{reg_${{[[:digit:]]+}}<int Element{SymRegion{reg_${{[[:digit:]]+}}<struct s * ps>},0 S64b,struct s}.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_typedef(T1 *ps) {
T2 ss = *ps;
clang_analyzer_dump((*ps).v);
- // expected-warning-re@-1{{reg_${{[[:digit:]]+}}<int SymRegion{reg_${{[[:digit:]]+}}<T1 * ps>}.v>}}
+ // expected-warning-re@-1{{reg_${{[[:digit:]]+}}<int Element{SymRegion{reg_${{[[:digit:]]+}}<T1 * ps>},0 S64b,struct s}.v>}}
clang_analyzer_dump(ps[0].v);
- // expected-warning-re@-1{{reg_${{[[:digit:]]+}}<int SymRegion{reg_${{[[:digit:]]+}}<T1 * ps>}.v>}}
+ // expected-warning-re@-1{{reg_${{[[:digit:]]+}}<int Element{SymRegion{reg_${{[[:digit:]]+}}<T1 * ps>},0 S64b,struct s}.v>}}
clang_analyzer_dump(ps->v);
- // expected-warning-re@-1{{reg_${{[[:digit:]]+}}<int SymRegion{reg_${{[[:digit:]]+}}<T1 * ps>}.v>}}
+ // expected-warning-re@-1{{reg_${{[[:digit:]]+}}<int Element{SymRegion{reg_${{[[:digit:]]+}}<T1 * ps>},0 S64b,struct s}.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_const(const struct s *ps) {
struct s ss = *ps;
clang_analyzer_dump((*ps).v);
- // expected-warning-re@-1{{reg_${{[[:digit:]]+}}<int SymRegion{reg_${{[[:digit:]]+}}<const struct s * ps>}.v>}}
+ // expected-warning-re@-1{{reg_${{[[:digit:]]+}}<int Element{SymRegion{reg_${{[[:digit:]]+}}<const struct s * ps>},0 S64b,struct s}.v>}}
clang_analyzer_dump(ps[0].v);
- // expected-warning-re@-1{{reg_${{[[:digit:]]+}}<int SymRegion{reg_${{[[:digit:]]+}}<const struct s * ps>}.v>}}
+ // expected-warning-re@-1{{reg_${{[[:digit:]]+}}<int Element{SymRegion{reg_${{[[:digit:]]+}}<const struct s * ps>},0 S64b,struct s}.v>}}
clang_analyzer_dump(ps->v);
- // expected-warning-re@-1{{reg_${{[[:digit:]]+}}<int SymRegion{reg_${{[[:digit:]]+}}<const struct s * ps>}.v>}}
+ // expected-warning-re@-1{{reg_${{[[:digit:]]+}}<int Element{SymRegion{reg_${{[[:digit:]]+}}<const struct s * ps>},0 S64b,struct s}.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}}
int parse(parse_t *p) {
unsigned copy = p->bits2;
clang_analyzer_dump(copy);
- // expected-warning@-1 {{reg_$1<unsigned int SymRegion{reg_$0<parse_t * p>}.bits2>}}
+ // expected-warning@-1 {{reg_$1<unsigned int Element{SymRegion{reg_$0<parse_t * p>},0 S64b,struct Bug_55934::parse_t}.bits2>}}
header *bits = (header *)©
clang_analyzer_dump(bits->b);
- // expected-warning@-1 {{derived_$2{reg_$1<unsigned int SymRegion{reg_$0<parse_t * p>}.bits2>,Element{copy,0 S64b,struct Bug_55934::header}.b}}}
+ // expected-warning@-1 {{derived_$2{reg_$1<unsigned int Element{SymRegion{reg_$0<parse_t * p>},0 S64b,struct Bug_55934::parse_t}.bits2>,Element{copy,0 S64b,struct Bug_55934::header}.b}}}
return bits->b; // no-warning
}
} // namespace Bug_55934
--- /dev/null
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -verify %s
+
+template <typename T> void clang_analyzer_dump(T);
+void clang_analyzer_warnIfReached();
+
+struct Node { int* ptr; };
+
+void copy_on_stack(Node* n1) {
+ Node tmp = *n1;
+ Node* n2 = &tmp;
+ clang_analyzer_dump(n1); // expected-warning-re {{&SymRegion{reg_${{[0-9]+}}<Node * n1>}}}
+ clang_analyzer_dump(n2); // expected-warning {{&tmp}}
+
+ clang_analyzer_dump(n1->ptr); // expected-warning-re {{&SymRegion{reg_${{[0-9]+}}<int * Element{SymRegion{reg_${{[0-9]+}}<Node * n1>},0 S64b,struct Node}.ptr>}}}
+ clang_analyzer_dump(n2->ptr); // expected-warning-re {{&SymRegion{reg_${{[0-9]+}}<int * Element{SymRegion{reg_${{[0-9]+}}<Node * n1>},0 S64b,struct Node}.ptr>}}}
+
+ if (n1->ptr != n2->ptr)
+ clang_analyzer_warnIfReached(); // unreachable
+ (void)(n1->ptr);
+ (void)(n2->ptr);
+}
+
+void copy_on_heap(Node* n1) {
+ Node* n2 = new Node(*n1);
+
+ clang_analyzer_dump(n1); // expected-warning-re {{&SymRegion{reg_${{[0-9]+}}<Node * n1>}}}
+ clang_analyzer_dump(n2); // expected-warning-re {{&HeapSymRegion{conj_${{[0-9]+}}{Node *, LC{{[0-9]+}}, S{{[0-9]+}}, #{{[0-9]+}}}}}}
+
+ clang_analyzer_dump(n1->ptr); // expected-warning-re {{&SymRegion{reg_${{[0-9]+}}<int * Element{SymRegion{reg_${{[0-9]+}}<Node * n1>},0 S64b,struct Node}.ptr>}}}
+ clang_analyzer_dump(n2->ptr); // expected-warning {{Unknown}} FIXME: This should be the same as above.
+
+ if (n1->ptr != n2->ptr)
+ clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}} FIXME: This should not be reachable.
+ (void)(n1->ptr);
+ (void)(n2->ptr);
+}