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/DeclCXX.h"
19 #include "clang/StaticAnalyzer/Core/PathSensitive/SValVisitor.h"
20 
21 namespace clang {
22 
23 namespace ento {
24 
25 class SValExplainer : public FullSValVisitor<SValExplainer, std::string> {
26 private:
27   ASTContext &ACtx;
28 
printStmt(const Stmt * S)29   std::string printStmt(const Stmt *S) {
30     std::string Str;
31     llvm::raw_string_ostream OS(Str);
32     S->printPretty(OS, nullptr, PrintingPolicy(ACtx.getLangOpts()));
33     return OS.str();
34   }
35 
isThisObject(const SymbolicRegion * R)36   bool isThisObject(const SymbolicRegion *R) {
37     if (auto S = dyn_cast<SymbolRegionValue>(R->getSymbol()))
38       if (isa<CXXThisRegion>(S->getRegion()))
39         return true;
40     return false;
41   }
42 
43 public:
SValExplainer(ASTContext & Ctx)44   SValExplainer(ASTContext &Ctx) : ACtx(Ctx) {}
45 
VisitUnknownVal(UnknownVal V)46   std::string VisitUnknownVal(UnknownVal V) {
47     return "unknown value";
48   }
49 
VisitUndefinedVal(UndefinedVal V)50   std::string VisitUndefinedVal(UndefinedVal V) {
51     return "undefined value";
52   }
53 
VisitLocMemRegionVal(loc::MemRegionVal V)54   std::string VisitLocMemRegionVal(loc::MemRegionVal V) {
55     const MemRegion *R = V.getRegion();
56     // Avoid the weird "pointer to pointee of ...".
57     if (auto SR = dyn_cast<SymbolicRegion>(R)) {
58       // However, "pointer to 'this' object" is fine.
59       if (!isThisObject(SR))
60         return Visit(SR->getSymbol());
61     }
62     return "pointer to " + Visit(R);
63   }
64 
VisitLocConcreteInt(loc::ConcreteInt V)65   std::string VisitLocConcreteInt(loc::ConcreteInt V) {
66     llvm::APSInt I = V.getValue();
67     std::string Str;
68     llvm::raw_string_ostream OS(Str);
69     OS << "concrete memory address '" << I << "'";
70     return OS.str();
71   }
72 
VisitNonLocSymbolVal(nonloc::SymbolVal V)73   std::string VisitNonLocSymbolVal(nonloc::SymbolVal V) {
74     return Visit(V.getSymbol());
75   }
76 
VisitNonLocConcreteInt(nonloc::ConcreteInt V)77   std::string VisitNonLocConcreteInt(nonloc::ConcreteInt V) {
78     llvm::APSInt I = V.getValue();
79     std::string Str;
80     llvm::raw_string_ostream OS(Str);
81     OS << (I.isSigned() ? "signed " : "unsigned ") << I.getBitWidth()
82        << "-bit integer '" << I << "'";
83     return OS.str();
84   }
85 
VisitNonLocLazyCompoundVal(nonloc::LazyCompoundVal V)86   std::string VisitNonLocLazyCompoundVal(nonloc::LazyCompoundVal V) {
87     return "lazily frozen compound value of " + Visit(V.getRegion());
88   }
89 
VisitSymbolRegionValue(const SymbolRegionValue * S)90   std::string VisitSymbolRegionValue(const SymbolRegionValue *S) {
91     const MemRegion *R = S->getRegion();
92     // Special handling for argument values.
93     if (auto V = dyn_cast<VarRegion>(R))
94       if (auto D = dyn_cast<ParmVarDecl>(V->getDecl()))
95         return "argument '" + D->getQualifiedNameAsString() + "'";
96     return "initial value of " + Visit(R);
97   }
98 
VisitSymbolConjured(const SymbolConjured * S)99   std::string VisitSymbolConjured(const SymbolConjured *S) {
100     return "symbol of type '" + S->getType().getAsString() +
101            "' conjured at statement '" + printStmt(S->getStmt()) + "'";
102   }
103 
VisitSymbolDerived(const SymbolDerived * S)104   std::string VisitSymbolDerived(const SymbolDerived *S) {
105     return "value derived from (" + Visit(S->getParentSymbol()) +
106            ") for " + Visit(S->getRegion());
107   }
108 
VisitSymbolExtent(const SymbolExtent * S)109   std::string VisitSymbolExtent(const SymbolExtent *S) {
110     return "extent of " + Visit(S->getRegion());
111   }
112 
VisitSymbolMetadata(const SymbolMetadata * S)113   std::string VisitSymbolMetadata(const SymbolMetadata *S) {
114     return "metadata of type '" + S->getType().getAsString() + "' tied to " +
115            Visit(S->getRegion());
116   }
117 
VisitSymIntExpr(const SymIntExpr * S)118   std::string VisitSymIntExpr(const SymIntExpr *S) {
119     std::string Str;
120     llvm::raw_string_ostream OS(Str);
121     OS << "(" << Visit(S->getLHS()) << ") "
122        << std::string(BinaryOperator::getOpcodeStr(S->getOpcode())) << " "
123        << S->getRHS();
124     return OS.str();
125   }
126 
127   // TODO: IntSymExpr doesn't appear in practice.
128   // Add the relevant code once it does.
129 
VisitSymSymExpr(const SymSymExpr * S)130   std::string VisitSymSymExpr(const SymSymExpr *S) {
131     return "(" + Visit(S->getLHS()) + ") " +
132            std::string(BinaryOperator::getOpcodeStr(S->getOpcode())) +
133            " (" + Visit(S->getRHS()) + ")";
134   }
135 
136   // TODO: SymbolCast doesn't appear in practice.
137   // Add the relevant code once it does.
138 
VisitSymbolicRegion(const SymbolicRegion * R)139   std::string VisitSymbolicRegion(const SymbolicRegion *R) {
140     // Explain 'this' object here.
141     // TODO: Explain CXXThisRegion itself, find a way to test it.
142     if (isThisObject(R))
143       return "'this' object";
144     // Objective-C objects are not normal symbolic regions. At least,
145     // they're always on the heap.
146     if (R->getSymbol()->getType()
147             .getCanonicalType()->getAs<ObjCObjectPointerType>())
148       return "object at " + Visit(R->getSymbol());
149     // Other heap-based symbolic regions are also special.
150     if (isa<HeapSpaceRegion>(R->getMemorySpace()))
151       return "heap segment that starts at " + Visit(R->getSymbol());
152     return "pointee of " + Visit(R->getSymbol());
153   }
154 
VisitAllocaRegion(const AllocaRegion * R)155   std::string VisitAllocaRegion(const AllocaRegion *R) {
156     return "region allocated by '" + printStmt(R->getExpr()) + "'";
157   }
158 
VisitCompoundLiteralRegion(const CompoundLiteralRegion * R)159   std::string VisitCompoundLiteralRegion(const CompoundLiteralRegion *R) {
160     return "compound literal " + printStmt(R->getLiteralExpr());
161   }
162 
VisitStringRegion(const StringRegion * R)163   std::string VisitStringRegion(const StringRegion *R) {
164     return "string literal " + R->getString();
165   }
166 
VisitElementRegion(const ElementRegion * R)167   std::string VisitElementRegion(const ElementRegion *R) {
168     std::string Str;
169     llvm::raw_string_ostream OS(Str);
170     OS << "element of type '" << R->getElementType().getAsString()
171        << "' with index ";
172     // For concrete index: omit type of the index integer.
173     if (auto I = R->getIndex().getAs<nonloc::ConcreteInt>())
174       OS << I->getValue();
175     else
176       OS << "'" << Visit(R->getIndex()) << "'";
177     OS << " of " + Visit(R->getSuperRegion());
178     return OS.str();
179   }
180 
VisitVarRegion(const VarRegion * R)181   std::string VisitVarRegion(const VarRegion *R) {
182     const VarDecl *VD = R->getDecl();
183     std::string Name = VD->getQualifiedNameAsString();
184     if (isa<ParmVarDecl>(VD))
185       return "parameter '" + Name + "'";
186     else if (VD->hasAttr<BlocksAttr>())
187       return "block variable '" + Name + "'";
188     else if (VD->hasLocalStorage())
189       return "local variable '" + Name + "'";
190     else if (VD->isStaticLocal())
191       return "static local variable '" + Name + "'";
192     else if (VD->hasGlobalStorage())
193       return "global variable '" + Name + "'";
194     else
195       llvm_unreachable("A variable is either local or global");
196   }
197 
VisitObjCIvarRegion(const ObjCIvarRegion * R)198   std::string VisitObjCIvarRegion(const ObjCIvarRegion *R) {
199     return "instance variable '" + R->getDecl()->getNameAsString() + "' of " +
200            Visit(R->getSuperRegion());
201   }
202 
VisitFieldRegion(const FieldRegion * R)203   std::string VisitFieldRegion(const FieldRegion *R) {
204     return "field '" + R->getDecl()->getNameAsString() + "' of " +
205            Visit(R->getSuperRegion());
206   }
207 
VisitCXXTempObjectRegion(const CXXTempObjectRegion * R)208   std::string VisitCXXTempObjectRegion(const CXXTempObjectRegion *R) {
209     return "temporary object constructed at statement '" +
210            printStmt(R->getExpr()) + "'";
211   }
212 
VisitCXXBaseObjectRegion(const CXXBaseObjectRegion * R)213   std::string VisitCXXBaseObjectRegion(const CXXBaseObjectRegion *R) {
214     return "base object '" + R->getDecl()->getQualifiedNameAsString() +
215            "' inside " + Visit(R->getSuperRegion());
216   }
217 
VisitSVal(SVal V)218   std::string VisitSVal(SVal V) {
219     std::string Str;
220     llvm::raw_string_ostream OS(Str);
221     OS << V;
222     return "a value unsupported by the explainer: (" +
223            std::string(OS.str()) + ")";
224   }
225 
VisitSymExpr(SymbolRef S)226   std::string VisitSymExpr(SymbolRef S) {
227     std::string Str;
228     llvm::raw_string_ostream OS(Str);
229     S->dumpToStream(OS);
230     return "a symbolic expression unsupported by the explainer: (" +
231            std::string(OS.str()) + ")";
232   }
233 
VisitMemRegion(const MemRegion * R)234   std::string VisitMemRegion(const MemRegion *R) {
235     std::string Str;
236     llvm::raw_string_ostream OS(Str);
237     OS << R;
238     return "a memory region unsupported by the explainer (" +
239            std::string(OS.str()) + ")";
240   }
241 };
242 
243 } // end namespace ento
244 
245 } // end namespace clang
246 
247 #endif
248