From 8d13d83aba4a103959b127cbb5666e28667ac338 Mon Sep 17 00:00:00 2001 From: Tom Tromey Date: Thu, 8 May 2025 14:04:05 -0600 Subject: [PATCH] Handle an argument-less operator in the C++ name parser While debugging a new failure in my long-suffering "search-in-psyms" series, I found that the C++ name canonicalizer did not handle a case like "some_name::operator new []". This should remove the space, resulting in "some_name::operator new[]" -- but does not. This happens because the parser requires an operator to be followed by argument types. That is, it's expected. However, it seems to me that we do want to be able to canonicalize a name like this. It will appear in the DWARF as a DW_AT_name, and furthermore it could be entered by the user. This patch fixes this problem by changing the grammar to supply the "()" itself, then removing the trailing "()" when changing to string form (in the functions that matter). This isn't ideal -- it might miss a very obscure case involving the gdb extension of providing fully-qualified names for function-local statics -- but it improves the situation at least. It's possible a better solution might be to rewrite the name canonicalizer. I was wondering if this could perhaps be done without reference to the grammar -- just by examining the tokens. However, that's much more involved. Let me know what you think. Regression tested on x86-64 Fedora 40. Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=32939 Reviewed-By: Keith Seitz --- gdb/cp-name-parser.y | 22 ++++++++++++++++++++++ gdb/cp-support.c | 17 +++++++++++++++++ gdb/cp-support.h | 4 ++++ 3 files changed, 43 insertions(+) diff --git a/gdb/cp-name-parser.y b/gdb/cp-name-parser.y index d4ab98c7a10..cb0babbf15e 100644 --- a/gdb/cp-name-parser.y +++ b/gdb/cp-name-parser.y @@ -374,6 +374,22 @@ function | colon_ext_only function_arglist start_opt { $$ = state->fill_comp (DEMANGLE_COMPONENT_TYPED_NAME, $1, $2.comp); if ($3) $$ = state->fill_comp (DEMANGLE_COMPONENT_LOCAL_NAME, $$, $3); } + | colon_ext_only + { + /* This production is a hack to handle + something like "name::operator new[]" -- + without arguments, this ordinarily would + not parse, but canonicalizing it is + important. So we infer the "()" and then + remove it when converting back to string. + Note that this works because this + production is terminal. */ + demangle_component *comp + = state->fill_comp (DEMANGLE_COMPONENT_FUNCTION_TYPE, + nullptr, nullptr); + $$ = state->fill_comp (DEMANGLE_COMPONENT_TYPED_NAME, $1, comp); + state->demangle_info->added_parens = true; + } | conversion_op_name start_opt { $$ = $1.comp; @@ -2111,6 +2127,12 @@ canonicalize_tests () should_be_the_same ("Foozle::fogey > (Empty)", "Foozle::fogey> (Empty)"); + + should_be_the_same ("something :: operator new [ ]", + "something::operator new[]"); + should_be_the_same ("something :: operator new", + "something::operator new"); + should_be_the_same ("operator()", "operator ()"); } #endif diff --git a/gdb/cp-support.c b/gdb/cp-support.c index c71b8d901b5..900132901e0 100644 --- a/gdb/cp-support.c +++ b/gdb/cp-support.c @@ -573,6 +573,17 @@ replace_typedefs (struct demangle_parse_info *info, } } +/* A helper to strip a trailing "()" from PTR. The string is modified + in place. */ + +static void +maybe_strip_parens (char *ptr) +{ + size_t len = strlen (ptr); + if (len > 2 && ptr[len - 2] == '(' && ptr[len - 1] == ')') + ptr[len - 2] = '\0'; +} + /* Parse STRING and convert it to canonical form, resolving any typedefs. If parsing fails, or if STRING is already canonical, return nullptr. Otherwise return the canonical form. If @@ -599,6 +610,9 @@ cp_canonicalize_string_full (const char *string, estimated_len); gdb_assert (us); + if (info->added_parens) + maybe_strip_parens (us.get ()); + /* Finally, compare the original string with the computed name, returning NULL if they are the same. */ if (strcmp (us.get (), string) == 0) @@ -647,6 +661,9 @@ cp_canonicalize_string (const char *string) return nullptr; } + if (info->added_parens) + maybe_strip_parens (us.get ()); + if (strcmp (us.get (), string) == 0) return nullptr; diff --git a/gdb/cp-support.h b/gdb/cp-support.h index 3ed354c1f16..ffe0ba13468 100644 --- a/gdb/cp-support.h +++ b/gdb/cp-support.h @@ -63,6 +63,10 @@ struct demangle_parse_info /* Any memory used during processing. */ auto_obstack obstack; + /* True if the parser had to add a dummy '()' at the end of the + input text to make it parse. */ + bool added_parens = false; + /* Any other objects referred to by this object, and whose storage lifetime must be linked. */ std::vector> infos; -- 2.39.5