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 | |
23 | namespace clang { |
24 | |
25 | namespace ento { |
26 | |
27 | class SValExplainer : public FullSValVisitor<SValExplainer, std::string> { |
28 | private: |
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 | |
57 | public: |
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 |