]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
testsuite: Add C++ plugin to check for libstdc++ header uglification
authorJakub Jelinek <jakub@redhat.com>
Wed, 29 Apr 2026 05:49:32 +0000 (07:49 +0200)
committerJakub Jelinek <jakub@gcc.gnu.org>
Wed, 29 Apr 2026 05:49:32 +0000 (07:49 +0200)
The following patch adds a plugin (sorry, to check-g++ testsuite rather
than libstdc++ testsuite because in plugin.exp we have all the needed
infrastructure.

The plugin diagnoses non-obfuscated function parameter names, automatic
variable names, template arguments, requires arguments etc., but as an
exception allows non-obfuscated names which appear as function/template
etc. names in std namespace.  The uglification.C test verifies the plugin
diagnoses what it should be.

2026-04-29  Jakub Jelinek  <jakub@redhat.com>

* g++.dg/plugin/plugin.exp (plugin_test_list): Add uglification tests.
* g++.dg/plugin/uglification_plugin.cc: New file.
* g++.dg/plugin/uglification.C: New test.
* g++.dg/plugin/uglification-c++98.C: New test.
* g++.dg/plugin/uglification-c++11.C: New test.
* g++.dg/plugin/uglification-c++14.C: New test.
* g++.dg/plugin/uglification-c++17.C: New test.
* g++.dg/plugin/uglification-c++20.C: New test.
* g++.dg/plugin/uglification-c++23.C: New test.
* g++.dg/plugin/uglification-c++26.C: New test.

Reviewed-by: Andrew Pinski <andrew.pinski@oss.qualcomm.com>
gcc/testsuite/g++.dg/plugin/plugin.exp
gcc/testsuite/g++.dg/plugin/uglification-c++11.C [new file with mode: 0644]
gcc/testsuite/g++.dg/plugin/uglification-c++14.C [new file with mode: 0644]
gcc/testsuite/g++.dg/plugin/uglification-c++17.C [new file with mode: 0644]
gcc/testsuite/g++.dg/plugin/uglification-c++20.C [new file with mode: 0644]
gcc/testsuite/g++.dg/plugin/uglification-c++23.C [new file with mode: 0644]
gcc/testsuite/g++.dg/plugin/uglification-c++26.C [new file with mode: 0644]
gcc/testsuite/g++.dg/plugin/uglification-c++98.C [new file with mode: 0644]
gcc/testsuite/g++.dg/plugin/uglification.C [new file with mode: 0644]
gcc/testsuite/g++.dg/plugin/uglification_plugin.cc [new file with mode: 0644]

index ec403e26d51629b099552b67ec3fe8024ff3b37b..26df19b1b5a8b2053b00ab6b3cf0f125dd8cbbc1 100644 (file)
@@ -78,6 +78,15 @@ set plugin_test_list [list \
          show-template-tree-color-no-highlight-colors.C \
          show-template-tree-color-labels.C \
          show-template-tree-color-no-elide-type.C } \
+    { uglification_plugin.cc \
+         uglification.C \
+         uglification-c++98.C \
+         uglification-c++11.C \
+         uglification-c++14.C \
+         uglification-c++17.C \
+         uglification-c++20.C \
+         uglification-c++23.C \
+         uglification-c++26.C } \
     { comment_plugin.cc comments-1.C } \
 ]
 
diff --git a/gcc/testsuite/g++.dg/plugin/uglification-c++11.C b/gcc/testsuite/g++.dg/plugin/uglification-c++11.C
new file mode 100644 (file)
index 0000000..b40a32c
--- /dev/null
@@ -0,0 +1,9 @@
+// Verify template parameter names, names of arguments and block scope decls
+// in functions and function templates are uglified and don't contain
+// badnames.
+// { dg-options "-O0 -std=c++11" }
+
+#include <bits/stdc++.h>
+
+// { dg-bogus "note: non-uglified name" "" { target *-*-* } 0 }
+// { dg-bogus "note: badname" "" { target *-*-* } 0 }
diff --git a/gcc/testsuite/g++.dg/plugin/uglification-c++14.C b/gcc/testsuite/g++.dg/plugin/uglification-c++14.C
new file mode 100644 (file)
index 0000000..bc1be7b
--- /dev/null
@@ -0,0 +1,9 @@
+// Verify template parameter names, names of arguments and block scope decls
+// in functions and function templates are uglified and don't contain
+// badnames.
+// { dg-options "-O0 -std=c++14" }
+
+#include <bits/stdc++.h>
+
+// { dg-bogus "note: non-uglified name" "" { target *-*-* } 0 }
+// { dg-bogus "note: badname" "" { target *-*-* } 0 }
diff --git a/gcc/testsuite/g++.dg/plugin/uglification-c++17.C b/gcc/testsuite/g++.dg/plugin/uglification-c++17.C
new file mode 100644 (file)
index 0000000..973e3c2
--- /dev/null
@@ -0,0 +1,9 @@
+// Verify template parameter names, names of arguments and block scope decls
+// in functions and function templates are uglified and don't contain
+// badnames.
+// { dg-options "-O0 -std=c++17" }
+
+#include <bits/stdc++.h>
+
+// { dg-bogus "note: non-uglified name" "" { target *-*-* } 0 }
+// { dg-bogus "note: badname" "" { target *-*-* } 0 }
diff --git a/gcc/testsuite/g++.dg/plugin/uglification-c++20.C b/gcc/testsuite/g++.dg/plugin/uglification-c++20.C
new file mode 100644 (file)
index 0000000..8e83e03
--- /dev/null
@@ -0,0 +1,9 @@
+// Verify template parameter names, names of arguments and block scope decls
+// in functions and function templates are uglified and don't contain
+// badnames.
+// { dg-options "-O0 -std=c++20" }
+
+#include <bits/stdc++.h>
+
+// { dg-bogus "note: non-uglified name" "" { target *-*-* } 0 }
+// { dg-bogus "note: badname" "" { target *-*-* } 0 }
diff --git a/gcc/testsuite/g++.dg/plugin/uglification-c++23.C b/gcc/testsuite/g++.dg/plugin/uglification-c++23.C
new file mode 100644 (file)
index 0000000..cdbd6e8
--- /dev/null
@@ -0,0 +1,9 @@
+// Verify template parameter names, names of arguments and block scope decls
+// in functions and function templates are uglified and don't contain
+// badnames.
+// { dg-options "-O0 -std=c++23" }
+
+#include <bits/stdc++.h>
+
+// { dg-bogus "note: non-uglified name" "" { target *-*-* } 0 }
+// { dg-bogus "note: badname" "" { target *-*-* } 0 }
diff --git a/gcc/testsuite/g++.dg/plugin/uglification-c++26.C b/gcc/testsuite/g++.dg/plugin/uglification-c++26.C
new file mode 100644 (file)
index 0000000..6852461
--- /dev/null
@@ -0,0 +1,9 @@
+// Verify template parameter names, names of arguments and block scope decls
+// in functions and function templates are uglified and don't contain
+// badnames.
+// { dg-options "-O0 -std=c++26 -freflection -fcontracts" }
+
+#include <bits/stdc++.h>
+
+// { dg-bogus "note: non-uglified name" "" { target *-*-* } 0 }
+// { dg-bogus "note: badname" "" { target *-*-* } 0 }
diff --git a/gcc/testsuite/g++.dg/plugin/uglification-c++98.C b/gcc/testsuite/g++.dg/plugin/uglification-c++98.C
new file mode 100644 (file)
index 0000000..527e876
--- /dev/null
@@ -0,0 +1,9 @@
+// Verify template parameter names, names of arguments and block scope decls
+// in functions and function templates are uglified and don't contain
+// badnames.
+// { dg-options "-O0 -std=c++98" }
+
+#include <bits/stdc++.h>
+
+// { dg-bogus "note: non-uglified name" "" { target *-*-* } 0 }
+// { dg-bogus "note: badname" "" { target *-*-* } 0 }
diff --git a/gcc/testsuite/g++.dg/plugin/uglification.C b/gcc/testsuite/g++.dg/plugin/uglification.C
new file mode 100644 (file)
index 0000000..6193a3b
--- /dev/null
@@ -0,0 +1,151 @@
+// Test that uglification_plugin.cc works properly.
+// { dg-options "-O0 -std=c++20" }
+
+// Pretend this is in a libstdc++-v3 header.
+#line 6 "gcc/libstdc++-v3/include/bits/universe.h"
+
+// Stuff outside of namespace std not reported.
+int
+foo (int a, int b)                     // { dg-bogus "note: non-uglified name '\[ab]'" }
+{
+  int c = a + b;                       // { dg-bogus "note: non-uglified name 'c'" }
+  return c;
+}
+
+namespace std {
+  struct foo {
+    int bar (int d, int __e) {         // { dg-message "note: non-uglified name 'd'" }
+      int f = d + __e;                 // { dg-message "note: non-uglified name 'f'" }
+      int _E12 = f;                    // { dg-message "note: badname '_E12'" }
+      using g = int;                   // { dg-message "note: non-uglified name 'g'" }
+      return f + _E12;
+    }
+    template <typename T>              // { dg-message "note: non-uglified name 'T'" }
+    int baz (int __h, int __tg_foo) {  // { dg-message "note: badname '__tg_foo'" }
+      int i = __h;                     // { dg-message "note: non-uglified name 'i'" }
+      using j = int;                   // { dg-message "note: non-uglified name 'j'" }
+      return i;
+    }
+    template <double>
+    int baz (int aa) {                 // { dg-message "note: non-uglified name 'aa'" }
+      int ab = aa;                     // { dg-message "note: non-uglified name 'ab'" }
+      using ac = double;               // { dg-message "note: non-uglified name 'ac'" }
+      return ab;
+    }
+    int zz;                            // { dg-bogus "note: non-uglified name 'zz'" }
+  };
+  template <typename _T>               // { dg-message "note: badname '_T'" }
+  struct qux {
+    int corge (int k, int __l) {       // { dg-message "note: non-uglified name 'k'" }
+      int __maskrune = k + __l;                // { dg-message "note: badname '__maskrune'" }
+      typedef std::foo __in_opt;       // { dg-message "note: badname '__in_opt'" }
+      return __maskrune;
+    }
+    template <typename _Tp>
+    int garply (int m) {               // { dg-message "note: non-uglified name 'm'" }
+      int n = m;                       // { dg-message "note: non-uglified name 'n'" }
+      struct foo { int baz; } __o;     // { dg-message "note: non-uglified whitelisted name 'foo'" }
+      __o.baz = n;
+      return __o.baz;
+    }
+  };
+  template <typename _Tp, typename _Up>
+  struct S {
+    S () = delete;
+    ~S () = delete;
+  };
+  template <typename _Tp>
+  struct S <_Tp, _Tp> {
+    int grault (int ad) {              // { dg-message "note: non-uglified name 'ad'" }
+      int ae = ad;                     // { dg-message "note: non-uglified name 'ae'" }
+      return ae;
+    }
+    int quux () { return 0; }
+  };
+  template <>
+  struct S <int, long> {
+    int grault (int af) {              // { dg-message "note: non-uglified name 'af'" }
+      int ag = af;                     // { dg-message "note: non-uglified name 'ag'" }
+      return ag;
+    }
+  };
+
+  inline namespace __cxx11 {
+    enum freddy {
+      frog, waldo
+    };
+    struct fred {
+      int garply (int p) {             // { dg-message "note: non-uglified name 'p'" }
+       int _res_ext = p;               // { dg-message "note: non-uglified name '_res_ext'" }
+       return _res_ext;
+      }
+    };
+  }
+  namespace __gnu_cxx {
+    template <typename ...U>           // { dg-message "note: non-uglified name 'U'" }
+    int foo (U... q) {                 // { dg-message "note: non-uglified name 'q'" }
+      return 0;                                // { dg-message "note: non-uglified whitelisted name 'foo'" "" { target *-*-* } .-1 }
+    }
+    enum blah {                                // { dg-message "note: non-uglified name 'blah'" }
+      plugh,                           // { dg-message "note: non-uglified name 'plugh'" }
+      thud,                            // { dg-message "note: non-uglified name 'thud'" }
+      _E5,                             // { dg-message "note: badname '_E5'" }
+      garply,                          // { dg-message "note: non-uglified whitelisted name 'garply'" }
+      grault,                          // { dg-message "note: non-uglified whitelisted name 'grault'" }
+      quux,                            // { dg-message "note: non-uglified whitelisted name 'quux'" }
+      lorem,                           // { dg-message "note: non-uglified whitelisted name 'lorem'" }
+      ipsum,                           // { dg-message "note: non-uglified whitelisted name 'ipsum'" }
+      dolor,                           // { dg-message "note: non-uglified whitelisted name 'dolor'" }
+      sit,                             // { dg-message "note: non-uglified whitelisted name 'sit'" }
+      amet,                            // { dg-message "note: non-uglified whitelisted name 'amet'" }
+      consectetur,                     // { dg-message "note: non-uglified whitelisted name 'consectetur'" }
+      adipiscing,                      // { dg-message "note: non-uglified whitelisted name 'adipiscing'" }
+      elit                             // { dg-message "note: non-uglified whitelisted name 'elit'" }
+    };
+    int waldo (int r) {                        // { dg-message "note: non-uglified name 'r'" }
+      using s = std::foo;              // { dg-message "note: non-uglified name 's'" }
+      return r;                                // { dg-message "note: non-uglified whitelisted name 'waldo'" "" { target *-*-* } .-2 }
+    }
+    int xyzzy () { return 0; }         // { dg-message "note: non-uglified whitelisted name 'xyzzy'" }
+    int barf () { return 0; }          // { dg-message "note: non-uglified name 'barf'" }
+    int __foobar () { return 0; }
+    struct V {                         // { dg-message "note: non-uglified name 'V'" }
+      union {
+       int t;                          // { dg-message "note: non-uglified name 't'" }
+      };
+      int zz () { return 0; }          // { dg-message "note: non-uglified whitelisted name 'zz'" }
+      int ww () { return 0; }          // { dg-message "note: non-uglified name 'ww'" }
+    };
+  }
+  int xyzzy (int s) {                  // { dg-message "note: non-uglified name 's'" }
+    return s;
+  }
+  template <int _Idx>
+  struct lorem {
+    template <int _Idx2>
+    friend int ipsum (int ah) {                // { dg-message "note: non-uglified name 'ah'" }
+      int ai = ah;                     // { dg-message "note: non-uglified name 'ai'" }
+      return ai;
+    }
+  };
+  template <int _Idx>
+  struct dolor {
+    friend int sit (int aj) {          // { dg-message "note: non-uglified name 'aj'" }
+      int ak = aj;                     // { dg-message "note: non-uglified name 'ak'" }
+      return ak;
+    }
+  };
+  struct amet {
+    template <int _Idx>
+    friend int consectetur (int al) {  // { dg-message "note: non-uglified name 'al'" }
+      int am = al;                     // { dg-message "note: non-uglified name 'am'" }
+      return am;
+    }
+  };
+  struct adipiscing {
+    friend int elit (int an) {         // { dg-message "note: non-uglified name 'an'" }
+      int ao = an;                     // { dg-message "note: non-uglified name 'ao'" }
+      return ao;
+    }
+  };
+}
diff --git a/gcc/testsuite/g++.dg/plugin/uglification_plugin.cc b/gcc/testsuite/g++.dg/plugin/uglification_plugin.cc
new file mode 100644 (file)
index 0000000..87fdc66
--- /dev/null
@@ -0,0 +1,353 @@
+#include "gcc-plugin.h"
+#include <stdlib.h>
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tree.h"
+#include "intl.h"
+#include "cp/cp-tree.h"
+#include "cp/name-lookup.h"
+#include "diagnostic.h"
+#include "stringpool.h"
+
+int plugin_is_GPL_compatible;
+
+void plugin_walk_ns (tree);
+
+const char *badnames[] = {
+  /* See libstdc++-v3/testsuite/17_intro/{,bad}names.cc.  */
+  "_A", "_B", "_C", "_G", "_L", "_N", "_P", "_S", "_T", "_U", "_X",
+  "__deref", "__used", "__unused", "__inline", "_Complex",
+  "__istype", "__maskrune", "__tolower", "__toupper", "__wchar_t",
+  "__wint_t", "_res", "_res_ext", "_C2", "__lockable", "__null_sentinel",
+  "__packed", "__weak", "__strong", "_In_", "_Inout_", "_Out_",
+  "_Reserved_", "__inout", "__in_opt", "__out_opt"
+};
+
+vec<tree> nonugly_names;
+hash_set<tree> whitelist;
+bool impl_ns;
+
+void
+plugin_check_decl (tree decl)
+{
+  if (decl == NULL_TREE)
+    return;
+
+  if (VAR_OR_FUNCTION_DECL_P (decl)
+      && DECL_EXTERNAL (decl)
+      && DECL_CONTEXT (decl)
+      && TREE_CODE (DECL_CONTEXT (decl)) == FUNCTION_DECL)
+    return;
+
+  tree name = DECL_NAME (decl);
+  if (name == NULL_TREE
+      || IDENTIFIER_ANON_P (name)
+      || name == this_identifier
+      || IDENTIFIER_ANY_OP_P (name))
+    return;
+
+  const char *cname = IDENTIFIER_POINTER (name);
+  if (memcmp (cname, "auto:", 5) == 0)
+    return;
+
+  if (cname[0] != '_'
+      || (cname[1] != '_'
+         && !ISUPPER (cname[1])
+         && cname[1]))
+    {
+      nonugly_names.safe_push (decl);
+      return;
+    }
+
+  bool badname = false;
+  for (int i = 0; i < ARRAY_SIZE (badnames); ++i)
+    if (strcmp (badnames[i], cname) == 0)
+      {
+       badname = true;
+       break;
+      }
+  if (!badname && cname[1] == 'E' && ISDIGIT (cname[2]))
+    {
+      if (cname[2] != '0'
+         && (!cname[3] || ((cname[2] == '1' || cname[2] == '2')
+                           && ISDIGIT (cname[3])
+                           && !cname[4])))
+       badname = true;
+    }
+  else if (memcmp (cname + 1, "_tg_", 4) == 0)
+    badname = true;
+  if (badname)
+    inform (DECL_SOURCE_LOCATION (decl), "badname %qs", cname);
+}
+
+tree
+plugin_check_tree (tree *tp, int */*walk_subtrees*/, void */*data*/)
+{
+  if (TREE_CODE (*tp) == BIND_EXPR)
+    for (tree var = BIND_EXPR_VARS (*tp); var; var = DECL_CHAIN (var))
+      plugin_check_decl (var);
+  if (TREE_CODE (*tp) == DECL_EXPR)
+    plugin_check_decl (DECL_EXPR_DECL (*tp));
+  return NULL_TREE;
+}
+
+void
+plugin_check_fn (tree decl)
+{
+  for (tree arg = DECL_ARGUMENTS (decl); arg; arg = DECL_CHAIN (arg))
+    plugin_check_decl (arg);
+
+  if (DECL_DEFAULTED_FN (decl))
+    return;
+  cp_walk_tree_without_duplicates (&DECL_SAVED_TREE (decl), plugin_check_tree,
+                                  NULL);
+}
+
+bool
+plugin_header_check (tree decl)
+{
+  expanded_location eloc = expand_location (DECL_SOURCE_LOCATION (decl));
+  if (eloc.file == NULL)
+    return false;
+  if (strstr (eloc.file, "/libstdc++-v3/include/") == NULL
+      && strstr (eloc.file, "/libstdc++-v3/libsupc++/") == NULL)
+    return false;
+  return true;
+}
+
+void
+plugin_walk_decl (tree decl)
+{
+  if (TREE_CODE (decl) == NAMESPACE_DECL)
+    {
+      if (impl_ns
+         || (DECL_NAME (decl)
+             && IDENTIFIER_POINTER (DECL_NAME (decl))[0] != '_'
+             && !IDENTIFIER_ANON_P (DECL_NAME (decl)))
+         || DECL_NAMESPACE_INLINE_P (decl))
+       plugin_walk_ns (decl);
+      else
+       {
+         impl_ns = true;
+         plugin_walk_ns (decl);
+         impl_ns = false;
+       }
+      return;
+    }
+
+  if (!plugin_header_check (decl))
+    return;
+
+  if (!impl_ns
+      && DECL_NAME (decl)
+      && !IDENTIFIER_ANON_P (DECL_NAME (decl))
+      && !IDENTIFIER_ANY_OP_P (DECL_NAME (decl))
+      && IDENTIFIER_POINTER (DECL_NAME (decl))[0] != '_')
+    whitelist.add (DECL_NAME (decl));
+  else if (impl_ns && DECL_NAME (decl))
+    plugin_check_decl (decl);
+
+  if (TREE_CODE (decl) == TEMPLATE_DECL)
+    {
+      tree parms = DECL_INNERMOST_TEMPLATE_PARMS (decl);
+      for (tree node : tree_vec_range (parms))
+       plugin_check_decl (TREE_VALUE (node));
+    }
+
+  if (DECL_FUNCTION_TEMPLATE_P (decl))
+    plugin_check_fn (DECL_TEMPLATE_RESULT (decl));
+  else if (TREE_CODE (decl) == FUNCTION_DECL
+          && DECL_LANG_SPECIFIC (decl)
+          && DECL_TEMPLATE_INFO (decl))
+    plugin_check_fn (decl);
+
+  if (DECL_CLASS_TEMPLATE_P (decl))
+    decl = DECL_TEMPLATE_RESULT (decl);
+  if (TREE_CODE (decl) == TYPE_DECL
+      && DECL_IMPLICIT_TYPEDEF_P (decl))
+    {
+      bool save_impl_ns = impl_ns;
+      if (impl_ns
+         && DECL_NAME (decl)
+         && id_equal (DECL_NAME (decl), "_Promise_erased")
+         && DECL_CONTEXT (decl)
+         && TREE_CODE (DECL_CONTEXT (decl)) == NAMESPACE_DECL
+         && DECL_NAME (DECL_CONTEXT (decl))
+         && id_equal (DECL_NAME (DECL_CONTEXT (decl)), "__gen"))
+       /* Workaround: std::generator<R, V, A>::promise_type in
+          the standard is actually defined as std::__gen::_Promise_erased,
+          but we want to whitelist its members.  Temporarily clear
+          impl_ns in that class.  */
+       impl_ns = false;
+      if (RECORD_OR_UNION_TYPE_P (TREE_TYPE (decl)))
+       {
+         for (tree fld = TYPE_FIELDS (TREE_TYPE (decl));
+              fld; fld = DECL_CHAIN (fld))
+           plugin_walk_decl (fld);
+         for (tree f = DECL_FRIENDLIST (decl); f; f = TREE_CHAIN (f))
+           for (tree l = FRIEND_DECLS (f); l; l = TREE_CHAIN (l))
+             plugin_walk_decl (TREE_VALUE (l));
+       }
+      else if (TREE_CODE (TREE_TYPE (decl)) == ENUMERAL_TYPE)
+       for (tree en = TYPE_VALUES (TREE_TYPE (decl));
+            en; en = TREE_CHAIN (en))
+         plugin_walk_decl (TREE_VALUE (en));
+      impl_ns = save_impl_ns;
+    }
+}
+
+void
+plugin_walk_binding (tree binding)
+{
+  tree value = NULL_TREE;
+
+  if (STAT_HACK_P (binding))
+    {
+      if (!STAT_TYPE_HIDDEN_P (binding)
+         && STAT_TYPE (binding))
+       return plugin_walk_decl (STAT_TYPE (binding));
+      else if (!STAT_DECL_HIDDEN_P (binding))
+       value = STAT_DECL (binding);
+    }
+  else
+    value = binding;
+
+  value = ovl_skip_hidden (value);
+  if (value)
+    {
+      value = OVL_FIRST (value);
+      return plugin_walk_decl (value);
+    }
+}
+
+void
+plugin_walk_ns (tree ns)
+{
+  using itert = hash_table<named_decl_hash>::iterator;
+  itert end (DECL_NAMESPACE_BINDINGS (ns)->end ());
+  for (itert iter (DECL_NAMESPACE_BINDINGS (ns)->begin ());
+       iter != end; ++iter)
+    {
+      tree b = *iter;
+      gcc_assert (TREE_CODE (b) != BINDING_VECTOR);
+      plugin_walk_binding (b);
+    }
+}
+
+int
+plugin_ctx_check (tree decl)
+{
+  int ret = 1;
+  tree ctx = DECL_CONTEXT (decl);
+  while (ctx)
+    {
+      if (ctx == std_node)
+       return ret;
+      if (TREE_CODE (ctx) == NAMESPACE_DECL
+         && DECL_NAME (ctx)
+         && id_equal (DECL_NAME (ctx), "__gnu_cxx")
+         && CP_DECL_CONTEXT (ctx) == global_namespace)
+       return 2;
+      if (TREE_CODE (ctx) == NAMESPACE_DECL
+         && (!DECL_NAME (ctx)
+             || IDENTIFIER_POINTER (DECL_NAME (ctx))[0] == '_'
+             || IDENTIFIER_ANON_P (DECL_NAME (ctx)))
+         && !DECL_NAMESPACE_INLINE_P (ctx))
+       ret = 2;
+      if (TYPE_P (ctx))
+       ctx = TYPE_CONTEXT (ctx);
+      else
+       ctx = DECL_CONTEXT (ctx);
+    }
+  return 0;
+}
+
+void
+plugin_finish_parse_function (void *event_data, void *)
+{
+  tree decl = (tree) event_data;
+  if (!plugin_header_check (decl))
+    return;
+
+  /* Only diagnose stuff nested in ::std or ::__gnu_cxx
+     namespaces.  */
+  if (!plugin_ctx_check (decl))
+    return;
+
+  /* Templates are handled from plugin_walk_ns.  */
+  if (DECL_LANG_SPECIFIC (decl) && DECL_TEMPLATE_INFO (decl))
+    return;
+  plugin_check_fn (decl);
+}
+
+void
+plugin_walk_specialization (bool, spec_entry *entry, void *)
+{
+  tree info;
+  if (TYPE_P (entry->spec))
+    info = TYPE_TEMPLATE_INFO (entry->spec);
+  else
+    info = DECL_TEMPLATE_INFO (entry->spec);
+  if (PRIMARY_TEMPLATE_P (TI_TEMPLATE (info)) && TI_PARTIAL_INFO (info))
+    info = TI_PARTIAL_INFO (info);
+  tree decl = TI_TEMPLATE (info);
+  if (int c = plugin_ctx_check (decl))
+    {
+      impl_ns = c == 2;
+      plugin_walk_decl (decl);
+    }
+}
+
+void
+plugin_finish_unit (void *, void *)
+{
+  plugin_walk_ns (std_node);
+  tree gnucxx_ns = lookup_qualified_name (global_namespace, "__gnu_cxx");
+  if (gnucxx_ns && TREE_CODE (gnucxx_ns) == NAMESPACE_DECL)
+    plugin_walk_ns (gnucxx_ns);
+  walk_specializations (false, plugin_walk_specialization, NULL);
+  walk_specializations (true, plugin_walk_specialization, NULL);
+  unsigned int i;
+  tree decl;
+  FOR_EACH_VEC_ELT (nonugly_names, i, decl)
+    if (!whitelist.contains (DECL_NAME (decl)))
+      inform (DECL_SOURCE_LOCATION (decl), "non-uglified name %qs",
+             IDENTIFIER_POINTER (DECL_NAME (decl)));
+    else
+      inform (DECL_SOURCE_LOCATION (decl), "non-uglified whitelisted name %qs",
+             IDENTIFIER_POINTER (DECL_NAME (decl)));
+}
+
+void
+plugin_ggc_walker (void *)
+{
+  unsigned int i;
+  tree decl;
+  FOR_EACH_VEC_ELT (nonugly_names, i, decl)
+    gt_ggc_m_9tree_node (decl);
+  for (hash_set<tree>::iterator it = whitelist.begin ();
+       it != whitelist.end (); ++it)
+    gt_ggc_m_9tree_node (*it);
+}
+
+static const struct ggc_root_tab xtratab[] = {
+  { (void *) &xtratab, 1, 1, plugin_ggc_walker, NULL },
+  LAST_GGC_ROOT_TAB
+};
+
+int
+plugin_init (struct plugin_name_args *plugin_info,
+            struct plugin_gcc_version *version)
+{
+  const char *plugin_name = plugin_info->base_name;
+
+  register_callback (plugin_name, PLUGIN_FINISH_UNIT,
+                    plugin_finish_unit, NULL);
+  register_callback (plugin_name, PLUGIN_FINISH_PARSE_FUNCTION,
+                    plugin_finish_parse_function, NULL);
+  register_callback (plugin_name, PLUGIN_REGISTER_GGC_ROOTS,
+                    NULL, (void *) xtratab);
+
+  return 0;
+}