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 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 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 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 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 std::string VisitUnarySymExpr(const UnarySymExpr *S) { 139 return std::string(UnaryOperator::getOpcodeStr(S->getOpcode())) + " (" + 140 Visit(S->getOperand()) + ")"; 141 } 142 143 // TODO: SymbolCast doesn't appear in practice. 144 // Add the relevant code once it does. 145 146 std::string VisitSymbolicRegion(const SymbolicRegion *R) { 147 // Explain 'this' object here. 148 // TODO: Explain CXXThisRegion itself, find a way to test it. 149 if (isThisObject(R)) 150 return "'this' object"; 151 // Objective-C objects are not normal symbolic regions. At least, 152 // they're always on the heap. 153 if (R->getSymbol()->getType() 154 .getCanonicalType()->getAs<ObjCObjectPointerType>()) 155 return "object at " + Visit(R->getSymbol()); 156 // Other heap-based symbolic regions are also special. 157 if (isa<HeapSpaceRegion>(R->getMemorySpace())) 158 return "heap segment that starts at " + Visit(R->getSymbol()); 159 return "pointee of " + Visit(R->getSymbol()); 160 } 161 162 std::string VisitAllocaRegion(const AllocaRegion *R) { 163 return "region allocated by '" + printStmt(R->getExpr()) + "'"; 164 } 165 166 std::string VisitCompoundLiteralRegion(const CompoundLiteralRegion *R) { 167 return "compound literal " + printStmt(R->getLiteralExpr()); 168 } 169 170 std::string VisitStringRegion(const StringRegion *R) { 171 return "string literal " + R->getString(); 172 } 173 174 std::string VisitElementRegion(const ElementRegion *R) { 175 std::string Str; 176 llvm::raw_string_ostream OS(Str); 177 OS << "element of type '" << R->getElementType() << "' with index "; 178 // For concrete index: omit type of the index integer. 179 if (auto I = R->getIndex().getAs<nonloc::ConcreteInt>()) 180 OS << I->getValue(); 181 else 182 OS << "'" << Visit(R->getIndex()) << "'"; 183 OS << " of " + Visit(R->getSuperRegion()); 184 return Str; 185 } 186 187 std::string VisitNonParamVarRegion(const NonParamVarRegion *R) { 188 const VarDecl *VD = R->getDecl(); 189 std::string Name = VD->getQualifiedNameAsString(); 190 if (isa<ParmVarDecl>(VD)) 191 return "parameter '" + Name + "'"; 192 else if (VD->hasAttr<BlocksAttr>()) 193 return "block variable '" + Name + "'"; 194 else if (VD->hasLocalStorage()) 195 return "local variable '" + Name + "'"; 196 else if (VD->isStaticLocal()) 197 return "static local variable '" + Name + "'"; 198 else if (VD->hasGlobalStorage()) 199 return "global variable '" + Name + "'"; 200 else 201 llvm_unreachable("A variable is either local or global"); 202 } 203 204 std::string VisitObjCIvarRegion(const ObjCIvarRegion *R) { 205 return "instance variable '" + R->getDecl()->getNameAsString() + "' of " + 206 Visit(R->getSuperRegion()); 207 } 208 209 std::string VisitFieldRegion(const FieldRegion *R) { 210 return "field '" + R->getDecl()->getNameAsString() + "' of " + 211 Visit(R->getSuperRegion()); 212 } 213 214 std::string VisitCXXTempObjectRegion(const CXXTempObjectRegion *R) { 215 return "temporary object constructed at statement '" + 216 printStmt(R->getExpr()) + "'"; 217 } 218 219 std::string VisitCXXBaseObjectRegion(const CXXBaseObjectRegion *R) { 220 return "base object '" + R->getDecl()->getQualifiedNameAsString() + 221 "' inside " + Visit(R->getSuperRegion()); 222 } 223 224 std::string VisitParamVarRegion(const ParamVarRegion *R) { 225 std::string Str; 226 llvm::raw_string_ostream OS(Str); 227 228 const ParmVarDecl *PVD = R->getDecl(); 229 std::string Name = PVD->getQualifiedNameAsString(); 230 if (!Name.empty()) { 231 OS << "parameter '" << Name << "'"; 232 return std::string(OS.str()); 233 } 234 235 unsigned Index = R->getIndex() + 1; 236 OS << Index << llvm::getOrdinalSuffix(Index) << " parameter of "; 237 const Decl *Parent = R->getStackFrame()->getDecl(); 238 if (const auto *FD = dyn_cast<FunctionDecl>(Parent)) 239 OS << "function '" << FD->getQualifiedNameAsString() << "()'"; 240 else if (const auto *CD = dyn_cast<CXXConstructorDecl>(Parent)) 241 OS << "C++ constructor '" << CD->getQualifiedNameAsString() << "()'"; 242 else if (const auto *MD = dyn_cast<ObjCMethodDecl>(Parent)) { 243 if (MD->isClassMethod()) 244 OS << "Objective-C method '+" << MD->getQualifiedNameAsString() << "'"; 245 else 246 OS << "Objective-C method '-" << MD->getQualifiedNameAsString() << "'"; 247 } else if (isa<BlockDecl>(Parent)) { 248 if (cast<BlockDecl>(Parent)->isConversionFromLambda()) 249 OS << "lambda"; 250 else 251 OS << "block"; 252 } 253 254 return std::string(OS.str()); 255 } 256 257 std::string VisitSVal(SVal V) { 258 std::string Str; 259 llvm::raw_string_ostream OS(Str); 260 OS << V; 261 return "a value unsupported by the explainer: (" + 262 std::string(OS.str()) + ")"; 263 } 264 265 std::string VisitSymExpr(SymbolRef S) { 266 std::string Str; 267 llvm::raw_string_ostream OS(Str); 268 S->dumpToStream(OS); 269 return "a symbolic expression unsupported by the explainer: (" + 270 std::string(OS.str()) + ")"; 271 } 272 273 std::string VisitMemRegion(const MemRegion *R) { 274 std::string Str; 275 llvm::raw_string_ostream OS(Str); 276 OS << R; 277 return "a memory region unsupported by the explainer (" + 278 std::string(OS.str()) + ")"; 279 } 280 }; 281 282 } // end namespace ento 283 284 } // end namespace clang 285 286 #endif 287