Implements custom description string for MATCHER*.
diff --git a/include/gmock/gmock-generated-matchers.h b/include/gmock/gmock-generated-matchers.h
index 727109c..26c064b 100644
--- a/include/gmock/gmock-generated-matchers.h
+++ b/include/gmock/gmock-generated-matchers.h
@@ -40,18 +40,11 @@
 #include <string>
 #include <vector>
 #include <gmock/gmock-matchers.h>
+#include <gmock/gmock-printers.h>
 
 namespace testing {
 namespace internal {
 
-// Generates a non-fatal failure iff 'description' is not a valid
-// matcher description.
-inline void ValidateMatcherDescription(const char* description) {
-  EXPECT_STREQ("", description)
-      << "The description string in a MATCHER*() macro must be \"\" "
-         "at this moment.  We will implement custom description string soon.";
-}
-
 // Implements ElementsAre() and ElementsAreArray().
 template <typename Container>
 class ElementsAreMatcherImpl : public MatcherInterface<Container> {
@@ -674,10 +667,18 @@
 //
 // will define a matcher with the given name that executes the
 // statements, which must return a bool to indicate if the match
-// succeeds.  For now, the description_string must be "", but we'll
-// allow other values soon.  Inside the statements, you can refer to
-// the value being matched by 'arg', and refer to its type by
-// 'arg_type'.  For example:
+// succeeds.  Inside the statements, you can refer to the value being
+// matched by 'arg', and refer to its type by 'arg_type'.
+//
+// The description string documents what the matcher does, and is used
+// to generate the failure message when the match fails.  Since a
+// MATCHER() is usually defined in a header file shared by multiple
+// C++ source files, we require the description to be a C-string
+// literal to avoid possible side effects.  It can be empty, in which
+// case we'll use the sequence of words in the matcher name as the
+// description.
+//
+// For example:
 //
 //   MATCHER(IsEven, "") { return (arg % 2) == 0; }
 //
@@ -740,6 +741,38 @@
 // We also provide MATCHER_P2, MATCHER_P3, ..., up to MATCHER_P10 to
 // support multi-parameter matchers.
 //
+// When defining a parameterized matcher, you can use Python-style
+// interpolations in the description string to refer to the parameter
+// values.  We support the following syntax currently:
+//
+//   %%       a single '%' character
+//   %(*)s    all parameters of the matcher printed as a tuple
+//   %(foo)s  value of the matcher parameter named 'foo'
+//
+// For example,
+//
+//   MATCHER_P2(InClosedRange, low, hi, "is in range [%(low)s, %(hi)s]") {
+//     return low <= arg && arg <= hi;
+//   }
+//   ...
+//   EXPECT_THAT(3, InClosedRange(4, 6));
+//
+// would generate a failure that contains the message:
+//
+//   Expected: is in range [4, 6]
+//
+// If you specify "" as the description, the failure message will
+// contain the sequence of words in the matcher name followed by the
+// parameter values printed as a tuple.  For example,
+//
+//   MATCHER_P2(InClosedRange, low, hi, "") { ... }
+//   ...
+//   EXPECT_THAT(3, InClosedRange(4, 6));
+//
+// would generate a failure that contains the text:
+//
+//   Expected: in closed range (4, 6)
+//
 // For the purpose of typing, you can view
 //
 //   MATCHER_Pk(Foo, p1, ..., pk, description_string) { ... }
@@ -796,25 +829,77 @@
 // To learn more about using these macros, please search for 'MATCHER'
 // on http://code.google.com/p/googlemock/wiki/CookBook.
 
+namespace testing {
+namespace internal {
+
+// Constants denoting interpolations in a matcher description string.
+const int kTupleInterpolation = -1;    // "%(*)s"
+const int kPercentInterpolation = -2;  // "%%"
+const int kInvalidInterpolation = -3;  // "%" followed by invalid text
+
+// Records the location and content of an interpolation.
+struct Interpolation {
+  Interpolation(const char* start, const char* end, int param)
+      : start_pos(start), end_pos(end), param_index(param) {}
+
+  // Points to the start of the interpolation (the '%' character).
+  const char* start_pos;
+  // Points to the first character after the interpolation.
+  const char* end_pos;
+  // 0-based index of the interpolated matcher parameter;
+  // kTupleInterpolation for "%(*)s"; kPercentInterpolation for "%%".
+  int param_index;
+};
+
+typedef ::std::vector<Interpolation> Interpolations;
+
+// Parses a matcher description string and returns a vector of
+// interpolations that appear in the string; generates non-fatal
+// failures iff 'description' is an invalid matcher description.
+// 'param_names' is a NULL-terminated array of parameter names in the
+// order they appear in the MATCHER_P*() parameter list.
+Interpolations ValidateMatcherDescription(
+    const char* param_names[], const char* description);
+
+// Returns the actual matcher description, given the matcher name,
+// user-supplied description template string, interpolations in the
+// string, and the printed values of the matcher parameters.
+string FormatMatcherDescription(
+    const char* matcher_name, const char* description,
+    const Interpolations& interp, const Strings& param_values);
+
+}  // namespace internal
+}  // namespace testing
+
 #define MATCHER(name, description)\
   class name##Matcher {\
    public:\
     template <typename arg_type>\
     class gmock_Impl : public ::testing::MatcherInterface<arg_type> {\
      public:\
-      gmock_Impl() {}\
+      gmock_Impl(const ::testing::internal::Interpolations& gmock_interp)\
+           : gmock_interp_(gmock_interp) {}\
       virtual bool Matches(arg_type arg) const;\
-      virtual void DescribeTo(::std::ostream* os) const {\
-        *os << ::testing::internal::ConvertIdentifierNameToWords(#name);\
+      virtual void DescribeTo(::std::ostream* gmock_os) const {\
+        const ::testing::internal::Strings& gmock_printed_params = \
+            ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\
+                ::std::tr1::tuple<>());\
+        *gmock_os << ::testing::internal::FormatMatcherDescription(\
+                     #name, description, gmock_interp_, gmock_printed_params);\
       }\
+      const ::testing::internal::Interpolations gmock_interp_;\
     };\
     template <typename arg_type>\
     operator ::testing::Matcher<arg_type>() const {\
-      return ::testing::Matcher<arg_type>(new gmock_Impl<arg_type>());\
+      return ::testing::Matcher<arg_type>(\
+          new gmock_Impl<arg_type>(gmock_interp_));\
     }\
     name##Matcher() {\
-      ::testing::internal::ValidateMatcherDescription(description);\
+      const char* gmock_param_names[] = { NULL };\
+      gmock_interp_ = ::testing::internal::ValidateMatcherDescription(\
+          gmock_param_names, ("" description ""));\
     }\
+    ::testing::internal::Interpolations gmock_interp_;\
   };\
   inline name##Matcher name() {\
     return name##Matcher();\
@@ -830,23 +915,32 @@
     template <typename arg_type>\
     class gmock_Impl : public ::testing::MatcherInterface<arg_type> {\
      public:\
-      explicit gmock_Impl(p0##_type gmock_p0) : p0(gmock_p0) {}\
+      explicit gmock_Impl(p0##_type gmock_p0, \
+          const ::testing::internal::Interpolations& gmock_interp)\
+           : p0(gmock_p0), gmock_interp_(gmock_interp) {}\
       virtual bool Matches(arg_type arg) const;\
-      virtual void DescribeTo(::std::ostream* os) const {\
-        *os << ::testing::internal::ConvertIdentifierNameToWords(#name);\
-        *os << " ";\
-        ::testing::internal::UniversalPrint(p0, os);\
+      virtual void DescribeTo(::std::ostream* gmock_os) const {\
+        const ::testing::internal::Strings& gmock_printed_params = \
+            ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\
+                ::std::tr1::tuple<p0##_type>(p0));\
+        *gmock_os << ::testing::internal::FormatMatcherDescription(\
+                     #name, description, gmock_interp_, gmock_printed_params);\
       }\
       p0##_type p0;\
+      const ::testing::internal::Interpolations gmock_interp_;\
     };\
     template <typename arg_type>\
     operator ::testing::Matcher<arg_type>() const {\
-      return ::testing::Matcher<arg_type>(new gmock_Impl<arg_type>(p0));\
+      return ::testing::Matcher<arg_type>(\
+          new gmock_Impl<arg_type>(p0, gmock_interp_));\
     }\
     name##MatcherP(p0##_type gmock_p0) : p0(gmock_p0) {\
-      ::testing::internal::ValidateMatcherDescription(description);\
+      const char* gmock_param_names[] = { #p0, NULL };\
+      gmock_interp_ = ::testing::internal::ValidateMatcherDescription(\
+          gmock_param_names, ("" description ""));\
     }\
     p0##_type p0;\
+    ::testing::internal::Interpolations gmock_interp_;\
   };\
   template <typename p0##_type>\
   inline name##MatcherP<p0##_type> name(p0##_type p0) {\
@@ -864,30 +958,35 @@
     template <typename arg_type>\
     class gmock_Impl : public ::testing::MatcherInterface<arg_type> {\
      public:\
-      gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1) : p0(gmock_p0), \
-          p1(gmock_p1) {}\
+      gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, \
+          const ::testing::internal::Interpolations& gmock_interp)\
+           : p0(gmock_p0), p1(gmock_p1), gmock_interp_(gmock_interp) {}\
       virtual bool Matches(arg_type arg) const;\
-      virtual void DescribeTo(::std::ostream* os) const {\
-        *os << ::testing::internal::ConvertIdentifierNameToWords(#name);\
-        *os << " (";\
-        ::testing::internal::UniversalPrint(p0, os);\
-        *os << ", ";\
-        ::testing::internal::UniversalPrint(p1, os);\
-        *os << ")";\
+      virtual void DescribeTo(::std::ostream* gmock_os) const {\
+        const ::testing::internal::Strings& gmock_printed_params = \
+            ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\
+                ::std::tr1::tuple<p0##_type, p1##_type>(p0, p1));\
+        *gmock_os << ::testing::internal::FormatMatcherDescription(\
+                     #name, description, gmock_interp_, gmock_printed_params);\
       }\
       p0##_type p0;\
       p1##_type p1;\
+      const ::testing::internal::Interpolations gmock_interp_;\
     };\
     template <typename arg_type>\
     operator ::testing::Matcher<arg_type>() const {\
-      return ::testing::Matcher<arg_type>(new gmock_Impl<arg_type>(p0, p1));\
+      return ::testing::Matcher<arg_type>(\
+          new gmock_Impl<arg_type>(p0, p1, gmock_interp_));\
     }\
     name##MatcherP2(p0##_type gmock_p0, p1##_type gmock_p1) : p0(gmock_p0), \
         p1(gmock_p1) {\
-      ::testing::internal::ValidateMatcherDescription(description);\
+      const char* gmock_param_names[] = { #p0, #p1, NULL };\
+      gmock_interp_ = ::testing::internal::ValidateMatcherDescription(\
+          gmock_param_names, ("" description ""));\
     }\
     p0##_type p0;\
     p1##_type p1;\
+    ::testing::internal::Interpolations gmock_interp_;\
   };\
   template <typename p0##_type, typename p1##_type>\
   inline name##MatcherP2<p0##_type, p1##_type> name(p0##_type p0, \
@@ -906,35 +1005,39 @@
     template <typename arg_type>\
     class gmock_Impl : public ::testing::MatcherInterface<arg_type> {\
      public:\
-      gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, \
-          p2##_type gmock_p2) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2) {}\
+      gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
+          const ::testing::internal::Interpolations& gmock_interp)\
+           : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
+               gmock_interp_(gmock_interp) {}\
       virtual bool Matches(arg_type arg) const;\
-      virtual void DescribeTo(::std::ostream* os) const {\
-        *os << ::testing::internal::ConvertIdentifierNameToWords(#name);\
-        *os << " (";\
-        ::testing::internal::UniversalPrint(p0, os);\
-        *os << ", ";\
-        ::testing::internal::UniversalPrint(p1, os);\
-        *os << ", ";\
-        ::testing::internal::UniversalPrint(p2, os);\
-        *os << ")";\
+      virtual void DescribeTo(::std::ostream* gmock_os) const {\
+        const ::testing::internal::Strings& gmock_printed_params = \
+            ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\
+                ::std::tr1::tuple<p0##_type, p1##_type, p2##_type>(p0, p1, \
+                    p2));\
+        *gmock_os << ::testing::internal::FormatMatcherDescription(\
+                     #name, description, gmock_interp_, gmock_printed_params);\
       }\
       p0##_type p0;\
       p1##_type p1;\
       p2##_type p2;\
+      const ::testing::internal::Interpolations gmock_interp_;\
     };\
     template <typename arg_type>\
     operator ::testing::Matcher<arg_type>() const {\
-      return ::testing::Matcher<arg_type>(new gmock_Impl<arg_type>(p0, p1, \
-          p2));\
+      return ::testing::Matcher<arg_type>(\
+          new gmock_Impl<arg_type>(p0, p1, p2, gmock_interp_));\
     }\
     name##MatcherP3(p0##_type gmock_p0, p1##_type gmock_p1, \
         p2##_type gmock_p2) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2) {\
-      ::testing::internal::ValidateMatcherDescription(description);\
+      const char* gmock_param_names[] = { #p0, #p1, #p2, NULL };\
+      gmock_interp_ = ::testing::internal::ValidateMatcherDescription(\
+          gmock_param_names, ("" description ""));\
     }\
     p0##_type p0;\
     p1##_type p1;\
     p2##_type p2;\
+    ::testing::internal::Interpolations gmock_interp_;\
   };\
   template <typename p0##_type, typename p1##_type, typename p2##_type>\
   inline name##MatcherP3<p0##_type, p1##_type, p2##_type> name(p0##_type p0, \
@@ -955,40 +1058,42 @@
     class gmock_Impl : public ::testing::MatcherInterface<arg_type> {\
      public:\
       gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
-          p3##_type gmock_p3) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
-          p3(gmock_p3) {}\
+          p3##_type gmock_p3, \
+          const ::testing::internal::Interpolations& gmock_interp)\
+           : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), p3(gmock_p3), \
+               gmock_interp_(gmock_interp) {}\
       virtual bool Matches(arg_type arg) const;\
-      virtual void DescribeTo(::std::ostream* os) const {\
-        *os << ::testing::internal::ConvertIdentifierNameToWords(#name);\
-        *os << " (";\
-        ::testing::internal::UniversalPrint(p0, os);\
-        *os << ", ";\
-        ::testing::internal::UniversalPrint(p1, os);\
-        *os << ", ";\
-        ::testing::internal::UniversalPrint(p2, os);\
-        *os << ", ";\
-        ::testing::internal::UniversalPrint(p3, os);\
-        *os << ")";\
+      virtual void DescribeTo(::std::ostream* gmock_os) const {\
+        const ::testing::internal::Strings& gmock_printed_params = \
+            ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\
+                ::std::tr1::tuple<p0##_type, p1##_type, p2##_type, \
+                    p3##_type>(p0, p1, p2, p3));\
+        *gmock_os << ::testing::internal::FormatMatcherDescription(\
+                     #name, description, gmock_interp_, gmock_printed_params);\
       }\
       p0##_type p0;\
       p1##_type p1;\
       p2##_type p2;\
       p3##_type p3;\
+      const ::testing::internal::Interpolations gmock_interp_;\
     };\
     template <typename arg_type>\
     operator ::testing::Matcher<arg_type>() const {\
-      return ::testing::Matcher<arg_type>(new gmock_Impl<arg_type>(p0, p1, \
-          p2, p3));\
+      return ::testing::Matcher<arg_type>(\
+          new gmock_Impl<arg_type>(p0, p1, p2, p3, gmock_interp_));\
     }\
     name##MatcherP4(p0##_type gmock_p0, p1##_type gmock_p1, \
         p2##_type gmock_p2, p3##_type gmock_p3) : p0(gmock_p0), p1(gmock_p1), \
         p2(gmock_p2), p3(gmock_p3) {\
-      ::testing::internal::ValidateMatcherDescription(description);\
+      const char* gmock_param_names[] = { #p0, #p1, #p2, #p3, NULL };\
+      gmock_interp_ = ::testing::internal::ValidateMatcherDescription(\
+          gmock_param_names, ("" description ""));\
     }\
     p0##_type p0;\
     p1##_type p1;\
     p2##_type p2;\
     p3##_type p3;\
+    ::testing::internal::Interpolations gmock_interp_;\
   };\
   template <typename p0##_type, typename p1##_type, typename p2##_type, \
       typename p3##_type>\
@@ -1013,45 +1118,45 @@
     class gmock_Impl : public ::testing::MatcherInterface<arg_type> {\
      public:\
       gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
-          p3##_type gmock_p3, p4##_type gmock_p4) : p0(gmock_p0), \
-          p1(gmock_p1), p2(gmock_p2), p3(gmock_p3), p4(gmock_p4) {}\
+          p3##_type gmock_p3, p4##_type gmock_p4, \
+          const ::testing::internal::Interpolations& gmock_interp)\
+           : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), p3(gmock_p3), \
+               p4(gmock_p4), gmock_interp_(gmock_interp) {}\
       virtual bool Matches(arg_type arg) const;\
-      virtual void DescribeTo(::std::ostream* os) const {\
-        *os << ::testing::internal::ConvertIdentifierNameToWords(#name);\
-        *os << " (";\
-        ::testing::internal::UniversalPrint(p0, os);\
-        *os << ", ";\
-        ::testing::internal::UniversalPrint(p1, os);\
-        *os << ", ";\
-        ::testing::internal::UniversalPrint(p2, os);\
-        *os << ", ";\
-        ::testing::internal::UniversalPrint(p3, os);\
-        *os << ", ";\
-        ::testing::internal::UniversalPrint(p4, os);\
-        *os << ")";\
+      virtual void DescribeTo(::std::ostream* gmock_os) const {\
+        const ::testing::internal::Strings& gmock_printed_params = \
+            ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\
+                ::std::tr1::tuple<p0##_type, p1##_type, p2##_type, p3##_type, \
+                    p4##_type>(p0, p1, p2, p3, p4));\
+        *gmock_os << ::testing::internal::FormatMatcherDescription(\
+                     #name, description, gmock_interp_, gmock_printed_params);\
       }\
       p0##_type p0;\
       p1##_type p1;\
       p2##_type p2;\
       p3##_type p3;\
       p4##_type p4;\
+      const ::testing::internal::Interpolations gmock_interp_;\
     };\
     template <typename arg_type>\
     operator ::testing::Matcher<arg_type>() const {\
-      return ::testing::Matcher<arg_type>(new gmock_Impl<arg_type>(p0, p1, \
-          p2, p3, p4));\
+      return ::testing::Matcher<arg_type>(\
+          new gmock_Impl<arg_type>(p0, p1, p2, p3, p4, gmock_interp_));\
     }\
     name##MatcherP5(p0##_type gmock_p0, p1##_type gmock_p1, \
         p2##_type gmock_p2, p3##_type gmock_p3, \
         p4##_type gmock_p4) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
         p3(gmock_p3), p4(gmock_p4) {\
-      ::testing::internal::ValidateMatcherDescription(description);\
+      const char* gmock_param_names[] = { #p0, #p1, #p2, #p3, #p4, NULL };\
+      gmock_interp_ = ::testing::internal::ValidateMatcherDescription(\
+          gmock_param_names, ("" description ""));\
     }\
     p0##_type p0;\
     p1##_type p1;\
     p2##_type p2;\
     p3##_type p3;\
     p4##_type p4;\
+    ::testing::internal::Interpolations gmock_interp_;\
   };\
   template <typename p0##_type, typename p1##_type, typename p2##_type, \
       typename p3##_type, typename p4##_type>\
@@ -1076,25 +1181,18 @@
     class gmock_Impl : public ::testing::MatcherInterface<arg_type> {\
      public:\
       gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
-          p3##_type gmock_p3, p4##_type gmock_p4, \
-          p5##_type gmock_p5) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
-          p3(gmock_p3), p4(gmock_p4), p5(gmock_p5) {}\
+          p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \
+          const ::testing::internal::Interpolations& gmock_interp)\
+           : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), p3(gmock_p3), \
+               p4(gmock_p4), p5(gmock_p5), gmock_interp_(gmock_interp) {}\
       virtual bool Matches(arg_type arg) const;\
-      virtual void DescribeTo(::std::ostream* os) const {\
-        *os << ::testing::internal::ConvertIdentifierNameToWords(#name);\
-        *os << " (";\
-        ::testing::internal::UniversalPrint(p0, os);\
-        *os << ", ";\
-        ::testing::internal::UniversalPrint(p1, os);\
-        *os << ", ";\
-        ::testing::internal::UniversalPrint(p2, os);\
-        *os << ", ";\
-        ::testing::internal::UniversalPrint(p3, os);\
-        *os << ", ";\
-        ::testing::internal::UniversalPrint(p4, os);\
-        *os << ", ";\
-        ::testing::internal::UniversalPrint(p5, os);\
-        *os << ")";\
+      virtual void DescribeTo(::std::ostream* gmock_os) const {\
+        const ::testing::internal::Strings& gmock_printed_params = \
+            ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\
+                ::std::tr1::tuple<p0##_type, p1##_type, p2##_type, p3##_type, \
+                    p4##_type, p5##_type>(p0, p1, p2, p3, p4, p5));\
+        *gmock_os << ::testing::internal::FormatMatcherDescription(\
+                     #name, description, gmock_interp_, gmock_printed_params);\
       }\
       p0##_type p0;\
       p1##_type p1;\
@@ -1102,17 +1200,20 @@
       p3##_type p3;\
       p4##_type p4;\
       p5##_type p5;\
+      const ::testing::internal::Interpolations gmock_interp_;\
     };\
     template <typename arg_type>\
     operator ::testing::Matcher<arg_type>() const {\
-      return ::testing::Matcher<arg_type>(new gmock_Impl<arg_type>(p0, p1, \
-          p2, p3, p4, p5));\
+      return ::testing::Matcher<arg_type>(\
+          new gmock_Impl<arg_type>(p0, p1, p2, p3, p4, p5, gmock_interp_));\
     }\
     name##MatcherP6(p0##_type gmock_p0, p1##_type gmock_p1, \
         p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \
         p5##_type gmock_p5) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
         p3(gmock_p3), p4(gmock_p4), p5(gmock_p5) {\
-      ::testing::internal::ValidateMatcherDescription(description);\
+      const char* gmock_param_names[] = { #p0, #p1, #p2, #p3, #p4, #p5, NULL };\
+      gmock_interp_ = ::testing::internal::ValidateMatcherDescription(\
+          gmock_param_names, ("" description ""));\
     }\
     p0##_type p0;\
     p1##_type p1;\
@@ -1120,6 +1221,7 @@
     p3##_type p3;\
     p4##_type p4;\
     p5##_type p5;\
+    ::testing::internal::Interpolations gmock_interp_;\
   };\
   template <typename p0##_type, typename p1##_type, typename p2##_type, \
       typename p3##_type, typename p4##_type, typename p5##_type>\
@@ -1147,26 +1249,20 @@
      public:\
       gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
           p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \
-          p6##_type gmock_p6) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
-          p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6) {}\
+          p6##_type gmock_p6, \
+          const ::testing::internal::Interpolations& gmock_interp)\
+           : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), p3(gmock_p3), \
+               p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), \
+               gmock_interp_(gmock_interp) {}\
       virtual bool Matches(arg_type arg) const;\
-      virtual void DescribeTo(::std::ostream* os) const {\
-        *os << ::testing::internal::ConvertIdentifierNameToWords(#name);\
-        *os << " (";\
-        ::testing::internal::UniversalPrint(p0, os);\
-        *os << ", ";\
-        ::testing::internal::UniversalPrint(p1, os);\
-        *os << ", ";\
-        ::testing::internal::UniversalPrint(p2, os);\
-        *os << ", ";\
-        ::testing::internal::UniversalPrint(p3, os);\
-        *os << ", ";\
-        ::testing::internal::UniversalPrint(p4, os);\
-        *os << ", ";\
-        ::testing::internal::UniversalPrint(p5, os);\
-        *os << ", ";\
-        ::testing::internal::UniversalPrint(p6, os);\
-        *os << ")";\
+      virtual void DescribeTo(::std::ostream* gmock_os) const {\
+        const ::testing::internal::Strings& gmock_printed_params = \
+            ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\
+                ::std::tr1::tuple<p0##_type, p1##_type, p2##_type, p3##_type, \
+                    p4##_type, p5##_type, p6##_type>(p0, p1, p2, p3, p4, p5, \
+                    p6));\
+        *gmock_os << ::testing::internal::FormatMatcherDescription(\
+                     #name, description, gmock_interp_, gmock_printed_params);\
       }\
       p0##_type p0;\
       p1##_type p1;\
@@ -1175,18 +1271,22 @@
       p4##_type p4;\
       p5##_type p5;\
       p6##_type p6;\
+      const ::testing::internal::Interpolations gmock_interp_;\
     };\
     template <typename arg_type>\
     operator ::testing::Matcher<arg_type>() const {\
-      return ::testing::Matcher<arg_type>(new gmock_Impl<arg_type>(p0, p1, \
-          p2, p3, p4, p5, p6));\
+      return ::testing::Matcher<arg_type>(\
+          new gmock_Impl<arg_type>(p0, p1, p2, p3, p4, p5, p6, gmock_interp_));\
     }\
     name##MatcherP7(p0##_type gmock_p0, p1##_type gmock_p1, \
         p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \
         p5##_type gmock_p5, p6##_type gmock_p6) : p0(gmock_p0), p1(gmock_p1), \
         p2(gmock_p2), p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), \
         p6(gmock_p6) {\
-      ::testing::internal::ValidateMatcherDescription(description);\
+      const char* gmock_param_names[] = { #p0, #p1, #p2, #p3, #p4, #p5, #p6, \
+          NULL };\
+      gmock_interp_ = ::testing::internal::ValidateMatcherDescription(\
+          gmock_param_names, ("" description ""));\
     }\
     p0##_type p0;\
     p1##_type p1;\
@@ -1195,6 +1295,7 @@
     p4##_type p4;\
     p5##_type p5;\
     p6##_type p6;\
+    ::testing::internal::Interpolations gmock_interp_;\
   };\
   template <typename p0##_type, typename p1##_type, typename p2##_type, \
       typename p3##_type, typename p4##_type, typename p5##_type, \
@@ -1225,29 +1326,20 @@
      public:\
       gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
           p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \
-          p6##_type gmock_p6, p7##_type gmock_p7) : p0(gmock_p0), \
-          p1(gmock_p1), p2(gmock_p2), p3(gmock_p3), p4(gmock_p4), \
-          p5(gmock_p5), p6(gmock_p6), p7(gmock_p7) {}\
+          p6##_type gmock_p6, p7##_type gmock_p7, \
+          const ::testing::internal::Interpolations& gmock_interp)\
+           : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), p3(gmock_p3), \
+               p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), p7(gmock_p7), \
+               gmock_interp_(gmock_interp) {}\
       virtual bool Matches(arg_type arg) const;\
-      virtual void DescribeTo(::std::ostream* os) const {\
-        *os << ::testing::internal::ConvertIdentifierNameToWords(#name);\
-        *os << " (";\
-        ::testing::internal::UniversalPrint(p0, os);\
-        *os << ", ";\
-        ::testing::internal::UniversalPrint(p1, os);\
-        *os << ", ";\
-        ::testing::internal::UniversalPrint(p2, os);\
-        *os << ", ";\
-        ::testing::internal::UniversalPrint(p3, os);\
-        *os << ", ";\
-        ::testing::internal::UniversalPrint(p4, os);\
-        *os << ", ";\
-        ::testing::internal::UniversalPrint(p5, os);\
-        *os << ", ";\
-        ::testing::internal::UniversalPrint(p6, os);\
-        *os << ", ";\
-        ::testing::internal::UniversalPrint(p7, os);\
-        *os << ")";\
+      virtual void DescribeTo(::std::ostream* gmock_os) const {\
+        const ::testing::internal::Strings& gmock_printed_params = \
+            ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\
+                ::std::tr1::tuple<p0##_type, p1##_type, p2##_type, p3##_type, \
+                    p4##_type, p5##_type, p6##_type, p7##_type>(p0, p1, p2, \
+                    p3, p4, p5, p6, p7));\
+        *gmock_os << ::testing::internal::FormatMatcherDescription(\
+                     #name, description, gmock_interp_, gmock_printed_params);\
       }\
       p0##_type p0;\
       p1##_type p1;\
@@ -1257,11 +1349,13 @@
       p5##_type p5;\
       p6##_type p6;\
       p7##_type p7;\
+      const ::testing::internal::Interpolations gmock_interp_;\
     };\
     template <typename arg_type>\
     operator ::testing::Matcher<arg_type>() const {\
-      return ::testing::Matcher<arg_type>(new gmock_Impl<arg_type>(p0, p1, \
-          p2, p3, p4, p5, p6, p7));\
+      return ::testing::Matcher<arg_type>(\
+          new gmock_Impl<arg_type>(p0, p1, p2, p3, p4, p5, p6, p7, \
+              gmock_interp_));\
     }\
     name##MatcherP8(p0##_type gmock_p0, p1##_type gmock_p1, \
         p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \
@@ -1269,7 +1363,10 @@
         p7##_type gmock_p7) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
         p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), \
         p7(gmock_p7) {\
-      ::testing::internal::ValidateMatcherDescription(description);\
+      const char* gmock_param_names[] = { #p0, #p1, #p2, #p3, #p4, #p5, #p6, \
+          #p7, NULL };\
+      gmock_interp_ = ::testing::internal::ValidateMatcherDescription(\
+          gmock_param_names, ("" description ""));\
     }\
     p0##_type p0;\
     p1##_type p1;\
@@ -1279,6 +1376,7 @@
     p5##_type p5;\
     p6##_type p6;\
     p7##_type p7;\
+    ::testing::internal::Interpolations gmock_interp_;\
   };\
   template <typename p0##_type, typename p1##_type, typename p2##_type, \
       typename p3##_type, typename p4##_type, typename p5##_type, \
@@ -1310,32 +1408,20 @@
      public:\
       gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
           p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \
-          p6##_type gmock_p6, p7##_type gmock_p7, \
-          p8##_type gmock_p8) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
-          p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), \
-          p7(gmock_p7), p8(gmock_p8) {}\
+          p6##_type gmock_p6, p7##_type gmock_p7, p8##_type gmock_p8, \
+          const ::testing::internal::Interpolations& gmock_interp)\
+           : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), p3(gmock_p3), \
+               p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), p7(gmock_p7), \
+               p8(gmock_p8), gmock_interp_(gmock_interp) {}\
       virtual bool Matches(arg_type arg) const;\
-      virtual void DescribeTo(::std::ostream* os) const {\
-        *os << ::testing::internal::ConvertIdentifierNameToWords(#name);\
-        *os << " (";\
-        ::testing::internal::UniversalPrint(p0, os);\
-        *os << ", ";\
-        ::testing::internal::UniversalPrint(p1, os);\
-        *os << ", ";\
-        ::testing::internal::UniversalPrint(p2, os);\
-        *os << ", ";\
-        ::testing::internal::UniversalPrint(p3, os);\
-        *os << ", ";\
-        ::testing::internal::UniversalPrint(p4, os);\
-        *os << ", ";\
-        ::testing::internal::UniversalPrint(p5, os);\
-        *os << ", ";\
-        ::testing::internal::UniversalPrint(p6, os);\
-        *os << ", ";\
-        ::testing::internal::UniversalPrint(p7, os);\
-        *os << ", ";\
-        ::testing::internal::UniversalPrint(p8, os);\
-        *os << ")";\
+      virtual void DescribeTo(::std::ostream* gmock_os) const {\
+        const ::testing::internal::Strings& gmock_printed_params = \
+            ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\
+                ::std::tr1::tuple<p0##_type, p1##_type, p2##_type, p3##_type, \
+                    p4##_type, p5##_type, p6##_type, p7##_type, \
+                    p8##_type>(p0, p1, p2, p3, p4, p5, p6, p7, p8));\
+        *gmock_os << ::testing::internal::FormatMatcherDescription(\
+                     #name, description, gmock_interp_, gmock_printed_params);\
       }\
       p0##_type p0;\
       p1##_type p1;\
@@ -1346,11 +1432,13 @@
       p6##_type p6;\
       p7##_type p7;\
       p8##_type p8;\
+      const ::testing::internal::Interpolations gmock_interp_;\
     };\
     template <typename arg_type>\
     operator ::testing::Matcher<arg_type>() const {\
-      return ::testing::Matcher<arg_type>(new gmock_Impl<arg_type>(p0, p1, \
-          p2, p3, p4, p5, p6, p7, p8));\
+      return ::testing::Matcher<arg_type>(\
+          new gmock_Impl<arg_type>(p0, p1, p2, p3, p4, p5, p6, p7, p8, \
+              gmock_interp_));\
     }\
     name##MatcherP9(p0##_type gmock_p0, p1##_type gmock_p1, \
         p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \
@@ -1358,7 +1446,10 @@
         p8##_type gmock_p8) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
         p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), p7(gmock_p7), \
         p8(gmock_p8) {\
-      ::testing::internal::ValidateMatcherDescription(description);\
+      const char* gmock_param_names[] = { #p0, #p1, #p2, #p3, #p4, #p5, #p6, \
+          #p7, #p8, NULL };\
+      gmock_interp_ = ::testing::internal::ValidateMatcherDescription(\
+          gmock_param_names, ("" description ""));\
     }\
     p0##_type p0;\
     p1##_type p1;\
@@ -1369,6 +1460,7 @@
     p6##_type p6;\
     p7##_type p7;\
     p8##_type p8;\
+    ::testing::internal::Interpolations gmock_interp_;\
   };\
   template <typename p0##_type, typename p1##_type, typename p2##_type, \
       typename p3##_type, typename p4##_type, typename p5##_type, \
@@ -1403,33 +1495,20 @@
       gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
           p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \
           p6##_type gmock_p6, p7##_type gmock_p7, p8##_type gmock_p8, \
-          p9##_type gmock_p9) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
-          p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), \
-          p7(gmock_p7), p8(gmock_p8), p9(gmock_p9) {}\
+          p9##_type gmock_p9, \
+          const ::testing::internal::Interpolations& gmock_interp)\
+           : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), p3(gmock_p3), \
+               p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), p7(gmock_p7), \
+               p8(gmock_p8), p9(gmock_p9), gmock_interp_(gmock_interp) {}\
       virtual bool Matches(arg_type arg) const;\
-      virtual void DescribeTo(::std::ostream* os) const {\
-        *os << ::testing::internal::ConvertIdentifierNameToWords(#name);\
-        *os << " (";\
-        ::testing::internal::UniversalPrint(p0, os);\
-        *os << ", ";\
-        ::testing::internal::UniversalPrint(p1, os);\
-        *os << ", ";\
-        ::testing::internal::UniversalPrint(p2, os);\
-        *os << ", ";\
-        ::testing::internal::UniversalPrint(p3, os);\
-        *os << ", ";\
-        ::testing::internal::UniversalPrint(p4, os);\
-        *os << ", ";\
-        ::testing::internal::UniversalPrint(p5, os);\
-        *os << ", ";\
-        ::testing::internal::UniversalPrint(p6, os);\
-        *os << ", ";\
-        ::testing::internal::UniversalPrint(p7, os);\
-        *os << ", ";\
-        ::testing::internal::UniversalPrint(p8, os);\
-        *os << ", ";\
-        ::testing::internal::UniversalPrint(p9, os);\
-        *os << ")";\
+      virtual void DescribeTo(::std::ostream* gmock_os) const {\
+        const ::testing::internal::Strings& gmock_printed_params = \
+            ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\
+                ::std::tr1::tuple<p0##_type, p1##_type, p2##_type, p3##_type, \
+                    p4##_type, p5##_type, p6##_type, p7##_type, p8##_type, \
+                    p9##_type>(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9));\
+        *gmock_os << ::testing::internal::FormatMatcherDescription(\
+                     #name, description, gmock_interp_, gmock_printed_params);\
       }\
       p0##_type p0;\
       p1##_type p1;\
@@ -1441,11 +1520,13 @@
       p7##_type p7;\
       p8##_type p8;\
       p9##_type p9;\
+      const ::testing::internal::Interpolations gmock_interp_;\
     };\
     template <typename arg_type>\
     operator ::testing::Matcher<arg_type>() const {\
-      return ::testing::Matcher<arg_type>(new gmock_Impl<arg_type>(p0, p1, \
-          p2, p3, p4, p5, p6, p7, p8, p9));\
+      return ::testing::Matcher<arg_type>(\
+          new gmock_Impl<arg_type>(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, \
+              gmock_interp_));\
     }\
     name##MatcherP10(p0##_type gmock_p0, p1##_type gmock_p1, \
         p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \
@@ -1453,7 +1534,10 @@
         p8##_type gmock_p8, p9##_type gmock_p9) : p0(gmock_p0), p1(gmock_p1), \
         p2(gmock_p2), p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), \
         p7(gmock_p7), p8(gmock_p8), p9(gmock_p9) {\
-      ::testing::internal::ValidateMatcherDescription(description);\
+      const char* gmock_param_names[] = { #p0, #p1, #p2, #p3, #p4, #p5, #p6, \
+          #p7, #p8, #p9, NULL };\
+      gmock_interp_ = ::testing::internal::ValidateMatcherDescription(\
+          gmock_param_names, ("" description ""));\
     }\
     p0##_type p0;\
     p1##_type p1;\
@@ -1465,6 +1549,7 @@
     p7##_type p7;\
     p8##_type p8;\
     p9##_type p9;\
+    ::testing::internal::Interpolations gmock_interp_;\
   };\
   template <typename p0##_type, typename p1##_type, typename p2##_type, \
       typename p3##_type, typename p4##_type, typename p5##_type, \
diff --git a/include/gmock/gmock-generated-matchers.h.pump b/include/gmock/gmock-generated-matchers.h.pump
index 8fd7919..31be698 100644
--- a/include/gmock/gmock-generated-matchers.h.pump
+++ b/include/gmock/gmock-generated-matchers.h.pump
@@ -43,18 +43,11 @@
 #include <string>
 #include <vector>
 #include <gmock/gmock-matchers.h>
+#include <gmock/gmock-printers.h>
 
 namespace testing {
 namespace internal {
 
-// Generates a non-fatal failure iff 'description' is not a valid
-// matcher description.
-inline void ValidateMatcherDescription(const char* description) {
-  EXPECT_STREQ("", description)
-      << "The description string in a MATCHER*() macro must be \"\" "
-         "at this moment.  We will implement custom description string soon.";
-}
-
 // Implements ElementsAre() and ElementsAreArray().
 template <typename Container>
 class ElementsAreMatcherImpl : public MatcherInterface<Container> {
@@ -310,6 +303,9 @@
 }
 
 }  // namespace testing
+$$ } // This Pump meta comment fixes auto-indentation in Emacs. It will not
+$$   // show up in the generated code.
+
 
 // The MATCHER* family of macros can be used in a namespace scope to
 // define custom matchers easily.  The syntax:
@@ -318,10 +314,18 @@
 //
 // will define a matcher with the given name that executes the
 // statements, which must return a bool to indicate if the match
-// succeeds.  For now, the description_string must be "", but we'll
-// allow other values soon.  Inside the statements, you can refer to
-// the value being matched by 'arg', and refer to its type by
-// 'arg_type'.  For example:
+// succeeds.  Inside the statements, you can refer to the value being
+// matched by 'arg', and refer to its type by 'arg_type'.
+//
+// The description string documents what the matcher does, and is used
+// to generate the failure message when the match fails.  Since a
+// MATCHER() is usually defined in a header file shared by multiple
+// C++ source files, we require the description to be a C-string
+// literal to avoid possible side effects.  It can be empty, in which
+// case we'll use the sequence of words in the matcher name as the
+// description.
+//
+// For example:
 //
 //   MATCHER(IsEven, "") { return (arg % 2) == 0; }
 //
@@ -384,6 +388,38 @@
 // We also provide MATCHER_P2, MATCHER_P3, ..., up to MATCHER_P$n to
 // support multi-parameter matchers.
 //
+// When defining a parameterized matcher, you can use Python-style
+// interpolations in the description string to refer to the parameter
+// values.  We support the following syntax currently:
+//
+//   %%       a single '%' character
+//   %(*)s    all parameters of the matcher printed as a tuple
+//   %(foo)s  value of the matcher parameter named 'foo'
+//
+// For example,
+//
+//   MATCHER_P2(InClosedRange, low, hi, "is in range [%(low)s, %(hi)s]") {
+//     return low <= arg && arg <= hi;
+//   }
+//   ...
+//   EXPECT_THAT(3, InClosedRange(4, 6));
+//
+// would generate a failure that contains the message:
+//
+//   Expected: is in range [4, 6]
+//
+// If you specify "" as the description, the failure message will
+// contain the sequence of words in the matcher name followed by the
+// parameter values printed as a tuple.  For example,
+//
+//   MATCHER_P2(InClosedRange, low, hi, "") { ... }
+//   ...
+//   EXPECT_THAT(3, InClosedRange(4, 6));
+//
+// would generate a failure that contains the text:
+//
+//   Expected: in closed range (4, 6)
+//
 // For the purpose of typing, you can view
 //
 //   MATCHER_Pk(Foo, p1, ..., pk, description_string) { ... }
@@ -440,6 +476,48 @@
 // To learn more about using these macros, please search for 'MATCHER'
 // on http://code.google.com/p/googlemock/wiki/CookBook.
 
+namespace testing {
+namespace internal {
+
+// Constants denoting interpolations in a matcher description string.
+const int kTupleInterpolation = -1;    // "%(*)s"
+const int kPercentInterpolation = -2;  // "%%"
+const int kInvalidInterpolation = -3;  // "%" followed by invalid text
+
+// Records the location and content of an interpolation.
+struct Interpolation {
+  Interpolation(const char* start, const char* end, int param)
+      : start_pos(start), end_pos(end), param_index(param) {}
+
+  // Points to the start of the interpolation (the '%' character).
+  const char* start_pos;
+  // Points to the first character after the interpolation.
+  const char* end_pos;
+  // 0-based index of the interpolated matcher parameter;
+  // kTupleInterpolation for "%(*)s"; kPercentInterpolation for "%%".
+  int param_index;
+};
+
+typedef ::std::vector<Interpolation> Interpolations;
+
+// Parses a matcher description string and returns a vector of
+// interpolations that appear in the string; generates non-fatal
+// failures iff 'description' is an invalid matcher description.
+// 'param_names' is a NULL-terminated array of parameter names in the
+// order they appear in the MATCHER_P*() parameter list.
+Interpolations ValidateMatcherDescription(
+    const char* param_names[], const char* description);
+
+// Returns the actual matcher description, given the matcher name,
+// user-supplied description template string, interpolations in the
+// string, and the printed values of the matcher parameters.
+string FormatMatcherDescription(
+    const char* matcher_name, const char* description,
+    const Interpolations& interp, const Strings& param_values);
+
+}  // namespace internal
+}  // namespace testing
+
 $range i 0..n
 $for i
 
@@ -454,7 +532,11 @@
   template <$for j, [[typename p$j##_type]]>\
 ]]]]
 $var ctor_param_list = [[$for j, [[p$j##_type gmock_p$j]]]]
+$var impl_ctor_param_list = [[$for j [[p$j##_type gmock_p$j, ]]
+const ::testing::internal::Interpolations& gmock_interp]]
+$var impl_inits = [[ : $for j [[p$j(gmock_p$j), ]]gmock_interp_(gmock_interp)]]
 $var inits = [[$if i==0 [[]] $else [[ : $for j, [[p$j(gmock_p$j)]]]]]]
+$var params_and_interp = [[$for j [[p$j, ]]gmock_interp_]]
 $var params = [[$for j, [[p$j]]]]
 $var param_types = [[$if i==0 [[]] $else [[<$for j, [[p$j##_type]]>]]]]
 $var param_types_and_names = [[$for j, [[p$j##_type p$j]]]]
@@ -475,34 +557,29 @@
     template <typename arg_type>\
     class gmock_Impl : public ::testing::MatcherInterface<arg_type> {\
      public:\
-      [[$if i==1 [[explicit ]]]]gmock_Impl($ctor_param_list)$inits {}\
+      [[$if i==1 [[explicit ]]]]gmock_Impl($impl_ctor_param_list)\
+          $impl_inits {}\
       virtual bool Matches(arg_type arg) const;\
-      virtual void DescribeTo(::std::ostream* os) const {\
-        *os << ::testing::internal::ConvertIdentifierNameToWords(#name);\
-[[$if i==1 [[        *os << " ";\
-        ::testing::internal::UniversalPrint(p0, os);\
-
-]] $elif i>=2 [[        *os << " (";\
-        ::testing::internal::UniversalPrint(p0, os);\
-$range k 1..i-1
-$for k [[
-
-        *os << ", ";\
-        ::testing::internal::UniversalPrint(p$k, os);\
-]]
-
-        *os << ")";\
-
-]]]]
+      virtual void DescribeTo(::std::ostream* gmock_os) const {\
+        const ::testing::internal::Strings& gmock_printed_params = \
+            ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\
+                ::std::tr1::tuple<$for j, [[p$j##_type]]>($for j, [[p$j]]));\
+        *gmock_os << ::testing::internal::FormatMatcherDescription(\
+                     #name, description, gmock_interp_, gmock_printed_params);\
       }\$param_field_decls
+      const ::testing::internal::Interpolations gmock_interp_;\
     };\
     template <typename arg_type>\
     operator ::testing::Matcher<arg_type>() const {\
-      return ::testing::Matcher<arg_type>(new gmock_Impl<arg_type>($params));\
+      return ::testing::Matcher<arg_type>(\
+          new gmock_Impl<arg_type>($params_and_interp));\
     }\
     $class_name($ctor_param_list)$inits {\
-      ::testing::internal::ValidateMatcherDescription(description);\
+      const char* gmock_param_names[] = { $for j [[#p$j, ]]NULL };\
+      gmock_interp_ = ::testing::internal::ValidateMatcherDescription(\
+          gmock_param_names, ("" description ""));\
     }\$param_field_decls2
+    ::testing::internal::Interpolations gmock_interp_;\
   };\$template
   inline $class_name$param_types name($param_types_and_names) {\
     return $class_name$param_types($params);\
diff --git a/include/gmock/gmock-printers.h b/include/gmock/gmock-printers.h
index 5cd5f12..28e904c 100644
--- a/include/gmock/gmock-printers.h
+++ b/include/gmock/gmock-printers.h
@@ -41,22 +41,36 @@
 // type Foo by defining either operator<<(::std::ostream&, const Foo&)
 // or void PrintTo(const Foo&, ::std::ostream*) in the namespace that
 // defines Foo.  If both are defined, PrintTo() takes precedence.
-// When T is a reference type, the address of the value is also
+//
+// To aid debugging: when T is a reference type, the address of the
+// value is also printed; when T is a (const) char pointer, both the
+// pointer value and the NUL-terminated string it points to are
 // printed.
 //
 // We also provide some convenient wrappers:
 //
-//   // Prints to a string.
-//   string ::testing::internal::UniversalPrinter<T>::PrintAsString(value);
-//   // Prints a value using its inferred type.
-//   void ::testing::internal::UniversalPrint(const T& value, ostream*);
+//   // Prints a value as the given type to a string.
+//   string ::testing::internal::UniversalPrinter<T>::PrintToString(value);
+//
+//   // Prints a value tersely: for a reference type, the referenced
+//   // value (but not the address) is printed; for a (const) char
+//   // pointer, the NUL-terminated string (but not the pointer) is
+//   // printed.
+//   void ::testing::internal::UniversalTersePrint(const T& value, ostream*);
+//
+//   // Prints the fields of a tuple tersely to a string vector, one
+//   // element for each field.
+//   std::vector<string> UniversalTersePrintTupleFieldsToStrings(
+//       const Tuple& value);
 
 #ifndef GMOCK_INCLUDE_GMOCK_GMOCK_PRINTERS_H_
 #define GMOCK_INCLUDE_GMOCK_GMOCK_PRINTERS_H_
 
 #include <ostream>  // NOLINT
+#include <sstream>
 #include <string>
 #include <utility>
+#include <vector>
 
 #include <gmock/internal/gmock-internal-utils.h>
 #include <gmock/internal/gmock-port.h>
@@ -350,13 +364,19 @@
 // Overload for ::std::tr1::tuple.  Needed for printing function
 // arguments, which are packed as tuples.
 
-// This helper template allows PrintTo() for tuples to be defined by
+typedef ::std::vector<string> Strings;
+
+// This helper template allows PrintTo() for tuples and
+// UniversalTersePrintTupleFieldsToStrings() to be defined by
 // induction on the number of tuple fields.  The idea is that
 // TuplePrefixPrinter<N>::PrintPrefixTo(t, os) prints the first N
 // fields in tuple t, and can be defined in terms of
 // TuplePrefixPrinter<N - 1>.
+
+// The inductive case.
 template <size_t N>
 struct TuplePrefixPrinter {
+  // Prints the first N fields of a tuple.
   template <typename Tuple>
   static void PrintPrefixTo(const Tuple& t, ::std::ostream* os) {
     TuplePrefixPrinter<N - 1>::PrintPrefixTo(t, os);
@@ -364,20 +384,33 @@
     UniversalPrinter<typename ::std::tr1::tuple_element<N - 1, Tuple>::type>
         ::Print(::std::tr1::get<N - 1>(t), os);
   }
+
+  // Tersely prints the first N fields of a tuple to a string vector,
+  // one element for each field.
+  template <typename Tuple>
+  static void TersePrintPrefixToStrings(const Tuple& t, Strings* strings) {
+    TuplePrefixPrinter<N - 1>::TersePrintPrefixToStrings(t, strings);
+    ::std::stringstream ss;
+    UniversalTersePrint(::std::tr1::get<N - 1>(t), &ss);
+    strings->push_back(ss.str());
+  }
 };
+
+// Base cases.
 template <>
 struct TuplePrefixPrinter<0> {
   template <typename Tuple>
   static void PrintPrefixTo(const Tuple&, ::std::ostream*) {}
+
+  template <typename Tuple>
+  static void TersePrintPrefixToStrings(const Tuple&, Strings*) {}
 };
 template <>
-struct TuplePrefixPrinter<1> {
-  template <typename Tuple>
-  static void PrintPrefixTo(const Tuple& t, ::std::ostream* os) {
-    UniversalPrinter<typename ::std::tr1::tuple_element<0, Tuple>::type>::
-        Print(::std::tr1::get<0>(t), os);
-  }
-};
+template <typename Tuple>
+void TuplePrefixPrinter<1>::PrintPrefixTo(const Tuple& t, ::std::ostream* os) {
+  UniversalPrinter<typename ::std::tr1::tuple_element<0, Tuple>::type>::
+      Print(::std::tr1::get<0>(t), os);
+}
 
 // Helper function for printing a tuple.  T must be instantiated with
 // a tuple type.
@@ -499,7 +532,7 @@
 
   // A convenient wrapper for Print() that returns the print-out as a
   // string.
-  static string PrintAsString(const T& value) {
+  static string PrintToString(const T& value) {
     ::std::stringstream ss;
     Print(value, &ss);
     return ss.str();
@@ -548,7 +581,7 @@
 
   // A convenient wrapper for Print() that returns the print-out as a
   // string.
-  static string PrintAsString(const T (&a)[N]) {
+  static string PrintToString(const T (&a)[N]) {
     ::std::stringstream ss;
     Print(a, &ss);
     return ss.str();
@@ -577,7 +610,7 @@
 
   // A convenient wrapper for Print() that returns the print-out as a
   // string.
-  static string PrintAsString(const T& value) {
+  static string PrintToString(const T& value) {
     ::std::stringstream ss;
     Print(value, &ss);
     return ss.str();
@@ -588,15 +621,34 @@
 #endif  // _MSC_VER
 };
 
-// Prints a value using its inferred type.  In particular, if the
-// original type of the value is a reference, the *referenced* type
-// (as opposed to the reference type) will be used, as C++ doesn't
-// infer reference types.  This is useful when you just want to know
-// what the value is and don't care if it's a reference or not.
+// Prints a value tersely: for a reference type, the referenced value
+// (but not the address) is printed; for a (const) char pointer, the
+// NUL-terminated string (but not the pointer) is printed.
 template <typename T>
-void UniversalPrint(const T& value, ::std::ostream* os) {
+void UniversalTersePrint(const T& value, ::std::ostream* os) {
   UniversalPrinter<T>::Print(value, os);
 }
+inline void UniversalTersePrint(const char* str, ::std::ostream* os) {
+  if (str == NULL) {
+    *os << "NULL";
+  } else {
+    UniversalPrinter<string>::Print(string(str), os);
+  }
+}
+inline void UniversalTersePrint(char* str, ::std::ostream* os) {
+  UniversalTersePrint(static_cast<const char*>(str), os);
+}
+
+// Prints the fields of a tuple tersely to a string vector, one
+// element for each field.  See the comment before
+// UniversalTersePrint() for how we define "tersely".
+template <typename Tuple>
+Strings UniversalTersePrintTupleFieldsToStrings(const Tuple& value) {
+  Strings result;
+  TuplePrefixPrinter< ::std::tr1::tuple_size<Tuple>::value>::
+      TersePrintPrefixToStrings(value, &result);
+  return result;
+}
 
 }  // namespace internal
 }  // namespace testing
diff --git a/src/gmock-matchers.cc b/src/gmock-matchers.cc
index 99fd3a2..79b525d 100644
--- a/src/gmock-matchers.cc
+++ b/src/gmock-matchers.cc
@@ -31,10 +31,15 @@
 
 // Google Mock - a framework for writing C++ mock classes.
 //
-// This file implements the Matcher<const string&> and
-// Matcher<string>.
+// This file implements Matcher<const string&>, Matcher<string>, and
+// utilities for defining matchers.
 
 #include <gmock/gmock-matchers.h>
+#include <gmock/gmock-generated-matchers.h>
+
+#include <string.h>
+#include <sstream>
+#include <string>
 
 namespace testing {
 
@@ -58,4 +63,140 @@
   *this = Eq(internal::string(s));
 }
 
+namespace internal {
+
+// Utilities for validating and formatting description strings in the
+// MATCHER*() macros.
+
+// Returns the 0-based index of the given parameter in the
+// NULL-terminated parameter array; if the parameter is "*", returns
+// kTupleInterpolation; if it's not found in the list, returns
+// kInvalidInterpolation.
+int GetParamIndex(const char* param_names[], const string& param_name) {
+  if (param_name == "*")
+    return kTupleInterpolation;
+
+  for (int i = 0; param_names[i] != NULL; i++) {
+    if (param_name == param_names[i])
+      return i;
+  }
+  return kInvalidInterpolation;
+}
+
+// If *pstr starts with the given prefix, modifies *pstr to be right
+// past the prefix and returns true; otherwise leaves *pstr unchanged
+// and returns false.  None of pstr, *pstr, and prefix can be NULL.
+bool SkipPrefix(const char* prefix, const char** pstr) {
+  const size_t prefix_len = strlen(prefix);
+  if (strncmp(*pstr, prefix, prefix_len) == 0) {
+    *pstr += prefix_len;
+    return true;
+  }
+  return false;
+}
+
+// Helper function used by ValidateMatcherDescription() to format
+// error messages.
+string FormatMatcherDescriptionSyntaxError(const char* description,
+                                           const char* error_pos) {
+  ::std::stringstream ss;
+  ss << "Syntax error at index " << (error_pos - description)
+     << " in matcher description \"" << description << "\": ";
+  return ss.str();
+}
+
+// Parses a matcher description string and returns a vector of
+// interpolations that appear in the string; generates non-fatal
+// failures iff 'description' is an invalid matcher description.
+// 'param_names' is a NULL-terminated array of parameter names in the
+// order they appear in the MATCHER_P*() parameter list.
+Interpolations ValidateMatcherDescription(
+    const char* param_names[], const char* description) {
+  Interpolations interps;
+  for (const char* p = description; *p != '\0';) {
+    if (SkipPrefix("%%", &p)) {
+      interps.push_back(Interpolation(p - 2, p, kPercentInterpolation));
+    } else if (SkipPrefix("%(", &p)) {
+      const char* const q = strstr(p, ")s");
+      if (q == NULL) {
+        // TODO(wan@google.com): change the source file location in
+        // the failure to point to where the MATCHER*() macro is used.
+        ADD_FAILURE() << FormatMatcherDescriptionSyntaxError(description, p - 2)
+                      << "an interpolation must end with \")s\", "
+                      << "but \"" << (p - 2) << "\" does not.";
+      } else {
+        const string param_name(p, q);
+        const int param_index = GetParamIndex(param_names, param_name);
+        if (param_index == kInvalidInterpolation) {
+          ADD_FAILURE() << FormatMatcherDescriptionSyntaxError(description, p)
+                        << "\"" << param_name
+                        << "\" is an invalid parameter name.";
+        } else {
+          interps.push_back(Interpolation(p - 2, q + 2, param_index));
+          p = q + 2;
+        }
+      }
+    } else {
+      EXPECT_NE(*p, '%') << FormatMatcherDescriptionSyntaxError(description, p)
+                         << "use \"%%\" instead of \"%\" to print \"%\".";
+      ++p;
+    }
+  }
+  return interps;
+}
+
+// Joins a vector of strings as if they are fields of a tuple; returns
+// the joined string.
+string JoinAsTuple(const Strings& fields) {
+  switch (fields.size()) {
+    case 0:
+      return "";
+    case 1:
+      return fields[0];
+    default:
+      string result = "(" + fields[0];
+      for (size_t i = 1; i < fields.size(); i++) {
+        result += ", ";
+        result += fields[i];
+      }
+      result += ")";
+      return result;
+  }
+}
+
+// Returns the actual matcher description, given the matcher name,
+// user-supplied description template string, interpolations in the
+// string, and the printed values of the matcher parameters.
+string FormatMatcherDescription(
+    const char* matcher_name, const char* description,
+    const Interpolations& interp, const Strings& param_values) {
+  string result;
+  if (*description == '\0') {
+    // When the user supplies an empty description, we calculate one
+    // from the matcher name.
+    result = ConvertIdentifierNameToWords(matcher_name);
+    if (param_values.size() >= 1)
+      result += " " + JoinAsTuple(param_values);
+  } else {
+    // The end position of the last interpolation.
+    const char* last_interp_end = description;
+    for (size_t i = 0; i < interp.size(); i++) {
+      result.append(last_interp_end, interp[i].start_pos);
+      const int param_index = interp[i].param_index;
+      if (param_index == kTupleInterpolation) {
+        result += JoinAsTuple(param_values);
+      } else if (param_index == kPercentInterpolation) {
+        result += '%';
+      } else if (param_index != kInvalidInterpolation) {
+        result += param_values[param_index];
+      }
+      last_interp_end = interp[i].end_pos;
+    }
+    result += last_interp_end;
+  }
+
+  return result;
+}
+
+}  // namespace internal
 }  // namespace testing
diff --git a/test/gmock-generated-matchers_test.cc b/test/gmock-generated-matchers_test.cc
index 136ef5c..1f88626 100644
--- a/test/gmock-generated-matchers_test.cc
+++ b/test/gmock-generated-matchers_test.cc
@@ -390,17 +390,23 @@
 }
 
 // Tests that the description string supplied to MATCHER() must be
-// empty.
+// valid.
 
-MATCHER(HasBadDescription, "not empty?") {
-  return true;
-}
+MATCHER(HasBadDescription, "Invalid%") { return true; }
 
 TEST(MatcherMacroTest,
      CreatingMatcherWithBadDescriptionGeneratesNonfatalFailure) {
-  EXPECT_NONFATAL_FAILURE(HasBadDescription(),
-                          "The description string in a MATCHER*() macro "
-                          "must be \"\" at this moment");
+  EXPECT_NONFATAL_FAILURE(
+      HasBadDescription(),
+      "Syntax error at index 7 in matcher description \"Invalid%\": "
+      "use \"%%\" instead of \"%\" to print \"%\".");
+}
+
+MATCHER(HasGoodDescription, "good") { return true; }
+
+TEST(MatcherMacroTest, AcceptsValidDescription) {
+  const Matcher<int> m = HasGoodDescription();
+  EXPECT_EQ("good", Describe(m));
 }
 
 // Tests that the body of MATCHER() can reference the type of the
@@ -452,17 +458,26 @@
 }
 
 // Tests that the description string supplied to MATCHER_P() must be
-// empty.
+// valid.
 
-MATCHER_P(HasBadDescription1, n, "not empty?") {
+MATCHER_P(HasBadDescription1, n, "not %(m)s good") {
   return arg > n;
 }
 
 TEST(MatcherPMacroTest,
      CreatingMatcherWithBadDescriptionGeneratesNonfatalFailure) {
-  EXPECT_NONFATAL_FAILURE(HasBadDescription1(2),
-                          "The description string in a MATCHER*() macro "
-                          "must be \"\" at this moment");
+  EXPECT_NONFATAL_FAILURE(
+      HasBadDescription1(2),
+      "Syntax error at index 6 in matcher description \"not %(m)s good\": "
+      "\"m\" is an invalid parameter name.");
+}
+
+
+MATCHER_P(HasGoodDescription1, n, "good %(n)s") { return true; }
+
+TEST(MatcherPMacroTest, AcceptsValidDescription) {
+  const Matcher<int> m = HasGoodDescription1(5);
+  EXPECT_EQ("good 5", Describe(m));
 }
 
 // Tests that the description is calculated correctly from the matcher name.
@@ -509,17 +524,29 @@
 
 
 // Tests that the description string supplied to MATCHER_Pn() must be
-// empty.
+// valid.
 
-MATCHER_P2(HasBadDescription2, m, n, "not empty?") {
+MATCHER_P2(HasBadDescription2, m, n, "not %(good") {
   return arg > m + n;
 }
 
 TEST(MatcherPnMacroTest,
      CreatingMatcherWithBadDescriptionGeneratesNonfatalFailure) {
-  EXPECT_NONFATAL_FAILURE(HasBadDescription2(3, 4),
-                          "The description string in a MATCHER*() macro "
-                          "must be \"\" at this moment");
+  EXPECT_NONFATAL_FAILURE(
+      HasBadDescription2(3, 4),
+      "Syntax error at index 4 in matcher description \"not %(good\": "
+      "an interpolation must end with \")s\", but \"%(good\" does not.");
+}
+
+MATCHER_P2(HasComplexDescription, foo, bar,
+           "is as complex as %(foo)s %(bar)s (i.e. %(*)s or %%%(foo)s!)") {
+  return true;
+}
+
+TEST(MatcherPnMacroTest, AcceptsValidDescription) {
+  Matcher<int> m = HasComplexDescription(100, "ducks");
+  EXPECT_EQ("is as complex as 100 \"ducks\" (i.e. (100, \"ducks\") or %100!)",
+            Describe(m));
 }
 
 // Tests that the body of MATCHER_Pn() can reference the parameter
diff --git a/test/gmock-matchers_test.cc b/test/gmock-matchers_test.cc
index e9d12b2..0afa623 100644
--- a/test/gmock-matchers_test.cc
+++ b/test/gmock-matchers_test.cc
@@ -48,6 +48,15 @@
 #include <gtest/gtest-spi.h>
 
 namespace testing {
+
+namespace internal {
+string FormatMatcherDescriptionSyntaxError(const char* description,
+                                           const char* error_pos);
+int GetParamIndex(const char* param_names[], const string& param_name);
+string JoinAsTuple(const Strings& fields);
+bool SkipPrefix(const char* prefix, const char** pstr);
+}  // namespace internal
+
 namespace gmock_matchers_test {
 
 using std::stringstream;
@@ -91,7 +100,18 @@
 using testing::TypedEq;
 using testing::_;
 using testing::internal::FloatingEqMatcher;
+using testing::internal::FormatMatcherDescriptionSyntaxError;
+using testing::internal::GetParamIndex;
+using testing::internal::Interpolation;
+using testing::internal::Interpolations;
+using testing::internal::JoinAsTuple;
+using testing::internal::SkipPrefix;
 using testing::internal::String;
+using testing::internal::Strings;
+using testing::internal::ValidateMatcherDescription;
+using testing::internal::kInvalidInterpolation;
+using testing::internal::kPercentInterpolation;
+using testing::internal::kTupleInterpolation;
 using testing::internal::string;
 
 #ifdef GMOCK_HAS_REGEX
@@ -2769,5 +2789,299 @@
             Explain(m, test_map));
 }
 
+// Tests GetParamIndex().
+
+TEST(GetParamIndexTest, WorksForEmptyParamList) {
+  const char* params[] = { NULL };
+  EXPECT_EQ(kTupleInterpolation, GetParamIndex(params, "*"));
+  EXPECT_EQ(kInvalidInterpolation, GetParamIndex(params, "a"));
+}
+
+TEST(GetParamIndexTest, RecognizesStar) {
+  const char* params[] = { "a", "b", NULL };
+  EXPECT_EQ(kTupleInterpolation, GetParamIndex(params, "*"));
+}
+
+TEST(GetParamIndexTest, RecognizesKnownParam) {
+  const char* params[] = { "foo", "bar", NULL };
+  EXPECT_EQ(0, GetParamIndex(params, "foo"));
+  EXPECT_EQ(1, GetParamIndex(params, "bar"));
+}
+
+TEST(GetParamIndexTest, RejectsUnknownParam) {
+  const char* params[] = { "foo", "bar", NULL };
+  EXPECT_EQ(kInvalidInterpolation, GetParamIndex(params, "foobar"));
+}
+
+// Tests SkipPrefix().
+
+TEST(SkipPrefixTest, SkipsWhenPrefixMatches) {
+  const char* const str = "hello";
+
+  const char* p = str;
+  EXPECT_TRUE(SkipPrefix("", &p));
+  EXPECT_EQ(str, p);
+
+  p = str;
+  EXPECT_TRUE(SkipPrefix("hell", &p));
+  EXPECT_EQ(str + 4, p);
+}
+
+TEST(SkipPrefixTest, DoesNotSkipWhenPrefixDoesNotMatch) {
+  const char* const str = "world";
+
+  const char* p = str;
+  EXPECT_FALSE(SkipPrefix("W", &p));
+  EXPECT_EQ(str, p);
+
+  p = str;
+  EXPECT_FALSE(SkipPrefix("world!", &p));
+  EXPECT_EQ(str, p);
+}
+
+// Tests FormatMatcherDescriptionSyntaxError().
+TEST(FormatMatcherDescriptionSyntaxErrorTest, FormatsCorrectly) {
+  const char* const description = "hello%world";
+  EXPECT_EQ("Syntax error at index 5 in matcher description \"hello%world\": ",
+            FormatMatcherDescriptionSyntaxError(description, description + 5));
+}
+
+// Tests ValidateMatcherDescription().
+
+TEST(ValidateMatcherDescriptionTest, AcceptsEmptyDescription) {
+  const char* params[] = { "foo", "bar", NULL };
+  EXPECT_THAT(ValidateMatcherDescription(params, ""),
+              ElementsAre());
+}
+
+TEST(ValidateMatcherDescriptionTest,
+     AcceptsNonEmptyDescriptionWithNoInterpolation) {
+  const char* params[] = { "foo", "bar", NULL };
+  EXPECT_THAT(ValidateMatcherDescription(params, "a simple description"),
+              ElementsAre());
+}
+
+// We use MATCHER_P3() to define a matcher for testing
+// ValidateMatcherDescription(); otherwise we'll end up with much
+// plumbing code.  This is not circular as
+// ValidateMatcherDescription() doesn't affect whether the matcher
+// matches a value or not.
+MATCHER_P3(EqInterpolation, start, end, index, "equals Interpolation%(*)s") {
+  return arg.start_pos == start && arg.end_pos == end &&
+      arg.param_index == index;
+}
+
+TEST(ValidateMatcherDescriptionTest, AcceptsPercentInterpolation) {
+  const char* params[] = { "foo", NULL };
+  const char* const desc = "one %%";
+  EXPECT_THAT(ValidateMatcherDescription(params, desc),
+              ElementsAre(EqInterpolation(desc + 4, desc + 6,
+                                          kPercentInterpolation)));
+}
+
+TEST(ValidateMatcherDescriptionTest, AcceptsTupleInterpolation) {
+  const char* params[] = { "foo", "bar", "baz", NULL };
+  const char* const desc = "%(*)s after";
+  EXPECT_THAT(ValidateMatcherDescription(params, desc),
+              ElementsAre(EqInterpolation(desc, desc + 5,
+                                          kTupleInterpolation)));
+}
+
+TEST(ValidateMatcherDescriptionTest, AcceptsParamInterpolation) {
+  const char* params[] = { "foo", "bar", "baz", NULL };
+  const char* const desc = "a %(bar)s.";
+  EXPECT_THAT(ValidateMatcherDescription(params, desc),
+              ElementsAre(EqInterpolation(desc + 2, desc + 9, 1)));
+}
+
+TEST(ValidateMatcherDescriptionTest, AcceptsMultiplenterpolations) {
+  const char* params[] = { "foo", "bar", "baz", NULL };
+  const char* const desc = "%(baz)s %(foo)s %(bar)s";
+  EXPECT_THAT(ValidateMatcherDescription(params, desc),
+              ElementsAre(EqInterpolation(desc, desc + 7, 2),
+                          EqInterpolation(desc + 8, desc + 15, 0),
+                          EqInterpolation(desc + 16, desc + 23, 1)));
+}
+
+TEST(ValidateMatcherDescriptionTest, AcceptsRepeatedParams) {
+  const char* params[] = { "foo", "bar", NULL };
+  const char* const desc = "%(foo)s and %(foo)s";
+  EXPECT_THAT(ValidateMatcherDescription(params, desc),
+              ElementsAre(EqInterpolation(desc, desc + 7, 0),
+                          EqInterpolation(desc + 12, desc + 19, 0)));
+}
+
+TEST(ValidateMatcherDescriptionTest, RejectsUnknownParam) {
+  const char* params[] = { "a", "bar", NULL };
+  EXPECT_NONFATAL_FAILURE({
+    EXPECT_THAT(ValidateMatcherDescription(params, "%(foo)s"),
+                ElementsAre());
+  }, "Syntax error at index 2 in matcher description \"%(foo)s\": "
+     "\"foo\" is an invalid parameter name.");
+}
+
+TEST(ValidateMatcherDescriptionTest, RejectsUnfinishedParam) {
+  const char* params[] = { "a", "bar", NULL };
+  EXPECT_NONFATAL_FAILURE({
+    EXPECT_THAT(ValidateMatcherDescription(params, "%(foo)"),
+                ElementsAre());
+  }, "Syntax error at index 0 in matcher description \"%(foo)\": "
+     "an interpolation must end with \")s\", but \"%(foo)\" does not.");
+
+  EXPECT_NONFATAL_FAILURE({
+    EXPECT_THAT(ValidateMatcherDescription(params, "x%(a"),
+                ElementsAre());
+  }, "Syntax error at index 1 in matcher description \"x%(a\": "
+     "an interpolation must end with \")s\", but \"%(a\" does not.");
+}
+
+TEST(ValidateMatcherDescriptionTest, RejectsSinglePercent) {
+  const char* params[] = { "a", NULL };
+  EXPECT_NONFATAL_FAILURE({
+    EXPECT_THAT(ValidateMatcherDescription(params, "a %."),
+                ElementsAre());
+  }, "Syntax error at index 2 in matcher description \"a %.\": "
+     "use \"%%\" instead of \"%\" to print \"%\".");
+
+}
+
+// Tests JoinAsTuple().
+
+TEST(JoinAsTupleTest, JoinsEmptyTuple) {
+  EXPECT_EQ("", JoinAsTuple(Strings()));
+}
+
+TEST(JoinAsTupleTest, JoinsOneTuple) {
+  const char* fields[] = { "1" };
+  EXPECT_EQ("1", JoinAsTuple(Strings(fields, fields + 1)));
+}
+
+TEST(JoinAsTupleTest, JoinsTwoTuple) {
+  const char* fields[] = { "1", "a" };
+  EXPECT_EQ("(1, a)", JoinAsTuple(Strings(fields, fields + 2)));
+}
+
+TEST(JoinAsTupleTest, JoinsTenTuple) {
+  const char* fields[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9", "10" };
+  EXPECT_EQ("(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)",
+            JoinAsTuple(Strings(fields, fields + 10)));
+}
+
+// Tests FormatMatcherDescription().
+
+TEST(FormatMatcherDescriptionTest, WorksForEmptyDescription) {
+  EXPECT_EQ("is even",
+            FormatMatcherDescription("IsEven", "", Interpolations(),
+                                     Strings()));
+
+  const char* params[] = { "5" };
+  EXPECT_EQ("equals 5",
+            FormatMatcherDescription("Equals", "", Interpolations(),
+                                     Strings(params, params + 1)));
+
+  const char* params2[] = { "5", "8" };
+  EXPECT_EQ("is in range (5, 8)",
+            FormatMatcherDescription("IsInRange", "", Interpolations(),
+                                     Strings(params2, params2 + 2)));
+}
+
+TEST(FormatMatcherDescriptionTest, WorksForDescriptionWithNoInterpolation) {
+  EXPECT_EQ("is positive",
+            FormatMatcherDescription("Gt0", "is positive", Interpolations(),
+                                     Strings()));
+
+  const char* params[] = { "5", "6" };
+  EXPECT_EQ("is negative",
+            FormatMatcherDescription("Lt0", "is negative", Interpolations(),
+                                     Strings(params, params + 2)));
+}
+
+TEST(FormatMatcherDescriptionTest,
+     WorksWhenDescriptionStartsWithInterpolation) {
+  const char* params[] = { "5" };
+  const char* const desc = "%(num)s times bigger";
+  const Interpolation interp[] = { Interpolation(desc, desc + 7, 0) };
+  EXPECT_EQ("5 times bigger",
+            FormatMatcherDescription("Foo", desc,
+                                     Interpolations(interp, interp + 1),
+                                     Strings(params, params + 1)));
+}
+
+TEST(FormatMatcherDescriptionTest,
+     WorksWhenDescriptionEndsWithInterpolation) {
+  const char* params[] = { "5", "6" };
+  const char* const desc = "is bigger than %(y)s";
+  const Interpolation interp[] = { Interpolation(desc + 15, desc + 20, 1) };
+  EXPECT_EQ("is bigger than 6",
+            FormatMatcherDescription("Foo", desc,
+                                     Interpolations(interp, interp + 1),
+                                     Strings(params, params + 2)));
+}
+
+TEST(FormatMatcherDescriptionTest,
+     WorksWhenDescriptionStartsAndEndsWithInterpolation) {
+  const char* params[] = { "5", "6" };
+  const char* const desc = "%(x)s <= arg <= %(y)s";
+  const Interpolation interp[] = {
+    Interpolation(desc, desc + 5, 0),
+    Interpolation(desc + 16, desc + 21, 1)
+  };
+  EXPECT_EQ("5 <= arg <= 6",
+            FormatMatcherDescription("Foo", desc,
+                                     Interpolations(interp, interp + 2),
+                                     Strings(params, params + 2)));
+}
+
+TEST(FormatMatcherDescriptionTest,
+     WorksWhenDescriptionDoesNotStartOrEndWithInterpolation) {
+  const char* params[] = { "5.2" };
+  const char* const desc = "has %(x)s cents";
+  const Interpolation interp[] = { Interpolation(desc + 4, desc + 9, 0) };
+  EXPECT_EQ("has 5.2 cents",
+            FormatMatcherDescription("Foo", desc,
+                                     Interpolations(interp, interp + 1),
+                                     Strings(params, params + 1)));
+}
+
+TEST(FormatMatcherDescriptionTest,
+     WorksWhenDescriptionContainsMultipleInterpolations) {
+  const char* params[] = { "5", "6" };
+  const char* const desc = "in %(*)s or [%(x)s, %(y)s]";
+  const Interpolation interp[] = {
+    Interpolation(desc + 3, desc + 8, kTupleInterpolation),
+    Interpolation(desc + 13, desc + 18, 0),
+    Interpolation(desc + 20, desc + 25, 1)
+  };
+  EXPECT_EQ("in (5, 6) or [5, 6]",
+            FormatMatcherDescription("Foo", desc,
+                                     Interpolations(interp, interp + 3),
+                                     Strings(params, params + 2)));
+}
+
+TEST(FormatMatcherDescriptionTest,
+     WorksWhenDescriptionContainsRepeatedParams) {
+  const char* params[] = { "9" };
+  const char* const desc = "in [-%(x)s, %(x)s]";
+  const Interpolation interp[] = {
+    Interpolation(desc + 5, desc + 10, 0),
+    Interpolation(desc + 12, desc + 17, 0)
+  };
+  EXPECT_EQ("in [-9, 9]",
+            FormatMatcherDescription("Foo", desc,
+                                     Interpolations(interp, interp + 2),
+                                     Strings(params, params + 1)));
+}
+
+TEST(FormatMatcherDescriptionTest,
+     WorksForDescriptionWithInvalidInterpolation) {
+  const char* params[] = { "9" };
+  const char* const desc = "> %(x)s %(x)";
+  const Interpolation interp[] = { Interpolation(desc + 2, desc + 7, 0)  };
+  EXPECT_EQ("> 9 %(x)",
+            FormatMatcherDescription("Foo", desc,
+                                     Interpolations(interp, interp + 1),
+                                     Strings(params, params + 1)));
+}
+
 }  // namespace gmock_matchers_test
 }  // namespace testing
diff --git a/test/gmock-printers_test.cc b/test/gmock-printers_test.cc
index 8ce2b73..e5e3ff1 100644
--- a/test/gmock-printers_test.cc
+++ b/test/gmock-printers_test.cc
@@ -47,6 +47,7 @@
 #include <string>
 #include <utility>
 #include <vector>
+#include <gmock/gmock-generated-matchers.h>
 #include <gmock/gmock-matchers.h>
 #include <gmock/internal/gmock-port.h>
 #include <gtest/gtest.h>
@@ -151,8 +152,11 @@
 using ::std::tr1::make_tuple;
 using ::std::tr1::tuple;
 using ::std::vector;
+using ::testing::ElementsAre;
 using ::testing::StartsWith;
-using ::testing::internal::UniversalPrint;
+using ::testing::internal::Strings;
+using ::testing::internal::UniversalTersePrint;
+using ::testing::internal::UniversalTersePrintTupleFieldsToStrings;
 using ::testing::internal::UniversalPrinter;
 using ::testing::internal::string;
 
@@ -981,28 +985,67 @@
                          + " " + Print(sizeof(p)) + "-byte object "));
 }
 
-TEST(PrintAsStringTest, WorksForNonReference) {
-  EXPECT_EQ("123", UniversalPrinter<int>::PrintAsString(123));
+TEST(PrintToStringTest, WorksForNonReference) {
+  EXPECT_EQ("123", UniversalPrinter<int>::PrintToString(123));
 }
 
-TEST(PrintAsStringTest, WorksForReference) {
+TEST(PrintToStringTest, WorksForReference) {
   int n = 123;
   EXPECT_EQ("@" + PrintPointer(&n) + " 123",
-            UniversalPrinter<const int&>::PrintAsString(n));
+            UniversalPrinter<const int&>::PrintToString(n));
 }
 
-TEST(UniversalPrintTest, WorksForNonReference) {
+TEST(UniversalTersePrintTest, WorksForNonReference) {
   ::std::stringstream ss;
-  UniversalPrint(123, &ss);
+  UniversalTersePrint(123, &ss);
   EXPECT_EQ("123", ss.str());
 }
 
-TEST(UniversalPrintTest, WorksForReference) {
+TEST(UniversalTersePrintTest, WorksForReference) {
   const int& n = 123;
   ::std::stringstream ss;
-  UniversalPrint(n, &ss);
+  UniversalTersePrint(n, &ss);
   EXPECT_EQ("123", ss.str());
 }
 
+TEST(UniversalTersePrintTest, WorksForCString) {
+  const char* s1 = "abc";
+  ::std::stringstream ss1;
+  UniversalTersePrint(s1, &ss1);
+  EXPECT_EQ("\"abc\"", ss1.str());
+
+  char* s2 = const_cast<char*>(s1);
+  ::std::stringstream ss2;
+  UniversalTersePrint(s2, &ss2);
+  EXPECT_EQ("\"abc\"", ss2.str());
+
+  const char* s3 = NULL;
+  ::std::stringstream ss3;
+  UniversalTersePrint(s3, &ss3);
+  EXPECT_EQ("NULL", ss3.str());
+}
+
+TEST(UniversalTersePrintTupleFieldsToStringsTest, PrintsEmptyTuple) {
+  EXPECT_THAT(UniversalTersePrintTupleFieldsToStrings(make_tuple()),
+              ElementsAre());
+}
+
+TEST(UniversalTersePrintTupleFieldsToStringsTest, PrintsOneTuple) {
+  EXPECT_THAT(UniversalTersePrintTupleFieldsToStrings(make_tuple(1)),
+              ElementsAre("1"));
+}
+
+TEST(UniversalTersePrintTupleFieldsToStringsTest, PrintsTwoTuple) {
+  EXPECT_THAT(UniversalTersePrintTupleFieldsToStrings(make_tuple(1, 'a')),
+              ElementsAre("1", "'a' (97)"));
+}
+
+TEST(UniversalTersePrintTupleFieldsToStringsTest, PrintsTersely) {
+  const int n = 1;
+  EXPECT_THAT(UniversalTersePrintTupleFieldsToStrings(
+                  tuple<const int&, const char*>(n, "a")),
+              ElementsAre("1", "\"a\""));
+}
+
 }  // namespace gmock_printers_test
 }  // namespace testing