1 | //===-- SVals.cpp - Abstract RValues for Path-Sens. Value Tracking --------===// |
---|---|
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 defines SVal, Loc, and NonLoc, classes that represent |
10 | // abstract r-values for use with path-sensitive value tracking. |
11 | // |
12 | //===----------------------------------------------------------------------===// |
13 | |
14 | #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" |
15 | #include "clang/AST/ASTContext.h" |
16 | #include "clang/AST/Decl.h" |
17 | #include "clang/AST/DeclCXX.h" |
18 | #include "clang/AST/Expr.h" |
19 | #include "clang/AST/Type.h" |
20 | #include "clang/Basic/JsonSupport.h" |
21 | #include "clang/Basic/LLVM.h" |
22 | #include "clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h" |
23 | #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" |
24 | #include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" |
25 | #include "clang/StaticAnalyzer/Core/PathSensitive/SValVisitor.h" |
26 | #include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" |
27 | #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" |
28 | #include "llvm/Support/Compiler.h" |
29 | #include "llvm/Support/ErrorHandling.h" |
30 | #include "llvm/Support/raw_ostream.h" |
31 | #include <cassert> |
32 | #include <optional> |
33 | |
34 | using namespace clang; |
35 | using namespace ento; |
36 | |
37 | //===----------------------------------------------------------------------===// |
38 | // Symbol iteration within an SVal. |
39 | //===----------------------------------------------------------------------===// |
40 | |
41 | //===----------------------------------------------------------------------===// |
42 | // Utility methods. |
43 | //===----------------------------------------------------------------------===// |
44 | |
45 | const FunctionDecl *SVal::getAsFunctionDecl() const { |
46 | if (std::optional<loc::MemRegionVal> X = getAs<loc::MemRegionVal>()) { |
47 | const MemRegion* R = X->getRegion(); |
48 | if (const FunctionCodeRegion *CTR = R->getAs<FunctionCodeRegion>()) |
49 | if (const auto *FD = dyn_cast<FunctionDecl>(Val: CTR->getDecl())) |
50 | return FD; |
51 | } |
52 | |
53 | if (auto X = getAs<nonloc::PointerToMember>()) { |
54 | if (const auto *MD = dyn_cast_or_null<CXXMethodDecl>(Val: X->getDecl())) |
55 | return MD; |
56 | } |
57 | return nullptr; |
58 | } |
59 | |
60 | /// If this SVal is a location (subclasses Loc) and wraps a symbol, |
61 | /// return that SymbolRef. Otherwise return 0. |
62 | /// |
63 | /// Implicit casts (ex: void* -> char*) can turn Symbolic region into Element |
64 | /// region. If that is the case, gets the underlining region. |
65 | /// When IncludeBaseRegions is set to true and the SubRegion is non-symbolic, |
66 | /// the first symbolic parent region is returned. |
67 | SymbolRef SVal::getAsLocSymbol(bool IncludeBaseRegions) const { |
68 | // FIXME: should we consider SymbolRef wrapped in CodeTextRegion? |
69 | if (const MemRegion *R = getAsRegion()) |
70 | if (const SymbolicRegion *SymR = |
71 | IncludeBaseRegions ? R->getSymbolicBase() |
72 | : dyn_cast<SymbolicRegion>(Val: R->StripCasts())) |
73 | return SymR->getSymbol(); |
74 | |
75 | return nullptr; |
76 | } |
77 | |
78 | /// Get the symbol in the SVal or its base region. |
79 | SymbolRef SVal::getLocSymbolInBase() const { |
80 | std::optional<loc::MemRegionVal> X = getAs<loc::MemRegionVal>(); |
81 | |
82 | if (!X) |
83 | return nullptr; |
84 | |
85 | const MemRegion *R = X->getRegion(); |
86 | |
87 | while (const auto *SR = dyn_cast<SubRegion>(Val: R)) { |
88 | if (const auto *SymR = dyn_cast<SymbolicRegion>(Val: SR)) |
89 | return SymR->getSymbol(); |
90 | else |
91 | R = SR->getSuperRegion(); |
92 | } |
93 | |
94 | return nullptr; |
95 | } |
96 | |
97 | /// If this SVal wraps a symbol return that SymbolRef. |
98 | /// Otherwise, return 0. |
99 | /// |
100 | /// Casts are ignored during lookup. |
101 | /// \param IncludeBaseRegions The boolean that controls whether the search |
102 | /// should continue to the base regions if the region is not symbolic. |
103 | SymbolRef SVal::getAsSymbol(bool IncludeBaseRegions) const { |
104 | // FIXME: should we consider SymbolRef wrapped in CodeTextRegion? |
105 | if (std::optional<nonloc::SymbolVal> X = getAs<nonloc::SymbolVal>()) |
106 | return X->getSymbol(); |
107 | |
108 | return getAsLocSymbol(IncludeBaseRegions); |
109 | } |
110 | |
111 | const llvm::APSInt *SVal::getAsInteger() const { |
112 | if (auto CI = getAs<nonloc::ConcreteInt>()) |
113 | return CI->getValue().get(); |
114 | if (auto CI = getAs<loc::ConcreteInt>()) |
115 | return CI->getValue().get(); |
116 | return nullptr; |
117 | } |
118 | |
119 | const MemRegion *SVal::getAsRegion() const { |
120 | if (std::optional<loc::MemRegionVal> X = getAs<loc::MemRegionVal>()) |
121 | return X->getRegion(); |
122 | |
123 | if (std::optional<nonloc::LocAsInteger> X = getAs<nonloc::LocAsInteger>()) |
124 | return X->getLoc().getAsRegion(); |
125 | |
126 | return nullptr; |
127 | } |
128 | |
129 | namespace { |
130 | class TypeRetrievingVisitor |
131 | : public FullSValVisitor<TypeRetrievingVisitor, QualType> { |
132 | private: |
133 | const ASTContext &Context; |
134 | |
135 | public: |
136 | TypeRetrievingVisitor(const ASTContext &Context) : Context(Context) {} |
137 | |
138 | QualType VisitMemRegionVal(loc::MemRegionVal MRV) { |
139 | return Visit(R: MRV.getRegion()); |
140 | } |
141 | QualType VisitGotoLabel(loc::GotoLabel GL) { |
142 | return QualType{Context.VoidPtrTy}; |
143 | } |
144 | template <class ConcreteInt> QualType VisitConcreteInt(ConcreteInt CI) { |
145 | const llvm::APSInt &Value = CI.getValue(); |
146 | if (1 == Value.getBitWidth()) |
147 | return Context.BoolTy; |
148 | return Context.getIntTypeForBitwidth(DestWidth: Value.getBitWidth(), Signed: Value.isSigned()); |
149 | } |
150 | QualType VisitLocAsInteger(nonloc::LocAsInteger LI) { |
151 | QualType NestedType = Visit(V: LI.getLoc()); |
152 | if (NestedType.isNull()) |
153 | return NestedType; |
154 | |
155 | return Context.getIntTypeForBitwidth(DestWidth: LI.getNumBits(), |
156 | Signed: NestedType->isSignedIntegerType()); |
157 | } |
158 | QualType VisitCompoundVal(nonloc::CompoundVal CV) { |
159 | return CV.getValue()->getType(); |
160 | } |
161 | QualType VisitLazyCompoundVal(nonloc::LazyCompoundVal LCV) { |
162 | return LCV.getRegion()->getValueType(); |
163 | } |
164 | QualType VisitSymbolVal(nonloc::SymbolVal SV) { |
165 | return Visit(S: SV.getSymbol()); |
166 | } |
167 | QualType VisitSymbolicRegion(const SymbolicRegion *SR) { |
168 | return Visit(S: SR->getSymbol()); |
169 | } |
170 | QualType VisitAllocaRegion(const AllocaRegion *) { |
171 | return QualType{Context.VoidPtrTy}; |
172 | } |
173 | QualType VisitTypedRegion(const TypedRegion *TR) { |
174 | return TR->getLocationType(); |
175 | } |
176 | QualType VisitSymExpr(const SymExpr *SE) { return SE->getType(); } |
177 | }; |
178 | } // end anonymous namespace |
179 | |
180 | QualType SVal::getType(const ASTContext &Context) const { |
181 | TypeRetrievingVisitor TRV{Context}; |
182 | return TRV.Visit(V: *this); |
183 | } |
184 | |
185 | const MemRegion *loc::MemRegionVal::stripCasts(bool StripBaseCasts) const { |
186 | return getRegion()->StripCasts(StripBaseAndDerivedCasts: StripBaseCasts); |
187 | } |
188 | |
189 | const void *nonloc::LazyCompoundVal::getStore() const { |
190 | return static_cast<const LazyCompoundValData*>(Data)->getStore(); |
191 | } |
192 | |
193 | const TypedValueRegion *nonloc::LazyCompoundVal::getRegion() const { |
194 | return static_cast<const LazyCompoundValData*>(Data)->getRegion(); |
195 | } |
196 | |
197 | bool nonloc::PointerToMember::isNullMemberPointer() const { |
198 | return getPTMData().isNull(); |
199 | } |
200 | |
201 | const NamedDecl *nonloc::PointerToMember::getDecl() const { |
202 | const auto PTMD = this->getPTMData(); |
203 | if (PTMD.isNull()) |
204 | return nullptr; |
205 | |
206 | const NamedDecl *ND = nullptr; |
207 | if (const auto *NDP = dyn_cast<const NamedDecl *>(Val: PTMD)) |
208 | ND = NDP; |
209 | else |
210 | ND = cast<const PointerToMemberData *>(Val: PTMD)->getDeclaratorDecl(); |
211 | |
212 | return ND; |
213 | } |
214 | |
215 | //===----------------------------------------------------------------------===// |
216 | // Other Iterators. |
217 | //===----------------------------------------------------------------------===// |
218 | |
219 | nonloc::CompoundVal::iterator nonloc::CompoundVal::begin() const { |
220 | return getValue()->begin(); |
221 | } |
222 | |
223 | nonloc::CompoundVal::iterator nonloc::CompoundVal::end() const { |
224 | return getValue()->end(); |
225 | } |
226 | |
227 | nonloc::PointerToMember::iterator nonloc::PointerToMember::begin() const { |
228 | const PTMDataType PTMD = getPTMData(); |
229 | if (isa<const NamedDecl *>(Val: PTMD)) |
230 | return {}; |
231 | return cast<const PointerToMemberData *>(Val: PTMD)->begin(); |
232 | } |
233 | |
234 | nonloc::PointerToMember::iterator nonloc::PointerToMember::end() const { |
235 | const PTMDataType PTMD = getPTMData(); |
236 | if (isa<const NamedDecl *>(Val: PTMD)) |
237 | return {}; |
238 | return cast<const PointerToMemberData *>(Val: PTMD)->end(); |
239 | } |
240 | |
241 | //===----------------------------------------------------------------------===// |
242 | // Useful predicates. |
243 | //===----------------------------------------------------------------------===// |
244 | |
245 | bool SVal::isConstant() const { |
246 | return getAs<nonloc::ConcreteInt>() || getAs<loc::ConcreteInt>(); |
247 | } |
248 | |
249 | bool SVal::isConstant(int I) const { |
250 | if (std::optional<loc::ConcreteInt> LV = getAs<loc::ConcreteInt>()) |
251 | return *LV->getValue() == I; |
252 | if (std::optional<nonloc::ConcreteInt> NV = getAs<nonloc::ConcreteInt>()) |
253 | return *NV->getValue() == I; |
254 | return false; |
255 | } |
256 | |
257 | bool SVal::isZeroConstant() const { |
258 | return isConstant(I: 0); |
259 | } |
260 | |
261 | //===----------------------------------------------------------------------===// |
262 | // Pretty-Printing. |
263 | //===----------------------------------------------------------------------===// |
264 | |
265 | StringRef SVal::getKindStr() const { |
266 | switch (getKind()) { |
267 | #define BASIC_SVAL(Id, Parent) \ |
268 | case Id##Kind: \ |
269 | return #Id; |
270 | #define LOC_SVAL(Id, Parent) \ |
271 | case Loc##Id##Kind: \ |
272 | return #Id; |
273 | #define NONLOC_SVAL(Id, Parent) \ |
274 | case NonLoc##Id##Kind: \ |
275 | return #Id; |
276 | #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.def" |
277 | #undef REGION |
278 | } |
279 | llvm_unreachable("Unkown kind!"); |
280 | } |
281 | |
282 | LLVM_DUMP_METHOD void SVal::dump() const { dumpToStream(OS&: llvm::errs()); } |
283 | |
284 | void SVal::printJson(raw_ostream &Out, bool AddQuotes) const { |
285 | std::string Buf; |
286 | llvm::raw_string_ostream TempOut(Buf); |
287 | |
288 | dumpToStream(OS&: TempOut); |
289 | |
290 | Out << JsonFormat(RawSR: Buf, AddQuotes); |
291 | } |
292 | |
293 | void SVal::dumpToStream(raw_ostream &os) const { |
294 | if (isUndef()) { |
295 | os << "Undefined"; |
296 | return; |
297 | } |
298 | if (isUnknown()) { |
299 | os << "Unknown"; |
300 | return; |
301 | } |
302 | if (NonLoc::classof(V: *this)) { |
303 | castAs<NonLoc>().dumpToStream(Out&: os); |
304 | return; |
305 | } |
306 | if (Loc::classof(V: *this)) { |
307 | castAs<Loc>().dumpToStream(Out&: os); |
308 | return; |
309 | } |
310 | llvm_unreachable("Unhandled SVal kind!"); |
311 | } |
312 | |
313 | void NonLoc::dumpToStream(raw_ostream &os) const { |
314 | switch (getKind()) { |
315 | case nonloc::ConcreteIntKind: { |
316 | APSIntPtr Value = castAs<nonloc::ConcreteInt>().getValue(); |
317 | os << Value << ' ' << (Value->isSigned() ? 'S' : 'U') |
318 | << Value->getBitWidth() << 'b'; |
319 | break; |
320 | } |
321 | case nonloc::SymbolValKind: |
322 | os << castAs<nonloc::SymbolVal>().getSymbol(); |
323 | break; |
324 | |
325 | case nonloc::LocAsIntegerKind: { |
326 | const nonloc::LocAsInteger& C = castAs<nonloc::LocAsInteger>(); |
327 | os << C.getLoc() << " [as "<< C.getNumBits() << " bit integer]"; |
328 | break; |
329 | } |
330 | case nonloc::CompoundValKind: { |
331 | const nonloc::CompoundVal& C = castAs<nonloc::CompoundVal>(); |
332 | os << "compoundVal{"; |
333 | bool first = true; |
334 | for (const auto &I : C) { |
335 | if (first) { |
336 | os << ' '; first = false; |
337 | } |
338 | else |
339 | os << ", "; |
340 | |
341 | I.dumpToStream(os); |
342 | } |
343 | os << "}"; |
344 | break; |
345 | } |
346 | case nonloc::LazyCompoundValKind: { |
347 | const nonloc::LazyCompoundVal &C = castAs<nonloc::LazyCompoundVal>(); |
348 | os << "lazyCompoundVal{"<< const_cast<void *>(C.getStore()) |
349 | << ',' << C.getRegion() |
350 | << '}'; |
351 | break; |
352 | } |
353 | case nonloc::PointerToMemberKind: { |
354 | os << "pointerToMember{"; |
355 | const nonloc::PointerToMember &CastRes = |
356 | castAs<nonloc::PointerToMember>(); |
357 | if (CastRes.getDecl()) |
358 | os << "|"<< CastRes.getDecl()->getQualifiedNameAsString() << "|"; |
359 | bool first = true; |
360 | for (const auto &I : CastRes) { |
361 | if (first) { |
362 | os << ' '; first = false; |
363 | } |
364 | else |
365 | os << ", "; |
366 | |
367 | os << I->getType(); |
368 | } |
369 | |
370 | os << '}'; |
371 | break; |
372 | } |
373 | default: |
374 | assert(false && "Pretty-printed not implemented for this NonLoc."); |
375 | break; |
376 | } |
377 | } |
378 | |
379 | void Loc::dumpToStream(raw_ostream &os) const { |
380 | switch (getKind()) { |
381 | case loc::ConcreteIntKind: |
382 | os << castAs<loc::ConcreteInt>().getValue()->getZExtValue() << " (Loc)"; |
383 | break; |
384 | case loc::GotoLabelKind: |
385 | os << "&&"<< castAs<loc::GotoLabel>().getLabel()->getName(); |
386 | break; |
387 | case loc::MemRegionValKind: |
388 | os << '&' << castAs<loc::MemRegionVal>().getRegion()->getString(); |
389 | break; |
390 | default: |
391 | llvm_unreachable("Pretty-printing not implemented for this Loc."); |
392 | } |
393 | } |
394 |
Definitions
- getAsFunctionDecl
- getAsLocSymbol
- getLocSymbolInBase
- getAsSymbol
- getAsInteger
- getAsRegion
- TypeRetrievingVisitor
- TypeRetrievingVisitor
- VisitMemRegionVal
- VisitGotoLabel
- VisitConcreteInt
- VisitLocAsInteger
- VisitCompoundVal
- VisitLazyCompoundVal
- VisitSymbolVal
- VisitSymbolicRegion
- VisitAllocaRegion
- VisitTypedRegion
- VisitSymExpr
- getType
- stripCasts
- getStore
- getRegion
- isNullMemberPointer
- getDecl
- begin
- end
- begin
- end
- isConstant
- isConstant
- isZeroConstant
- getKindStr
- dump
- printJson
- dumpToStream
- dumpToStream
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more