/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/StaticAnalyzer/Core/ConstraintManager.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===- ConstraintManager.cpp - Constraints on symbolic values. ------------===// |
2 | | // |
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 |
6 | | // |
7 | | //===----------------------------------------------------------------------===// |
8 | | // |
9 | | // This file defined the interface to manage constraints on symbolic values. |
10 | | // |
11 | | //===----------------------------------------------------------------------===// |
12 | | |
13 | | #include "clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h" |
14 | | #include "clang/AST/Type.h" |
15 | | #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" |
16 | | #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" |
17 | | #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" |
18 | | #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" |
19 | | #include "llvm/ADT/ScopeExit.h" |
20 | | |
21 | | using namespace clang; |
22 | | using namespace ento; |
23 | | |
24 | 16.2k | ConstraintManager::~ConstraintManager() = default; |
25 | | |
26 | | static DefinedSVal getLocFromSymbol(const ProgramStateRef &State, |
27 | 0 | SymbolRef Sym) { |
28 | 0 | const MemRegion *R = |
29 | 0 | State->getStateManager().getRegionManager().getSymbolicRegion(Sym); |
30 | 0 | return loc::MemRegionVal(R); |
31 | 0 | } |
32 | | |
33 | | ConditionTruthVal ConstraintManager::checkNull(ProgramStateRef State, |
34 | 0 | SymbolRef Sym) { |
35 | 0 | QualType Ty = Sym->getType(); |
36 | 0 | DefinedSVal V = Loc::isLocType(Ty) ? getLocFromSymbol(State, Sym) |
37 | 0 | : nonloc::SymbolVal(Sym); |
38 | 0 | const ProgramStatePair &P = assumeDual(State, V); |
39 | 0 | if (P.first && !P.second) |
40 | 0 | return ConditionTruthVal(false); |
41 | 0 | if (!P.first && P.second) |
42 | 0 | return ConditionTruthVal(true); |
43 | 0 | return {}; |
44 | 0 | } |
45 | | |
46 | | template <typename AssumeFunction> |
47 | | ConstraintManager::ProgramStatePair |
48 | | ConstraintManager::assumeDualImpl(ProgramStateRef &State, |
49 | 670k | AssumeFunction &Assume) { |
50 | 670k | if (LLVM_UNLIKELY(State->isPosteriorlyOverconstrained())) |
51 | 258 | return {State, State}; |
52 | | |
53 | | // Assume functions might recurse (see `reAssume` or `tryRearrange`). During |
54 | | // the recursion the State might not change anymore, that means we reached a |
55 | | // fixpoint. |
56 | | // We avoid infinite recursion of assume calls by checking already visited |
57 | | // States on the stack of assume function calls. |
58 | 670k | const ProgramState *RawSt = State.get(); |
59 | 670k | if (LLVM_UNLIKELY(AssumeStack.contains(RawSt))) |
60 | 0 | return {State, State}; |
61 | 670k | AssumeStack.push(RawSt); |
62 | 670k | auto AssumeStackBuilder = |
63 | 670k | llvm::make_scope_exit([this]() { AssumeStack.pop(); }); ConstraintManager.cpp:std::__1::pair<llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const> > clang::ento::ConstraintManager::assumeDualImpl<clang::ento::ConstraintManager::assumeDual(llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, clang::ento::DefinedSVal)::$_0>(llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>&, clang::ento::ConstraintManager::assumeDual(llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, clang::ento::DefinedSVal)::$_0&)::'lambda'()::operator()() const Line | Count | Source | 63 | 661k | llvm::make_scope_exit([this]() { AssumeStack.pop(); }); |
ConstraintManager.cpp:std::__1::pair<llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const> > clang::ento::ConstraintManager::assumeDualImpl<clang::ento::ConstraintManager::assumeInclusiveRangeDual(llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, clang::ento::NonLoc, llvm::APSInt const&, llvm::APSInt const&)::$_0>(llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>&, clang::ento::ConstraintManager::assumeInclusiveRangeDual(llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, clang::ento::NonLoc, llvm::APSInt const&, llvm::APSInt const&)::$_0&)::'lambda'()::operator()() const Line | Count | Source | 63 | 8.97k | llvm::make_scope_exit([this]() { AssumeStack.pop(); }); |
|
64 | | |
65 | 670k | ProgramStateRef StTrue = Assume(true); |
66 | | |
67 | 670k | if (!StTrue) { |
68 | 94.2k | ProgramStateRef StFalse = Assume(false); |
69 | 94.2k | if (LLVM_UNLIKELY(!StFalse)) { // both infeasible |
70 | 76 | ProgramStateRef StInfeasible = State->cloneAsPosteriorlyOverconstrained(); |
71 | 76 | assert(StInfeasible->isPosteriorlyOverconstrained()); |
72 | | // Checkers might rely on the API contract that both returned states |
73 | | // cannot be null. Thus, we return StInfeasible for both branches because |
74 | | // it might happen that a Checker uncoditionally uses one of them if the |
75 | | // other is a nullptr. This may also happen with the non-dual and |
76 | | // adjacent `assume(true)` and `assume(false)` calls. By implementing |
77 | | // assume in therms of assumeDual, we can keep our API contract there as |
78 | | // well. |
79 | 76 | return ProgramStatePair(StInfeasible, StInfeasible); |
80 | 76 | } |
81 | 94.1k | return ProgramStatePair(nullptr, StFalse); |
82 | 94.2k | } |
83 | | |
84 | 576k | ProgramStateRef StFalse = Assume(false); |
85 | 576k | if (!StFalse) { |
86 | 507k | return ProgramStatePair(StTrue, nullptr); |
87 | 507k | } |
88 | | |
89 | 69.0k | return ProgramStatePair(StTrue, StFalse); |
90 | 576k | } ConstraintManager.cpp:std::__1::pair<llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const> > clang::ento::ConstraintManager::assumeDualImpl<clang::ento::ConstraintManager::assumeDual(llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, clang::ento::DefinedSVal)::$_0>(llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>&, clang::ento::ConstraintManager::assumeDual(llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, clang::ento::DefinedSVal)::$_0&) Line | Count | Source | 49 | 661k | AssumeFunction &Assume) { | 50 | 661k | if (LLVM_UNLIKELY(State->isPosteriorlyOverconstrained())) | 51 | 130 | return {State, State}; | 52 | | | 53 | | // Assume functions might recurse (see `reAssume` or `tryRearrange`). During | 54 | | // the recursion the State might not change anymore, that means we reached a | 55 | | // fixpoint. | 56 | | // We avoid infinite recursion of assume calls by checking already visited | 57 | | // States on the stack of assume function calls. | 58 | 661k | const ProgramState *RawSt = State.get(); | 59 | 661k | if (LLVM_UNLIKELY(AssumeStack.contains(RawSt))) | 60 | 0 | return {State, State}; | 61 | 661k | AssumeStack.push(RawSt); | 62 | 661k | auto AssumeStackBuilder = | 63 | 661k | llvm::make_scope_exit([this]() { AssumeStack.pop(); }); | 64 | | | 65 | 661k | ProgramStateRef StTrue = Assume(true); | 66 | | | 67 | 661k | if (!StTrue) { | 68 | 92.7k | ProgramStateRef StFalse = Assume(false); | 69 | 92.7k | if (LLVM_UNLIKELY(!StFalse)) { // both infeasible | 70 | 74 | ProgramStateRef StInfeasible = State->cloneAsPosteriorlyOverconstrained(); | 71 | 74 | assert(StInfeasible->isPosteriorlyOverconstrained()); | 72 | | // Checkers might rely on the API contract that both returned states | 73 | | // cannot be null. Thus, we return StInfeasible for both branches because | 74 | | // it might happen that a Checker uncoditionally uses one of them if the | 75 | | // other is a nullptr. This may also happen with the non-dual and | 76 | | // adjacent `assume(true)` and `assume(false)` calls. By implementing | 77 | | // assume in therms of assumeDual, we can keep our API contract there as | 78 | | // well. | 79 | 74 | return ProgramStatePair(StInfeasible, StInfeasible); | 80 | 74 | } | 81 | 92.6k | return ProgramStatePair(nullptr, StFalse); | 82 | 92.7k | } | 83 | | | 84 | 568k | ProgramStateRef StFalse = Assume(false); | 85 | 568k | if (!StFalse) { | 86 | 502k | return ProgramStatePair(StTrue, nullptr); | 87 | 502k | } | 88 | | | 89 | 66.5k | return ProgramStatePair(StTrue, StFalse); | 90 | 568k | } |
ConstraintManager.cpp:std::__1::pair<llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const> > clang::ento::ConstraintManager::assumeDualImpl<clang::ento::ConstraintManager::assumeInclusiveRangeDual(llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, clang::ento::NonLoc, llvm::APSInt const&, llvm::APSInt const&)::$_0>(llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>&, clang::ento::ConstraintManager::assumeInclusiveRangeDual(llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, clang::ento::NonLoc, llvm::APSInt const&, llvm::APSInt const&)::$_0&) Line | Count | Source | 49 | 9.10k | AssumeFunction &Assume) { | 50 | 9.10k | if (LLVM_UNLIKELY(State->isPosteriorlyOverconstrained())) | 51 | 128 | return {State, State}; | 52 | | | 53 | | // Assume functions might recurse (see `reAssume` or `tryRearrange`). During | 54 | | // the recursion the State might not change anymore, that means we reached a | 55 | | // fixpoint. | 56 | | // We avoid infinite recursion of assume calls by checking already visited | 57 | | // States on the stack of assume function calls. | 58 | 8.97k | const ProgramState *RawSt = State.get(); | 59 | 8.97k | if (LLVM_UNLIKELY(AssumeStack.contains(RawSt))) | 60 | 0 | return {State, State}; | 61 | 8.97k | AssumeStack.push(RawSt); | 62 | 8.97k | auto AssumeStackBuilder = | 63 | 8.97k | llvm::make_scope_exit([this]() { AssumeStack.pop(); }); | 64 | | | 65 | 8.97k | ProgramStateRef StTrue = Assume(true); | 66 | | | 67 | 8.97k | if (!StTrue) { | 68 | 1.46k | ProgramStateRef StFalse = Assume(false); | 69 | 1.46k | if (LLVM_UNLIKELY(!StFalse)) { // both infeasible | 70 | 2 | ProgramStateRef StInfeasible = State->cloneAsPosteriorlyOverconstrained(); | 71 | 2 | assert(StInfeasible->isPosteriorlyOverconstrained()); | 72 | | // Checkers might rely on the API contract that both returned states | 73 | | // cannot be null. Thus, we return StInfeasible for both branches because | 74 | | // it might happen that a Checker uncoditionally uses one of them if the | 75 | | // other is a nullptr. This may also happen with the non-dual and | 76 | | // adjacent `assume(true)` and `assume(false)` calls. By implementing | 77 | | // assume in therms of assumeDual, we can keep our API contract there as | 78 | | // well. | 79 | 2 | return ProgramStatePair(StInfeasible, StInfeasible); | 80 | 2 | } | 81 | 1.46k | return ProgramStatePair(nullptr, StFalse); | 82 | 1.46k | } | 83 | | | 84 | 7.50k | ProgramStateRef StFalse = Assume(false); | 85 | 7.50k | if (!StFalse) { | 86 | 5.04k | return ProgramStatePair(StTrue, nullptr); | 87 | 5.04k | } | 88 | | | 89 | 2.46k | return ProgramStatePair(StTrue, StFalse); | 90 | 7.50k | } |
|
91 | | |
92 | | ConstraintManager::ProgramStatePair |
93 | 661k | ConstraintManager::assumeDual(ProgramStateRef State, DefinedSVal Cond) { |
94 | 1.32M | auto AssumeFun = [&, Cond](bool Assumption) { |
95 | 1.32M | return assumeInternal(State, Cond, Assumption); |
96 | 1.32M | }; |
97 | 661k | return assumeDualImpl(State, AssumeFun); |
98 | 661k | } |
99 | | |
100 | | ConstraintManager::ProgramStatePair |
101 | | ConstraintManager::assumeInclusiveRangeDual(ProgramStateRef State, NonLoc Value, |
102 | | const llvm::APSInt &From, |
103 | 9.10k | const llvm::APSInt &To) { |
104 | 17.9k | auto AssumeFun = [&](bool Assumption) { |
105 | 17.9k | return assumeInclusiveRangeInternal(State, Value, From, To, Assumption); |
106 | 17.9k | }; |
107 | 9.10k | return assumeDualImpl(State, AssumeFun); |
108 | 9.10k | } |
109 | | |
110 | | ProgramStateRef ConstraintManager::assume(ProgramStateRef State, |
111 | 32.0k | DefinedSVal Cond, bool Assumption) { |
112 | 32.0k | ConstraintManager::ProgramStatePair R = assumeDual(State, Cond); |
113 | 32.0k | return Assumption ? R.first18.0k : R.second13.9k ; |
114 | 32.0k | } |
115 | | |
116 | | ProgramStateRef |
117 | | ConstraintManager::assumeInclusiveRange(ProgramStateRef State, NonLoc Value, |
118 | | const llvm::APSInt &From, |
119 | 8.07k | const llvm::APSInt &To, bool InBound) { |
120 | 8.07k | ConstraintManager::ProgramStatePair R = |
121 | 8.07k | assumeInclusiveRangeDual(State, Value, From, To); |
122 | 8.07k | return InBound ? R.first4.77k : R.second3.29k ; |
123 | 8.07k | } |