From fe03e40d837ed2291de7f667d760709358f50c1f Mon Sep 17 00:00:00 2001 From: Jordan Rose Date: Thu, 21 Feb 2013 23:57:17 +0000 Subject: [PATCH] [analyzer] Make sure a temporary object region matches its initial bindings. When creating a temporary region (say, when a struct rvalue is used as the base of a member expr), make sure we account for any derived-to-base casts. We don't actually record these in the LazyCompoundVal that represents the rvalue, but we need to make sure that the temporary region we're creating (a) matches the bindings, and (b) matches its expression. Most of the time this will do exactly the same thing as before, but it fixes spurious "garbage value" warnings introduced in r175234 by the use of lazy bindings to model trivial copy constructors. llvm-svn: 175830 --- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 35 ++++++++++++++++++++++++---- clang/test/Analysis/temporaries.cpp | 26 +++++++++++++++++++++ 2 files changed, 57 insertions(+), 4 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index 06b1db5..60fd8d0 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -173,10 +173,37 @@ static ProgramStateRef createTemporaryRegionIfNeeded(ProgramStateRef State, SVal V = State->getSVal(E, LC); if (V.getAs()) { - MemRegionManager &MRMgr = State->getStateManager().getRegionManager(); - const MemRegion *R = MRMgr.getCXXTempObjectRegion(E, LC); - State = State->bindLoc(loc::MemRegionVal(R), V); - State = State->BindExpr(E, LC, loc::MemRegionVal(R)); + ProgramStateManager &StateMgr = State->getStateManager(); + MemRegionManager &MRMgr = StateMgr.getRegionManager(); + StoreManager &StoreMgr = StateMgr.getStoreManager(); + + // We need to be careful about treating a derived type's value as + // bindings for a base type. Start by stripping and recording base casts. + SmallVector Casts; + const Expr *Inner = E->IgnoreParens(); + while (const CastExpr *CE = dyn_cast(Inner)) { + if (CE->getCastKind() == CK_DerivedToBase || + CE->getCastKind() == CK_UncheckedDerivedToBase) + Casts.push_back(CE); + else if (CE->getCastKind() != CK_NoOp) + break; + + Inner = CE->getSubExpr()->IgnoreParens(); + } + + // Create a temporary object region for the inner expression (which may have + // a more derived type) and bind the NonLoc value into it. + SVal Reg = loc::MemRegionVal(MRMgr.getCXXTempObjectRegion(Inner, LC)); + State = State->bindLoc(Reg, V); + + // Re-apply the casts (from innermost to outermost) for type sanity. + for (SmallVectorImpl::reverse_iterator I = Casts.rbegin(), + E = Casts.rend(); + I != E; ++I) { + Reg = StoreMgr.evalDerivedToBase(Reg, *I); + } + + State = State->BindExpr(E, LC, Reg); } return State; diff --git a/clang/test/Analysis/temporaries.cpp b/clang/test/Analysis/temporaries.cpp index e885878..8d0dd6e 100644 --- a/clang/test/Analysis/temporaries.cpp +++ b/clang/test/Analysis/temporaries.cpp @@ -1,5 +1,7 @@ // RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -verify -w %s +extern bool clang_analyzer_eval(bool); + struct Trivial { Trivial(int x) : value(x) {} int value; @@ -28,3 +30,27 @@ const NonTrivial &getNonTrivialRef() { return NonTrivial(42); // expected-warning {{Address of stack memory associated with temporary object of type 'const struct NonTrivial' returned to caller}} } +namespace rdar13265460 { + struct TrivialSubclass : public Trivial { + TrivialSubclass(int x) : Trivial(x), anotherValue(-x) {} + int anotherValue; + }; + + TrivialSubclass getTrivialSub() { + TrivialSubclass obj(1); + obj.value = 42; + obj.anotherValue = -42; + return obj; + } + + void test() { + TrivialSubclass obj = getTrivialSub(); + + clang_analyzer_eval(obj.value == 42); // expected-warning{{TRUE}} + clang_analyzer_eval(obj.anotherValue == -42); // expected-warning{{TRUE}} + + clang_analyzer_eval(getTrivialSub().value == 42); // expected-warning{{TRUE}} + clang_analyzer_eval(getTrivialSub().anotherValue == -42); // expected-warning{{TRUE}} + } +} + -- 2.7.4