diff mbox

[RFA,3/3] Move logic out of symbol_find_demangled_name

Message ID 8760tl6xbf.fsf@tromey.com
State New
Headers show

Commit Message

Tom Tromey June 7, 2016, 11:41 a.m. UTC
Yao> How about "la_sniff_by_symbol"?
[...]
Yao> Why don't we return int to indicate the name can be demangled or not and
Yao> put the demangled name in the second parameter if it can be?

Here's an updated version with these changes.

Tom

commit becd005e4df2877f3bd3e8c6f3b262c1a684786b
Author: Tom Tromey <tom@tromey.com>
Date:   Thu May 26 15:04:07 2016 -0600

    Move logic out of symbol_find_demangled_name
    
    This patch moves most of the demangling logic out of
    symbol_find_demangled_name into the various language_defn objects.
    
    The simplest way to do this seemed to be to add a new method to
    language_defn.  This is shame given the existing la_demangle, but
    given Ada's unusual needs, and the differing demangling options
    between languages, la_demangle didn't seem to fit.
    
    In order to make this work, I made enum language order-sensitive.
    This helps preserve the current ordering of demangling operations.
    
    2016-06-04  Tom Tromey  <tom@tromey.com>
    
    	* symtab.c (symbol_find_demangled_name): Loop over languages and
    	use language_sniff_by_symbol.
    	* rust-lang.c (rust_sniff_by_symbol): New function.
    	(rust_language_defn): Update.
    	* p-lang.c (pascal_language_defn): Update.
    	* opencl-lang.c (opencl_language_defn): Update.
    	* objc-lang.c (objc_sniff_by_symbol): New function.
    	(objc_language_defn): Update.
    	* m2-lang.c (m2_language_defn): Update.
    	* language.h (struct language_defn) <la_sniff_by_symbol>: New
    	field.
    	(language_sniff_by_symbol): Declare.
    	* language.c (language_sniff_by_symbol): New function.
    	(unknown_language_defn, auto_language_defn, local_language_defn):
    	Update.
    	* jv-lang.c (java_sniff_by_symbol): New function.
    	(java_language_defn): Use it.
    	* go-lang.c (go_sniff_by_symbol): New function.
    	(go_language_defn): Use it.
    	* f-lang.c (f_language_defn): Update.
    	* defs.h (enum language): Reorder.
    	* d-lang.c (d_sniff_by_symbol): New function.
    	(d_language_defn): Use it.
    	* cp-support.h (gdb_sniff_by_symbol): Declare.
    	* cp-support.c (gdb_sniff_by_symbol): New function.
    	* c-lang.c (c_language_defn, cplus_language_defn)
    	(asm_language_defn, minimal_language_defn): Update.
    	* ada-lang.c (ada_sniff_by_symbol): New function.
    	(ada_language_defn): Use it.

Comments

Pedro Alves June 7, 2016, 2:27 p.m. UTC | #1
On 06/07/2016 12:41 PM, Tom Tromey wrote:
> -  if (gsymbol->language == language_cplus
> -      || gsymbol->language == language_rust
> -      || gsymbol->language == language_auto)
> -    {
> -      demangled =
> -        gdb_demangle (mangled, DMGL_PARAMS | DMGL_ANSI);
> -      if (demangled != NULL)
> -	{
> -	  gsymbol->language = language_cplus;
> -	  return demangled;
> -	}
> -    }

Before, this would set language to language_cplus, even if the
symbol's language was Rust.

> +/* la_sniff_by_symbol for Rust.  */
> +
> +static int
> +rust_sniff_by_symbol (const char *mangled, char **demangled)
> +{
> +  *demangled = gdb_demangle (mangled, DMGL_PARAMS | DMGL_ANSI);
> +  return 1;
> +}

However, this no longer forces C++, is that intended?

Also, before we'd check whether gdb_demangle returned not-NULL,
before taking the language, but now several languages
return 1 even if gdb_demangle returns NULL:

> +/* la_sniff_by_symbol for Java.  */
> +
> +static int
> +java_sniff_by_symbol (const char *mangled, char **demangled)
> +{
> +  *demangled = java_demangle (mangled, DMGL_PARAMS | DMGL_ANSI);
> +  return 1;
> +}
> +

Seems like the first language to be consulted always wins?
Is that intended?

Thanks,
Pedro Alves
Tom Tromey June 7, 2016, 3:11 p.m. UTC | #2
Pedro> Before, this would set language to language_cplus, even if the
Pedro> symbol's language was Rust.
[...]
Pedro> However, this no longer forces C++, is that intended?

I think the old code was in error.

Pedro> Also, before we'd check whether gdb_demangle returned not-NULL,
Pedro> before taking the language, but now several languages
Pedro> return 1 even if gdb_demangle returns NULL:

Sorry about that.  I'll fix up the patch a bit more.

Tom
Pedro Alves June 13, 2016, 1:35 p.m. UTC | #3
On 06/07/2016 04:11 PM, Tom Tromey wrote:
> Pedro> Before, this would set language to language_cplus, even if the
> Pedro> symbol's language was Rust.
> [...]
> Pedro> However, this no longer forces C++, is that intended?
> 
> I think the old code was in error.

Actually, AFAICS, the new code does the same thing, because the C++ version
and the Rust version are exactly the same.  From your latest patch:

+/* See cp-support.h.  */
+
+int
+gdb_sniff_from_mangled_name (const char *mangled, char **demangled)
+{
+  *demangled = gdb_demangle (mangled, DMGL_PARAMS | DMGL_ANSI);
+  return *demangled != NULL;
+}

+/* la_sniff_from_mangled_name for Rust.  */
+
+static int
+rust_sniff_from_mangled_name (const char *mangled, char **demangled)
+{
+  *demangled = gdb_demangle (mangled, DMGL_PARAMS | DMGL_ANSI);
+  return *demangled != NULL;
+}
+

... and, the C++ version always wins, because C++ is listed first
in the languages enum.  Sounds like rust_sniff_from_mangled_name is
not reachable.

I think this warrants at least a comment somewhere.

IIUC, from Rust 1.9 onward, Rust uses C++ mangling, so basically
there's no way to tell a C++ symbol from a Rust symbol from the
mangled name alone.  Correct?

Thanks,
Pedro Alves
Tom Tromey June 17, 2016, 8:27 a.m. UTC | #4
Pedro> Actually, AFAICS, the new code does the same thing, because the
Pedro> C++ version and the Rust version are exactly the same.

Yeah, I forgot about that when replying.

Pedro> IIUC, from Rust 1.9 onward, Rust uses C++ mangling, so basically
Pedro> there's no way to tell a C++ symbol from a Rust symbol from the
Pedro> mangled name alone.  Correct?

Yes.  Java is in this situation as well.  However I think it only
matters for minimal symbols as symbols coming from DWARF typically have
a language.

I think it would be better if Rust changed its mangling, but AFAIK
nobody has seriously proposed this there yet.

Tom
Pedro Alves June 17, 2016, 12:25 p.m. UTC | #5
On 06/17/2016 09:27 AM, Tom Tromey wrote:
> Pedro> Actually, AFAICS, the new code does the same thing, because the
> Pedro> C++ version and the Rust version are exactly the same.
> 
> Yeah, I forgot about that when replying.
> 
> Pedro> IIUC, from Rust 1.9 onward, Rust uses C++ mangling, so basically
> Pedro> there's no way to tell a C++ symbol from a Rust symbol from the
> Pedro> mangled name alone.  Correct?
> 
> Yes.  Java is in this situation as well.  However I think it only
> matters for minimal symbols as symbols coming from DWARF typically have
> a language.

OK.  I still think we should have a comment clarifying this.  
The new comment mentions that we'll attempt demangling in the
order the constants are defined, but doesn't explain the
dependencies that led to the particular order you chose.

I think we could tweak the comment at the top of enum language
to something around this:

   Note that there's ambiguity between the mangling schemes of some of
   these languages, so some symbols could be successfully demangled by
   several languages.  For that reason, the constants here are sorted
   in the order we'll attempt demangling them.  For example: Java and
   Rust use C++ mangling, so must come after C++; Ada must come last
   (see ada_sniff_from_mangled_name).

> 
> I think it would be better if Rust changed its mangling, but AFAIK
> nobody has seriously proposed this there yet.

Thanks,
Pedro Alves
Tom Tromey June 17, 2016, 12:36 p.m. UTC | #6
Pedro> OK.  I still think we should have a comment clarifying this.  
Pedro> The new comment mentions that we'll attempt demangling in the
Pedro> order the constants are defined, but doesn't explain the
Pedro> dependencies that led to the particular order you chose.

I added one in language.h but I can move it to enum language instead.

Tom
Pedro Alves June 17, 2016, 1:50 p.m. UTC | #7
On 06/17/2016 01:36 PM, Tom Tromey wrote:
> Pedro> OK.  I still think we should have a comment clarifying this.  
> Pedro> The new comment mentions that we'll attempt demangling in the
> Pedro> order the constants are defined, but doesn't explain the
> Pedro> dependencies that led to the particular order you chose.
> 
> I added one in language.h but I can move it to enum language instead.

I think I'd prefer it close to where the order is defined.
But maybe there is OK as well; hard to say without seeing.
Maybe do both, and everyone's happy.

As long as we have a comment somewhere discoverable, I'm sure
it'll be fine.  So OK however you prefer.

Thanks,
Pedro Alves
diff mbox

Patch

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index b58ccde..6fd1d85 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,5 +1,37 @@ 
 2016-06-04  Tom Tromey  <tom@tromey.com>
 
+	* symtab.c (symbol_find_demangled_name): Loop over languages and
+	use language_sniff_by_symbol.
+	* rust-lang.c (rust_sniff_by_symbol): New function.
+	(rust_language_defn): Update.
+	* p-lang.c (pascal_language_defn): Update.
+	* opencl-lang.c (opencl_language_defn): Update.
+	* objc-lang.c (objc_sniff_by_symbol): New function.
+	(objc_language_defn): Update.
+	* m2-lang.c (m2_language_defn): Update.
+	* language.h (struct language_defn) <la_sniff_by_symbol>: New
+	field.
+	(language_sniff_by_symbol): Declare.
+	* language.c (language_sniff_by_symbol): New function.
+	(unknown_language_defn, auto_language_defn, local_language_defn):
+	Update.
+	* jv-lang.c (java_sniff_by_symbol): New function.
+	(java_language_defn): Use it.
+	* go-lang.c (go_sniff_by_symbol): New function.
+	(go_language_defn): Use it.
+	* f-lang.c (f_language_defn): Update.
+	* defs.h (enum language): Reorder.
+	* d-lang.c (d_sniff_by_symbol): New function.
+	(d_language_defn): Use it.
+	* cp-support.h (gdb_sniff_by_symbol): Declare.
+	* cp-support.c (gdb_sniff_by_symbol): New function.
+	* c-lang.c (c_language_defn, cplus_language_defn)
+	(asm_language_defn, minimal_language_defn): Update.
+	* ada-lang.c (ada_sniff_by_symbol): New function.
+	(ada_language_defn): Use it.
+
+2016-06-04  Tom Tromey  <tom@tromey.com>
+
 	* ada-lang.c (ada_extensions): New array.
 	(ada_language_defn): Use it.
 	* c-lang.c (c_extensions): New array.
diff --git a/gdb/ada-lang.c b/gdb/ada-lang.c
index e3f236f..8dcffbe 100644
--- a/gdb/ada-lang.c
+++ b/gdb/ada-lang.c
@@ -1452,6 +1452,43 @@  ada_la_decode (const char *encoded, int options)
   return xstrdup (ada_decode (encoded));
 }
 
+/* Implement la_sniff_by_symbol for Ada.  */
+
+static int
+ada_sniff_by_symbol (const char *mangled, char **out)
+{
+  const char *demangled = ada_decode (mangled);
+
+  if (demangled != mangled && demangled != NULL && demangled[0] != '<')
+    {
+      /* Set the gsymbol language to Ada, but still return 0.
+	 Two reasons for that:
+
+	 1. For Ada, we prefer computing the symbol's decoded name
+	 on the fly rather than pre-compute it, in order to save
+	 memory (Ada projects are typically very large).
+
+	 2. There are some areas in the definition of the GNAT
+	 encoding where, with a bit of bad luck, we might be able
+	 to decode a non-Ada symbol, generating an incorrect
+	 demangled name (Eg: names ending with "TB" for instance
+	 are identified as task bodies and so stripped from
+	 the decoded name returned).
+
+	 Returning 1, here, but not setting *DEMANGLED, helps us get a
+	 little bit of the best of both worlds.  Because we're last,
+	 we should not affect any of the other languages that were
+	 able to demangle the symbol before us; we get to correctly
+	 tag Ada symbols as such; and even if we incorrectly tagged a
+	 non-Ada symbol, which should be rare, any routing through the
+	 Ada language should be transparent (Ada tries to behave much
+	 like C/C++ with non-Ada symbols).  */
+      return 1;
+    }
+
+  return 0;
+}
+
 /* Returns non-zero iff SYM_NAME matches NAME, ignoring any trailing
    suffixes that encode debugging information or leading _ada_ on
    SYM_NAME (see is_name_suffix commentary for the debugging
@@ -14077,6 +14114,7 @@  const struct language_defn ada_language_defn = {
   ada_lookup_symbol_nonlocal,   /* Looking up non-local symbols.  */
   basic_lookup_transparent_type,        /* lookup_transparent_type */
   ada_la_decode,                /* Language specific symbol demangler */
+  ada_sniff_by_symbol,
   NULL,                         /* Language specific
 				   class_name_from_physname */
   ada_op_print_tab,             /* expression operators for printing */
diff --git a/gdb/c-lang.c b/gdb/c-lang.c
index 9ccb87f..15b773f 100644
--- a/gdb/c-lang.c
+++ b/gdb/c-lang.c
@@ -856,6 +856,7 @@  const struct language_defn c_language_defn =
   basic_lookup_symbol_nonlocal,	/* lookup_symbol_nonlocal */
   basic_lookup_transparent_type,/* lookup_transparent_type */
   NULL,				/* Language specific symbol demangler */
+  NULL,
   NULL,				/* Language specific
 				   class_name_from_physname */
   c_op_print_tab,		/* expression operators for printing */
@@ -989,6 +990,7 @@  const struct language_defn cplus_language_defn =
   cp_lookup_symbol_nonlocal,	/* lookup_symbol_nonlocal */
   cp_lookup_transparent_type,   /* lookup_transparent_type */
   gdb_demangle,			/* Language specific symbol demangler */
+  gdb_sniff_by_symbol,
   cp_class_name_from_physname,  /* Language specific
 				   class_name_from_physname */
   c_op_print_tab,		/* expression operators for printing */
@@ -1040,6 +1042,7 @@  const struct language_defn asm_language_defn =
   basic_lookup_symbol_nonlocal,	/* lookup_symbol_nonlocal */
   basic_lookup_transparent_type,/* lookup_transparent_type */
   NULL,				/* Language specific symbol demangler */
+  NULL,
   NULL,				/* Language specific
 				   class_name_from_physname */
   c_op_print_tab,		/* expression operators for printing */
@@ -1091,6 +1094,7 @@  const struct language_defn minimal_language_defn =
   basic_lookup_symbol_nonlocal,	/* lookup_symbol_nonlocal */
   basic_lookup_transparent_type,/* lookup_transparent_type */
   NULL,				/* Language specific symbol demangler */
+  NULL,
   NULL,				/* Language specific
 				   class_name_from_physname */
   c_op_print_tab,		/* expression operators for printing */
diff --git a/gdb/cp-support.c b/gdb/cp-support.c
index 5662f86..d5f11cf 100644
--- a/gdb/cp-support.c
+++ b/gdb/cp-support.c
@@ -1627,6 +1627,15 @@  gdb_demangle (const char *name, int options)
   return result;
 }
 
+/* See cp-support.h.  */
+
+int
+gdb_sniff_by_symbol (const char *mangled, char **demangled)
+{
+  *demangled = gdb_demangle (mangled, DMGL_PARAMS | DMGL_ANSI);
+  return 1;
+}
+
 /* Don't allow just "maintenance cplus".  */
 
 static  void
diff --git a/gdb/cp-support.h b/gdb/cp-support.h
index 78749c8..30b9e79 100644
--- a/gdb/cp-support.h
+++ b/gdb/cp-support.h
@@ -158,4 +158,8 @@  extern struct cmd_list_element *maint_cplus_cmd_list;
 
 char *gdb_demangle (const char *name, int options);
 
+/* Like gdb_demangle, but suitable for use as la_sniff_by_symbol.  */
+
+int gdb_sniff_by_symbol (const char *mangled, char **demangled);
+
 #endif /* CP_SUPPORT_H */
diff --git a/gdb/d-lang.c b/gdb/d-lang.c
index b25136d..64511c2 100644
--- a/gdb/d-lang.c
+++ b/gdb/d-lang.c
@@ -55,6 +55,15 @@  d_demangle (const char *symbol, int options)
   return gdb_demangle (symbol, options | DMGL_DLANG);
 }
 
+/* la_sniff_by_symbol implementation for D.  */
+
+static int
+d_sniff_by_symbol (const char *mangled, char **demangled)
+{
+  *demangled = d_demangle (mangled, 0);
+  return 1;
+}
+
 /* Table mapping opcodes into strings for printing operators
    and precedences of the operators.  */
 static const struct op_print d_op_print_tab[] =
@@ -223,6 +232,7 @@  static const struct language_defn d_language_defn =
   d_lookup_symbol_nonlocal,
   basic_lookup_transparent_type,
   d_demangle,			/* Language specific symbol demangler.  */
+  d_sniff_by_symbol,
   NULL,				/* Language specific
 				   class_name_from_physname.  */
   d_op_print_tab,		/* Expression operators for printing.  */
diff --git a/gdb/defs.h b/gdb/defs.h
index ed51396..15d3805 100644
--- a/gdb/defs.h
+++ b/gdb/defs.h
@@ -194,26 +194,29 @@  extern void quit_serial_event_clear (void);
 /* * Languages represented in the symbol table and elsewhere.
    This should probably be in language.h, but since enum's can't
    be forward declared to satisfy opaque references before their
-   actual definition, needs to be here.  */
+   actual definition, needs to be here.
+
+   The constants here are in priority order.  In particular,
+   demangling is attempted according to this order.  */
 
 enum language
   {
     language_unknown,		/* Language not known */
     language_auto,		/* Placeholder for automatic setting */
     language_c,			/* C */
+    language_objc,		/* Objective-C */
     language_cplus,		/* C++ */
+    language_java,		/* Java */
     language_d,			/* D */
     language_go,		/* Go */
-    language_objc,		/* Objective-C */
-    language_java,		/* Java */
     language_fortran,		/* Fortran */
     language_m2,		/* Modula-2 */
     language_asm,		/* Assembly language */
     language_pascal,		/* Pascal */
-    language_ada,		/* Ada */
     language_opencl,		/* OpenCL */
     language_rust,		/* Rust */
     language_minimal,		/* All other languages, minimal support only */
+    language_ada,		/* Ada */
     nr_languages
   };
 
diff --git a/gdb/f-lang.c b/gdb/f-lang.c
index 719083b..58f58dd 100644
--- a/gdb/f-lang.c
+++ b/gdb/f-lang.c
@@ -268,7 +268,14 @@  const struct language_defn f_language_defn =
   NULL,                    	/* name_of_this */
   cp_lookup_symbol_nonlocal,	/* lookup_symbol_nonlocal */
   basic_lookup_transparent_type,/* lookup_transparent_type */
+
+  /* We could support demangling here to provide module namespaces
+     also for inferiors with only minimal symbol table (ELF symbols).
+     Just the mangling standard is not standardized across compilers
+     and there is no DW_AT_producer available for inferiors with only
+     the ELF symbols to check the mangling kind.  */
   NULL,				/* Language specific symbol demangler */
+  NULL,
   NULL,				/* Language specific
 				   class_name_from_physname */
   f_op_print_tab,		/* expression operators for printing */
diff --git a/gdb/go-lang.c b/gdb/go-lang.c
index 15ca78b..5fcaf0b 100644
--- a/gdb/go-lang.c
+++ b/gdb/go-lang.c
@@ -385,6 +385,15 @@  go_demangle (const char *mangled_name, int options)
   return result;
 }
 
+/* la_sniff_by_symbol for Go.  */
+
+static int
+go_sniff_by_symbol (const char *mangled, char **demangled)
+{
+  *demangled = go_demangle (mangled, 0);
+  return 1;
+}
+
 /* Given a Go symbol, return its package or NULL if unknown.
    Space for the result is malloc'd, caller must free.  */
 
@@ -584,6 +593,7 @@  static const struct language_defn go_language_defn =
   basic_lookup_symbol_nonlocal, 
   basic_lookup_transparent_type,
   go_demangle,			/* Language specific symbol demangler.  */
+  go_sniff_by_symbol,
   NULL,				/* Language specific
 				   class_name_from_physname.  */
   go_op_print_tab,		/* Expression operators for printing.  */
diff --git a/gdb/jv-lang.c b/gdb/jv-lang.c
index 797a7f0..85c4d87 100644
--- a/gdb/jv-lang.c
+++ b/gdb/jv-lang.c
@@ -1018,6 +1018,15 @@  static char *java_demangle (const char *mangled, int options)
   return gdb_demangle (mangled, options | DMGL_JAVA);
 }
 
+/* la_sniff_by_symbol for Java.  */
+
+static int
+java_sniff_by_symbol (const char *mangled, char **demangled)
+{
+  *demangled = java_demangle (mangled, DMGL_PARAMS | DMGL_ANSI);
+  return 1;
+}
+
 /* Find the member function name of the demangled name NAME.  NAME
    must be a method name including arguments, in order to correctly
    locate the last component.
@@ -1194,6 +1203,7 @@  const struct language_defn java_language_defn =
   basic_lookup_symbol_nonlocal,	/* lookup_symbol_nonlocal */
   basic_lookup_transparent_type,/* lookup_transparent_type */
   java_demangle,		/* Language specific symbol demangler */
+  java_sniff_by_symbol,
   java_class_name_from_physname,/* Language specific class name */
   java_op_print_tab,		/* expression operators for printing */
   0,				/* not c-style arrays */
diff --git a/gdb/language.c b/gdb/language.c
index 02e21ca..032727c 100644
--- a/gdb/language.c
+++ b/gdb/language.c
@@ -663,6 +663,21 @@  language_demangle (const struct language_defn *current_language,
   return NULL;
 }
 
+/* See langauge.h.  */
+
+int
+language_sniff_by_symbol (const struct language_defn *lang,
+			  const char *mangled, char **demangled)
+{
+  gdb_assert (lang != NULL);
+
+  *demangled = NULL;
+  if (lang->la_sniff_by_symbol == NULL)
+    return 0;
+
+  return lang->la_sniff_by_symbol (mangled, demangled);
+}
+
 /* Return class name from physname or NULL.  */
 char *
 language_class_name_from_physname (const struct language_defn *lang,
@@ -843,6 +858,7 @@  const struct language_defn unknown_language_defn =
   basic_lookup_symbol_nonlocal, /* lookup_symbol_nonlocal */
   basic_lookup_transparent_type,/* lookup_transparent_type */
   unk_lang_demangle,		/* Language specific symbol demangler */
+  NULL,
   unk_lang_class_name,		/* Language specific
 				   class_name_from_physname */
   unk_op_print_tab,		/* expression operators for printing */
@@ -891,6 +907,7 @@  const struct language_defn auto_language_defn =
   basic_lookup_symbol_nonlocal,	/* lookup_symbol_nonlocal */
   basic_lookup_transparent_type,/* lookup_transparent_type */
   unk_lang_demangle,		/* Language specific symbol demangler */
+  NULL,
   unk_lang_class_name,		/* Language specific
 				   class_name_from_physname */
   unk_op_print_tab,		/* expression operators for printing */
@@ -937,6 +954,7 @@  const struct language_defn local_language_defn =
   basic_lookup_symbol_nonlocal,	/* lookup_symbol_nonlocal */
   basic_lookup_transparent_type,/* lookup_transparent_type */
   unk_lang_demangle,		/* Language specific symbol demangler */
+  NULL,
   unk_lang_class_name,		/* Language specific
 				   class_name_from_physname */
   unk_op_print_tab,		/* expression operators for printing */
diff --git a/gdb/language.h b/gdb/language.h
index d57c60e..56f12bc 100644
--- a/gdb/language.h
+++ b/gdb/language.h
@@ -292,6 +292,20 @@  struct language_defn
     /* Return demangled language symbol, or NULL.  */
     char *(*la_demangle) (const char *mangled, int options);
 
+    /* Demangle a symbol according to this language's rules.  Unlike
+       la_demangle, this does not take any options.  The return value
+       should either be 0 if the name cannot be demangled, or 1 if it
+       can be.
+
+       *DEMANGLED is initialized by the caller.  If this function
+       returns 1, the implementation may set this to a xmalloc'd
+       string holding the demangled form.  However, it is not required
+       to.  The string is owned by the caller.
+       
+       If this function returns 0 then it must not set *DEMANGLED.
+    */
+    int (*la_sniff_by_symbol) (const char *mangled, char **demangled);
+
     /* Return class name of a mangled method name or NULL.  */
     char *(*la_class_name_from_physname) (const char *physname);
 
@@ -565,6 +579,13 @@  extern CORE_ADDR skip_language_trampoline (struct frame_info *, CORE_ADDR pc);
 extern char *language_demangle (const struct language_defn *current_language, 
 				const char *mangled, int options);
 
+/* A wrapper for la_sniff_by_symbol that initializes *DEMANGLED.  The
+   arguments and result are as for the method.  */
+
+extern int language_sniff_by_symbol (const struct language_defn *lang,
+				     const char *mangled,
+				     char **demangled);
+
 /* Return class name from physname, or NULL.  */
 extern char *language_class_name_from_physname (const struct language_defn *,
 					        const char *physname);
diff --git a/gdb/m2-lang.c b/gdb/m2-lang.c
index 4626bfc..67d0f6b 100644
--- a/gdb/m2-lang.c
+++ b/gdb/m2-lang.c
@@ -381,6 +381,7 @@  const struct language_defn m2_language_defn =
   basic_lookup_symbol_nonlocal,	/* lookup_symbol_nonlocal */
   basic_lookup_transparent_type,/* lookup_transparent_type */
   NULL,				/* Language specific symbol demangler */
+  NULL,
   NULL,				/* Language specific
 				   class_name_from_physname */
   m2_op_print_tab,		/* expression operators for printing */
diff --git a/gdb/objc-lang.c b/gdb/objc-lang.c
index fe3e082..6ff77d9 100644
--- a/gdb/objc-lang.c
+++ b/gdb/objc-lang.c
@@ -281,6 +281,15 @@  objc_demangle (const char *mangled, int options)
     return NULL;	/* Not an objc mangled name.  */
 }
 
+/* la_sniff_by_symbol for ObjC.  */
+
+static int
+objc_sniff_by_symbol (const char *mangled, char **demangled)
+{
+  *demangled = objc_demangle (mangled, 0);
+  return 1;
+}
+
 /* Determine if we are currently in the Objective-C dispatch function.
    If so, get the address of the method function that the dispatcher
    would call and use that as the function to step into instead.  Also
@@ -381,6 +390,7 @@  const struct language_defn objc_language_defn = {
   basic_lookup_symbol_nonlocal,	/* lookup_symbol_nonlocal */
   basic_lookup_transparent_type,/* lookup_transparent_type */
   objc_demangle,		/* Language specific symbol demangler */
+  objc_sniff_by_symbol,
   NULL,				/* Language specific
 				   class_name_from_physname */
   objc_op_print_tab,		/* Expression operators for printing */
diff --git a/gdb/opencl-lang.c b/gdb/opencl-lang.c
index 74651bc..2a46d86 100644
--- a/gdb/opencl-lang.c
+++ b/gdb/opencl-lang.c
@@ -1070,6 +1070,7 @@  const struct language_defn opencl_language_defn =
   basic_lookup_symbol_nonlocal,	/* lookup_symbol_nonlocal */
   basic_lookup_transparent_type,/* lookup_transparent_type */
   NULL,				/* Language specific symbol demangler */
+  NULL,
   NULL,				/* Language specific
 				   class_name_from_physname */
   c_op_print_tab,		/* expression operators for printing */
diff --git a/gdb/p-lang.c b/gdb/p-lang.c
index 0897be2..86e787b 100644
--- a/gdb/p-lang.c
+++ b/gdb/p-lang.c
@@ -444,6 +444,7 @@  const struct language_defn pascal_language_defn =
   basic_lookup_symbol_nonlocal,	/* lookup_symbol_nonlocal */
   basic_lookup_transparent_type,/* lookup_transparent_type */
   NULL,				/* Language specific symbol demangler */
+  NULL,
   NULL,				/* Language specific class_name_from_physname */
   pascal_op_print_tab,		/* expression operators for printing */
   1,				/* c-style arrays */
diff --git a/gdb/rust-lang.c b/gdb/rust-lang.c
index 36dab67..9ee1783 100644
--- a/gdb/rust-lang.c
+++ b/gdb/rust-lang.c
@@ -25,6 +25,7 @@ 
 #include "c-lang.h"
 #include "charset.h"
 #include "cp-support.h"
+#include "demangle.h"
 #include "gdbarch.h"
 #include "infcall.h"
 #include "objfiles.h"
@@ -1988,6 +1989,17 @@  rust_lookup_symbol_nonlocal (const struct language_defn *langdef,
 
 
 
+/* la_sniff_by_symbol for Rust.  */
+
+static int
+rust_sniff_by_symbol (const char *mangled, char **demangled)
+{
+  *demangled = gdb_demangle (mangled, DMGL_PARAMS | DMGL_ANSI);
+  return 1;
+}
+
+
+
 static const struct exp_descriptor exp_descriptor_rust = 
 {
   rust_print_subexp,
@@ -2030,6 +2042,7 @@  static const struct language_defn rust_language_defn =
   rust_lookup_symbol_nonlocal,	/* lookup_symbol_nonlocal */
   basic_lookup_transparent_type,/* lookup_transparent_type */
   gdb_demangle,			/* Language specific symbol demangler */
+  rust_sniff_by_symbol,
   NULL,				/* Language specific
 				   class_name_from_physname */
   c_op_print_tab,		/* expression operators for printing */
diff --git a/gdb/symtab.c b/gdb/symtab.c
index f7a207a..3157734 100644
--- a/gdb/symtab.c
+++ b/gdb/symtab.c
@@ -747,111 +747,32 @@  symbol_find_demangled_name (struct general_symbol_info *gsymbol,
 			    const char *mangled)
 {
   char *demangled = NULL;
+  int i;
+  int recognized;
 
   if (gsymbol->language == language_unknown)
     gsymbol->language = language_auto;
 
-  if (gsymbol->language == language_objc
-      || gsymbol->language == language_auto)
-    {
-      demangled =
-	objc_demangle (mangled, 0);
-      if (demangled != NULL)
-	{
-	  gsymbol->language = language_objc;
-	  return demangled;
-	}
-    }
-  if (gsymbol->language == language_cplus
-      || gsymbol->language == language_rust
-      || gsymbol->language == language_auto)
-    {
-      demangled =
-        gdb_demangle (mangled, DMGL_PARAMS | DMGL_ANSI);
-      if (demangled != NULL)
-	{
-	  gsymbol->language = language_cplus;
-	  return demangled;
-	}
-    }
-  if (gsymbol->language == language_java)
+  if (gsymbol->language != language_auto)
     {
-      demangled =
-        gdb_demangle (mangled,
-		      DMGL_PARAMS | DMGL_ANSI | DMGL_JAVA);
-      if (demangled != NULL)
-	{
-	  gsymbol->language = language_java;
-	  return demangled;
-	}
-    }
-  if (gsymbol->language == language_d
-      || gsymbol->language == language_auto)
-    {
-      demangled = d_demangle(mangled, 0);
-      if (demangled != NULL)
-	{
-	  gsymbol->language = language_d;
-	  return demangled;
-	}
+      const struct language_defn *lang = language_def (gsymbol->language);
+
+      language_sniff_by_symbol (lang, mangled, &demangled);
+      return demangled;
     }
-  /* FIXME(dje): Continually adding languages here is clumsy.
-     Better to just call la_demangle if !auto, and if auto then call
-     a utility routine that tries successive languages in turn and reports
-     which one it finds.  I realize the la_demangle options may be different
-     for different languages but there's already a FIXME for that.  */
-  if (gsymbol->language == language_go
-      || gsymbol->language == language_auto)
+
+  for (i = language_unknown; i < nr_languages; ++i)
     {
-      demangled = go_demangle (mangled, 0);
-      if (demangled != NULL)
+      enum language l = (enum language) i;
+      const struct language_defn *lang = language_def (l);
+
+      if (language_sniff_by_symbol (lang, mangled, &demangled))
 	{
-	  gsymbol->language = language_go;
+	  gsymbol->language = l;
 	  return demangled;
 	}
     }
 
-  /* We could support `gsymbol->language == language_fortran' here to provide
-     module namespaces also for inferiors with only minimal symbol table (ELF
-     symbols).  Just the mangling standard is not standardized across compilers
-     and there is no DW_AT_producer available for inferiors with only the ELF
-     symbols to check the mangling kind.  */
-
-  /* Check for Ada symbols last.  See comment below explaining why.  */
-
-  if (gsymbol->language == language_auto)
-   {
-     const char *demangled = ada_decode (mangled);
-
-     if (demangled != mangled && demangled != NULL && demangled[0] != '<')
-       {
-	 /* Set the gsymbol language to Ada, but still return NULL.
-	    Two reasons for that:
-
-	      1. For Ada, we prefer computing the symbol's decoded name
-		 on the fly rather than pre-compute it, in order to save
-		 memory (Ada projects are typically very large).
-
-	      2. There are some areas in the definition of the GNAT
-		 encoding where, with a bit of bad luck, we might be able
-		 to decode a non-Ada symbol, generating an incorrect
-		 demangled name (Eg: names ending with "TB" for instance
-		 are identified as task bodies and so stripped from
-		 the decoded name returned).
-
-		 Returning NULL, here, helps us get a little bit of
-		 the best of both worlds.  Because we're last, we should
-		 not affect any of the other languages that were able to
-		 demangle the symbol before us; we get to correctly tag
-		 Ada symbols as such; and even if we incorrectly tagged
-		 a non-Ada symbol, which should be rare, any routing
-		 through the Ada language should be transparent (Ada
-		 tries to behave much like C/C++ with non-Ada symbols).  */
-	 gsymbol->language = language_ada;
-	 return NULL;
-       }
-   }
-
   return NULL;
 }