Skip to content

Commit 3f9fa7f

Browse files
author
git apple-llvm automerger
committed
Merge commit '3241ed8ee357' from apple/master into swift/master-next
2 parents b34aa4b + 3241ed8 commit 3f9fa7f

File tree

8 files changed

+168
-47
lines changed

8 files changed

+168
-47
lines changed

clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -869,6 +869,23 @@ class ExprEngine {
869869
void handleConstructor(const Expr *E, ExplodedNode *Pred,
870870
ExplodedNodeSet &Dst);
871871

872+
public:
873+
/// Note whether this loop has any more iteratios to model. These methods are
874+
/// essentially an interface for a GDM trait. Further reading in
875+
/// ExprEngine::VisitObjCForCollectionStmt().
876+
LLVM_NODISCARD static ProgramStateRef
877+
setWhetherHasMoreIteration(ProgramStateRef State,
878+
const ObjCForCollectionStmt *O,
879+
const LocationContext *LC, bool HasMoreIteraton);
880+
881+
LLVM_NODISCARD static ProgramStateRef
882+
removeIterationState(ProgramStateRef State, const ObjCForCollectionStmt *O,
883+
const LocationContext *LC);
884+
885+
LLVM_NODISCARD static bool hasMoreIteration(ProgramStateRef State,
886+
const ObjCForCollectionStmt *O,
887+
const LocationContext *LC);
888+
private:
872889
/// Store the location of a C++ object corresponding to a statement
873890
/// until the statement is actually encountered. For example, if a DeclStmt
874891
/// has CXXConstructExpr as its initializer, the object would be considered

clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -978,8 +978,7 @@ void ObjCLoopChecker::checkPostStmt(const ObjCForCollectionStmt *FCS,
978978
ProgramStateRef State = C.getState();
979979

980980
// Check if this is the branch for the end of the loop.
981-
SVal CollectionSentinel = C.getSVal(FCS);
982-
if (CollectionSentinel.isZeroConstant()) {
981+
if (!ExprEngine::hasMoreIteration(State, FCS, C.getLocationContext())) {
983982
if (!alreadyExecutedAtLeastOneLoopIteration(C.getPredecessor(), FCS))
984983
State = assumeCollectionNonEmpty(C, State, FCS, /*Assumption*/false);
985984

clang/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
//
1212
//===----------------------------------------------------------------------===//
1313

14+
#include "clang/AST/StmtObjC.h"
15+
#include "clang/AST/Type.h"
1416
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
1517
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
1618
#include "clang/StaticAnalyzer/Core/Checker.h"
@@ -54,10 +56,13 @@ class UndefBranchChecker : public Checker<check::BranchCondition> {
5456
void checkBranchCondition(const Stmt *Condition, CheckerContext &Ctx) const;
5557
};
5658

57-
}
59+
} // namespace
5860

5961
void UndefBranchChecker::checkBranchCondition(const Stmt *Condition,
6062
CheckerContext &Ctx) const {
63+
// ObjCForCollection is a loop, but has no actual condition.
64+
if (isa<ObjCForCollectionStmt>(Condition))
65+
return;
6166
SVal X = Ctx.getSVal(Condition);
6267
if (X.isUndef()) {
6368
// Generate a sink node, which implicitly marks both outgoing branches as

clang/lib/StaticAnalyzer/Core/Environment.cpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "clang/AST/ExprCXX.h"
1616
#include "clang/AST/PrettyPrinter.h"
1717
#include "clang/AST/Stmt.h"
18+
#include "clang/AST/StmtObjC.h"
1819
#include "clang/Analysis/AnalysisDeclContext.h"
1920
#include "clang/Basic/LLVM.h"
2021
#include "clang/Basic/LangOptions.h"
@@ -85,6 +86,12 @@ SVal Environment::lookupExpr(const EnvironmentEntry &E) const {
8586
SVal Environment::getSVal(const EnvironmentEntry &Entry,
8687
SValBuilder& svalBuilder) const {
8788
const Stmt *S = Entry.getStmt();
89+
assert(!isa<ObjCForCollectionStmt>(S) &&
90+
"Use ExprEngine::hasMoreIteration()!");
91+
assert((isa<Expr>(S) || isa<ReturnStmt>(S)) &&
92+
"Environment can only argue about Exprs, since only they express "
93+
"a value! Any non-expression statement stored in Environment is a "
94+
"result of a hack!");
8895
const LocationContext *LCtx = Entry.getLocationContext();
8996

9097
switch (S->getStmtClass()) {
@@ -188,7 +195,14 @@ EnvironmentManager::removeDeadBindings(Environment Env,
188195
const EnvironmentEntry &BlkExpr = I.getKey();
189196
const SVal &X = I.getData();
190197

191-
if (SymReaper.isLive(BlkExpr.getStmt(), BlkExpr.getLocationContext())) {
198+
const bool IsBlkExprLive =
199+
SymReaper.isLive(BlkExpr.getStmt(), BlkExpr.getLocationContext());
200+
201+
assert((isa<Expr>(BlkExpr.getStmt()) || !IsBlkExprLive) &&
202+
"Only Exprs can be live, LivenessAnalysis argues about the liveness "
203+
"of *values*!");
204+
205+
if (IsBlkExprLive) {
192206
// Copy the binding to the new map.
193207
EBMapRef = EBMapRef.add(BlkExpr, X);
194208

clang/lib/StaticAnalyzer/Core/ExprEngine.cpp

Lines changed: 91 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2129,6 +2129,83 @@ static const Stmt *ResolveCondition(const Stmt *Condition,
21292129
llvm_unreachable("could not resolve condition");
21302130
}
21312131

2132+
using ObjCForLctxPair =
2133+
std::pair<const ObjCForCollectionStmt *, const LocationContext *>;
2134+
2135+
REGISTER_MAP_WITH_PROGRAMSTATE(ObjCForHasMoreIterations, ObjCForLctxPair, bool)
2136+
2137+
ProgramStateRef ExprEngine::setWhetherHasMoreIteration(
2138+
ProgramStateRef State, const ObjCForCollectionStmt *O,
2139+
const LocationContext *LC, bool HasMoreIteraton) {
2140+
assert(!State->contains<ObjCForHasMoreIterations>({O, LC}));
2141+
return State->set<ObjCForHasMoreIterations>({O, LC}, HasMoreIteraton);
2142+
}
2143+
2144+
ProgramStateRef
2145+
ExprEngine::removeIterationState(ProgramStateRef State,
2146+
const ObjCForCollectionStmt *O,
2147+
const LocationContext *LC) {
2148+
assert(State->contains<ObjCForHasMoreIterations>({O, LC}));
2149+
return State->remove<ObjCForHasMoreIterations>({O, LC});
2150+
}
2151+
2152+
bool ExprEngine::hasMoreIteration(ProgramStateRef State,
2153+
const ObjCForCollectionStmt *O,
2154+
const LocationContext *LC) {
2155+
assert(State->contains<ObjCForHasMoreIterations>({O, LC}));
2156+
return *State->get<ObjCForHasMoreIterations>({O, LC});
2157+
}
2158+
2159+
/// Split the state on whether there are any more iterations left for this loop.
2160+
/// Returns a (HasMoreIteration, HasNoMoreIteration) pair, or None when the
2161+
/// acquisition of the loop condition value failed.
2162+
static Optional<std::pair<ProgramStateRef, ProgramStateRef>>
2163+
assumeCondition(const Stmt *Condition, ExplodedNode *N) {
2164+
ProgramStateRef State = N->getState();
2165+
if (const auto *ObjCFor = dyn_cast<ObjCForCollectionStmt>(Condition)) {
2166+
bool HasMoreIteraton =
2167+
ExprEngine::hasMoreIteration(State, ObjCFor, N->getLocationContext());
2168+
// Checkers have already ran on branch conditions, so the current
2169+
// information as to whether the loop has more iteration becomes outdated
2170+
// after this point.
2171+
State = ExprEngine::removeIterationState(State, ObjCFor,
2172+
N->getLocationContext());
2173+
if (HasMoreIteraton)
2174+
return std::pair<ProgramStateRef, ProgramStateRef>{State, nullptr};
2175+
else
2176+
return std::pair<ProgramStateRef, ProgramStateRef>{nullptr, State};
2177+
}
2178+
SVal X = State->getSVal(Condition, N->getLocationContext());
2179+
2180+
if (X.isUnknownOrUndef()) {
2181+
// Give it a chance to recover from unknown.
2182+
if (const auto *Ex = dyn_cast<Expr>(Condition)) {
2183+
if (Ex->getType()->isIntegralOrEnumerationType()) {
2184+
// Try to recover some path-sensitivity. Right now casts of symbolic
2185+
// integers that promote their values are currently not tracked well.
2186+
// If 'Condition' is such an expression, try and recover the
2187+
// underlying value and use that instead.
2188+
SVal recovered =
2189+
RecoverCastedSymbol(State, Condition, N->getLocationContext(),
2190+
N->getState()->getStateManager().getContext());
2191+
2192+
if (!recovered.isUnknown()) {
2193+
X = recovered;
2194+
}
2195+
}
2196+
}
2197+
}
2198+
2199+
// If the condition is still unknown, give up.
2200+
if (X.isUnknownOrUndef())
2201+
return None;
2202+
2203+
DefinedSVal V = X.castAs<DefinedSVal>();
2204+
2205+
ProgramStateRef StTrue, StFalse;
2206+
return State->assume(V);
2207+
}
2208+
21322209
void ExprEngine::processBranch(const Stmt *Condition,
21332210
NodeBuilderContext& BldCtx,
21342211
ExplodedNode *Pred,
@@ -2165,56 +2242,36 @@ void ExprEngine::processBranch(const Stmt *Condition,
21652242
return;
21662243

21672244
BranchNodeBuilder builder(CheckersOutSet, Dst, BldCtx, DstT, DstF);
2168-
for (const auto PredI : CheckersOutSet) {
2169-
if (PredI->isSink())
2245+
for (ExplodedNode *PredN : CheckersOutSet) {
2246+
if (PredN->isSink())
21702247
continue;
21712248

2172-
ProgramStateRef PrevState = PredI->getState();
2173-
SVal X = PrevState->getSVal(Condition, PredI->getLocationContext());
2174-
2175-
if (X.isUnknownOrUndef()) {
2176-
// Give it a chance to recover from unknown.
2177-
if (const auto *Ex = dyn_cast<Expr>(Condition)) {
2178-
if (Ex->getType()->isIntegralOrEnumerationType()) {
2179-
// Try to recover some path-sensitivity. Right now casts of symbolic
2180-
// integers that promote their values are currently not tracked well.
2181-
// If 'Condition' is such an expression, try and recover the
2182-
// underlying value and use that instead.
2183-
SVal recovered = RecoverCastedSymbol(PrevState, Condition,
2184-
PredI->getLocationContext(),
2185-
getContext());
2186-
2187-
if (!recovered.isUnknown()) {
2188-
X = recovered;
2189-
}
2190-
}
2191-
}
2192-
}
2249+
ProgramStateRef PrevState = PredN->getState();
21932250

2194-
// If the condition is still unknown, give up.
2195-
if (X.isUnknownOrUndef()) {
2196-
builder.generateNode(PrevState, true, PredI);
2197-
builder.generateNode(PrevState, false, PredI);
2251+
ProgramStateRef StTrue, StFalse;
2252+
if (const auto KnownCondValueAssumption = assumeCondition(Condition, PredN))
2253+
std::tie(StTrue, StFalse) = *KnownCondValueAssumption;
2254+
else {
2255+
assert(!isa<ObjCForCollectionStmt>(Condition));
2256+
builder.generateNode(PrevState, true, PredN);
2257+
builder.generateNode(PrevState, false, PredN);
21982258
continue;
21992259
}
2200-
2201-
DefinedSVal V = X.castAs<DefinedSVal>();
2202-
2203-
ProgramStateRef StTrue, StFalse;
2204-
std::tie(StTrue, StFalse) = PrevState->assume(V);
2260+
if (StTrue && StFalse)
2261+
assert(!isa<ObjCForCollectionStmt>(Condition));;
22052262

22062263
// Process the true branch.
22072264
if (builder.isFeasible(true)) {
22082265
if (StTrue)
2209-
builder.generateNode(StTrue, true, PredI);
2266+
builder.generateNode(StTrue, true, PredN);
22102267
else
22112268
builder.markInfeasible(true);
22122269
}
22132270

22142271
// Process the false branch.
22152272
if (builder.isFeasible(false)) {
22162273
if (StFalse)
2217-
builder.generateNode(StFalse, false, PredI);
2274+
builder.generateNode(StFalse, false, PredN);
22182275
else
22192276
builder.markInfeasible(false);
22202277
}

clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,8 @@ static void populateObjCForDestinationSet(
5353
ProgramStateRef state = Pred->getState();
5454
const LocationContext *LCtx = Pred->getLocationContext();
5555

56-
SVal hasElementsV = svalBuilder.makeTruthVal(hasElements);
57-
58-
// FIXME: S is not an expression. We should not be binding values to it.
59-
ProgramStateRef nextState = state->BindExpr(S, LCtx, hasElementsV);
56+
ProgramStateRef nextState =
57+
ExprEngine::setWhetherHasMoreIteration(state, S, LCtx, hasElements);
6058

6159
if (auto MV = elementV.getAs<loc::MemRegionVal>())
6260
if (const auto *R = dyn_cast<TypedValueRegion>(MV->getRegion())) {
@@ -93,10 +91,9 @@ void ExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S,
9391
// (1) binds the next container value to 'element'. This creates a new
9492
// node in the ExplodedGraph.
9593
//
96-
// (2) binds the value 0/1 to the ObjCForCollectionStmt* itself, indicating
97-
// whether or not the container has any more elements. This value
98-
// will be tested in ProcessBranch. We need to explicitly bind
99-
// this value because a container can contain nil elements.
94+
// (2) note whether the collection has any more elements (or in other words,
95+
// whether the loop has more iterations). This will be tested in
96+
// processBranch.
10097
//
10198
// FIXME: Eventually this logic should actually do dispatches to
10299
// 'countByEnumeratingWithState:objects:count:' (NSFastEnumeration).

clang/lib/StaticAnalyzer/Core/SymbolManager.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
1515
#include "clang/AST/ASTContext.h"
1616
#include "clang/AST/Expr.h"
17+
#include "clang/AST/StmtObjC.h"
1718
#include "clang/Analysis/Analyses/LiveVariables.h"
1819
#include "clang/Analysis/AnalysisDeclContext.h"
1920
#include "clang/Basic/LLVM.h"
@@ -494,7 +495,8 @@ SymbolReaper::isLive(const Stmt *ExprVal, const LocationContext *ELCtx) const {
494495
return true;
495496
}
496497

497-
// If no statement is provided, everything is this and parent contexts is live.
498+
// If no statement is provided, everything in this and parent contexts is
499+
// live.
498500
if (!Loc)
499501
return true;
500502

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// RUN: %clang --analyze %s -fblocks
2+
3+
// https://reviews.llvm.org/D82598#2171312
4+
5+
@interface Item
6+
// ...
7+
@end
8+
9+
@interface Collection
10+
// ...
11+
@end
12+
13+
typedef void (^Blk)();
14+
15+
struct RAII {
16+
Blk blk;
17+
18+
public:
19+
RAII(Blk blk): blk(blk) {}
20+
~RAII() { blk(); }
21+
};
22+
23+
void foo(Collection *coll) {
24+
RAII raii(^{});
25+
for (Item *item in coll) {}
26+
int i;
27+
{
28+
int j;
29+
}
30+
}

0 commit comments

Comments
 (0)