1 //= ProgramState.cpp - Path-Sensitive "State" for tracking values --*- C++ -*--=
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // This file implements ProgramState and ProgramStateManager.
11 //===----------------------------------------------------------------------===//
13 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
14 #include "clang/Analysis/CFG.h"
15 #include "clang/Basic/JsonSupport.h"
16 #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
17 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
18 #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
19 #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
20 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
21 #include "llvm/Support/raw_ostream.h"
24 using namespace clang
;
27 namespace clang
{ namespace ento
{
28 /// Increments the number of times this state is referenced.
30 void ProgramStateRetain(const ProgramState
*state
) {
31 ++const_cast<ProgramState
*>(state
)->refCount
;
34 /// Decrement the number of times this state is referenced.
35 void ProgramStateRelease(const ProgramState
*state
) {
36 assert(state
->refCount
> 0);
37 ProgramState
*s
= const_cast<ProgramState
*>(state
);
38 if (--s
->refCount
== 0) {
39 ProgramStateManager
&Mgr
= s
->getStateManager();
40 Mgr
.StateSet
.RemoveNode(s
);
42 Mgr
.freeStates
.push_back(s
);
47 ProgramState::ProgramState(ProgramStateManager
*mgr
, const Environment
& env
,
48 StoreRef st
, GenericDataMap gdm
)
54 stateMgr
->getStoreManager().incrementReferenceCount(store
);
57 ProgramState::ProgramState(const ProgramState
&RHS
)
58 : stateMgr(RHS
.stateMgr
), Env(RHS
.Env
), store(RHS
.store
), GDM(RHS
.GDM
),
59 PosteriorlyOverconstrained(RHS
.PosteriorlyOverconstrained
), refCount(0) {
60 stateMgr
->getStoreManager().incrementReferenceCount(store
);
63 ProgramState::~ProgramState() {
65 stateMgr
->getStoreManager().decrementReferenceCount(store
);
68 int64_t ProgramState::getID() const {
69 return getStateManager().Alloc
.identifyKnownAlignedObject
<ProgramState
>(this);
72 ProgramStateManager::ProgramStateManager(ASTContext
&Ctx
,
73 StoreManagerCreator CreateSMgr
,
74 ConstraintManagerCreator CreateCMgr
,
75 llvm::BumpPtrAllocator
&alloc
,
77 : Eng(ExprEng
), EnvMgr(alloc
), GDMFactory(alloc
),
78 svalBuilder(createSimpleSValBuilder(alloc
, Ctx
, *this)),
79 CallEventMgr(new CallEventManager(alloc
)), Alloc(alloc
) {
80 StoreMgr
= (*CreateSMgr
)(*this);
81 ConstraintMgr
= (*CreateCMgr
)(*this, ExprEng
);
85 ProgramStateManager::~ProgramStateManager() {
86 for (GDMContextsTy::iterator I
=GDMContexts
.begin(), E
=GDMContexts
.end();
88 I
->second
.second(I
->second
.first
);
91 ProgramStateRef
ProgramStateManager::removeDeadBindingsFromEnvironmentAndStore(
92 ProgramStateRef state
, const StackFrameContext
*LCtx
,
93 SymbolReaper
&SymReaper
) {
95 // This code essentially performs a "mark-and-sweep" of the VariableBindings.
96 // The roots are any Block-level exprs and Decls that our liveness algorithm
97 // tells us are live. We then see what Decls they may reference, and keep
98 // those around. This code more than likely can be made faster, and the
99 // frequency of which this method is called should be experimented with
100 // for optimum performance.
101 ProgramState NewState
= *state
;
103 NewState
.Env
= EnvMgr
.removeDeadBindings(NewState
.Env
, SymReaper
, state
);
105 // Clean up the store.
106 StoreRef newStore
= StoreMgr
->removeDeadBindings(NewState
.getStore(), LCtx
,
108 NewState
.setStore(newStore
);
109 SymReaper
.setReapedStore(newStore
);
111 return getPersistentState(NewState
);
114 ProgramStateRef
ProgramState::bindLoc(Loc LV
,
116 const LocationContext
*LCtx
,
117 bool notifyChanges
) const {
118 ProgramStateManager
&Mgr
= getStateManager();
119 ProgramStateRef newState
= makeWithStore(Mgr
.StoreMgr
->Bind(getStore(),
121 const MemRegion
*MR
= LV
.getAsRegion();
122 if (MR
&& notifyChanges
)
123 return Mgr
.getOwningEngine().processRegionChange(newState
, MR
, LCtx
);
129 ProgramState::bindDefaultInitial(SVal loc
, SVal V
,
130 const LocationContext
*LCtx
) const {
131 ProgramStateManager
&Mgr
= getStateManager();
132 const MemRegion
*R
= loc
.castAs
<loc::MemRegionVal
>().getRegion();
133 const StoreRef
&newStore
= Mgr
.StoreMgr
->BindDefaultInitial(getStore(), R
, V
);
134 ProgramStateRef new_state
= makeWithStore(newStore
);
135 return Mgr
.getOwningEngine().processRegionChange(new_state
, R
, LCtx
);
139 ProgramState::bindDefaultZero(SVal loc
, const LocationContext
*LCtx
) const {
140 ProgramStateManager
&Mgr
= getStateManager();
141 const MemRegion
*R
= loc
.castAs
<loc::MemRegionVal
>().getRegion();
142 const StoreRef
&newStore
= Mgr
.StoreMgr
->BindDefaultZero(getStore(), R
);
143 ProgramStateRef new_state
= makeWithStore(newStore
);
144 return Mgr
.getOwningEngine().processRegionChange(new_state
, R
, LCtx
);
147 typedef ArrayRef
<const MemRegion
*> RegionList
;
148 typedef ArrayRef
<SVal
> ValueList
;
150 ProgramStateRef
ProgramState::invalidateRegions(
151 RegionList Regions
, const Stmt
*S
, unsigned Count
,
152 const LocationContext
*LCtx
, bool CausedByPointerEscape
,
153 InvalidatedSymbols
*IS
, const CallEvent
*Call
,
154 RegionAndSymbolInvalidationTraits
*ITraits
) const {
155 SmallVector
<SVal
, 8> Values
;
156 for (const MemRegion
*Reg
: Regions
)
157 Values
.push_back(loc::MemRegionVal(Reg
));
159 return invalidateRegions(Values
, S
, Count
, LCtx
, CausedByPointerEscape
, IS
,
163 ProgramStateRef
ProgramState::invalidateRegions(
164 ValueList Values
, const Stmt
*S
, unsigned Count
,
165 const LocationContext
*LCtx
, bool CausedByPointerEscape
,
166 InvalidatedSymbols
*IS
, const CallEvent
*Call
,
167 RegionAndSymbolInvalidationTraits
*ITraits
) const {
169 ProgramStateManager
&Mgr
= getStateManager();
170 ExprEngine
&Eng
= Mgr
.getOwningEngine();
172 InvalidatedSymbols InvalidatedSyms
;
174 IS
= &InvalidatedSyms
;
176 RegionAndSymbolInvalidationTraits ITraitsLocal
;
178 ITraits
= &ITraitsLocal
;
180 StoreManager::InvalidatedRegions TopLevelInvalidated
;
181 StoreManager::InvalidatedRegions Invalidated
;
182 const StoreRef
&NewStore
= Mgr
.StoreMgr
->invalidateRegions(
183 getStore(), Values
, S
, Count
, LCtx
, Call
, *IS
, *ITraits
,
184 &TopLevelInvalidated
, &Invalidated
);
186 ProgramStateRef NewState
= makeWithStore(NewStore
);
188 if (CausedByPointerEscape
) {
189 NewState
= Eng
.notifyCheckersOfPointerEscape(
190 NewState
, IS
, TopLevelInvalidated
, Call
, *ITraits
);
193 return Eng
.processRegionChanges(NewState
, IS
, TopLevelInvalidated
,
194 Invalidated
, LCtx
, Call
);
197 ProgramStateRef
ProgramState::killBinding(Loc LV
) const {
198 Store OldStore
= getStore();
199 const StoreRef
&newStore
=
200 getStateManager().StoreMgr
->killBinding(OldStore
, LV
);
202 if (newStore
.getStore() == OldStore
)
205 return makeWithStore(newStore
);
208 /// We should never form a MemRegion that would wrap a TypedValueRegion of a
209 /// reference type. What we actually wanted was to create a MemRegion refering
210 /// to the pointee of that reference.
211 SVal
ProgramState::desugarReference(SVal Val
) const {
212 const auto *TyReg
= dyn_cast_or_null
<TypedValueRegion
>(Val
.getAsRegion());
213 if (!TyReg
|| !TyReg
->getValueType()->isReferenceType())
215 return getSVal(TyReg
);
218 /// SymbolicRegions are expected to be wrapped by an ElementRegion as a
219 /// canonical representation. As a canonical representation, SymbolicRegions
220 /// should be wrapped by ElementRegions before getting a FieldRegion.
221 /// See f8643a9b31c4029942f67d4534c9139b45173504 why.
222 SVal
ProgramState::wrapSymbolicRegion(SVal Val
) const {
223 const auto *BaseReg
= dyn_cast_or_null
<SymbolicRegion
>(Val
.getAsRegion());
227 StoreManager
&SM
= getStateManager().getStoreManager();
228 QualType ElemTy
= BaseReg
->getPointeeStaticType();
229 return loc::MemRegionVal
{SM
.GetElementZeroRegion(BaseReg
, ElemTy
)};
233 ProgramState::enterStackFrame(const CallEvent
&Call
,
234 const StackFrameContext
*CalleeCtx
) const {
235 const StoreRef
&NewStore
=
236 getStateManager().StoreMgr
->enterStackFrame(getStore(), Call
, CalleeCtx
);
237 return makeWithStore(NewStore
);
240 SVal
ProgramState::getSelfSVal(const LocationContext
*LCtx
) const {
241 const ImplicitParamDecl
*SelfDecl
= LCtx
->getSelfDecl();
244 return getSVal(getRegion(SelfDecl
, LCtx
));
247 SVal
ProgramState::getSValAsScalarOrLoc(const MemRegion
*R
) const {
248 // We only want to do fetches from regions that we can actually bind
249 // values. For example, SymbolicRegions of type 'id<...>' cannot
250 // have direct bindings (but their can be bindings on their subregions).
251 if (!R
->isBoundable())
254 if (const TypedValueRegion
*TR
= dyn_cast
<TypedValueRegion
>(R
)) {
255 QualType T
= TR
->getValueType();
256 if (Loc::isLocType(T
) || T
->isIntegralOrEnumerationType())
263 SVal
ProgramState::getSVal(Loc location
, QualType T
) const {
264 SVal V
= getRawSVal(location
, T
);
266 // If 'V' is a symbolic value that is *perfectly* constrained to
267 // be a constant value, use that value instead to lessen the burden
268 // on later analysis stages (so we have less symbolic values to reason
270 // We only go into this branch if we can convert the APSInt value we have
271 // to the type of T, which is not always the case (e.g. for void).
272 if (!T
.isNull() && (T
->isIntegralOrEnumerationType() || Loc::isLocType(T
))) {
273 if (SymbolRef sym
= V
.getAsSymbol()) {
274 if (const llvm::APSInt
*Int
= getStateManager()
275 .getConstraintManager()
276 .getSymVal(this, sym
)) {
277 // FIXME: Because we don't correctly model (yet) sign-extension
278 // and truncation of symbolic values, we need to convert
279 // the integer value to the correct signedness and bitwidth.
281 // This shows up in the following:
284 // unsigned x = foo();
288 // The symbolic value stored to 'x' is actually the conjured
289 // symbol for the call to foo(); the type of that symbol is 'char',
291 APSIntPtr NewV
= getBasicVals().Convert(T
, *Int
);
293 return loc::ConcreteInt(NewV
);
294 return nonloc::ConcreteInt(NewV
);
302 ProgramStateRef
ProgramState::BindExpr(const Stmt
*S
,
303 const LocationContext
*LCtx
,
304 SVal V
, bool Invalidate
) const{
306 getStateManager().EnvMgr
.bindExpr(Env
, EnvironmentEntry(S
, LCtx
), V
,
311 ProgramState NewSt
= *this;
313 return getStateManager().getPersistentState(NewSt
);
316 [[nodiscard
]] std::pair
<ProgramStateRef
, ProgramStateRef
>
317 ProgramState::assumeInBoundDual(DefinedOrUnknownSVal Idx
,
318 DefinedOrUnknownSVal UpperBound
,
319 QualType indexTy
) const {
320 if (Idx
.isUnknown() || UpperBound
.isUnknown())
323 // Build an expression for 0 <= Idx < UpperBound.
324 // This is the same as Idx + MIN < UpperBound + MIN, if overflow is allowed.
325 // FIXME: This should probably be part of SValBuilder.
326 ProgramStateManager
&SM
= getStateManager();
327 SValBuilder
&svalBuilder
= SM
.getSValBuilder();
328 ASTContext
&Ctx
= svalBuilder
.getContext();
330 // Get the offset: the minimum value of the array index type.
331 BasicValueFactory
&BVF
= svalBuilder
.getBasicValueFactory();
332 if (indexTy
.isNull())
333 indexTy
= svalBuilder
.getArrayIndexType();
334 nonloc::ConcreteInt
Min(BVF
.getMinValue(indexTy
));
337 SVal newIdx
= svalBuilder
.evalBinOpNN(this, BO_Add
,
338 Idx
.castAs
<NonLoc
>(), Min
, indexTy
);
339 if (newIdx
.isUnknownOrUndef())
342 // Adjust the upper bound.
344 svalBuilder
.evalBinOpNN(this, BO_Add
, UpperBound
.castAs
<NonLoc
>(),
347 if (newBound
.isUnknownOrUndef())
350 // Build the actual comparison.
351 SVal inBound
= svalBuilder
.evalBinOpNN(this, BO_LT
, newIdx
.castAs
<NonLoc
>(),
352 newBound
.castAs
<NonLoc
>(), Ctx
.IntTy
);
353 if (inBound
.isUnknownOrUndef())
356 // Finally, let the constraint manager take care of it.
357 ConstraintManager
&CM
= SM
.getConstraintManager();
358 return CM
.assumeDual(this, inBound
.castAs
<DefinedSVal
>());
361 ProgramStateRef
ProgramState::assumeInBound(DefinedOrUnknownSVal Idx
,
362 DefinedOrUnknownSVal UpperBound
,
364 QualType indexTy
) const {
365 std::pair
<ProgramStateRef
, ProgramStateRef
> R
=
366 assumeInBoundDual(Idx
, UpperBound
, indexTy
);
367 return Assumption
? R
.first
: R
.second
;
370 ConditionTruthVal
ProgramState::isNonNull(SVal V
) const {
371 ConditionTruthVal IsNull
= isNull(V
);
372 if (IsNull
.isUnderconstrained())
374 return ConditionTruthVal(!IsNull
.getValue());
377 ConditionTruthVal
ProgramState::areEqual(SVal Lhs
, SVal Rhs
) const {
378 return stateMgr
->getSValBuilder().areEqual(this, Lhs
, Rhs
);
381 ConditionTruthVal
ProgramState::isNull(SVal V
) const {
382 if (V
.isZeroConstant())
388 SymbolRef Sym
= V
.getAsSymbol(/* IncludeBaseRegion */ true);
390 return ConditionTruthVal();
392 return getStateManager().ConstraintMgr
->isNull(this, Sym
);
395 ProgramStateRef
ProgramStateManager::getInitialState(const LocationContext
*InitLoc
) {
396 ProgramState
State(this,
397 EnvMgr
.getInitialEnvironment(),
398 StoreMgr
->getInitialStore(InitLoc
),
399 GDMFactory
.getEmptyMap());
401 return getPersistentState(State
);
404 ProgramStateRef
ProgramStateManager::getPersistentStateWithGDM(
405 ProgramStateRef FromState
,
406 ProgramStateRef GDMState
) {
407 ProgramState
NewState(*FromState
);
408 NewState
.GDM
= GDMState
->GDM
;
409 return getPersistentState(NewState
);
412 ProgramStateRef
ProgramStateManager::getPersistentState(ProgramState
&State
) {
414 llvm::FoldingSetNodeID ID
;
418 if (ProgramState
*I
= StateSet
.FindNodeOrInsertPos(ID
, InsertPos
))
421 ProgramState
*newState
= nullptr;
422 if (!freeStates
.empty()) {
423 newState
= freeStates
.back();
424 freeStates
.pop_back();
427 newState
= Alloc
.Allocate
<ProgramState
>();
429 new (newState
) ProgramState(State
);
430 StateSet
.InsertNode(newState
, InsertPos
);
434 ProgramStateRef
ProgramState::makeWithStore(const StoreRef
&store
) const {
435 ProgramState
NewSt(*this);
436 NewSt
.setStore(store
);
437 return getStateManager().getPersistentState(NewSt
);
440 ProgramStateRef
ProgramState::cloneAsPosteriorlyOverconstrained() const {
441 ProgramState
NewSt(*this);
442 NewSt
.PosteriorlyOverconstrained
= true;
443 return getStateManager().getPersistentState(NewSt
);
446 void ProgramState::setStore(const StoreRef
&newStore
) {
447 Store newStoreStore
= newStore
.getStore();
449 stateMgr
->getStoreManager().incrementReferenceCount(newStoreStore
);
451 stateMgr
->getStoreManager().decrementReferenceCount(store
);
452 store
= newStoreStore
;
455 SVal
ProgramState::getLValue(const FieldDecl
*D
, SVal Base
) const {
456 Base
= desugarReference(Base
);
457 Base
= wrapSymbolicRegion(Base
);
458 return getStateManager().StoreMgr
->getLValueField(D
, Base
);
461 SVal
ProgramState::getLValue(const IndirectFieldDecl
*D
, SVal Base
) const {
462 StoreManager
&SM
= *getStateManager().StoreMgr
;
463 Base
= desugarReference(Base
);
464 Base
= wrapSymbolicRegion(Base
);
466 // FIXME: This should work with `SM.getLValueField(D->getAnonField(), Base)`,
467 // but that would break some tests. There is probably a bug somewhere that it
469 for (const auto *I
: D
->chain()) {
470 Base
= SM
.getLValueField(cast
<FieldDecl
>(I
), Base
);
475 //===----------------------------------------------------------------------===//
476 // State pretty-printing.
477 //===----------------------------------------------------------------------===//
479 void ProgramState::printJson(raw_ostream
&Out
, const LocationContext
*LCtx
,
480 const char *NL
, unsigned int Space
,
482 Indent(Out
, Space
, IsDot
) << "\"program_state\": {" << NL
;
485 ProgramStateManager
&Mgr
= getStateManager();
488 Mgr
.getStoreManager().printJson(Out
, getStore(), NL
, Space
, IsDot
);
490 // Print out the environment.
491 Env
.printJson(Out
, Mgr
.getContext(), LCtx
, NL
, Space
, IsDot
);
493 // Print out the constraints.
494 Mgr
.getConstraintManager().printJson(Out
, this, NL
, Space
, IsDot
);
496 // Print out the tracked dynamic types.
497 printDynamicTypeInfoJson(Out
, this, NL
, Space
, IsDot
);
499 // Print checker-specific data.
500 Mgr
.getOwningEngine().printJson(Out
, this, LCtx
, NL
, Space
, IsDot
);
503 Indent(Out
, Space
, IsDot
) << '}';
506 void ProgramState::printDOT(raw_ostream
&Out
, const LocationContext
*LCtx
,
507 unsigned int Space
) const {
508 printJson(Out
, LCtx
, /*NL=*/"\\l", Space
, /*IsDot=*/true);
511 LLVM_DUMP_METHOD
void ProgramState::dump() const {
512 printJson(llvm::errs());
515 AnalysisManager
& ProgramState::getAnalysisManager() const {
516 return stateMgr
->getOwningEngine().getAnalysisManager();
519 //===----------------------------------------------------------------------===//
521 //===----------------------------------------------------------------------===//
523 void *const* ProgramState::FindGDM(void *K
) const {
524 return GDM
.lookup(K
);
528 ProgramStateManager::FindGDMContext(void *K
,
529 void *(*CreateContext
)(llvm::BumpPtrAllocator
&),
530 void (*DeleteContext
)(void*)) {
532 std::pair
<void*, void (*)(void*)>& p
= GDMContexts
[K
];
534 p
.first
= CreateContext(Alloc
);
535 p
.second
= DeleteContext
;
541 ProgramStateRef
ProgramStateManager::addGDM(ProgramStateRef St
, void *Key
, void *Data
){
542 ProgramState::GenericDataMap M1
= St
->getGDM();
543 ProgramState::GenericDataMap M2
= GDMFactory
.add(M1
, Key
, Data
);
548 ProgramState NewSt
= *St
;
550 return getPersistentState(NewSt
);
553 ProgramStateRef
ProgramStateManager::removeGDM(ProgramStateRef state
, void *Key
) {
554 ProgramState::GenericDataMap OldM
= state
->getGDM();
555 ProgramState::GenericDataMap NewM
= GDMFactory
.remove(OldM
, Key
);
560 ProgramState NewState
= *state
;
562 return getPersistentState(NewState
);
565 bool ScanReachableSymbols::scan(nonloc::LazyCompoundVal val
) {
566 bool wasVisited
= !visited
.insert(val
.getCVData()).second
;
570 StoreManager
&StoreMgr
= state
->getStateManager().getStoreManager();
571 // FIXME: We don't really want to use getBaseRegion() here because pointer
572 // arithmetic doesn't apply, but scanReachableSymbols only accepts base
573 // regions right now.
574 const MemRegion
*R
= val
.getRegion()->getBaseRegion();
575 return StoreMgr
.scanReachableSymbols(val
.getStore(), R
, *this);
578 bool ScanReachableSymbols::scan(nonloc::CompoundVal val
) {
586 bool ScanReachableSymbols::scan(const SymExpr
*sym
) {
587 for (SymbolRef SubSym
: sym
->symbols()) {
588 bool wasVisited
= !visited
.insert(SubSym
).second
;
592 if (!visitor
.VisitSymbol(SubSym
))
599 bool ScanReachableSymbols::scan(SVal val
) {
600 if (std::optional
<loc::MemRegionVal
> X
= val
.getAs
<loc::MemRegionVal
>())
601 return scan(X
->getRegion());
603 if (std::optional
<nonloc::LazyCompoundVal
> X
=
604 val
.getAs
<nonloc::LazyCompoundVal
>())
607 if (std::optional
<nonloc::LocAsInteger
> X
= val
.getAs
<nonloc::LocAsInteger
>())
608 return scan(X
->getLoc());
610 if (SymbolRef Sym
= val
.getAsSymbol())
613 if (std::optional
<nonloc::CompoundVal
> X
= val
.getAs
<nonloc::CompoundVal
>())
619 bool ScanReachableSymbols::scan(const MemRegion
*R
) {
620 if (isa
<MemSpaceRegion
>(R
))
623 bool wasVisited
= !visited
.insert(R
).second
;
627 if (!visitor
.VisitMemRegion(R
))
630 // If this is a symbolic region, visit the symbol for the region.
631 if (const SymbolicRegion
*SR
= dyn_cast
<SymbolicRegion
>(R
))
632 if (!visitor
.VisitSymbol(SR
->getSymbol()))
635 // If this is a subregion, also visit the parent regions.
636 if (const SubRegion
*SR
= dyn_cast
<SubRegion
>(R
)) {
637 const MemRegion
*Super
= SR
->getSuperRegion();
641 // When we reach the topmost region, scan all symbols in it.
642 if (isa
<MemSpaceRegion
>(Super
)) {
643 StoreManager
&StoreMgr
= state
->getStateManager().getStoreManager();
644 if (!StoreMgr
.scanReachableSymbols(state
->getStore(), SR
, *this))
649 // Regions captured by a block are also implicitly reachable.
650 if (const BlockDataRegion
*BDR
= dyn_cast
<BlockDataRegion
>(R
)) {
651 for (auto Var
: BDR
->referenced_vars()) {
652 if (!scan(Var
.getCapturedRegion()))
660 bool ProgramState::scanReachableSymbols(SVal val
, SymbolVisitor
& visitor
) const {
661 ScanReachableSymbols
S(this, visitor
);
665 bool ProgramState::scanReachableSymbols(
666 llvm::iterator_range
<region_iterator
> Reachable
,
667 SymbolVisitor
&visitor
) const {
668 ScanReachableSymbols
S(this, visitor
);
669 for (const MemRegion
*R
: Reachable
) {