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