diff --git a/cpybuilder.py b/cpybuilder.py
index 41f613d..756c504 100644
--- a/cpybuilder.py
+++ b/cpybuilder.py
@@ -33,6 +33,14 @@ def nullable_ptr(ptr):
     else:
         return 'NULL'
 
+def c_string_quote(text):
+    res = ''
+    for c in text:
+        if c in ('"', '\\'):
+            res += '\\';
+        res += c
+    return res
+
 def simple_unaryfunc(identifier, typename, c_expression):
     """Define a simple unaryfunc, using a specifc PyObject subclass"""
     self.add_defn("static PyObject *\n" +
@@ -98,7 +106,7 @@ class PyGetSetDef:
         result =  '    {"%s",\n' % self.name
         result += '     (getter)%s,\n' % nullable_ptr(self.get)
         result += '     (setter)%s,\n' % nullable_ptr(self.set)
-        result += '     "%s",\n' % nullable_ptr(self.doc)
+        result += '     "%s",\n' % c_string_quote(nullable_ptr(self.doc))
         result += '     %s},\n' % nullable_ptr(self.closure)
         return result
 
diff --git a/docs/tree.rst b/docs/tree.rst
index 316d8c4..a62b030 100644
--- a/docs/tree.rst
+++ b/docs/tree.rst
@@ -212,11 +212,16 @@ Declarations
 
       (string) the name of this declaration
 
-
    .. py:attribute:: location
 
       The :py:class:`gcc.Location` for this declaration
 
+   .. py:attribute:: attributes
+
+      The user-defined attributes on this declaration (using GCC's `__attribute`
+      syntax), as a dictionary (mapping from attribute names to list of
+      values).  Typically this will be the empty dictionary.
+
 
 .. py:class:: gcc.FieldDecl
 
@@ -296,6 +301,17 @@ Declarations
 
       (bool) For C++: is this declaration "static"
 
+   .. py:attribute:: is_virtual
+
+      (bool) For C++: is this declaration "virtual"
+
+   .. py:attribute:: is_user_provided
+
+      .. note:: This attribute is only usable with C++ code.  Attempting to use
+         it from another language will lead to a `RuntimeError` exception.
+
+      (bool) For C++: is this declaration user-provided
+
 .. py:class:: gcc.ParmDecl
 
    A subclass of :py:class:`gcc.Declaration` indicating the declaration of a
@@ -360,6 +376,12 @@ Types
 
       The :py:class:`gcc.PointerType` representing the `(this_type *)` type
 
+   .. py:attribute:: canonical
+
+      The :py:class:`gcc.Type` representing the "canonical" type for this type
+      (which is used by frontends to compare the type for equality with another
+      type)
+
    .. py:attribute:: attributes
 
       The user-defined attributes on this type (using GCC's `__attribute`
@@ -371,6 +393,14 @@ Types
       `sizeof()` this type, as an `int`, or raising `TypeError` for those
       types which don't have a well-defined size
 
+   .. py:attribute:: cv_unqualified
+
+      .. note:: This attribute is only usable with C++ code.  Attempting to use
+         it from another language will lead to a `RuntimeError` exception.
+
+      For C++: The :py:class:`gcc.Type` representing the cv-unqualified version
+      of this type
+
    The standard C types are accessible via class methods of :py:class:`gcc.Type`.
    They are only created by GCC after plugins are loaded, and so they're
    only visible during callbacks, not during the initial run of the code.
@@ -544,6 +574,15 @@ Additional attributes for various :py:class:`gcc.Type` subclasses:
 
       The fields of this type, as a list of :py:class:`gcc.FieldDecl` instances
 
+   .. py:attribute:: is_empty
+
+      Whether this class is "empty" in the sense of the C++ ABI
+
+   .. py:attribute:: is_nearly_empty
+
+      Whether this class is "nearly empty", i.e., contains only a virtual
+      function table pointer
+
    You can look up C structures by looking within the top-level
    :py:class:`gcc.Block` within the current translation unit.  For example,
    given this sample C code:
@@ -561,6 +600,14 @@ Additional attributes for various :py:class:`gcc.Type` subclasses:
 
     .. literalinclude:: ../tests/examples/c/struct/stdout.txt
 
+.. py:class:: gcc.EnumeralType
+
+   An enumeral type, such as a C `enum`
+
+   .. py:attribute:: values
+
+      The values of this type, as a list of :py:class:`gcc.TreeList` instances
+
 Constants
 ---------
 
diff --git a/gcc-python-tree.c b/gcc-python-tree.c
index 983edd5..bd23390 100644
--- a/gcc-python-tree.c
+++ b/gcc-python-tree.c
@@ -29,17 +29,18 @@
 
 
 /*
-  Unfortunately, decl_as_string() is only available from the C++
-  frontend: cc1plus (it's defined in gcc/cp/error.c).
+  Unfortunately, cv_unqualified(), decl_as_string(), and user_provided_p() are
+  only available from the C++ frontend: cc1plus (defined in gcc/cp/...c).
 
   See http://gcc.gnu.org/ml/gcc/2011-11/msg00504.html
 
-  Hence we redeclare the symbol as weak, and then check its definition
-  against 0 before using it.
+  Hence we redeclare the symbols as weak, and then check their definitions
+  against 0 before using them.
 */
 
+__typeof__ (cv_unqualified) cv_unqualified __attribute__ ((weak));
 __typeof__ (decl_as_string) decl_as_string __attribute__ ((weak));
-
+__typeof__ (user_provided_p) user_provided_p __attribute__ ((weak));
 
 //#include "rtl.h"
 /*
@@ -204,6 +205,26 @@ gcc_FunctionDecl_get_fullname(struct PyGccTree *self, void *closure)
 }
 
 PyObject *
+gcc_FunctionDecl_is_user_provided(struct PyGccTree *self, void *closure)
+{
+    if (user_provided_p == NULL) {
+        return PyErr_Format(PyExc_RuntimeError,
+                            "attribute 'is_user_provided' is only available when compiling C++ code");
+    }
+    return PyBool_FromLong(user_provided_p(self->t));
+}
+
+PyObject *
+gcc_FunctionDecl_cv_unqualified(struct PyGccTree *self, void *closure)
+{
+    if (cv_unqualified == NULL) {
+        return PyErr_Format(PyExc_RuntimeError,
+                            "attribute 'cv_unqualified' is only available when compiling C++ code");
+    }
+    return gcc_python_make_wrapper_tree(cv_unqualified(self->t));
+}
+
+PyObject *
 gcc_IdentifierNode_repr(struct PyGccTree * self)
 {
     if (IDENTIFIER_POINTER(self->t)) {
@@ -267,6 +288,36 @@ gcc_Type_get_attributes(struct PyGccTree *self, void *closure)
 }
 
 PyObject *
+gcc_Decl_get_attributes(struct PyGccTree *self, void *closure)
+{
+    tree attr;
+    PyObject *result = PyDict_New();
+    if (!result) {
+        return NULL;
+    }
+    for (attr = DECL_ATTRIBUTES(self->t); attr; attr = TREE_CHAIN(attr)) {
+        const char *attrname = IDENTIFIER_POINTER(TREE_PURPOSE(attr));
+        PyObject *values;
+        values = gcc_python_tree_make_list_from_tree_list_chain(TREE_VALUE(attr));
+        if (!values) {
+            goto error;
+        }
+
+        if (-1 == PyDict_SetItemString(result, attrname, values)) {
+            Py_DECREF(values);
+            goto error;
+        }
+        Py_DECREF(values);
+    }
+
+    return result;
+
+ error:
+    Py_DECREF(result);
+    return NULL;
+}
+
+PyObject *
 gcc_Type_get_sizeof(struct PyGccTree *self, void *closure)
 {
     /*
diff --git a/gcc-python-wrappers.h b/gcc-python-wrappers.h
index a3a5b42..0477bda 100644
--- a/gcc-python-wrappers.h
+++ b/gcc-python-wrappers.h
@@ -144,12 +144,21 @@ PyObject *
 gcc_FunctionDecl_get_fullname(struct PyGccTree *self, void *closure);
 
 PyObject *
+gcc_FunctionDecl_is_user_provided(struct PyGccTree *self, void *closure);
+
+PyObject *
+gcc_FunctionDecl_cv_unqualified(struct PyGccTree *self, void *closure);
+
+PyObject *
 gcc_IdentifierNode_repr(struct PyGccTree * self);
 
 PyObject *
 gcc_Type_get_attributes(struct PyGccTree *self, void *closure);
 
 PyObject *
+gcc_Decl_get_attributes(struct PyGccTree *self, void *closure);
+
+PyObject *
 gcc_Type_get_sizeof(struct PyGccTree *self, void *closure);
 
 PyObject *
diff --git a/generate-tree-c.py b/generate-tree-c.py
index 9e32924..2c4fa4c 100644
--- a/generate-tree-c.py
+++ b/generate-tree-c.py
@@ -31,6 +31,7 @@ cu.add_include('gcc-plugin.h')
 cu.add_include("tree.h")
 cu.add_include("function.h")
 cu.add_include("basic-block.h")
+cu.add_include("cp/cp-tree.h") # for CLASSTYPE[NEARLY_]_EMPTY_P
 cu.add_include("c-family/c-common.h")
 
 modinit_preinit = ''
@@ -185,6 +186,12 @@ gcc_Declaration_get_location(struct PyGccTree *self, void *closure)
                                   None,
                                   doc)
 
+        if localname == 'Declaration':
+            getsettable.add_gsdef('attributes',
+                                  'gcc_Decl_get_attributes',
+                                  None,
+                                  'The user-defined attributes on this declaration')
+
         if localname == 'Type':
             add_simple_getter('name',
                               'gcc_python_make_wrapper_tree(TYPE_NAME(self->t))',
@@ -192,6 +199,9 @@ gcc_Declaration_get_location(struct PyGccTree *self, void *closure)
             add_simple_getter('pointer',
                               'gcc_python_make_wrapper_tree(build_pointer_type(self->t))',
                               "The gcc.PointerType representing '(this_type *)'")
+            add_simple_getter('canonical',
+                              'gcc_python_make_wrapper_tree(TYPE_CANONICAL(self->t))',
+                              'The "canonical" type for this type (which is used by frontends to compare the type for equality with another type)')
             getsettable.add_gsdef('attributes',
                                   'gcc_Type_get_attributes',
                                   None,
@@ -200,6 +210,10 @@ gcc_Declaration_get_location(struct PyGccTree *self, void *closure)
                                   'gcc_Type_get_sizeof',
                                   None,
                                   'sizeof() this type, as a gcc.IntegerCst')
+            getsettable.add_gsdef('cv_unqualified',
+                                  'gcc_FunctionDecl_cv_unqualified',
+                                  None,
+                                  'C++ only: the cv-unqualified version of this type')
 
             def add_type(c_expr_for_node, typename):
                 # Expose the given global type node within the gcc.Tree API
@@ -435,6 +449,17 @@ def generate_tree_code_classes():
             add_simple_getter('fields',
                               'gcc_tree_list_from_chain(TYPE_FIELDS(self->t))',
                               "The fields of this type")
+            add_simple_getter('is_empty',
+                              'PyBool_FromLong(CLASSTYPE_EMPTY_P(self->t))',
+                              'Whether this class is "empty" in the sense of the C++ ABI')
+            add_simple_getter('is_nearly_empty',
+                              'PyBool_FromLong(CLASSTYPE_NEARLY_EMPTY_P(self->t))',
+                              'Whether this class is "nearly empty", i.e., contains only a virtual function table pointer')
+
+        if tree_type.SYM == 'ENUMERAL_TYPE':
+            add_simple_getter('values',
+                              'gcc_tree_list_from_chain(TYPE_VALUES(self->t))',
+                              "The values of this enum type")
 
         if tree_type.SYM == 'IDENTIFIER_NODE':
             add_simple_getter('name',
@@ -509,6 +534,15 @@ def generate_tree_code_classes():
                                               'PyBool_FromLong(TREE_%s(self->t))' % attr.upper(),
                                               None)
 
+            getsettable.add_simple_getter(cu,
+                                          'is_virtual',
+                                          'PyBool_FromLong(DECL_VIRTUAL_P(self->t))',
+                                          None)
+            getsettable.add_gsdef('is_user_provided',
+                                  'gcc_FunctionDecl_is_user_provided',
+                                  None,
+                                  'C++ only: whether this function declaration is user-provided')
+
         if tree_type.SYM == 'SSA_NAME':
             # c.f. "struct GTY(()) tree_ssa_name":
             add_simple_getter('var',
