public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
From: Bruno Larsen <blarsen@redhat.com>
To: Bruno Larsen <blarsen@redhat.com>,
	Bruno Larsen via Gdb-patches <gdb-patches@sourceware.org>
Subject: [PING][PATCH v4] gdb/c++: Detect ambiguous variables in imported namespaces
Date: Thu, 5 Jan 2023 08:59:07 +0100	[thread overview]
Message-ID: <4cca8dea-c448-cc7e-5f89-9a26cf7f4e60@redhat.com> (raw)
In-Reply-To: <20221221161253.2127653-1-blarsen@redhat.com>

Ping!

On 21/12/2022 17:12, Bruno Larsen wrote:
> When running gdb.cp/nsusing.cc and stopping at line 17, we can ask GDB
> to print x and get a compiler-dependent answer. Using gcc 12.2.1, GDB
> will print M::x, and using clang 16.0.0 prints N::x. Not only is this
> behavior confusing to users, it is also not consistent with compiler
> behaviors, which would warn that using x is ambiguous at this point.
>
> This commit makes GDB behavior consistent with compilers. it achieves
> this by making it so instead of exiting early when finding any symbol
> with the correct name, GDB continues searching through all include
> directives, storing all matching symbols in a relational map betwen the
> mangled name and the found symbols.
>
> If the resulting map has more than one entry, GDB says that the
> reference is ambiguous and lists all possibilities. Otherwise it returns
> the block_symbol structure for the desired symbol, or an empty struct if
> nothing was found.
>
> The commit also changes gdb.cp/nsusing.exp to test the ambiguous
> detection.
> ---
> Changelog for v4:
>   * Dropped first patch of the series, since it was pushed
>   * added a wrapper call to cp_lookup_symbols_via_imports
>      this simplifies error handling and calling it outside the recursive
> function
>   * removed set merging by passing a map reference
>   * removed the assert_not_reached thanks to Tom's comments
>
> Changelog for v3:
>   * changed map key to use std::string, so we get an ordered output.
>   * Improved documentation on some spots
>   * Fix bug where gdb could return a partial list of the ambiguous symbols
>
> Changelog for v2:
>   * factored out some code to avoid unnecessary repetition.
>   * made it so ambiguous variables are explicitly reported
>   * fixed formatting issues.
> ---
>   gdb/cp-namespace.c               | 97 ++++++++++++++++++++++++--------
>   gdb/testsuite/gdb.cp/nsusing.exp | 13 ++++-
>   2 files changed, 83 insertions(+), 27 deletions(-)
>
> diff --git a/gdb/cp-namespace.c b/gdb/cp-namespace.c
> index eff55b96ea9..093299d606e 100644
> --- a/gdb/cp-namespace.c
> +++ b/gdb/cp-namespace.c
> @@ -32,6 +32,7 @@
>   #include "buildsym.h"
>   #include "language.h"
>   #include "namespace.h"
> +#include <map>
>   #include <string>
>   
>   static struct block_symbol
> @@ -344,7 +345,10 @@ cp_lookup_symbol_in_namespace (const char *the_namespace, const char *name,
>     return sym;
>   }
>   
> -/* Search for NAME by applying all import statements belonging to
> +/* This version of the function is internal, use the wrapper unless
> +   the list of ambiguous symbols is needed.
> +
> +   Search for NAME by applying all import statements belonging to
>      BLOCK which are applicable in SCOPE.  If DECLARATION_ONLY the
>      search is restricted to using declarations.
>      Example:
> @@ -372,27 +376,35 @@ cp_lookup_symbol_in_namespace (const char *the_namespace, const char *name,
>      SEARCH_SCOPE_FIRST is an internal implementation detail: Callers must
>      pass 0 for it.  Internally we pass 1 when recursing.  */
>   
> -static struct block_symbol
> +static void
>   cp_lookup_symbol_via_imports (const char *scope,
>   			      const char *name,
>   			      const struct block *block,
>   			      const domain_enum domain,
>   			      const int search_scope_first,
>   			      const int declaration_only,
> -			      const int search_parents)
> +			      const int search_parents,
> +			      std::map<std::string,
> +				       struct block_symbol>& found_symbols)
>   {
>     struct using_direct *current;
>     struct block_symbol sym = {};
>     int len;
>     int directive_match;
>   
> +  /* All the symbols we found will be kept in this relational map between
> +     the mangled name and the block_symbol found.  We do this so that GDB
> +     won't incorrectly report an ambiguous symbol for finding the same
> +     thing twice.  */
> +
>     /* First, try to find the symbol in the given namespace if requested.  */
>     if (search_scope_first)
> -    sym = cp_lookup_symbol_in_namespace (scope, name,
> -					 block, domain, 1);
> -
> -  if (sym.symbol != NULL)
> -    return sym;
> +    {
> +      sym = cp_lookup_symbol_in_namespace (scope, name,
> +					   block, domain, 1);
> +      if (sym.symbol != nullptr)
> +	found_symbols[sym.symbol->m_name] = sym;
> +    }
>   
>     /* Due to a GCC bug, we need to know the boundaries of the current block
>        to know if a certain using directive is valid.  */
> @@ -446,7 +458,7 @@ cp_lookup_symbol_via_imports (const char *scope,
>   	  if (declaration_only || sym.symbol != NULL || current->declaration)
>   	    {
>   	      if (sym.symbol != NULL)
> -		return sym;
> +		found_symbols[sym.symbol->m_name] = sym;
>   
>   	      continue;
>   	    }
> @@ -467,23 +479,57 @@ cp_lookup_symbol_via_imports (const char *scope,
>   	      sym = cp_lookup_symbol_in_namespace (scope,
>   						   current->import_src,
>   						   block, domain, 1);
> +	      found_symbols[sym.symbol->m_name] = sym;
>   	    }
>   	  else if (current->alias == NULL)
>   	    {
>   	      /* If this import statement creates no alias, pass
>   		 current->inner as NAMESPACE to direct the search
>   		 towards the imported namespace.  */
> -	      sym = cp_lookup_symbol_via_imports (current->import_src,
> -						  name, block,
> -						  domain, 1, 0, 0);
> +	      cp_lookup_symbol_via_imports (current->import_src, name,
> +					    block, domain, 1, 0, 0,
> +					    found_symbols);
>   	    }
>   
> -	  if (sym.symbol != NULL)
> -	    return sym;
>   	}
>       }
> +}
>   
> -  return {};
> +/* Wrapper for the actual cp_lookup_symbol_via_imports.  This wrapper sets
> +   search_scope_first correctly and handles errors if needed.  */
> +static struct block_symbol
> +cp_lookup_symbol_via_imports (const char *scope,
> +			      const char *name,
> +			      const struct block *block,
> +			      const domain_enum domain,
> +			      const int declaration_only,
> +			      const int search_parents)
> +{
> +  std::map<std::string, struct block_symbol> found_symbols;
> +
> +  cp_lookup_symbol_via_imports(scope, name, block, domain, 0,
> +			       declaration_only, search_parents,
> +			       found_symbols);
> +
> +  if (found_symbols.size () > 1)
> +    {
> +      auto itr = found_symbols.cbegin ();
> +      std::string error_str = "Reference to \"";
> +      error_str += name;
> +      error_str += "\" is ambiguous, possibilities are: ";
> +      error_str += itr->second.symbol->print_name ();
> +      for (itr++; itr != found_symbols.end (); itr++)
> +	{
> +	  error_str += " and ";
> +	  error_str += itr->second.symbol->print_name ();
> +	}
> +      error (_("%s"), error_str.c_str ());
> +    }
> +
> +  if (found_symbols.size() == 1)
> +    return found_symbols.cbegin ()->second;
> +  else
> +    return {};
>   }
>   
>   /* Helper function that searches an array of symbols for one named NAME.  */
> @@ -503,9 +549,10 @@ search_symbol_list (const char *name, int num,
>     return NULL;
>   }
>   
> -/* Like cp_lookup_symbol_via_imports, but if BLOCK is a function, it
> -   searches through the template parameters of the function and the
> -   function's type.  */
> +/* Search for symbols whose name match NAME in the given SCOPE.
> +   if BLOCK is a function, we'll search first through the template
> +   parameters and function type. Afterwards (or if BLOCK is not a function)
> +   search through imported directives using cp_lookup_symbol_via_imports.  */
>   
>   struct block_symbol
>   cp_lookup_symbol_imports_or_template (const char *scope,
> @@ -514,7 +561,6 @@ cp_lookup_symbol_imports_or_template (const char *scope,
>   				      const domain_enum domain)
>   {
>     struct symbol *function = block->function ();
> -  struct block_symbol result;
>   
>     symbol_lookup_debug_printf
>       ("cp_lookup_symbol_imports_or_template (%s, %s, %s, %s)",
> @@ -583,10 +629,11 @@ cp_lookup_symbol_imports_or_template (const char *scope,
>   	}
>       }
>   
> -  result = cp_lookup_symbol_via_imports (scope, name, block, domain, 0, 1, 1);
> -  symbol_lookup_debug_printf
> -    ("cp_lookup_symbol_imports_or_template (...) = %s",
> -     result.symbol != NULL ? host_address_to_string (result.symbol) : "NULL");
> +  struct block_symbol result
> +    = cp_lookup_symbol_via_imports (scope, name, block, domain, 1, 1);
> +  symbol_lookup_debug_printf ("cp_lookup_symbol_imports_or_template (...) = %s\n",
> +		  result.symbol != nullptr
> +		  ? host_address_to_string (result.symbol) : "NULL");
>     return result;
>   }
>   
> @@ -603,8 +650,8 @@ cp_lookup_symbol_via_all_imports (const char *scope, const char *name,
>   
>     while (block != NULL)
>       {
> -      sym = cp_lookup_symbol_via_imports (scope, name, block, domain, 0, 0, 1);
> -      if (sym.symbol)
> +      sym = cp_lookup_symbol_via_imports (scope, name, block, domain, 0, 1);
> +      if (sym.symbol != nullptr)
>   	return sym;
>   
>         block = block->superblock ();
> diff --git a/gdb/testsuite/gdb.cp/nsusing.exp b/gdb/testsuite/gdb.cp/nsusing.exp
> index 0d374e5d03e..2a0aa29e8cf 100644
> --- a/gdb/testsuite/gdb.cp/nsusing.exp
> +++ b/gdb/testsuite/gdb.cp/nsusing.exp
> @@ -126,11 +126,20 @@ gdb_test_multiple "print x" "print x, before using statement" {
>       -re -wrap "No symbol .x. in current context.*" {
>   	pass $gdb_test_name
>       }
> -    -re -wrap "= 911.*" {
> +    -re -wrap "Reference to .x. is ambiguous.*" {
>   	# GCC doesn't properly set the decl_line for namespaces, so GDB believes
>   	# that the "using namespace M" line has already passed at this point.
>   	xfail $gdb_test_name
>       }
>   }
>   gdb_test "next" ".*" "using namespace M"
> -gdb_test "print x" "= 911" "print x, only using M"
> +gdb_test_multiple "print x" "print x, only using M" {
> +    -re -wrap "= 911.*" {
> +	pass $gdb_test_name
> +    }
> +    -re -wrap "Reference to .x. is ambiguous.*" {
> +	xfail $gdb_test_name
> +    }
> +}
> +gdb_test "next" ".*" "using namespace N"
> +gdb_test "print x"  "Reference to .x. is ambiguous.*" "print x, with M and N"


-- 
Cheers,
Bruno


  reply	other threads:[~2023-01-05  7:59 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-12-21 16:12 [PATCH " Bruno Larsen
2023-01-05  7:59 ` Bruno Larsen [this message]
2023-01-05 19:53 ` Tom Tromey
2023-01-06  9:53   ` Bruno Larsen

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=4cca8dea-c448-cc7e-5f89-9a26cf7f4e60@redhat.com \
    --to=blarsen@redhat.com \
    --cc=gdb-patches@sourceware.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).