Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
14 | pmbaty | 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, nullptr, PrintingPolicy(ACtx.getLangOpts())); |
||
35 | return Str; |
||
36 | } |
||
37 | |||
38 | bool isThisObject(const SymbolicRegion *R) { |
||
39 | if (auto S = dyn_cast<SymbolRegionValue>(R->getSymbol())) |
||
40 | if (isa<CXXThisRegion>(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(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 VisitLocMemRegionVal(loc::MemRegionVal V) { |
||
69 | const MemRegion *R = V.getRegion(); |
||
70 | // Avoid the weird "pointer to pointee of ...". |
||
71 | if (auto SR = dyn_cast<SymbolicRegion>(R)) { |
||
72 | // However, "pointer to 'this' object" is fine. |
||
73 | if (!isThisObject(SR)) |
||
74 | return Visit(SR->getSymbol()); |
||
75 | } |
||
76 | return "pointer to " + Visit(R); |
||
77 | } |
||
78 | |||
79 | std::string VisitLocConcreteInt(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 VisitNonLocSymbolVal(nonloc::SymbolVal V) { |
||
88 | return Visit(V.getSymbol()); |
||
89 | } |
||
90 | |||
91 | std::string VisitNonLocConcreteInt(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 VisitNonLocLazyCompoundVal(nonloc::LazyCompoundVal V) { |
||
101 | return "lazily frozen compound value of " + Visit(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>(R)) |
||
108 | if (auto D = dyn_cast<ParmVarDecl>(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->getStmt()) + "'"; |
||
116 | } |
||
117 | |||
118 | std::string VisitSymbolDerived(const SymbolDerived *S) { |
||
119 | return "value derived from (" + Visit(S->getParentSymbol()) + |
||
120 | ") for " + Visit(S->getRegion()); |
||
121 | } |
||
122 | |||
123 | std::string VisitSymbolExtent(const SymbolExtent *S) { |
||
124 | return "extent of " + Visit(S->getRegion()); |
||
125 | } |
||
126 | |||
127 | std::string VisitSymbolMetadata(const SymbolMetadata *S) { |
||
128 | return "metadata of type '" + S->getType().getAsString() + "' tied to " + |
||
129 | Visit(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->getLHS()) << ") " |
||
136 | << std::string(BinaryOperator::getOpcodeStr(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->getLHS()) + ") " + |
||
146 | std::string(BinaryOperator::getOpcodeStr(S->getOpcode())) + |
||
147 | " (" + Visit(S->getRHS()) + ")"; |
||
148 | } |
||
149 | |||
150 | std::string VisitUnarySymExpr(const UnarySymExpr *S) { |
||
151 | return std::string(UnaryOperator::getOpcodeStr(S->getOpcode())) + " (" + |
||
152 | Visit(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(R->getSymbol()); |
||
168 | // Other heap-based symbolic regions are also special. |
||
169 | if (isa<HeapSpaceRegion>(R->getMemorySpace())) |
||
170 | return "heap segment that starts at " + Visit(R->getSymbol()); |
||
171 | return "pointee of " + Visit(R->getSymbol()); |
||
172 | } |
||
173 | |||
174 | std::string VisitAllocaRegion(const AllocaRegion *R) { |
||
175 | return "region allocated by '" + printStmt(R->getExpr()) + "'"; |
||
176 | } |
||
177 | |||
178 | std::string VisitCompoundLiteralRegion(const CompoundLiteralRegion *R) { |
||
179 | return "compound literal " + printStmt(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(R->getIndex()) << "'"; |
||
202 | OS << " of " + Visit(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>(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->getSuperRegion()); |
||
226 | } |
||
227 | |||
228 | std::string VisitFieldRegion(const FieldRegion *R) { |
||
229 | return "field '" + R->getDecl()->getNameAsString() + "' of " + |
||
230 | Visit(R->getSuperRegion()); |
||
231 | } |
||
232 | |||
233 | std::string VisitCXXTempObjectRegion(const CXXTempObjectRegion *R) { |
||
234 | return "temporary object constructed at statement '" + |
||
235 | printStmt(R->getExpr()) + "'"; |
||
236 | } |
||
237 | |||
238 | std::string VisitCXXBaseObjectRegion(const CXXBaseObjectRegion *R) { |
||
239 | return "base object '" + R->getDecl()->getQualifiedNameAsString() + |
||
240 | "' inside " + Visit(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(Index) << " parameter of "; |
||
256 | const Decl *Parent = R->getStackFrame()->getDecl(); |
||
257 | if (const auto *FD = dyn_cast<FunctionDecl>(Parent)) |
||
258 | OS << "function '" << FD->getQualifiedNameAsString() << "()'"; |
||
259 | else if (const auto *CD = dyn_cast<CXXConstructorDecl>(Parent)) |
||
260 | OS << "C++ constructor '" << CD->getQualifiedNameAsString() << "()'"; |
||
261 | else if (const auto *MD = dyn_cast<ObjCMethodDecl>(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>(Parent)) { |
||
267 | if (cast<BlockDecl>(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); |
||
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 |