NoQ updated this revision to Diff 43683.
NoQ marked an inline comment as done.
NoQ added a comment.

An attempt on the .def-files.

The next step would probably be the `VisitChildren()` thing, and I'll see if it 
allows to refactor and simplify some code.

______

Forgot to answer: I guess there are a few minor-but-good things about the 
visitors in our case compared in-class methods (such as `dumpToStream()`):

1. Easy to develop incrementally - no need to put stubs into all subclasses for 
methods we didn't yet implement.
2. Easy to create incomplete visitors (eg. we want to visit only `SVal`'s that 
appear as `Store` values, and we won't ever see a `MemSpaceRegion` appear as a 
`Store` value)
3. Cover the whole sub-class with a single method (eg. 
`VisitTypedValueRegion()` covers all kinds of `TypedValueRegion`'s).
4. Easy to create checker-specific traversal methods - if a particular checker 
needs to visit the hierarchy, it's not forced to adjust all classes.

The question in what way the visitor is better than a recursive function with a 
very large switch is a bit more complicated (because a visitor essentially 
//is// a recursive function with a very large switch); only point 3 of the 
above still applies. With `VisitChildren()`, however, it becomes much more 
convenient.

Hmm. One more thing about `VisitChildren()`: Normally such method is 
re-implemented in every visitor easily using child iterators. We don't, 
however, have fully functional iterators for `SVal`/`SymExpr`/`MemRegion` 
children (only partial solutions like `symbol_begin()`..`symbol_end()`), and 
also such iterators would need to be polymorphic around these three classes 
(eg. a symbol-child of a region). In fact, i can make such polymorphic 
iterators, and that'd probably be a more generic solution.


http://reviews.llvm.org/D15448

Files:
  docs/analyzer/DebugChecks.rst
  include/clang/StaticAnalyzer/Checkers/SValExplainer.h
  include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h
  include/clang/StaticAnalyzer/Core/PathSensitive/Regions.def
  include/clang/StaticAnalyzer/Core/PathSensitive/SValVisitor.h
  include/clang/StaticAnalyzer/Core/PathSensitive/SVals.def
  include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h
  include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h
  include/clang/StaticAnalyzer/Core/PathSensitive/Symbols.def
  lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp
  test/Analysis/explain-svals.cpp

Index: test/Analysis/explain-svals.cpp
===================================================================
--- /dev/null
+++ test/Analysis/explain-svals.cpp
@@ -0,0 +1,98 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=core.builtin,debug.ExprInspection,unix.cstring -verify %s
+
+typedef unsigned long size_t;
+
+struct S {
+  struct S3 {
+    int y[10];
+  };
+  struct S2 : S3 {
+    int *x;
+  } s2[10];
+  int z;
+};
+
+
+void clang_analyzer_explain(int);
+void clang_analyzer_explain(void *);
+void clang_analyzer_explain(S);
+
+size_t clang_analyzer_getExtent(void *);
+
+size_t strlen(const char *);
+
+int conjure();
+S conjure_S();
+
+int glob;
+static int stat_glob;
+void *glob_ptr;
+
+// Test strings are regex'ed because we need to match exact string
+// rather than a substring.
+
+void test_1(int param, void *ptr) {
+  clang_analyzer_explain(&glob); // expected-warning-re{{{{^pointer to global variable 'glob'$}}}}
+  clang_analyzer_explain(param); // expected-warning-re{{{{^argument 'param'$}}}}
+  clang_analyzer_explain(ptr); // expected-warning-re{{{{^argument 'ptr'$}}}}
+  if (param == 42)
+    clang_analyzer_explain(param); // expected-warning-re{{{{^signed 32-bit integer '42'$}}}}
+}
+
+void test_2(char *ptr, int ext) {
+  clang_analyzer_explain((void *) "asdf"); // expected-warning-re{{{{^pointer to element of type 'char' with index 0 of string literal "asdf"$}}}}
+  clang_analyzer_explain(strlen(ptr)); // expected-warning-re{{{{^metadata of type 'unsigned long' tied to pointee of argument 'ptr'$}}}}
+  clang_analyzer_explain(conjure()); // expected-warning-re{{{{^symbol of type 'int' conjured at statement 'conjure\(\)'$}}}}
+  clang_analyzer_explain(glob); // expected-warning-re{{{{^value derived from \(symbol of type 'int' conjured at statement 'conjure\(\)'\) for global variable 'glob'$}}}}
+  clang_analyzer_explain(glob_ptr); // expected-warning-re{{{{^value derived from \(symbol of type 'int' conjured at statement 'conjure\(\)'\) for global variable 'glob_ptr'$}}}}
+  clang_analyzer_explain(clang_analyzer_getExtent(ptr)); // expected-warning-re{{{{^extent of pointee of argument 'ptr'$}}}}
+  int *x = new int[ext];
+  clang_analyzer_explain(x); // expected-warning-re{{{{^pointer to element of type 'int' with index 0 of pointee of symbol of type 'int \*' conjured at statement 'new int \[ext\]'$}}}}
+  // Sic! What gets computed is the extent of the element-region.
+  clang_analyzer_explain(clang_analyzer_getExtent(x)); // expected-warning-re{{{{^signed 32-bit integer '4'$}}}}
+  delete[] x;
+}
+
+void test_3(S s) {
+  clang_analyzer_explain(&s); // expected-warning-re{{{{^pointer to parameter 's'$}}}}
+  clang_analyzer_explain(s.z); // expected-warning-re{{{{^initial value of field 'z' of parameter 's'$}}}}
+  clang_analyzer_explain(&s.s2[5].y[3]); // expected-warning-re{{{{^pointer to element of type 'int' with index 3 of field 'y' of base object 'S::S3' inside element of type 'struct S::S2' with index 5 of field 's2' of parameter 's'$}}}}
+  if (!s.s2[7].x) {
+    clang_analyzer_explain(s.s2[7].x); // expected-warning-re{{{{^concrete memory address '0'$}}}}
+    // FIXME: we need to be explaining '1' rather than '0' here; not explainer bug.
+    clang_analyzer_explain(s.s2[7].x + 1); // expected-warning-re{{{{^concrete memory address '0'$}}}}
+  }
+}
+
+void test_4(int x, int y) {
+  int z;
+  static int stat;
+  clang_analyzer_explain(x + 1); // expected-warning-re{{{{^\(argument 'x'\) \+ 1$}}}}
+  clang_analyzer_explain(1 + y); // expected-warning-re{{{{^\(argument 'y'\) \+ 1$}}}}
+  clang_analyzer_explain(x + y); // expected-warning-re{{{{^unknown value$}}}}
+  clang_analyzer_explain(z); // expected-warning-re{{{{^undefined value$}}}}
+  clang_analyzer_explain(&z); // expected-warning-re{{{{^pointer to local variable 'z'$}}}}
+  clang_analyzer_explain(stat); // expected-warning-re{{{{^signed 32-bit integer '0'$}}}}
+  clang_analyzer_explain(&stat); // expected-warning-re{{{{^pointer to static local variable 'stat'$}}}}
+  clang_analyzer_explain(stat_glob); // expected-warning-re{{{{^initial value of global variable 'stat_glob'$}}}}
+  clang_analyzer_explain(&stat_glob); // expected-warning-re{{{{^pointer to global variable 'stat_glob'$}}}}
+  clang_analyzer_explain((int[]){1, 2, 3}); // expected-warning-re{{{{^pointer to element of type 'int' with index 0 of compound literal \(int \[3\]\)\{1, 2, 3\}$}}}}
+}
+
+namespace {
+class C {
+  int x[10];
+
+public:
+  void test_5(int i) {
+    clang_analyzer_explain(this); // expected-warning-re{{{{^pointer to 'this' object$}}}}
+    clang_analyzer_explain(&x[i]); // expected-warning-re{{{{^pointer to element of type 'int' with index 'argument 'i'' of field 'x' of 'this' object$}}}}
+    clang_analyzer_explain(__builtin_alloca(i)); // expected-warning-re{{{{^pointer to region allocated by '__builtin_alloca\(i\)'$}}}}
+  }
+};
+} // end of anonymous namespace
+
+void test_6() {
+  clang_analyzer_explain(conjure_S()); // expected-warning-re{{{{^lazily frozen compound value of temporary object constructed at statement 'conjure_S\(\)'$}}}}
+  clang_analyzer_explain(conjure_S().z); // expected-warning-re{{{{^value derived from \(symbol of type 'struct S' conjured at statement 'conjure_S\(\)'\) for field 'z' of temporary object constructed at statement 'conjure_S\(\)'$}}}}
+}
Index: lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp
===================================================================
--- lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp
+++ lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp
@@ -11,6 +11,7 @@
 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
 #include "clang/StaticAnalyzer/Core/Checker.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Checkers/SValExplainer.h"
 #include "llvm/ADT/StringSwitch.h"
 
 using namespace clang;
@@ -25,17 +26,21 @@
   void analyzerWarnIfReached(const CallExpr *CE, CheckerContext &C) const;
   void analyzerCrash(const CallExpr *CE, CheckerContext &C) const;
   void analyzerWarnOnDeadSymbol(const CallExpr *CE, CheckerContext &C) const;
+  void analyzerExplain(const CallExpr *CE, CheckerContext &C) const;
+  void analyzerGetExtent(const CallExpr *CE, CheckerContext &C) const;
 
   typedef void (ExprInspectionChecker::*FnCheck)(const CallExpr *,
                                                  CheckerContext &C) const;
 
+  void reportBug(llvm::StringRef Msg, CheckerContext &C) const;
+
 public:
   bool evalCall(const CallExpr *CE, CheckerContext &C) const;
   void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
 };
 }
 
-REGISTER_SET_WITH_PROGRAMSTATE(MarkedSymbols, const void *)
+REGISTER_SET_WITH_PROGRAMSTATE(MarkedSymbols, SymbolRef)
 
 bool ExprInspectionChecker::evalCall(const CallExpr *CE,
                                      CheckerContext &C) const {
@@ -50,6 +55,8 @@
           &ExprInspectionChecker::analyzerWarnIfReached)
     .Case("clang_analyzer_warnOnDeadSymbol",
           &ExprInspectionChecker::analyzerWarnOnDeadSymbol)
+    .Case("clang_analyzer_explain", &ExprInspectionChecker::analyzerExplain)
+    .Case("clang_analyzer_getExtent", &ExprInspectionChecker::analyzerGetExtent)
     .Default(nullptr);
 
   if (!Handler)
@@ -91,6 +98,18 @@
   }
 }
 
+void ExprInspectionChecker::reportBug(llvm::StringRef Msg,
+                                      CheckerContext &C) const {
+  if (!BT)
+    BT.reset(new BugType(this, "Checking analyzer assumptions", "debug"));
+
+  ExplodedNode *N = C.generateNonFatalErrorNode();
+  if (!N)
+    return;
+
+  C.emitReport(llvm::make_unique<BugReport>(*BT, Msg, N));
+}
+
 void ExprInspectionChecker::analyzerEval(const CallExpr *CE,
                                          CheckerContext &C) const {
   const LocationContext *LC = C.getPredecessor()->getLocationContext();
@@ -100,26 +119,12 @@
   if (LC->getCurrentStackFrame()->getParent() != nullptr)
     return;
 
-  if (!BT)
-    BT.reset(new BugType(this, "Checking analyzer assumptions", "debug"));
-
-  ExplodedNode *N = C.generateNonFatalErrorNode();
-  if (!N)
-    return;
-  C.emitReport(
-      llvm::make_unique<BugReport>(*BT, getArgumentValueString(CE, C), N));
+  reportBug(getArgumentValueString(CE, C), C);
 }
 
 void ExprInspectionChecker::analyzerWarnIfReached(const CallExpr *CE,
                                                   CheckerContext &C) const {
-
-  if (!BT)
-    BT.reset(new BugType(this, "Checking analyzer assumptions", "debug"));
-
-  ExplodedNode *N = C.generateNonFatalErrorNode();
-  if (!N)
-    return;
-  C.emitReport(llvm::make_unique<BugReport>(*BT, "REACHABLE", N));
+  reportBug("REACHABLE", C);
 }
 
 void ExprInspectionChecker::analyzerCheckInlined(const CallExpr *CE,
@@ -134,14 +139,32 @@
   if (LC->getCurrentStackFrame()->getParent() == nullptr)
     return;
 
-  if (!BT)
-    BT.reset(new BugType(this, "Checking analyzer assumptions", "debug"));
+  reportBug(getArgumentValueString(CE, C), C);
+}
 
-  ExplodedNode *N = C.generateNonFatalErrorNode();
-  if (!N)
-    return;
-  C.emitReport(
-      llvm::make_unique<BugReport>(*BT, getArgumentValueString(CE, C), N));
+void ExprInspectionChecker::analyzerExplain(const CallExpr *CE,
+                                            CheckerContext &C) const {
+  if (CE->getNumArgs() == 0)
+    reportBug("Missing argument for explaining", C);
+
+  SVal V = C.getSVal(CE->getArg(0));
+  SValExplainer Ex(C.getASTContext());
+  reportBug(Ex.Visit(V), C);
+}
+
+void ExprInspectionChecker::analyzerGetExtent(const CallExpr *CE,
+                                              CheckerContext &C) const {
+  if (CE->getNumArgs() == 0)
+    reportBug("Missing region for obtaining extent", C);
+
+  auto MR = dyn_cast_or_null<SubRegion>(C.getSVal(CE->getArg(0)).getAsRegion());
+  if (!MR)
+    reportBug("Obtaining extent of a non-region", C);
+
+  ProgramStateRef State = C.getState();
+  State = State->BindExpr(CE, C.getLocationContext(),
+                          MR->getExtent(C.getSValBuilder()));
+  C.addTransition(State);
 }
 
 void ExprInspectionChecker::analyzerWarnOnDeadSymbol(const CallExpr *CE,
@@ -163,20 +186,14 @@
   ProgramStateRef State = C.getState();
   const MarkedSymbolsTy &Syms = State->get<MarkedSymbols>();
   for (auto I = Syms.begin(), E = Syms.end(); I != E; ++I) {
-    SymbolRef Sym = static_cast<SymbolRef>(*I);
+    SymbolRef Sym = *I;
     if (!SymReaper.isDead(Sym))
       continue;
 
-    if (!BT)
-      BT.reset(new BugType(this, "Checking analyzer assumptions", "debug"));
-
-    ExplodedNode *N = C.generateNonFatalErrorNode();
-    if (!N)
-      return;
-
-    C.emitReport(llvm::make_unique<BugReport>(*BT, "SYMBOL DEAD", N));
-    C.addTransition(State->remove<MarkedSymbols>(Sym), N);
+    reportBug("SYMBOL DEAD", C);
+    State = State->remove<MarkedSymbols>(Sym);
   }
+  C.addTransition(State);
 }
 
 void ExprInspectionChecker::analyzerCrash(const CallExpr *CE,
Index: include/clang/StaticAnalyzer/Core/PathSensitive/Symbols.def
===================================================================
--- /dev/null
+++ include/clang/StaticAnalyzer/Core/PathSensitive/Symbols.def
@@ -0,0 +1,56 @@
+//===-- Symbols.def - Metadata about SymExpr kinds --------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// The list of symbols (SymExpr kinds) used in the Static Analyzer.
+// In order to use this information, users of this file must define
+// one or more of the three macros:
+//
+// SYMBOL(Id, Kind, Parent) - for specific SymExpr sub-kinds,
+// with kind-enum value specified, because those are named in
+// an often unobvious manner,
+//
+// ABSTRACT_SYMBOL(Id, Parent) - for abstract symbol classes,
+//
+// SYMBOL_RANGE(Id, First, Last) - for ranges of kind-enums,
+// allowing to determine abstract class of a symbol
+// based on the kind-enum value.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SYMBOL
+#define SYMBOL(Id, Kind, Parent)
+#endif
+
+#ifndef ABSTRACT_SYMBOL
+#define ABSTRACT_SYMBOL(Id, Parent)
+#endif
+
+#ifndef SYMBOL_RANGE
+#define SYMBOL_RANGE(Id, First, Last)
+#endif
+
+ABSTRACT_SYMBOL(BinarySymExpr, SymExpr)
+  SYMBOL(IntSymExpr, IntSymKind, BinarySymExpr)
+  SYMBOL(SymIntExpr, SymIntKind, BinarySymExpr)
+  SYMBOL(SymSymExpr, SymSymKind, BinarySymExpr)
+SYMBOL_RANGE(BINARYSYMEXPRS, IntSymKind, SymSymKind)
+
+SYMBOL(SymbolCast, CastSymbolKind, SymExpr)
+
+ABSTRACT_SYMBOL(SymbolData, SymExpr)
+  SYMBOL(SymbolConjured, ConjuredKind, SymbolData)
+  SYMBOL(SymbolDerived, DerivedKind, SymbolData)
+  SYMBOL(SymbolExtent, ExtentKind, SymbolData)
+  SYMBOL(SymbolMetadata, MetadataKind, SymbolData)
+  SYMBOL(SymbolRegionValue, RegionValueKind, SymbolData)
+SYMBOL_RANGE(SYMBOLS, ConjuredKind, RegionValueKind)
+
+#undef SYMBOL
+#undef ABSTRACT_SYMBOL
+#undef SYMBOL_RANGE
Index: include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h
===================================================================
--- include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h
+++ include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h
@@ -42,14 +42,11 @@
 class SymExpr : public llvm::FoldingSetNode {
   virtual void anchor();
 public:
-  enum Kind { RegionValueKind, ConjuredKind, DerivedKind, ExtentKind,
-              MetadataKind,
-              BEGIN_SYMBOLS = RegionValueKind,
-              END_SYMBOLS = MetadataKind,
-              SymIntKind, IntSymKind, SymSymKind,
-              BEGIN_BINARYSYMEXPRS = SymIntKind,
-              END_BINARYSYMEXPRS = SymSymKind,
-              CastSymbolKind };
+  enum Kind {
+#define SYMBOL(Id, Kind, Parent) Kind,
+#define SYMBOL_RANGE(Id, First, Last) BEGIN_##Id = First, END_##Id = Last,
+#include "clang/StaticAnalyzer/Core/PathSensitive/Symbols.def"
+  };
 private:
   Kind K;
 
Index: include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h
===================================================================
--- include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h
+++ include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h
@@ -45,11 +45,9 @@
 public:
   enum BaseKind {
     // The enumerators must be representable using 2 bits.
-    UndefinedKind = 0,  // for subclass UndefinedVal (an uninitialized value)
-    UnknownKind = 1,    // for subclass UnknownVal (a void value)
-    LocKind = 2,        // for subclass Loc (an L-value)
-    NonLocKind = 3      // for subclass NonLoc (an R-value that's not
-                        //   an L-value)
+#define BASIC_SVAL(Id, Kind, Parent) Kind,
+#define ABSTRACT_SVAL_WITH_KIND(Id, Kind, Parent) Kind,
+#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.def"
   };
   enum { BaseBits = 2, BaseMask = 0x3 };
 
@@ -306,8 +304,10 @@
 
 namespace nonloc {
 
-enum Kind { ConcreteIntKind, SymbolValKind,
-            LocAsIntegerKind, CompoundValKind, LazyCompoundValKind };
+enum Kind {
+#define NONLOC_SVAL(Id, Kind, Parent) Kind,
+#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.def"
+};
 
 /// \brief Represents symbolic expression.
 class SymbolVal : public NonLoc {
@@ -465,7 +465,10 @@
 
 namespace loc {
 
-enum Kind { GotoLabelKind, MemRegionKind, ConcreteIntKind };
+enum Kind {
+#define LOC_SVAL(Id, Kind, Parent) Kind,
+#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.def"
+};
 
 class GotoLabel : public Loc {
 public:
Index: include/clang/StaticAnalyzer/Core/PathSensitive/SVals.def
===================================================================
--- /dev/null
+++ include/clang/StaticAnalyzer/Core/PathSensitive/SVals.def
@@ -0,0 +1,77 @@
+//===-- SVals.def - Metadata about SVal kinds -------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// The list of symbolic values (SVal kinds and sub-kinds) used in the Static
+// Analyzer. The distinction between Loc and NonLoc SVal namespaces is currently
+// hardcoded, because it is too peculiar and explicit to be handled uniformly.
+// SVal kind-enum values (both base-kinds and sub-kinds) are written out
+// explicitly because those are often named in an unobvious manner.
+// In order to use this information, users of this file must define
+// one or more of the following macros:
+//
+// BASIC_SVAL(Id, Kind, Parent) - for specific SVal sub-kinds, which are
+// neither Loc nor NonLoc,
+//
+// ABSTRACT_SVAL(Id, Parent) - for abstract SVal classes which are
+// neither Loc nor NonLoc,
+//
+// ABSTRACT_SVAL_WITH_KIND(Id, Kind, Parent) - for abstract SVals which occupy
+// a whole base-kind,
+//
+// LOC_SVAL(Id, Kind, Parent) - for sub-kinds of Loc,
+//
+// NONLOC_SVAL(Id, Kind, Parent) - for sub-kinds of NonLoc.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef BASIC_SVAL
+#define BASIC_SVAL(Id, Kind, Parent)
+#endif
+
+#ifndef ABSTRACT_SVAL
+#define ABSTRACT_SVAL(Id, Parent)
+#endif
+
+#ifndef ABSTRACT_SVAL_WITH_KIND
+#define ABSTRACT_SVAL_WITH_KIND(Id, Kind, Parent) ABSTRACT_SVAL(Id, Parent)
+#endif
+
+#ifndef LOC_SVAL
+#define LOC_SVAL(Id, Kind, Parent)
+#endif
+
+#ifndef NONLOC_SVAL
+#define NONLOC_SVAL(Id, Kind, Parent)
+#endif
+
+BASIC_SVAL(UndefinedVal, UndefinedKind, SVal)
+
+ABSTRACT_SVAL(DefinedOrUnknownSVal, SVal)
+BASIC_SVAL(UnknownVal, UnknownKind, DefinedOrUnknownSVal)
+
+ABSTRACT_SVAL(DefinedSVal, DefinedOrUnknownSVal)
+
+ABSTRACT_SVAL_WITH_KIND(Loc, LocKind, DefinedSVal)
+LOC_SVAL(ConcreteInt, ConcreteIntKind, Loc)
+LOC_SVAL(GotoLabel, GotoLabelKind, Loc)
+LOC_SVAL(MemRegionVal, MemRegionKind, Loc)
+
+ABSTRACT_SVAL_WITH_KIND(NonLoc, NonLocKind, DefinedSVal)
+NONLOC_SVAL(CompoundVal, CompoundValKind, NonLoc)
+NONLOC_SVAL(ConcreteInt, ConcreteIntKind, NonLoc)
+NONLOC_SVAL(LazyCompoundVal, LazyCompoundValKind, NonLoc)
+NONLOC_SVAL(LocAsInteger, LocAsIntegerKind, NonLoc)
+NONLOC_SVAL(SymbolVal, SymbolValKind, NonLoc)
+
+#undef NONLOC_SVAL
+#undef LOC_SVAL
+#undef ABSTRACT_SVAL_WITH_KIND
+#undef ABSTRACT_SVAL
+#undef BASIC_SVAL
+
Index: include/clang/StaticAnalyzer/Core/PathSensitive/SValVisitor.h
===================================================================
--- /dev/null
+++ include/clang/StaticAnalyzer/Core/PathSensitive/SValVisitor.h
@@ -0,0 +1,159 @@
+//===--- SValVisitor.h - Visitor for SVal subclasses ------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+//  This file defines the SValVisitor, SymExprVisitor, and MemRegionVisitor
+//  interfaces, and also FullSValVisitor, which visits all three hierarchies.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SVALVISITOR_H
+#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SVALVISITOR_H
+
+#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
+
+namespace clang {
+
+namespace ento {
+
+/// SValVisitor - this class implements a simple visitor for SVal
+/// subclasses.
+template <typename ImplClass, typename RetTy = void> class SValVisitor {
+public:
+
+#define DISPATCH(NAME, CLASS) \
+  return static_cast<ImplClass *>(this)->Visit ## NAME(V.castAs<CLASS>())
+
+  RetTy Visit(SVal V) {
+    // Dispatch to VisitFooVal for each FooVal.
+    // Take namespaces (loc:: and nonloc::) into account.
+    switch (V.getBaseKind()) {
+#define BASIC_SVAL(Id, Kind, Parent) case SVal::Kind: DISPATCH(Id, Id);
+#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.def"
+    case SVal::LocKind:
+      switch (V.getSubKind()) {
+#define LOC_SVAL(Id, Kind, Parent) \
+      case loc::Kind: DISPATCH(Loc ## Id, loc :: Id);
+#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.def"
+      default:
+        llvm_unreachable("Unknown Loc sub-kind!");
+      }
+    case SVal::NonLocKind:
+      switch (V.getSubKind()) {
+#define NONLOC_SVAL(Id, Kind, Parent) \
+      case nonloc::Kind: DISPATCH(NonLoc ## Id, nonloc :: Id);
+#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.def"
+      default:
+        llvm_unreachable("Unknown Loc sub-kind!");
+      }
+    default:
+      llvm_unreachable("Unknown SVal kind!");
+    }
+  }
+
+#define BASIC_SVAL(Id, Kind, Parent) \
+  RetTy Visit ## Id(Id V) { DISPATCH(Parent, Id); }
+#define ABSTRACT_SVAL(Id, Parent) \
+  BASIC_SVAL(Id, /* unused anyway */, Parent)
+#define LOC_SVAL(Id, Kind, Parent) \
+  RetTy VisitLoc ## Id(loc::Id V) { DISPATCH(Parent, Parent); }
+#define NONLOC_SVAL(Id, Kind, Parent) \
+  RetTy VisitNonLoc ## Id(nonloc::Id V) { DISPATCH(Parent, Parent); }
+#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.def"
+
+  // Base case, ignore it. :)
+  RetTy VisitSVal(SVal V) { return RetTy(); }
+
+#undef DISPATCH
+};
+
+/// SymExprVisitor - this class implements a simple visitor for SymExpr
+/// subclasses.
+template <typename ImplClass, typename RetTy = void> class SymExprVisitor {
+public:
+
+#define DISPATCH(CLASS) \
+  return static_cast<ImplClass *>(this)->Visit ## CLASS(cast<CLASS>(S))
+
+  RetTy Visit(SymbolRef S) {
+    // Dispatch to VisitSymbolFoo for each SymbolFoo.
+    switch (S->getKind()) {
+#define SYMBOL(Id, Kind, Parent) \
+    case SymExpr::Kind: DISPATCH(Id);
+#include "clang/StaticAnalyzer/Core/PathSensitive/Symbols.def"
+    default:
+      llvm_unreachable("Unknown SymExpr kind!");
+    }
+  }
+
+  // If the implementation chooses not to implement a certain visit method, fall
+  // back on visiting the superclass.
+#define SYMBOL(Id, Kind, Parent) \
+  RetTy Visit ## Id(const Id *S) { DISPATCH(Parent); }
+#define ABSTRACT_SYMBOL(Id, Parent) \
+  SYMBOL(Id, /* unused anyway */, Parent)
+#include "clang/StaticAnalyzer/Core/PathSensitive/Symbols.def"
+
+  // Base case, ignore it. :)
+  RetTy VisitSymExpr(SymbolRef S) { return RetTy(); }
+
+#undef DISPATCH
+};
+
+/// MemRegionVisitor - this class implements a simple visitor for MemRegion
+/// subclasses.
+template <typename ImplClass, typename RetTy = void> class MemRegionVisitor {
+public:
+
+#define DISPATCH(CLASS) \
+  return static_cast<ImplClass *>(this)->Visit ## CLASS(cast<CLASS>(R))
+
+  RetTy Visit(const MemRegion *R) {
+    // Dispatch to VisitFooRegion for each FooRegion.
+    switch (R->getKind()) {
+#define REGION(Id, Kind, Parent) \
+    case MemRegion::Kind: DISPATCH(Id);
+#include "clang/StaticAnalyzer/Core/PathSensitive/Regions.def"
+    default:
+      llvm_unreachable("Unknown MemRegion kind!");
+    }
+  }
+
+  // If the implementation chooses not to implement a certain visit method, fall
+  // back on visiting the superclass.
+#define REGION(Id, Kind, Parent) \
+  RetTy Visit ## Id(const Id *R) { DISPATCH(Parent); }
+#define ABSTRACT_REGION(Id, Parent) \
+  REGION(Id, /* unused anyway */, Parent)
+#include "clang/StaticAnalyzer/Core/PathSensitive/Regions.def"
+
+  // Base case, ignore it. :)
+  RetTy VisitMemRegion(const MemRegion *R) { return RetTy(); }
+
+#undef DISPATCH
+};
+
+/// FullSValVisitor - a convenient mixed visitor for all three:
+/// SVal, SymExpr and MemRegion subclasses.
+template <typename ImplClass, typename RetTy = void>
+class FullSValVisitor : public SValVisitor<ImplClass, RetTy>,
+                        public SymExprVisitor<ImplClass, RetTy>,
+                        public MemRegionVisitor<ImplClass, RetTy> {
+public:
+  using SValVisitor<ImplClass, RetTy>::Visit;
+  using SymExprVisitor<ImplClass, RetTy>::Visit;
+  using MemRegionVisitor<ImplClass, RetTy>::Visit;
+};
+
+} // end namespace ento
+
+} // end namespace clang
+
+#endif
Index: include/clang/StaticAnalyzer/Core/PathSensitive/Regions.def
===================================================================
--- /dev/null
+++ include/clang/StaticAnalyzer/Core/PathSensitive/Regions.def
@@ -0,0 +1,98 @@
+//===-- Regions.def - Metadata about MemRegion kinds ------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// The list of regions (MemRegion kinds) used in the Static Analyzer.
+// In order to use this information, users of this file must define one or more
+// of the three macros:
+//
+// REGION(Id, Kind, Parent) - for specific MemRegion sub-kinds, with kind-enum
+// value specified because those are named in a sometimes unobvious manner,
+//
+// ABSTRACT_REGION(Id, Parent) - for abstract region classes,
+//
+// REGION_RANGE(Id, First, Last) - for ranges of kind-enums,
+// allowing to determine abstract class of a region
+// based on the kind-enum value.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef REGION
+#define REGION(Id, Kind, Parent)
+#endif
+
+// Accidentally, MemSpaceRegion is the only region class that
+// is both instantiated and derived from.
+// Additionally, its kind is not its name with "Kind" suffix,
+// unlike all other regions.
+// All other non-abstract regions are defined as NORMAL_REGION().
+#ifndef NORMAL_REGION
+#define NORMAL_REGION(Id, Parent) REGION(Id, Id ## Kind, Parent)
+#endif
+
+#ifndef ABSTRACT_REGION
+#define ABSTRACT_REGION(Id, Parent)
+#endif
+
+#ifndef REGION_RANGE
+#define REGION_RANGE(Id, First, Last)
+#endif
+
+REGION(MemSpaceRegion, GenericMemSpaceRegionKind, MemRegion)
+  ABSTRACT_REGION(GlobalsSpaceRegion, MemSpaceRegion)
+    ABSTRACT_REGION(NonStaticGlobalSpaceRegion, GlobalsSpaceRegion)
+      NORMAL_REGION(GlobalImmutableSpaceRegion, NonStaticGlobalSpaceRegion)
+      NORMAL_REGION(GlobalInternalSpaceRegion, NonStaticGlobalSpaceRegion)
+      NORMAL_REGION(GlobalSystemSpaceRegion, NonStaticGlobalSpaceRegion)
+      REGION_RANGE(NON_STATIC_GLOBAL_MEMSPACES, GlobalImmutableSpaceRegionKind,
+                                                GlobalSystemSpaceRegionKind)
+    NORMAL_REGION(StaticGlobalSpaceRegion, MemSpaceRegion)
+    REGION_RANGE(GLOBAL_MEMSPACES, GlobalImmutableSpaceRegionKind,
+                                   StaticGlobalSpaceRegionKind)
+  NORMAL_REGION(HeapSpaceRegion, MemSpaceRegion)
+  ABSTRACT_REGION(StackSpaceRegion, MemSpaceRegion)
+    NORMAL_REGION(StackArgumentsSpaceRegion, StackSpaceRegion)
+    NORMAL_REGION(StackLocalsSpaceRegion, StackSpaceRegion)
+    REGION_RANGE(STACK_MEMSPACES, StackArgumentsSpaceRegionKind,
+                                  StackLocalsSpaceRegionKind)
+  NORMAL_REGION(UnknownSpaceRegion, MemSpaceRegion)
+  REGION_RANGE(MEMSPACES, GenericMemSpaceRegionKind,
+                          UnknownSpaceRegionKind)
+ABSTRACT_REGION(SubRegion, MemRegion)
+  NORMAL_REGION(AllocaRegion, SubRegion)
+  NORMAL_REGION(SymbolicRegion, SubRegion)
+  ABSTRACT_REGION(TypedRegion, SubRegion)
+    NORMAL_REGION(BlockDataRegion, TypedRegion)
+    ABSTRACT_REGION(CodeTextRegion, TypedRegion)
+      NORMAL_REGION(BlockTextRegion, CodeTextRegion)
+      NORMAL_REGION(FunctionTextRegion, CodeTextRegion)
+      REGION_RANGE(CODE_TEXT_REGIONS, BlockTextRegionKind,
+                                      FunctionTextRegionKind)
+    ABSTRACT_REGION(TypedValueRegion, TypedRegion)
+      NORMAL_REGION(CompoundLiteralRegion, TypedValueRegion)
+      NORMAL_REGION(CXXBaseObjectRegion, TypedValueRegion)
+      NORMAL_REGION(CXXTempObjectRegion, TypedValueRegion)
+      NORMAL_REGION(CXXThisRegion, TypedValueRegion)
+      ABSTRACT_REGION(DeclRegion, TypedValueRegion)
+        NORMAL_REGION(FieldRegion, DeclRegion)
+        NORMAL_REGION(ObjCIvarRegion, DeclRegion)
+        NORMAL_REGION(VarRegion, DeclRegion)
+        REGION_RANGE(DECL_REGIONS, FieldRegionKind,
+                                   VarRegionKind)
+      NORMAL_REGION(ElementRegion, TypedValueRegion)
+      NORMAL_REGION(ObjCStringRegion, TypedValueRegion)
+      NORMAL_REGION(StringRegion, TypedValueRegion)
+      REGION_RANGE(TYPED_VALUE_REGIONS, CompoundLiteralRegionKind,
+                                        StringRegionKind)
+    REGION_RANGE(TYPED_REGIONS, BlockDataRegionKind,
+                                StringRegionKind)
+
+#undef REGION_RANGE
+#undef ABSTRACT_REGION
+#undef NORMAL_REGION
+#undef REGION
Index: include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h
===================================================================
--- include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h
+++ include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h
@@ -78,48 +78,11 @@
   friend class MemRegionManager;
 public:
   enum Kind {
-    // Memory spaces.
-    GenericMemSpaceRegionKind,
-    StackLocalsSpaceRegionKind,
-    StackArgumentsSpaceRegionKind,
-    HeapSpaceRegionKind,
-    UnknownSpaceRegionKind,
-    StaticGlobalSpaceRegionKind,
-    GlobalInternalSpaceRegionKind,
-    GlobalSystemSpaceRegionKind,
-    GlobalImmutableSpaceRegionKind,
-    BEG_NON_STATIC_GLOBAL_MEMSPACES = GlobalInternalSpaceRegionKind,
-    END_NON_STATIC_GLOBAL_MEMSPACES = GlobalImmutableSpaceRegionKind,
-    BEG_GLOBAL_MEMSPACES = StaticGlobalSpaceRegionKind,
-    END_GLOBAL_MEMSPACES = GlobalImmutableSpaceRegionKind,
-    BEG_MEMSPACES = GenericMemSpaceRegionKind,
-    END_MEMSPACES = GlobalImmutableSpaceRegionKind,
-    // Untyped regions.
-    SymbolicRegionKind,
-    AllocaRegionKind,
-    // Typed regions.
-    BEG_TYPED_REGIONS,
-    FunctionTextRegionKind = BEG_TYPED_REGIONS,
-    BlockTextRegionKind,
-    BlockDataRegionKind,
-    BEG_TYPED_VALUE_REGIONS,
-    CompoundLiteralRegionKind = BEG_TYPED_VALUE_REGIONS,
-    CXXThisRegionKind,
-    StringRegionKind,
-    ObjCStringRegionKind,
-    ElementRegionKind,
-    // Decl Regions.
-    BEG_DECL_REGIONS,
-    VarRegionKind = BEG_DECL_REGIONS,
-    FieldRegionKind,
-    ObjCIvarRegionKind,
-    END_DECL_REGIONS = ObjCIvarRegionKind,
-    CXXTempObjectRegionKind,
-    CXXBaseObjectRegionKind,
-    END_TYPED_VALUE_REGIONS = CXXBaseObjectRegionKind,
-    END_TYPED_REGIONS = CXXBaseObjectRegionKind
+#define REGION(Id, Kind, Parent) Kind,
+#define REGION_RANGE(Id, First, Last) BEG_##Id = First, END_##Id = Last,
+#include "clang/StaticAnalyzer/Core/PathSensitive/Regions.def"
   };
-    
+
 private:
   const Kind kind;
 
@@ -374,8 +337,7 @@
 
   static bool classof(const MemRegion *R) {
     Kind k = R->getKind();
-    return k >= StackLocalsSpaceRegionKind &&
-           k <= StackArgumentsSpaceRegionKind;
+    return k >= BEG_STACK_MEMSPACES && k <= END_STACK_MEMSPACES;
   }  
 };
   
@@ -537,7 +499,7 @@
 
   static bool classof(const MemRegion* R) {
     Kind k = R->getKind();
-    return k >= FunctionTextRegionKind && k <= BlockTextRegionKind;
+    return k >= BEG_CODE_TEXT_REGIONS && k <= END_CODE_TEXT_REGIONS;
   }
 };
 
Index: include/clang/StaticAnalyzer/Checkers/SValExplainer.h
===================================================================
--- /dev/null
+++ include/clang/StaticAnalyzer/Checkers/SValExplainer.h
@@ -0,0 +1,233 @@
+//== SValExplainer.h - Symbolic value explainer -----------------*- C++ -*--==//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+//  This file defines SValExplainer, a class for pretty-printing a
+//  human-readable description of a symbolic value. For example,
+//  "reg_$0<x>" is turned into "initial value of variable 'x'".
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_STATICANALYZER_CHECKERS_SVALEXPLAINER_H
+#define LLVM_CLANG_STATICANALYZER_CHECKERS_SVALEXPLAINER_H
+
+#include "clang/AST/DeclCXX.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SValVisitor.h"
+
+namespace clang {
+
+namespace ento {
+
+class SValExplainer : public FullSValVisitor<SValExplainer, std::string> {
+private:
+  ASTContext &ACtx;
+
+  std::string printStmt(const Stmt *S) {
+    std::string Str;
+    llvm::raw_string_ostream OS(Str);
+    S->printPretty(OS, nullptr, PrintingPolicy(ACtx.getLangOpts()));
+    return OS.str();
+  }
+
+  bool isThisObject(const SymbolicRegion *R) {
+    if (auto S = dyn_cast<SymbolRegionValue>(R->getSymbol()))
+      if (isa<CXXThisRegion>(S->getRegion()))
+        return true;
+    return false;
+  }
+
+public:
+  SValExplainer(ASTContext &Ctx) : ACtx(Ctx) {}
+
+  std::string VisitUnknownVal(UnknownVal V) {
+    return "unknown value";
+  }
+
+  std::string VisitUndefinedVal(UndefinedVal V) {
+    return "undefined value";
+  }
+
+  std::string VisitLocMemRegionVal(loc::MemRegionVal V) {
+    const MemRegion *R = V.getRegion();
+    // Avoid the weird "pointer to pointee of ...".
+    if (auto SR = dyn_cast<SymbolicRegion>(R)) {
+      // However, "pointer to 'this' object" is fine.
+      if (!isThisObject(SR))
+        return Visit(SR->getSymbol());
+    }
+    return "pointer to " + Visit(R);
+  }
+
+  std::string VisitLocConcreteInt(loc::ConcreteInt V) {
+    llvm::APSInt I = V.getValue();
+    std::string Str;
+    llvm::raw_string_ostream OS(Str);
+    OS << "concrete memory address '" << I << "'";
+    return OS.str();
+  }
+
+  std::string VisitNonLocSymbolVal(nonloc::SymbolVal V) {
+    return Visit(V.getSymbol());
+  }
+
+  std::string VisitNonLocConcreteInt(nonloc::ConcreteInt V) {
+    llvm::APSInt I = V.getValue();
+    std::string Str;
+    llvm::raw_string_ostream OS(Str);
+    OS << (I.isSigned() ? "signed " : "unsigned ") << I.getBitWidth()
+       << "-bit integer '" << I << "'";
+    return OS.str();
+  }
+
+  std::string VisitNonLocLazyCompoundVal(nonloc::LazyCompoundVal V) {
+    return "lazily frozen compound value of " + Visit(V.getRegion());
+  }
+
+  std::string VisitSymbolRegionValue(const SymbolRegionValue *S) {
+    const MemRegion *R = S->getRegion();
+    // Special handling for argument values.
+    if (auto V = dyn_cast<VarRegion>(R))
+      if (auto D = dyn_cast<ParmVarDecl>(V->getDecl()))
+        return "argument '" + D->getQualifiedNameAsString() + "'";
+    return "initial value of " + Visit(R);
+  }
+
+  std::string VisitSymbolConjured(const SymbolConjured *S) {
+    return "symbol of type '" + S->getType().getAsString() +
+           "' conjured at statement '" + printStmt(S->getStmt()) + "'";
+  }
+
+  std::string VisitSymbolDerived(const SymbolDerived *S) {
+    return "value derived from (" + Visit(S->getParentSymbol()) +
+           ") for " + Visit(S->getRegion());
+  }
+
+  std::string VisitSymbolExtent(const SymbolExtent *S) {
+    return "extent of " + Visit(S->getRegion());
+  }
+
+  std::string VisitSymbolMetadata(const SymbolMetadata *S) {
+    return "metadata of type '" + S->getType().getAsString() + "' tied to " +
+           Visit(S->getRegion());
+  }
+
+  std::string VisitSymIntExpr(const SymIntExpr *S) {
+    std::string Str;
+    llvm::raw_string_ostream OS(Str);
+    OS << "(" << Visit(S->getLHS()) << ") "
+       << std::string(BinaryOperator::getOpcodeStr(S->getOpcode())) << " "
+       << S->getRHS();
+    return OS.str();
+  }
+
+  // TODO: IntSymExpr doesn't appear in practice.
+  // Add the relevant code once it does.
+
+  std::string VisitSymSymExpr(const SymSymExpr *S) {
+    return "(" + Visit(S->getLHS()) + ") " +
+           std::string(BinaryOperator::getOpcodeStr(S->getOpcode())) +
+           " (" + Visit(S->getRHS()) + ")";
+  }
+
+  // TODO: SymbolCast doesn't appear in practice.
+  // Add the relevant code once it does.
+
+  std::string VisitSymbolicRegion(const SymbolicRegion *R) {
+    // Explain 'this' object here.
+    // TODO: Explain CXXThisRegion itself, find a way to test it.
+    if (isThisObject(R))
+      return "'this' object";
+    return "pointee of " + Visit(R->getSymbol());
+  }
+
+  std::string VisitAllocaRegion(const AllocaRegion *R) {
+    return "region allocated by '" + printStmt(R->getExpr()) + "'";
+  }
+
+  std::string VisitCompoundLiteralRegion(const CompoundLiteralRegion *R) {
+    return "compound literal " + printStmt(R->getLiteralExpr());
+  }
+
+  std::string VisitStringRegion(const StringRegion *R) {
+    return "string literal " + R->getString();
+  }
+
+  std::string VisitElementRegion(const ElementRegion *R) {
+    std::string Str;
+    llvm::raw_string_ostream OS(Str);
+    OS << "element of type '" << R->getElementType().getAsString()
+       << "' with index ";
+    // For concrete index: omit type of the index integer.
+    if (auto I = R->getIndex().getAs<nonloc::ConcreteInt>())
+      OS << I->getValue();
+    else
+      OS << "'" << Visit(R->getIndex()) << "'";
+    OS << " of " + Visit(R->getSuperRegion());
+    return OS.str();
+  }
+
+  std::string VisitVarRegion(const VarRegion *R) {
+    const VarDecl *VD = R->getDecl();
+    std::string Name = VD->getQualifiedNameAsString();
+    if (isa<ParmVarDecl>(VD))
+      return "parameter '" + Name + "'";
+    else if (VD->hasLocalStorage())
+      return "local variable '" + Name + "'";
+    else if (VD->isStaticLocal())
+      return "static local variable '" + Name + "'";
+    else if (VD->hasGlobalStorage())
+      return "global variable '" + Name + "'";
+    else
+      llvm_unreachable("A variable is either local or global");
+  }
+
+  std::string VisitFieldRegion(const FieldRegion *R) {
+    return "field '" + R->getDecl()->getNameAsString() + "' of " +
+           Visit(R->getSuperRegion());
+  }
+
+  std::string VisitCXXTempObjectRegion(const CXXTempObjectRegion *R) {
+    return "temporary object constructed at statement '" +
+           printStmt(R->getExpr()) + "'";
+  }
+
+  std::string VisitCXXBaseObjectRegion(const CXXBaseObjectRegion *R) {
+    return "base object '" + R->getDecl()->getQualifiedNameAsString() +
+           "' inside " + Visit(R->getSuperRegion());
+  }
+
+  std::string VisitSVal(SVal V) {
+    std::string Str;
+    llvm::raw_string_ostream OS(Str);
+    OS << V;
+    return "a value unsupported by the explainer: (" +
+           std::string(OS.str()) + ")";
+  }
+
+  std::string VisitSymExpr(SymbolRef S) {
+    std::string Str;
+    llvm::raw_string_ostream OS(Str);
+    S->dumpToStream(OS);
+    return "a symbolic expression unsupported by the explainer: (" +
+           std::string(OS.str()) + ")";
+  }
+
+  std::string VisitMemRegion(const MemRegion *R) {
+    std::string Str;
+    llvm::raw_string_ostream OS(Str);
+    OS << R;
+    return "a memory region unsupported by the explainer (" +
+           std::string(OS.str()) + ")";
+  }
+};
+
+} // end namespace ento
+
+} // end namespace clang
+
+#endif
Index: docs/analyzer/DebugChecks.rst
===================================================================
--- docs/analyzer/DebugChecks.rst
+++ docs/analyzer/DebugChecks.rst
@@ -162,6 +162,41 @@
     } while(0);  // expected-warning{{SYMBOL DEAD}}
 
 
+- void clang_analyzer_explain(a single argument of any type);
+
+  This function explains the value of its argument in a human-readable manner
+  in the warning message. You can make as many overrides of its prototype
+  in the test code as necessary to explain various integral, pointer,
+  or even record-type values.
+
+  Example usage::
+
+    void clang_analyzer_explain(int);
+    void clang_analyzer_explain(void *);
+
+    void foo(int param, void *ptr) {
+      clang_analyzer_explain(param); // expected-warning{{argument 'param'}}
+      if (!ptr)
+        clang_analyzer_explain(ptr); // expected-warning{{memory address '0'}}
+    }
+
+- size_t clang_analyzer_getExtent(void *);
+
+  This function returns the value that represents the extent of a memory region
+  pointed to by the argument. This value is often difficult to obtain otherwise,
+  because no valid code that produces this value. However, it may be useful
+  for testing purposes, to see how well does the analyzer model region extents.
+
+  Example usage::
+
+    void foo() {
+      int x, *y;
+      size_t xs = clang_analyzer_getExtent(&x);
+      clang_analyzer_explain(xs); // expected-warning{{'4'}}
+      size_t ys = clang_analyzer_getExtent(&y);
+      clang_analyzer_explain(ys); // expected-warning{{'8'}}
+    }
+
 Statistics
 ==========
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to