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
306