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