NoQ removed rL LLVM as the repository for this revision.
NoQ updated this revision to Diff 44758.
NoQ added a comment.

Reverted the patch due to a few issues. This revision should fix these issues.

The explain-svals test is fixed to target a specific target, in order to make 
sure that the definition of size_t always agrees with the target triple, 
otherwise the test would fail (shame on me, should have guessed!).

Not quite sure what to do with the failure of the Modules buildbot 
(http://lab.llvm.org:8011/builders/clang-x86_64-linux-selfhost-modules/builds/10562/steps/compile.llvm.stage2/logs/stdio).
 I added the new .def-files to the list of "textual" headers in 
module.modulemap, but i'm not brave enough to go ahead and commit again and 
make that sort of thing work through trial and error, as maybe there are more 
things i need to do.

Fix a small whitespace error introduced by the patch.

Right after committing http://reviews.llvm.org/D16062, i noticed that 
`MemRegion` itself also doesn't need to be a friend of `MemRegionManager`. 
Added this to this patch, as they're related, i guess.


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
  include/clang/module.modulemap
  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 -triple i386-apple-darwin10 -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/module.modulemap
===================================================================
--- include/clang/module.modulemap
+++ include/clang/module.modulemap
@@ -108,6 +108,9 @@
   umbrella "StaticAnalyzer/Core"
 
   textual header "StaticAnalyzer/Core/Analyses.def"
+  textual header "StaticAnalyzer/Core/PathSensitive/SVals.def"
+  textual header "StaticAnalyzer/Core/PathSensitive/Symbols.def"
+  textual header "StaticAnalyzer/Core/PathSensitive/Regions.def"
 
   module * { export * }
 }
Index: include/clang/StaticAnalyzer/Core/PathSensitive/Symbols.def
===================================================================
--- /dev/null
+++ include/clang/StaticAnalyzer/Core/PathSensitive/Symbols.def
@@ -0,0 +1,55 @@
+//===-- 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 sub-classes) 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, Parent) - for specific SymExpr sub-classes, reserving the
+// IdKind identifier for its kind enumeration value.
+//
+// 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 enumeration value.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SYMBOL
+#define SYMBOL(Id, 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, BinarySymExpr)
+  SYMBOL(SymIntExpr, BinarySymExpr)
+  SYMBOL(SymSymExpr, BinarySymExpr)
+SYMBOL_RANGE(BINARYSYMEXPRS, IntSymExprKind, SymSymExprKind)
+
+SYMBOL(SymbolCast, SymExpr)
+
+ABSTRACT_SYMBOL(SymbolData, SymExpr)
+  SYMBOL(SymbolConjured, SymbolData)
+  SYMBOL(SymbolDerived, SymbolData)
+  SYMBOL(SymbolExtent, SymbolData)
+  SYMBOL(SymbolMetadata, SymbolData)
+  SYMBOL(SymbolRegionValue, SymbolData)
+SYMBOL_RANGE(SYMBOLS, SymbolConjuredKind, SymbolRegionValueKind)
+
+#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
@@ -43,19 +43,9 @@
   virtual void anchor();
 public:
   enum Kind {
-    SymbolRegionValueKind,
-    SymbolConjuredKind,
-    SymbolDerivedKind,
-    SymbolExtentKind,
-    SymbolMetadataKind,
-    BEGIN_SYMBOLS = SymbolRegionValueKind,
-    END_SYMBOLS = SymbolMetadataKind,
-    SymIntExprKind,
-    IntSymExprKind,
-    SymSymExprKind,
-    BEGIN_BINARYSYMEXPRS = SymIntExprKind,
-    END_BINARYSYMEXPRS = SymSymExprKind,
-    SymbolCastKind
+#define SYMBOL(Id, Parent) Id ## Kind,
+#define SYMBOL_RANGE(Id, First, Last) BEGIN_##Id = First, END_##Id = Last,
+#include "clang/StaticAnalyzer/Core/PathSensitive/Symbols.def"
   };
 
 private:
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.
-    UndefinedValKind = 0,  // for subclass UndefinedVal (an uninitialized value)
-    UnknownValKind = 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, Parent) Id ## Kind,
+#define ABSTRACT_SVAL_WITH_KIND(Id, Parent) Id ## 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, Parent) Id ## 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, MemRegionValKind, ConcreteIntKind };
+enum Kind {
+#define LOC_SVAL(Id, Parent) Id ## 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,74 @@
+//===-- 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. In order to use this information, users of this file must define
+// one or more of the following macros:
+//
+// BASIC_SVAL(Id, Parent) - for specific SVal sub-kinds, which are
+// neither in loc:: nor in nonloc:: namespace; these classes occupy
+// their own base kind IdKind.
+//
+// ABSTRACT_SVAL(Id, Parent) - for abstract SVal classes which are
+// neither in loc:: nor in nonloc:: namespace,
+//
+// ABSTRACT_SVAL_WITH_KIND(Id, Parent) - for SVal classes which are also
+// neither in loc:: nor in nonloc:: namespace, but occupy a whole base kind
+// identifier IdKind, much like BASIC_SVALs.
+//
+// LOC_SVAL(Id, Parent) - for values in loc:: namespace, which occupy a sub-kind
+// loc::IdKind.
+//
+// NONLOC_SVAL(Id, Parent) - for values in nonloc:: namespace, which occupy a
+// sub-kind nonloc::IdKind.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef BASIC_SVAL
+#define BASIC_SVAL(Id, Parent)
+#endif
+
+#ifndef ABSTRACT_SVAL
+#define ABSTRACT_SVAL(Id, Parent)
+#endif
+
+#ifndef ABSTRACT_SVAL_WITH_KIND
+#define ABSTRACT_SVAL_WITH_KIND(Id, Parent) ABSTRACT_SVAL(Id, Parent)
+#endif
+
+#ifndef LOC_SVAL
+#define LOC_SVAL(Id, Parent)
+#endif
+
+#ifndef NONLOC_SVAL
+#define NONLOC_SVAL(Id, Parent)
+#endif
+
+BASIC_SVAL(UndefinedVal, SVal)
+ABSTRACT_SVAL(DefinedOrUnknownSVal, SVal)
+  BASIC_SVAL(UnknownVal, DefinedOrUnknownSVal)
+  ABSTRACT_SVAL(DefinedSVal, DefinedOrUnknownSVal)
+    ABSTRACT_SVAL_WITH_KIND(Loc, DefinedSVal)
+      LOC_SVAL(ConcreteInt, Loc)
+      LOC_SVAL(GotoLabel, Loc)
+      LOC_SVAL(MemRegionVal, Loc)
+    ABSTRACT_SVAL_WITH_KIND(NonLoc, DefinedSVal)
+      NONLOC_SVAL(CompoundVal, NonLoc)
+      NONLOC_SVAL(ConcreteInt, NonLoc)
+      NONLOC_SVAL(LazyCompoundVal, NonLoc)
+      NONLOC_SVAL(LocAsInteger, NonLoc)
+      NONLOC_SVAL(SymbolVal, 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,151 @@
+//===--- 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, Parent) case SVal::Id ## Kind: DISPATCH(Id, Id);
+#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.def"
+    case SVal::LocKind:
+      switch (V.getSubKind()) {
+#define LOC_SVAL(Id, Parent) \
+      case loc::Id ## Kind: DISPATCH(Loc ## Id, loc :: Id);
+#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.def"
+      }
+      llvm_unreachable("Unknown Loc sub-kind!");
+    case SVal::NonLocKind:
+      switch (V.getSubKind()) {
+#define NONLOC_SVAL(Id, Parent) \
+      case nonloc::Id ## Kind: DISPATCH(NonLoc ## Id, nonloc :: Id);
+#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.def"
+      }
+      llvm_unreachable("Unknown NonLoc sub-kind!");
+    }
+    llvm_unreachable("Unknown SVal kind!");
+  }
+
+#define BASIC_SVAL(Id, Parent) \
+  RetTy Visit ## Id(Id V) { DISPATCH(Parent, Id); }
+#define ABSTRACT_SVAL(Id, Parent) \
+  BASIC_SVAL(Id, Parent)
+#define LOC_SVAL(Id, Parent) \
+  RetTy VisitLoc ## Id(loc::Id V) { DISPATCH(Parent, Parent); }
+#define NONLOC_SVAL(Id, 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, Parent) \
+    case SymExpr::Id ## Kind: DISPATCH(Id);
+#include "clang/StaticAnalyzer/Core/PathSensitive/Symbols.def"
+    }
+    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, Parent) RetTy Visit ## Id(const Id *S) { DISPATCH(Parent); }
+#define ABSTRACT_SYMBOL(Id, Parent) SYMBOL(Id, 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, Parent) case MemRegion::Id ## Kind: DISPATCH(Id);
+#include "clang/StaticAnalyzer/Core/PathSensitive/Regions.def"
+    }
+    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, Parent) \
+  RetTy Visit ## Id(const Id *R) { DISPATCH(Parent); }
+#define ABSTRACT_REGION(Id, Parent) \
+  REGION(Id, 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,89 @@
+//===-- 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 sub-classes) 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, Parent) - for specific MemRegion sub-classes, reserving
+// enum value IdKind for their kind.
+//
+// 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, Parent)
+#endif
+
+#ifndef ABSTRACT_REGION
+#define ABSTRACT_REGION(Id, Parent)
+#endif
+
+#ifndef REGION_RANGE
+#define REGION_RANGE(Id, First, Last)
+#endif
+
+ABSTRACT_REGION(MemSpaceRegion, MemRegion)
+  REGION(CodeSpaceRegion, MemSpaceRegion)
+  ABSTRACT_REGION(GlobalsSpaceRegion, MemSpaceRegion)
+    ABSTRACT_REGION(NonStaticGlobalSpaceRegion, GlobalsSpaceRegion)
+      REGION(GlobalImmutableSpaceRegion, NonStaticGlobalSpaceRegion)
+      REGION(GlobalInternalSpaceRegion, NonStaticGlobalSpaceRegion)
+      REGION(GlobalSystemSpaceRegion, NonStaticGlobalSpaceRegion)
+      REGION_RANGE(NON_STATIC_GLOBAL_MEMSPACES, GlobalImmutableSpaceRegionKind,
+                                                GlobalSystemSpaceRegionKind)
+    REGION(StaticGlobalSpaceRegion, MemSpaceRegion)
+    REGION_RANGE(GLOBAL_MEMSPACES, GlobalImmutableSpaceRegionKind,
+                                   StaticGlobalSpaceRegionKind)
+  REGION(HeapSpaceRegion, MemSpaceRegion)
+  ABSTRACT_REGION(StackSpaceRegion, MemSpaceRegion)
+    REGION(StackArgumentsSpaceRegion, StackSpaceRegion)
+    REGION(StackLocalsSpaceRegion, StackSpaceRegion)
+    REGION_RANGE(STACK_MEMSPACES, StackArgumentsSpaceRegionKind,
+                                  StackLocalsSpaceRegionKind)
+  REGION(UnknownSpaceRegion, MemSpaceRegion)
+  REGION_RANGE(MEMSPACES, CodeSpaceRegionKind,
+                          UnknownSpaceRegionKind)
+ABSTRACT_REGION(SubRegion, MemRegion)
+  REGION(AllocaRegion, SubRegion)
+  REGION(SymbolicRegion, SubRegion)
+  ABSTRACT_REGION(TypedRegion, SubRegion)
+    REGION(BlockDataRegion, TypedRegion)
+    ABSTRACT_REGION(CodeTextRegion, TypedRegion)
+      REGION(BlockCodeRegion, CodeTextRegion)
+      REGION(FunctionCodeRegion, CodeTextRegion)
+      REGION_RANGE(CODE_TEXT_REGIONS, BlockCodeRegionKind,
+                                      FunctionCodeRegionKind)
+    ABSTRACT_REGION(TypedValueRegion, TypedRegion)
+      REGION(CompoundLiteralRegion, TypedValueRegion)
+      REGION(CXXBaseObjectRegion, TypedValueRegion)
+      REGION(CXXTempObjectRegion, TypedValueRegion)
+      REGION(CXXThisRegion, TypedValueRegion)
+      ABSTRACT_REGION(DeclRegion, TypedValueRegion)
+        REGION(FieldRegion, DeclRegion)
+        REGION(ObjCIvarRegion, DeclRegion)
+        REGION(VarRegion, DeclRegion)
+        REGION_RANGE(DECL_REGIONS, FieldRegionKind,
+                                   VarRegionKind)
+      REGION(ElementRegion, TypedValueRegion)
+      REGION(ObjCStringRegion, TypedValueRegion)
+      REGION(StringRegion, TypedValueRegion)
+      REGION_RANGE(TYPED_VALUE_REGIONS, CompoundLiteralRegionKind,
+                                        StringRegionKind)
+    REGION_RANGE(TYPED_REGIONS, BlockDataRegionKind,
+                                StringRegionKind)
+
+#undef REGION_RANGE
+#undef ABSTRACT_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
@@ -76,51 +76,13 @@
 
 /// MemRegion - The root abstract class for all memory regions.
 class MemRegion : public llvm::FoldingSetNode {
-  friend class MemRegionManager;
 public:
   enum Kind {
-    // Memory spaces.
-    CodeSpaceRegionKind,
-    StackLocalsSpaceRegionKind,
-    StackArgumentsSpaceRegionKind,
-    HeapSpaceRegionKind,
-    UnknownSpaceRegionKind,
-    StaticGlobalSpaceRegionKind,
-    GlobalInternalSpaceRegionKind,
-    GlobalSystemSpaceRegionKind,
-    GlobalImmutableSpaceRegionKind,
-    BEGIN_NON_STATIC_GLOBAL_MEMSPACES = GlobalInternalSpaceRegionKind,
-    END_NON_STATIC_GLOBAL_MEMSPACES = GlobalImmutableSpaceRegionKind,
-    BEGIN_GLOBAL_MEMSPACES = StaticGlobalSpaceRegionKind,
-    END_GLOBAL_MEMSPACES = GlobalImmutableSpaceRegionKind,
-    BEGIN_MEMSPACES = CodeSpaceRegionKind,
-    END_MEMSPACES = GlobalImmutableSpaceRegionKind,
-    // Untyped regions.
-    SymbolicRegionKind,
-    AllocaRegionKind,
-    // Typed regions.
-    BEGIN_TYPED_REGIONS,
-    FunctionCodeRegionKind = BEGIN_TYPED_REGIONS,
-    BlockCodeRegionKind,
-    BlockDataRegionKind,
-    BEGIN_TYPED_VALUE_REGIONS,
-    CompoundLiteralRegionKind = BEGIN_TYPED_VALUE_REGIONS,
-    CXXThisRegionKind,
-    StringRegionKind,
-    ObjCStringRegionKind,
-    ElementRegionKind,
-    // Decl Regions.
-    BEGIN_DECL_REGIONS,
-    VarRegionKind = BEGIN_DECL_REGIONS,
-    FieldRegionKind,
-    ObjCIvarRegionKind,
-    END_DECL_REGIONS = ObjCIvarRegionKind,
-    CXXTempObjectRegionKind,
-    CXXBaseObjectRegionKind,
-    END_TYPED_VALUE_REGIONS = CXXBaseObjectRegionKind,
-    END_TYPED_REGIONS = CXXBaseObjectRegionKind
+#define REGION(Id, Parent) Id ## Kind,
+#define REGION_RANGE(Id, First, Last) BEGIN_##Id = First, END_##Id = Last,
+#include "clang/StaticAnalyzer/Core/PathSensitive/Regions.def"
   };
-    
+
 private:
   const Kind kind;
 
@@ -386,8 +348,7 @@
 
   static bool classof(const MemRegion *R) {
     Kind k = R->getKind();
-    return k >= StackLocalsSpaceRegionKind &&
-           k <= StackArgumentsSpaceRegionKind;
+    return k >= BEGIN_STACK_MEMSPACES && k <= END_STACK_MEMSPACES;
   }
 };
 
@@ -549,7 +510,7 @@
 
   static bool classof(const MemRegion* R) {
     Kind k = R->getKind();
-    return k >= FunctionCodeRegionKind && k <= BlockCodeRegionKind;
+    return k >= BEGIN_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