gdb/c++: validate 'using' directives based on the current line
When asking GDB to print a variable from an imported namespace, we only want to see variables imported in lines that the inferior has already gone through, as is being tested last in gdb.cp/nsusing.exp. However with the proposed change to gdb.cp/nsusing.exp, we get the following failures: (gdb) PASS: gdb.cp/nsusing.exp: continue to breakpoint: marker10 stop print x $9 = 911 (gdb) FAIL: gdb.cp/nsusing.exp: print x, before using statement next 15 y += x; (gdb) PASS: gdb.cp/nsusing.exp: using namespace M print x $10 = 911 (gdb) PASS: gdb.cp/nsusing.exp: print x, only using M Showing that the feature wasn't functioning properly, it just so happened that gcc ordered the namespaces in a convenient way. This happens because GDB doesn't take into account the line where the "using namespace" directive is written. So long as it shows up in the current scope, we assume it is valid. To fix this, add a new member to struct using_direct, that stores the line where the directive was written, and a new function that informs if the using directive is valid already. Unfortunately, due to a GCC bug, the failure still shows up. Compilers that set the declaration line of the using directive correctly (such as Clang) do not show such a bug, so the test includes an XFAIL for gcc code. Finally, because the final test of gdb.cp/nsusing.exp has turned into multiple that all would need XFAILs for older GCCs (<= 4.3), and that GCC is very old, if it is detected, the test just exits early. Approved-by: Tom Tromey <tom@tromey.com>
This commit is contained in:
parent
ea6ed58e63
commit
68ce1575fc
@ -93,10 +93,12 @@ cp_scan_for_anonymous_namespaces (struct buildsym_compunit *compunit,
|
|||||||
/* We've found a component of the name that's an
|
/* We've found a component of the name that's an
|
||||||
anonymous namespace. So add symbols in it to the
|
anonymous namespace. So add symbols in it to the
|
||||||
namespace given by the previous component if there is
|
namespace given by the previous component if there is
|
||||||
one, or to the global namespace if there isn't. */
|
one, or to the global namespace if there isn't.
|
||||||
|
The declared line of this using directive can be set
|
||||||
|
to 0, this way it is always considered valid. */
|
||||||
std::vector<const char *> excludes;
|
std::vector<const char *> excludes;
|
||||||
add_using_directive (compunit->get_local_using_directives (),
|
add_using_directive (compunit->get_local_using_directives (),
|
||||||
dest, src, NULL, NULL, excludes,
|
dest, src, NULL, NULL, excludes, 0,
|
||||||
1, &objfile->objfile_obstack);
|
1, &objfile->objfile_obstack);
|
||||||
}
|
}
|
||||||
/* The "+ 2" is for the "::". */
|
/* The "+ 2" is for the "::". */
|
||||||
@ -392,16 +394,23 @@ cp_lookup_symbol_via_imports (const char *scope,
|
|||||||
if (sym.symbol != NULL)
|
if (sym.symbol != NULL)
|
||||||
return sym;
|
return 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. */
|
||||||
|
symtab_and_line boundary_sal = find_pc_line (block->end () - 1, 0);
|
||||||
|
|
||||||
/* Go through the using directives. If any of them add new names to
|
/* Go through the using directives. If any of them add new names to
|
||||||
the namespace we're searching in, see if we can find a match by
|
the namespace we're searching in, see if we can find a match by
|
||||||
applying them. */
|
applying them. */
|
||||||
|
|
||||||
for (current = block_using (block);
|
for (current = block_using (block);
|
||||||
current != NULL;
|
current != NULL;
|
||||||
current = current->next)
|
current = current->next)
|
||||||
{
|
{
|
||||||
const char **excludep;
|
const char **excludep;
|
||||||
|
|
||||||
|
/* If the using directive was below the place we are stopped at,
|
||||||
|
do not use this directive. */
|
||||||
|
if (!current->valid_line (boundary_sal.line))
|
||||||
|
continue;
|
||||||
len = strlen (current->import_dest);
|
len = strlen (current->import_dest);
|
||||||
directive_match = (search_parents
|
directive_match = (search_parents
|
||||||
? (startswith (scope, current->import_dest)
|
? (startswith (scope, current->import_dest)
|
||||||
|
@ -9306,6 +9306,30 @@ using_directives (struct dwarf2_cu *cu)
|
|||||||
return cu->get_builder ()->get_local_using_directives ();
|
return cu->get_builder ()->get_local_using_directives ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Read the DW_ATTR_decl_line attribute for the given DIE in the
|
||||||
|
given CU. If the format is not recognized or the attribute is
|
||||||
|
not present, set it to 0. */
|
||||||
|
|
||||||
|
static unsigned int
|
||||||
|
read_decl_line (struct die_info *die, struct dwarf2_cu *cu)
|
||||||
|
{
|
||||||
|
struct attribute *decl_line = dwarf2_attr (die, DW_AT_decl_line, cu);
|
||||||
|
if (decl_line == nullptr)
|
||||||
|
return 0;
|
||||||
|
if (decl_line->form_is_constant ())
|
||||||
|
{
|
||||||
|
LONGEST val = decl_line->constant_value (0);
|
||||||
|
if (0 <= val && val <= UINT_MAX)
|
||||||
|
return (unsigned int) val;
|
||||||
|
|
||||||
|
complaint (_("Declared line for using directive is too large"));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
complaint (_("Declared line for using directive is of incorrect format"));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* Read the import statement specified by the given die and record it. */
|
/* Read the import statement specified by the given die and record it. */
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -9448,6 +9472,7 @@ read_import_statement (struct die_info *die, struct dwarf2_cu *cu)
|
|||||||
import_alias,
|
import_alias,
|
||||||
imported_declaration,
|
imported_declaration,
|
||||||
excludes,
|
excludes,
|
||||||
|
read_decl_line (die, cu),
|
||||||
0,
|
0,
|
||||||
&objfile->objfile_obstack);
|
&objfile->objfile_obstack);
|
||||||
}
|
}
|
||||||
@ -16085,7 +16110,9 @@ read_namespace (struct die_info *die, struct dwarf2_cu *cu)
|
|||||||
std::vector<const char *> excludes;
|
std::vector<const char *> excludes;
|
||||||
add_using_directive (using_directives (cu),
|
add_using_directive (using_directives (cu),
|
||||||
previous_prefix, type->name (), NULL,
|
previous_prefix, type->name (), NULL,
|
||||||
NULL, excludes, 0, &objfile->objfile_obstack);
|
NULL, excludes,
|
||||||
|
read_decl_line (die, cu),
|
||||||
|
0, &objfile->objfile_obstack);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
#include "defs.h"
|
#include "defs.h"
|
||||||
#include "namespace.h"
|
#include "namespace.h"
|
||||||
|
#include "frame.h"
|
||||||
|
|
||||||
/* Add a using directive to USING_DIRECTIVES. If the using directive
|
/* Add a using directive to USING_DIRECTIVES. If the using directive
|
||||||
in question has already been added, don't add it twice.
|
in question has already been added, don't add it twice.
|
||||||
@ -40,6 +41,7 @@ add_using_directive (struct using_direct **using_directives,
|
|||||||
const char *alias,
|
const char *alias,
|
||||||
const char *declaration,
|
const char *declaration,
|
||||||
const std::vector<const char *> &excludes,
|
const std::vector<const char *> &excludes,
|
||||||
|
unsigned int decl_line,
|
||||||
int copy_names,
|
int copy_names,
|
||||||
struct obstack *obstack)
|
struct obstack *obstack)
|
||||||
{
|
{
|
||||||
@ -76,6 +78,9 @@ add_using_directive (struct using_direct **using_directives,
|
|||||||
if (ix < excludes.size () || current->excludes[ix] != NULL)
|
if (ix < excludes.size () || current->excludes[ix] != NULL)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if (decl_line != current->decl_line)
|
||||||
|
continue;
|
||||||
|
|
||||||
/* Parameters exactly match CURRENT. */
|
/* Parameters exactly match CURRENT. */
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -111,6 +116,26 @@ add_using_directive (struct using_direct **using_directives,
|
|||||||
excludes.size () * sizeof (*newobj->excludes));
|
excludes.size () * sizeof (*newobj->excludes));
|
||||||
newobj->excludes[excludes.size ()] = NULL;
|
newobj->excludes[excludes.size ()] = NULL;
|
||||||
|
|
||||||
|
newobj->decl_line = decl_line;
|
||||||
|
|
||||||
newobj->next = *using_directives;
|
newobj->next = *using_directives;
|
||||||
*using_directives = newobj;
|
*using_directives = newobj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* See namespace.h. */
|
||||||
|
|
||||||
|
bool
|
||||||
|
using_direct::valid_line (unsigned int boundary) const
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
CORE_ADDR curr_pc = get_frame_pc (get_selected_frame (nullptr));
|
||||||
|
symtab_and_line curr_sal = find_pc_line (curr_pc, 0);
|
||||||
|
return (decl_line <= curr_sal.line)
|
||||||
|
|| (decl_line >= boundary);
|
||||||
|
}
|
||||||
|
catch (const gdb_exception &ex)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -30,7 +30,8 @@
|
|||||||
string representing the alias. Otherwise, ALIAS is NULL.
|
string representing the alias. Otherwise, ALIAS is NULL.
|
||||||
DECLARATION is the name of the imported declaration, if this import
|
DECLARATION is the name of the imported declaration, if this import
|
||||||
statement represents one. Otherwise DECLARATION is NULL and this
|
statement represents one. Otherwise DECLARATION is NULL and this
|
||||||
import statement represents a namespace.
|
import statement represents a namespace. DECL_LINE is the line
|
||||||
|
where the using directive is written in the source code.
|
||||||
|
|
||||||
C++: using namespace A;
|
C++: using namespace A;
|
||||||
Fortran: use A
|
Fortran: use A
|
||||||
@ -96,6 +97,11 @@ struct using_direct
|
|||||||
|
|
||||||
struct using_direct *next;
|
struct using_direct *next;
|
||||||
|
|
||||||
|
/* The line where the using directive was declared on the source file.
|
||||||
|
This is used to check if the using directive is already active at the
|
||||||
|
point where the inferior is stopped. */
|
||||||
|
unsigned int decl_line;
|
||||||
|
|
||||||
/* Used during import search to temporarily mark this node as
|
/* Used during import search to temporarily mark this node as
|
||||||
searched. */
|
searched. */
|
||||||
int searched;
|
int searched;
|
||||||
@ -103,6 +109,13 @@ struct using_direct
|
|||||||
/* USING_DIRECT has variable allocation size according to the number of
|
/* USING_DIRECT has variable allocation size according to the number of
|
||||||
EXCLUDES entries, the last entry is NULL. */
|
EXCLUDES entries, the last entry is NULL. */
|
||||||
const char *excludes[1];
|
const char *excludes[1];
|
||||||
|
|
||||||
|
/* Returns true if the using_direcive USING_DIR is valid in CURR_LINE.
|
||||||
|
Because current GCC (at least version 12.2) sets the decl_line as
|
||||||
|
the last line in the current block, we need to take this into
|
||||||
|
consideration when checking the validity, by comparing it to
|
||||||
|
BOUNDARY, the last line of the current block. */
|
||||||
|
bool valid_line (unsigned int boundary) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern void add_using_directive (struct using_direct **using_directives,
|
extern void add_using_directive (struct using_direct **using_directives,
|
||||||
@ -111,6 +124,7 @@ extern void add_using_directive (struct using_direct **using_directives,
|
|||||||
const char *alias,
|
const char *alias,
|
||||||
const char *declaration,
|
const char *declaration,
|
||||||
const std::vector<const char *> &excludes,
|
const std::vector<const char *> &excludes,
|
||||||
|
const unsigned int decl_line,
|
||||||
int copy_names,
|
int copy_names,
|
||||||
struct obstack *obstack);
|
struct obstack *obstack);
|
||||||
|
|
||||||
|
@ -10,8 +10,9 @@ namespace N
|
|||||||
|
|
||||||
int marker10 ()
|
int marker10 ()
|
||||||
{
|
{
|
||||||
|
int y = 1; // marker10 stop
|
||||||
using namespace M;
|
using namespace M;
|
||||||
int y = x + 1; // marker10 stop
|
y += x;
|
||||||
using namespace N;
|
using namespace N;
|
||||||
return y;
|
return y;
|
||||||
}
|
}
|
||||||
|
@ -119,8 +119,18 @@ gdb_continue_to_breakpoint "marker10 stop"
|
|||||||
|
|
||||||
if { [test_compiler_info {gcc-[0-3]-*}] ||
|
if { [test_compiler_info {gcc-[0-3]-*}] ||
|
||||||
[test_compiler_info {gcc-4-[0-3]-*}]} {
|
[test_compiler_info {gcc-4-[0-3]-*}]} {
|
||||||
setup_xfail *-*-*
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
# Assert that M::x is printed and not N::x
|
gdb_test_multiple "print x" "print x, before using statement" {
|
||||||
gdb_test "print x" "= 911" "print x (from M::x)"
|
-re -wrap "No symbol .x. in current context.*" {
|
||||||
|
pass $gdb_test_name
|
||||||
|
}
|
||||||
|
-re -wrap "= 911.*" {
|
||||||
|
# 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"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user