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 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 57 public: 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 VisitLocMemRegionVal(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 VisitLocConcreteInt(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 VisitNonLocSymbolVal(nonloc::SymbolVal V) { 88 return Visit(V.getSymbol()); 89 } 90 91 std::string VisitNonLocConcreteInt(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 VisitNonLocLazyCompoundVal(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