Details | Last modification | View Log | RSS feed
| Rev | Author | Line No. | Line |
|---|---|---|---|
| 14 | pmbaty | 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 |