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