1//===- SVals.h - Abstract Values for Static Analysis ------------*- C++ -*-===//
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#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SVALS_H
15#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SVALS_H
16
17#include "clang/AST/Expr.h"
18#include "clang/AST/Type.h"
19#include "clang/Basic/LLVM.h"
20#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h"
21#include "llvm/ADT/APSInt.h"
22#include "llvm/ADT/FoldingSet.h"
23#include "llvm/ADT/ImmutableList.h"
24#include "llvm/ADT/PointerUnion.h"
25#include "llvm/ADT/STLForwardCompat.h"
26#include "llvm/ADT/iterator_range.h"
27#include "llvm/Support/Casting.h"
28#include <cassert>
29#include <cstdint>
30#include <optional>
31#include <utility>
32
33//==------------------------------------------------------------------------==//
34// Base SVal types.
35//==------------------------------------------------------------------------==//
36
37namespace clang {
38
39class CXXBaseSpecifier;
40class FunctionDecl;
41class LabelDecl;
42
43namespace ento {
44
45class CompoundValData;
46class LazyCompoundValData;
47class MemRegion;
48class PointerToMemberData;
49class SValBuilder;
50class TypedValueRegion;
51
52/// SVal - This represents a symbolic expression, which can be either
53/// an L-value or an R-value.
54///
55class SVal {
56public:
57 enum SValKind : unsigned char {
58#define BASIC_SVAL(Id, Parent) Id##Kind,
59#define LOC_SVAL(Id, Parent) Loc##Id##Kind,
60#define NONLOC_SVAL(Id, Parent) NonLoc##Id##Kind,
61#define SVAL_RANGE(Id, First, Last) \
62 BEGIN_##Id = Id##First##Kind, END_##Id = Id##Last##Kind,
63#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.def"
64 };
65
66protected:
67 const void *Data = nullptr;
68 SValKind Kind = UndefinedValKind;
69
70 explicit SVal(SValKind Kind, const void *Data = nullptr)
71 : Data(Data), Kind(Kind) {}
72
73 template <typename T> const T *castDataAs() const {
74 return static_cast<const T *>(Data);
75 }
76
77public:
78 explicit SVal() = default;
79
80 /// Convert to the specified SVal type, asserting that this SVal is of
81 /// the desired type.
82 template <typename T> T castAs() const { return llvm::cast<T>(*this); }
83
84 /// Convert to the specified SVal type, returning std::nullopt if this SVal is
85 /// not of the desired type.
86 template <typename T> std::optional<T> getAs() const {
87 return llvm::dyn_cast<T>(*this);
88 }
89
90 SValKind getKind() const { return Kind; }
91
92 // This method is required for using SVal in a FoldingSetNode. It
93 // extracts a unique signature for this SVal object.
94 void Profile(llvm::FoldingSetNodeID &ID) const {
95 ID.AddPointer(Ptr: Data);
96 ID.AddInteger(I: llvm::to_underlying(E: getKind()));
97 }
98
99 bool operator==(SVal R) const { return Kind == R.Kind && Data == R.Data; }
100 bool operator!=(SVal R) const { return !(*this == R); }
101
102 bool isUnknown() const { return getKind() == UnknownValKind; }
103
104 bool isUndef() const { return getKind() == UndefinedValKind; }
105
106 bool isUnknownOrUndef() const { return isUnknown() || isUndef(); }
107
108 bool isValid() const { return !isUnknownOrUndef(); }
109
110 bool isConstant() const;
111
112 bool isConstant(int I) const;
113
114 bool isZeroConstant() const;
115
116 /// getAsFunctionDecl - If this SVal is a MemRegionVal and wraps a
117 /// CodeTextRegion wrapping a FunctionDecl, return that FunctionDecl.
118 /// Otherwise return 0.
119 const FunctionDecl *getAsFunctionDecl() const;
120
121 /// If this SVal is a location and wraps a symbol, return that
122 /// SymbolRef. Otherwise return 0.
123 ///
124 /// Casts are ignored during lookup.
125 /// \param IncludeBaseRegions The boolean that controls whether the search
126 /// should continue to the base regions if the region is not symbolic.
127 SymbolRef getAsLocSymbol(bool IncludeBaseRegions = false) const;
128
129 /// Get the symbol in the SVal or its base region.
130 SymbolRef getLocSymbolInBase() const;
131
132 /// If this SVal wraps a symbol return that SymbolRef.
133 /// Otherwise, return 0.
134 ///
135 /// Casts are ignored during lookup.
136 /// \param IncludeBaseRegions The boolean that controls whether the search
137 /// should continue to the base regions if the region is not symbolic.
138 SymbolRef getAsSymbol(bool IncludeBaseRegions = false) const;
139
140 /// If this SVal is loc::ConcreteInt or nonloc::ConcreteInt,
141 /// return a pointer to APSInt which is held in it.
142 /// Otherwise, return nullptr.
143 const llvm::APSInt *getAsInteger() const;
144
145 const MemRegion *getAsRegion() const;
146
147 /// printJson - Pretty-prints in JSON format.
148 void printJson(raw_ostream &Out, bool AddQuotes) const;
149
150 void dumpToStream(raw_ostream &OS) const;
151 void dump() const;
152
153 llvm::iterator_range<SymExpr::symbol_iterator> symbols() const {
154 if (const SymExpr *SE = getAsSymbol(/*IncludeBaseRegions=*/IncludeBaseRegions: true))
155 return SE->symbols();
156 SymExpr::symbol_iterator end{};
157 return llvm::make_range(x: end, y: end);
158 }
159
160 /// Try to get a reasonable type for the given value.
161 ///
162 /// \returns The best approximation of the value type or Null.
163 /// In theory, all symbolic values should be typed, but this function
164 /// is still a WIP and might have a few blind spots.
165 ///
166 /// \note This function should not be used when the user has access to the
167 /// bound expression AST node as well, since AST always has exact types.
168 ///
169 /// \note Loc values are interpreted as pointer rvalues for the purposes of
170 /// this method.
171 QualType getType(const ASTContext &) const;
172};
173
174inline raw_ostream &operator<<(raw_ostream &os, clang::ento::SVal V) {
175 V.dumpToStream(OS&: os);
176 return os;
177}
178
179namespace nonloc {
180/// Sub-kinds for NonLoc values.
181#define NONLOC_SVAL(Id, Parent) \
182 inline constexpr auto Id##Kind = SVal::SValKind::NonLoc##Id##Kind;
183#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.def"
184} // namespace nonloc
185
186namespace loc {
187/// Sub-kinds for Loc values.
188#define LOC_SVAL(Id, Parent) \
189 inline constexpr auto Id##Kind = SVal::SValKind::Loc##Id##Kind;
190#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.def"
191} // namespace loc
192
193class UndefinedVal : public SVal {
194public:
195 UndefinedVal() : SVal(UndefinedValKind) {}
196 static bool classof(SVal V) { return V.getKind() == UndefinedValKind; }
197};
198
199class DefinedOrUnknownSVal : public SVal {
200public:
201 // We want calling these methods to be a compiler error since they are
202 // tautologically false.
203 bool isUndef() const = delete;
204 bool isValid() const = delete;
205
206 static bool classof(SVal V) { return !V.isUndef(); }
207
208protected:
209 explicit DefinedOrUnknownSVal(SValKind Kind, const void *Data = nullptr)
210 : SVal(Kind, Data) {}
211};
212
213class UnknownVal : public DefinedOrUnknownSVal {
214public:
215 explicit UnknownVal() : DefinedOrUnknownSVal(UnknownValKind) {}
216
217 static bool classof(SVal V) { return V.getKind() == UnknownValKind; }
218};
219
220class DefinedSVal : public DefinedOrUnknownSVal {
221public:
222 // We want calling these methods to be a compiler error since they are
223 // tautologically true/false.
224 bool isUnknown() const = delete;
225 bool isUnknownOrUndef() const = delete;
226 bool isValid() const = delete;
227
228 static bool classof(SVal V) { return !V.isUnknownOrUndef(); }
229
230protected:
231 explicit DefinedSVal(SValKind Kind, const void *Data)
232 : DefinedOrUnknownSVal(Kind, Data) {}
233};
234
235class NonLoc : public DefinedSVal {
236protected:
237 NonLoc(SValKind Kind, const void *Data) : DefinedSVal(Kind, Data) {}
238
239public:
240 void dumpToStream(raw_ostream &Out) const;
241
242 static bool isCompoundType(QualType T) {
243 return T->isArrayType() || T->isRecordType() ||
244 T->isAnyComplexType() || T->isVectorType();
245 }
246
247 static bool classof(SVal V) {
248 return BEGIN_NonLoc <= V.getKind() && V.getKind() <= END_NonLoc;
249 }
250};
251
252class Loc : public DefinedSVal {
253protected:
254 Loc(SValKind Kind, const void *Data) : DefinedSVal(Kind, Data) {}
255
256public:
257 void dumpToStream(raw_ostream &Out) const;
258
259 static bool isLocType(QualType T) {
260 return T->isAnyPointerType() || T->isBlockPointerType() ||
261 T->isReferenceType() || T->isNullPtrType();
262 }
263
264 static bool classof(SVal V) {
265 return BEGIN_Loc <= V.getKind() && V.getKind() <= END_Loc;
266 }
267};
268
269//==------------------------------------------------------------------------==//
270// Subclasses of NonLoc.
271//==------------------------------------------------------------------------==//
272
273namespace nonloc {
274
275/// Represents symbolic expression that isn't a location.
276class SymbolVal : public NonLoc {
277public:
278 SymbolVal() = delete;
279 explicit SymbolVal(SymbolRef Sym) : NonLoc(SymbolValKind, Sym) {
280 assert(Sym);
281 assert(!Loc::isLocType(Sym->getType()));
282 }
283
284 LLVM_ATTRIBUTE_RETURNS_NONNULL
285 SymbolRef getSymbol() const {
286 return (const SymExpr *) Data;
287 }
288
289 bool isExpression() const {
290 return !isa<SymbolData>(Val: getSymbol());
291 }
292
293 static bool classof(SVal V) { return V.getKind() == SymbolValKind; }
294};
295
296/// Value representing integer constant.
297class ConcreteInt : public NonLoc {
298public:
299 explicit ConcreteInt(const llvm::APSInt &V) : NonLoc(ConcreteIntKind, &V) {}
300
301 const llvm::APSInt &getValue() const { return *castDataAs<llvm::APSInt>(); }
302
303 static bool classof(SVal V) { return V.getKind() == ConcreteIntKind; }
304};
305
306class LocAsInteger : public NonLoc {
307 friend class ento::SValBuilder;
308
309 explicit LocAsInteger(const std::pair<SVal, uintptr_t> &data)
310 : NonLoc(LocAsIntegerKind, &data) {
311 // We do not need to represent loc::ConcreteInt as LocAsInteger,
312 // as it'd collapse into a nonloc::ConcreteInt instead.
313 [[maybe_unused]] SValKind K = data.first.getKind();
314 assert(K == loc::MemRegionValKind || K == loc::GotoLabelKind);
315 }
316
317public:
318 Loc getLoc() const {
319 return castDataAs<std::pair<SVal, uintptr_t>>()->first.castAs<Loc>();
320 }
321
322 unsigned getNumBits() const {
323 return castDataAs<std::pair<SVal, uintptr_t>>()->second;
324 }
325
326 static bool classof(SVal V) { return V.getKind() == LocAsIntegerKind; }
327};
328
329class CompoundVal : public NonLoc {
330 friend class ento::SValBuilder;
331
332 explicit CompoundVal(const CompoundValData *D) : NonLoc(CompoundValKind, D) {
333 assert(D);
334 }
335
336public:
337 LLVM_ATTRIBUTE_RETURNS_NONNULL
338 const CompoundValData* getValue() const {
339 return castDataAs<CompoundValData>();
340 }
341
342 using iterator = llvm::ImmutableList<SVal>::iterator;
343 iterator begin() const;
344 iterator end() const;
345
346 static bool classof(SVal V) { return V.getKind() == CompoundValKind; }
347};
348
349class LazyCompoundVal : public NonLoc {
350 friend class ento::SValBuilder;
351
352 explicit LazyCompoundVal(const LazyCompoundValData *D)
353 : NonLoc(LazyCompoundValKind, D) {
354 assert(D);
355 }
356
357public:
358 LLVM_ATTRIBUTE_RETURNS_NONNULL
359 const LazyCompoundValData *getCVData() const {
360 return castDataAs<LazyCompoundValData>();
361 }
362
363 /// It might return null.
364 const void *getStore() const;
365
366 LLVM_ATTRIBUTE_RETURNS_NONNULL
367 const TypedValueRegion *getRegion() const;
368
369 static bool classof(SVal V) { return V.getKind() == LazyCompoundValKind; }
370};
371
372/// Value representing pointer-to-member.
373///
374/// This value is qualified as NonLoc because neither loading nor storing
375/// operations are applied to it. Instead, the analyzer uses the L-value coming
376/// from pointer-to-member applied to an object.
377/// This SVal is represented by a NamedDecl which can be a member function
378/// pointer or a member data pointer and an optional list of CXXBaseSpecifiers.
379/// This list is required to accumulate the pointer-to-member cast history to
380/// figure out the correct subobject field. In particular, implicit casts grow
381/// this list and explicit casts like static_cast shrink this list.
382class PointerToMember : public NonLoc {
383 friend class ento::SValBuilder;
384
385public:
386 using PTMDataType =
387 llvm::PointerUnion<const NamedDecl *, const PointerToMemberData *>;
388
389 const PTMDataType getPTMData() const {
390 return PTMDataType::getFromOpaqueValue(VP: const_cast<void *>(Data));
391 }
392
393 bool isNullMemberPointer() const;
394
395 const NamedDecl *getDecl() const;
396
397 template<typename AdjustedDecl>
398 const AdjustedDecl *getDeclAs() const {
399 return dyn_cast_or_null<AdjustedDecl>(getDecl());
400 }
401
402 using iterator = llvm::ImmutableList<const CXXBaseSpecifier *>::iterator;
403
404 iterator begin() const;
405 iterator end() const;
406
407 static bool classof(SVal V) { return V.getKind() == PointerToMemberKind; }
408
409private:
410 explicit PointerToMember(const PTMDataType D)
411 : NonLoc(PointerToMemberKind, D.getOpaqueValue()) {}
412};
413
414} // namespace nonloc
415
416//==------------------------------------------------------------------------==//
417// Subclasses of Loc.
418//==------------------------------------------------------------------------==//
419
420namespace loc {
421
422class GotoLabel : public Loc {
423public:
424 explicit GotoLabel(const LabelDecl *Label) : Loc(GotoLabelKind, Label) {
425 assert(Label);
426 }
427
428 const LabelDecl *getLabel() const { return castDataAs<LabelDecl>(); }
429
430 static bool classof(SVal V) { return V.getKind() == GotoLabelKind; }
431};
432
433class MemRegionVal : public Loc {
434public:
435 explicit MemRegionVal(const MemRegion *r) : Loc(MemRegionValKind, r) {
436 assert(r);
437 }
438
439 /// Get the underlining region.
440 LLVM_ATTRIBUTE_RETURNS_NONNULL
441 const MemRegion *getRegion() const { return castDataAs<MemRegion>(); }
442
443 /// Get the underlining region and strip casts.
444 LLVM_ATTRIBUTE_RETURNS_NONNULL
445 const MemRegion* stripCasts(bool StripBaseCasts = true) const;
446
447 template <typename REGION>
448 const REGION* getRegionAs() const {
449 return dyn_cast<REGION>(getRegion());
450 }
451
452 bool operator==(const MemRegionVal &R) const {
453 return getRegion() == R.getRegion();
454 }
455
456 bool operator!=(const MemRegionVal &R) const {
457 return getRegion() != R.getRegion();
458 }
459
460 static bool classof(SVal V) { return V.getKind() == MemRegionValKind; }
461};
462
463class ConcreteInt : public Loc {
464public:
465 explicit ConcreteInt(const llvm::APSInt &V) : Loc(ConcreteIntKind, &V) {}
466
467 const llvm::APSInt &getValue() const { return *castDataAs<llvm::APSInt>(); }
468
469 static bool classof(SVal V) { return V.getKind() == ConcreteIntKind; }
470};
471
472} // namespace loc
473} // namespace ento
474} // namespace clang
475
476namespace llvm {
477template <typename To, typename From>
478struct CastInfo<
479 To, From,
480 std::enable_if_t<std::is_base_of<::clang::ento::SVal, From>::value>>
481 : public CastIsPossible<To, ::clang::ento::SVal> {
482 using Self = CastInfo<
483 To, From,
484 std::enable_if_t<std::is_base_of<::clang::ento::SVal, From>::value>>;
485 static bool isPossible(const From &V) {
486 return To::classof(*static_cast<const ::clang::ento::SVal *>(&V));
487 }
488 static std::optional<To> castFailed() { return std::optional<To>{}; }
489 static To doCast(const From &f) {
490 return *static_cast<const To *>(cast<::clang::ento::SVal>(&f));
491 }
492 static std::optional<To> doCastIfPossible(const From &f) {
493 if (!Self::isPossible(V: f))
494 return Self::castFailed();
495 return doCast(f);
496 }
497};
498} // namespace llvm
499
500#endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SVALS_H
501

source code of clang/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h