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