1//== SValExplainer.h - Symbolic value explainer -----------------*- 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 SValExplainer, a class for pretty-printing a
10// human-readable description of a symbolic value. For example,
11// "reg_$0<x>" is turned into "initial value of variable 'x'".
12//
13//===----------------------------------------------------------------------===//
14
15#ifndef LLVM_CLANG_STATICANALYZER_CHECKERS_SVALEXPLAINER_H
16#define LLVM_CLANG_STATICANALYZER_CHECKERS_SVALEXPLAINER_H
17
18#include "clang/AST/Attr.h"
19#include "clang/AST/DeclCXX.h"
20#include "clang/StaticAnalyzer/Core/PathSensitive/SValVisitor.h"
21#include "llvm/ADT/StringExtras.h"
22
23namespace clang {
24
25namespace ento {
26
27class SValExplainer : public FullSValVisitor<SValExplainer, std::string> {
28private:
29 ASTContext &ACtx;
30
31 std::string printStmt(const Stmt *S) {
32 std::string Str;
33 llvm::raw_string_ostream OS(Str);
34 S->printPretty(OS, Helper: nullptr, Policy: PrintingPolicy(ACtx.getLangOpts()));
35 return Str;
36 }
37
38 bool isThisObject(const SymbolicRegion *R) {
39 if (auto S = dyn_cast<SymbolRegionValue>(Val: R->getSymbol()))
40 if (isa<CXXThisRegion>(Val: S->getRegion()))
41 return true;
42 return false;
43 }
44
45 bool isThisObject(const ElementRegion *R) {
46 if (const auto *Idx = R->getIndex().getAsInteger()) {
47 if (const auto *SR = R->getSuperRegion()->getAs<SymbolicRegion>()) {
48 QualType Ty = SR->getPointeeStaticType();
49 bool IsNotReinterpretCast = R->getValueType() == Ty;
50 if (Idx->isZero() && IsNotReinterpretCast)
51 return isThisObject(R: SR);
52 }
53 }
54 return false;
55 }
56
57public:
58 SValExplainer(ASTContext &Ctx) : ACtx(Ctx) {}
59
60 std::string VisitUnknownVal(UnknownVal V) {
61 return "unknown value";
62 }
63
64 std::string VisitUndefinedVal(UndefinedVal V) {
65 return "undefined value";
66 }
67
68 std::string VisitMemRegionVal(loc::MemRegionVal V) {
69 const MemRegion *R = V.getRegion();
70 // Avoid the weird "pointer to pointee of ...".
71 if (auto SR = dyn_cast<SymbolicRegion>(Val: R)) {
72 // However, "pointer to 'this' object" is fine.
73 if (!isThisObject(R: SR))
74 return Visit(S: SR->getSymbol());
75 }
76 return "pointer to " + Visit(R);
77 }
78
79 std::string VisitConcreteInt(loc::ConcreteInt V) {
80 const llvm::APSInt &I = V.getValue();
81 std::string Str;
82 llvm::raw_string_ostream OS(Str);
83 OS << "concrete memory address '" << I << "'";
84 return Str;
85 }
86
87 std::string VisitSymbolVal(nonloc::SymbolVal V) {
88 return Visit(S: V.getSymbol());
89 }
90
91 std::string VisitConcreteInt(nonloc::ConcreteInt V) {
92 const llvm::APSInt &I = V.getValue();
93 std::string Str;
94 llvm::raw_string_ostream OS(Str);
95 OS << (I.isSigned() ? "signed " : "unsigned ") << I.getBitWidth()
96 << "-bit integer '" << I << "'";
97 return Str;
98 }
99
100 std::string VisitLazyCompoundVal(nonloc::LazyCompoundVal V) {
101 return "lazily frozen compound value of " + Visit(R: V.getRegion());
102 }
103
104 std::string VisitSymbolRegionValue(const SymbolRegionValue *S) {
105 const MemRegion *R = S->getRegion();
106 // Special handling for argument values.
107 if (auto V = dyn_cast<VarRegion>(Val: R))
108 if (auto D = dyn_cast<ParmVarDecl>(Val: V->getDecl()))
109 return "argument '" + D->getQualifiedNameAsString() + "'";
110 return "initial value of " + Visit(R);
111 }
112
113 std::string VisitSymbolConjured(const SymbolConjured *S) {
114 return "symbol of type '" + S->getType().getAsString() +
115 "' conjured at statement '" + printStmt(S: S->getStmt()) + "'";
116 }
117
118 std::string VisitSymbolDerived(const SymbolDerived *S) {
119 return "value derived from (" + Visit(S: S->getParentSymbol()) +
120 ") for " + Visit(R: S->getRegion());
121 }
122
123 std::string VisitSymbolExtent(const SymbolExtent *S) {
124 return "extent of " + Visit(R: S->getRegion());
125 }
126
127 std::string VisitSymbolMetadata(const SymbolMetadata *S) {
128 return "metadata of type '" + S->getType().getAsString() + "' tied to " +
129 Visit(R: S->getRegion());
130 }
131
132 std::string VisitSymIntExpr(const SymIntExpr *S) {
133 std::string Str;
134 llvm::raw_string_ostream OS(Str);
135 OS << "(" << Visit(S: S->getLHS()) << ") "
136 << std::string(BinaryOperator::getOpcodeStr(Op: S->getOpcode())) << " "
137 << S->getRHS();
138 return Str;
139 }
140
141 // TODO: IntSymExpr doesn't appear in practice.
142 // Add the relevant code once it does.
143
144 std::string VisitSymSymExpr(const SymSymExpr *S) {
145 return "(" + Visit(S: S->getLHS()) + ") " +
146 std::string(BinaryOperator::getOpcodeStr(Op: S->getOpcode())) +
147 " (" + Visit(S: S->getRHS()) + ")";
148 }
149
150 std::string VisitUnarySymExpr(const UnarySymExpr *S) {
151 return std::string(UnaryOperator::getOpcodeStr(Op: S->getOpcode())) + " (" +
152 Visit(S: S->getOperand()) + ")";
153 }
154
155 // TODO: SymbolCast doesn't appear in practice.
156 // Add the relevant code once it does.
157
158 std::string VisitSymbolicRegion(const SymbolicRegion *R) {
159 // Explain 'this' object here - if it's not wrapped by an ElementRegion.
160 // TODO: Explain CXXThisRegion itself, find a way to test it.
161 if (isThisObject(R))
162 return "'this' object";
163 // Objective-C objects are not normal symbolic regions. At least,
164 // they're always on the heap.
165 if (R->getSymbol()->getType()
166 .getCanonicalType()->getAs<ObjCObjectPointerType>())
167 return "object at " + Visit(S: R->getSymbol());
168 // Other heap-based symbolic regions are also special.
169 if (isa<HeapSpaceRegion>(Val: R->getMemorySpace()))
170 return "heap segment that starts at " + Visit(S: R->getSymbol());
171 return "pointee of " + Visit(S: R->getSymbol());
172 }
173
174 std::string VisitAllocaRegion(const AllocaRegion *R) {
175 return "region allocated by '" + printStmt(S: R->getExpr()) + "'";
176 }
177
178 std::string VisitCompoundLiteralRegion(const CompoundLiteralRegion *R) {
179 return "compound literal " + printStmt(S: R->getLiteralExpr());
180 }
181
182 std::string VisitStringRegion(const StringRegion *R) {
183 return "string literal " + R->getString();
184 }
185
186 std::string VisitElementRegion(const ElementRegion *R) {
187 std::string Str;
188 llvm::raw_string_ostream OS(Str);
189
190 // Explain 'this' object here.
191 // They are represented by a SymRegion wrapped by an ElementRegion; so
192 // match and handle it here.
193 if (isThisObject(R))
194 return "'this' object";
195
196 OS << "element of type '" << R->getElementType() << "' with index ";
197 // For concrete index: omit type of the index integer.
198 if (auto I = R->getIndex().getAs<nonloc::ConcreteInt>())
199 OS << I->getValue();
200 else
201 OS << "'" << Visit(V: R->getIndex()) << "'";
202 OS << " of " + Visit(R: R->getSuperRegion());
203 return Str;
204 }
205
206 std::string VisitNonParamVarRegion(const NonParamVarRegion *R) {
207 const VarDecl *VD = R->getDecl();
208 std::string Name = VD->getQualifiedNameAsString();
209 if (isa<ParmVarDecl>(Val: VD))
210 return "parameter '" + Name + "'";
211 else if (VD->hasAttr<BlocksAttr>())
212 return "block variable '" + Name + "'";
213 else if (VD->hasLocalStorage())
214 return "local variable '" + Name + "'";
215 else if (VD->isStaticLocal())
216 return "static local variable '" + Name + "'";
217 else if (VD->hasGlobalStorage())
218 return "global variable '" + Name + "'";
219 else
220 llvm_unreachable("A variable is either local or global");
221 }
222
223 std::string VisitObjCIvarRegion(const ObjCIvarRegion *R) {
224 return "instance variable '" + R->getDecl()->getNameAsString() + "' of " +
225 Visit(R: R->getSuperRegion());
226 }
227
228 std::string VisitFieldRegion(const FieldRegion *R) {
229 return "field '" + R->getDecl()->getNameAsString() + "' of " +
230 Visit(R: R->getSuperRegion());
231 }
232
233 std::string VisitCXXTempObjectRegion(const CXXTempObjectRegion *R) {
234 return "temporary object constructed at statement '" +
235 printStmt(S: R->getExpr()) + "'";
236 }
237
238 std::string VisitCXXBaseObjectRegion(const CXXBaseObjectRegion *R) {
239 return "base object '" + R->getDecl()->getQualifiedNameAsString() +
240 "' inside " + Visit(R: R->getSuperRegion());
241 }
242
243 std::string VisitParamVarRegion(const ParamVarRegion *R) {
244 std::string Str;
245 llvm::raw_string_ostream OS(Str);
246
247 const ParmVarDecl *PVD = R->getDecl();
248 std::string Name = PVD->getQualifiedNameAsString();
249 if (!Name.empty()) {
250 OS << "parameter '" << Name << "'";
251 return std::string(OS.str());
252 }
253
254 unsigned Index = R->getIndex() + 1;
255 OS << Index << llvm::getOrdinalSuffix(Val: Index) << " parameter of ";
256 const Decl *Parent = R->getStackFrame()->getDecl();
257 if (const auto *FD = dyn_cast<FunctionDecl>(Val: Parent))
258 OS << "function '" << FD->getQualifiedNameAsString() << "()'";
259 else if (const auto *CD = dyn_cast<CXXConstructorDecl>(Val: Parent))
260 OS << "C++ constructor '" << CD->getQualifiedNameAsString() << "()'";
261 else if (const auto *MD = dyn_cast<ObjCMethodDecl>(Val: Parent)) {
262 if (MD->isClassMethod())
263 OS << "Objective-C method '+" << MD->getQualifiedNameAsString() << "'";
264 else
265 OS << "Objective-C method '-" << MD->getQualifiedNameAsString() << "'";
266 } else if (isa<BlockDecl>(Val: Parent)) {
267 if (cast<BlockDecl>(Val: Parent)->isConversionFromLambda())
268 OS << "lambda";
269 else
270 OS << "block";
271 }
272
273 return std::string(OS.str());
274 }
275
276 std::string VisitSVal(SVal V) {
277 std::string Str;
278 llvm::raw_string_ostream OS(Str);
279 OS << V;
280 return "a value unsupported by the explainer: (" +
281 std::string(OS.str()) + ")";
282 }
283
284 std::string VisitSymExpr(SymbolRef S) {
285 std::string Str;
286 llvm::raw_string_ostream OS(Str);
287 S->dumpToStream(os&: OS);
288 return "a symbolic expression unsupported by the explainer: (" +
289 std::string(OS.str()) + ")";
290 }
291
292 std::string VisitMemRegion(const MemRegion *R) {
293 std::string Str;
294 llvm::raw_string_ostream OS(Str);
295 OS << R;
296 return "a memory region unsupported by the explainer (" +
297 std::string(OS.str()) + ")";
298 }
299};
300
301} // end namespace ento
302
303} // end namespace clang
304
305#endif
306