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