[3/3] RFC: tag name completion

Tom Tromey tromey@redhat.com
Wed Nov 21 18:43:00 GMT 2012


This implements tag name completion in the C parser.

The basic idea is simple: if the user enters "struct X<TAB>", then only
struct tags starting with "X" should be considered.  (And likewise for
class, enum, and union.)

The implementation is a bit hairy due to the need to pass this
information via la_make_symbol_completion_list.  I did this by adding a
type_code argument and fixing the fallout.  For Ada I simply added an
assertion because this code cannot be called (since the Ada expression
parser will never call mark_completion_tag).

I think the rest of the patch is straightforward.

Built and regtested on x86-64 Fedora 16.
New test case included.

Tom

	* ada-lang.c (ada_make_symbol_completion_list): Add 'code'
	argument, assertion.
	* c-exp.y (typebase): Add completion productions.
	* completer.c (expression_completer): Handle tag completion.
	* expression.h (parse_expression_for_completion): Add argument.
	* f-lang.c (f_make_symbol_completion_list): Add 'code'
	argument.
	* language.h (struct language_defn)
	<la_make_symbol_completion_list>: Add 'code' argument.
	* parse.c (expout_tag_completion_type, expout_completion_name):
	New globals.
	(mark_struct_expression): Add assertion.
	(mark_completion_tag): New function.
	(parse_exp_in_context): Initialize new globals.
	(parse_expression_for_completion): Add 'code' argument.  Handle
	tag completion.
	* parser-defs.h (mark_completion_tag): Declare.
	* symtab.c (default_make_symbol_completion_list_break_on): Add
	'code' argument.  Update.
	(default_make_symbol_completion_list): Add 'code' argument.
	(make_symbol_completion_list): Update.
	(make_symbol_completion_type): New function.
	* symtab.h (default_make_symbol_completion_list_break_on)
	(default_make_symbol_completion_list): Update.
	(make_symbol_completion_type): Declare.

	* gdb.base/break1.c (enum some_enum, union some_union): New.
	(some_enum_global, some_union_global, some_value): New globals.
	* gdb.base/completion.exp: Add tag completion tests.
---
 gdb/ada-lang.c                        |    4 +-
 gdb/c-exp.y                           |   44 +++++++++++++++++
 gdb/completer.c                       |   12 ++++-
 gdb/expression.h                      |    3 +-
 gdb/f-lang.c                          |    4 +-
 gdb/language.h                        |    7 ++-
 gdb/parse.c                           |   45 +++++++++++++++++-
 gdb/parser-defs.h                     |    4 ++
 gdb/symtab.c                          |   86 ++++++++++++++++++++++++---------
 gdb/symtab.h                          |    9 +++-
 gdb/testsuite/gdb.base/break1.c       |   17 +++++++
 gdb/testsuite/gdb.base/completion.exp |    8 +++
 12 files changed, 210 insertions(+), 33 deletions(-)

diff --git a/gdb/ada-lang.c b/gdb/ada-lang.c
index e1dced5..daa18dc 100644
--- a/gdb/ada-lang.c
+++ b/gdb/ada-lang.c
@@ -5802,7 +5802,7 @@ ada_expand_partial_symbol_name (const char *name, void *user_data)
    the entire command on which completion is made.  */
 
 static VEC (char_ptr) *
-ada_make_symbol_completion_list (char *text0, char *word)
+ada_make_symbol_completion_list (char *text0, char *word, enum type_code code)
 {
   char *text;
   int text_len;
@@ -5817,6 +5817,8 @@ ada_make_symbol_completion_list (char *text0, char *word)
   int i;
   struct block_iterator iter;
 
+  gdb_assert (code == TYPE_CODE_UNDEF);
+
   if (text0[0] == '<')
     {
       text = xstrdup (text0);
diff --git a/gdb/c-exp.y b/gdb/c-exp.y
index 23195e8..58edb53 100644
--- a/gdb/c-exp.y
+++ b/gdb/c-exp.y
@@ -1268,15 +1268,59 @@ typebase  /* Implements (approximately): (type-qualifier)* type-specifier */
 	|	STRUCT name
 			{ $$ = lookup_struct (copy_name ($2),
 					      expression_context_block); }
+	|	STRUCT COMPLETE
+			{
+			  mark_completion_tag (TYPE_CODE_STRUCT, "", 0);
+			  $$ = NULL;
+			}
+	|	STRUCT name COMPLETE
+			{
+			  mark_completion_tag (TYPE_CODE_STRUCT, $2.ptr,
+					       $2.length);
+			  $$ = NULL;
+			}
 	|	CLASS name
 			{ $$ = lookup_struct (copy_name ($2),
 					      expression_context_block); }
+	|	CLASS COMPLETE
+			{
+			  mark_completion_tag (TYPE_CODE_CLASS, "", 0);
+			  $$ = NULL;
+			}
+	|	CLASS name COMPLETE
+			{
+			  mark_completion_tag (TYPE_CODE_CLASS, $2.ptr,
+					       $2.length);
+			  $$ = NULL;
+			}
 	|	UNION name
 			{ $$ = lookup_union (copy_name ($2),
 					     expression_context_block); }
+	|	UNION COMPLETE
+			{
+			  mark_completion_tag (TYPE_CODE_UNION, "", 0);
+			  $$ = NULL;
+			}
+	|	UNION name COMPLETE
+			{
+			  mark_completion_tag (TYPE_CODE_UNION, $2.ptr,
+					       $2.length);
+			  $$ = NULL;
+			}
 	|	ENUM name
 			{ $$ = lookup_enum (copy_name ($2),
 					    expression_context_block); }
+	|	ENUM COMPLETE
+			{
+			  mark_completion_tag (TYPE_CODE_ENUM, "", 0);
+			  $$ = NULL;
+			}
+	|	ENUM name COMPLETE
+			{
+			  mark_completion_tag (TYPE_CODE_ENUM, $2.ptr,
+					       $2.length);
+			  $$ = NULL;
+			}
 	|	UNSIGNED typename
 			{ $$ = lookup_unsigned_typename (parse_language,
 							 parse_gdbarch,
diff --git a/gdb/completer.c b/gdb/completer.c
index 7954c2c..946717d 100644
--- a/gdb/completer.c
+++ b/gdb/completer.c
@@ -390,13 +390,14 @@ expression_completer (struct cmd_list_element *ignore,
   struct type *type = NULL;
   char *fieldname, *p;
   volatile struct gdb_exception except;
+  enum type_code code = TYPE_CODE_UNDEF;
 
   /* Perform a tentative parse of the expression, to see whether a
      field completion is required.  */
   fieldname = NULL;
   TRY_CATCH (except, RETURN_MASK_ERROR)
     {
-      type = parse_expression_for_completion (text, &fieldname);
+      type = parse_expression_for_completion (text, &fieldname, &code);
     }
   if (except.reason < 0)
     return NULL;
@@ -422,6 +423,15 @@ expression_completer (struct cmd_list_element *ignore,
 	  return result;
 	}
     }
+  else if (fieldname && code != TYPE_CODE_UNDEF)
+    {
+      VEC (char_ptr) *result;
+      struct cleanup *cleanup = make_cleanup (xfree, fieldname);
+
+      result = make_symbol_completion_type (fieldname, fieldname, code);
+      do_cleanups (cleanup);
+      return result;
+    }
   xfree (fieldname);
 
   /* Commands which complete on locations want to see the entire
diff --git a/gdb/expression.h b/gdb/expression.h
index 1a90cc1..b2077ca 100644
--- a/gdb/expression.h
+++ b/gdb/expression.h
@@ -98,7 +98,8 @@ struct expression
 
 extern struct expression *parse_expression (char *);
 
-extern struct type *parse_expression_for_completion (char *, char **);
+extern struct type *parse_expression_for_completion (char *, char **,
+						     enum type_code *);
 
 extern struct expression *parse_exp_1 (char **, CORE_ADDR pc, struct block *,
 				       int);
diff --git a/gdb/f-lang.c b/gdb/f-lang.c
index 0b3645f..344d9b8 100644
--- a/gdb/f-lang.c
+++ b/gdb/f-lang.c
@@ -230,9 +230,9 @@ f_word_break_characters (void)
    class.  */
 
 static VEC (char_ptr) *
-f_make_symbol_completion_list (char *text, char *word)
+f_make_symbol_completion_list (char *text, char *word, enum type_code code)
 {
-  return default_make_symbol_completion_list_break_on (text, word, ":");
+  return default_make_symbol_completion_list_break_on (text, word, ":", code);
 }
 
 const struct language_defn f_language_defn =
diff --git a/gdb/language.h b/gdb/language.h
index 3a1e390..49afbc4 100644
--- a/gdb/language.h
+++ b/gdb/language.h
@@ -284,8 +284,11 @@ struct language_defn
 
     /* Should return a vector of all symbols which are possible
        completions for TEXT.  WORD is the entire command on which the
-       completion is being made.  */
-    VEC (char_ptr) *(*la_make_symbol_completion_list) (char *text, char *word);
+       completion is being made.  If CODE is TYPE_CODE_UNDEF, then all
+       symbols should be examined; otherwise, only STRUCT_DOMAIN
+       symbols whose type has a code of CODE should be matched.  */
+    VEC (char_ptr) *(*la_make_symbol_completion_list) (char *text, char *word,
+						       enum type_code code);
 
     /* The per-architecture (OS/ABI) language information.  */
     void (*la_language_arch_info) (struct gdbarch *,
diff --git a/gdb/parse.c b/gdb/parse.c
index d927ca2..0ecaaf1 100644
--- a/gdb/parse.c
+++ b/gdb/parse.c
@@ -88,6 +88,13 @@ int parse_completion;
    '->'.  This is set when parsing and is only used when completing a
    field name.  It is -1 if no dereference operation was found.  */
 static int expout_last_struct = -1;
+
+/* If we are completing a tagged type name, this will be nonzero.  */
+static enum type_code expout_tag_completion_type = TYPE_CODE_UNDEF;
+
+/* The token for tagged type name completion.  */
+static char *expout_completion_name;
+
 
 static unsigned int expressiondebug = 0;
 static void
@@ -578,9 +585,32 @@ write_exp_msymbol (struct minimal_symbol *msymbol)
 void
 mark_struct_expression (void)
 {
+  gdb_assert (parse_completion
+	      && expout_tag_completion_type == TYPE_CODE_UNDEF);
   expout_last_struct = expout_ptr;
 }
 
+/* Indicate that the current parser invocation is completing a tag.
+   TAG is the type code of the tag, and PTR and LENGTH represent the
+   start of the tag name.  */
+
+void
+mark_completion_tag (enum type_code tag, const char *ptr, int length)
+{
+  gdb_assert (parse_completion
+	      && expout_tag_completion_type == TYPE_CODE_UNDEF
+	      && expout_completion_name == NULL
+	      && expout_last_struct == -1);
+  gdb_assert (tag == TYPE_CODE_UNION
+	      || tag == TYPE_CODE_STRUCT
+	      || tag == TYPE_CODE_CLASS
+	      || tag == TYPE_CODE_ENUM);
+  expout_tag_completion_type = tag;
+  expout_completion_name = xmalloc (length + 1);
+  memcpy (expout_completion_name, ptr, length);
+  expout_completion_name[length] = '\0';
+}
+
 
 /* Recognize tokens that start with '$'.  These include:
 
@@ -1123,6 +1153,9 @@ parse_exp_in_context (char **stringptr, CORE_ADDR pc, struct block *block,
   paren_depth = 0;
   type_stack.depth = 0;
   expout_last_struct = -1;
+  expout_tag_completion_type = TYPE_CODE_UNDEF;
+  xfree (expout_completion_name);
+  expout_completion_name = NULL;
 
   comma_terminates = comma;
 
@@ -1243,7 +1276,8 @@ parse_expression (char *string)
    *NAME must be freed by the caller.  */
 
 struct type *
-parse_expression_for_completion (char *string, char **name)
+parse_expression_for_completion (char *string, char **name,
+				 enum type_code *code)
 {
   struct expression *exp = NULL;
   struct value *val;
@@ -1258,6 +1292,15 @@ parse_expression_for_completion (char *string, char **name)
   parse_completion = 0;
   if (except.reason < 0 || ! exp)
     return NULL;
+
+  if (expout_tag_completion_type != TYPE_CODE_UNDEF)
+    {
+      *code = expout_tag_completion_type;
+      *name = expout_completion_name;
+      expout_completion_name = NULL;
+      return NULL;
+    }
+
   if (expout_last_struct == -1)
     {
       xfree (exp);
diff --git a/gdb/parser-defs.h b/gdb/parser-defs.h
index c889d56..8e0f7c2 100644
--- a/gdb/parser-defs.h
+++ b/gdb/parser-defs.h
@@ -372,4 +372,8 @@ extern void parser_fprintf (FILE *, const char *, ...) ATTRIBUTE_PRINTF (2, 3);
 
 extern int exp_uses_objfile (struct expression *exp, struct objfile *objfile);
 
+extern void mark_completion_tag (enum type_code, const char *ptr,
+				 int length);
+
 #endif /* PARSER_DEFS_H */
+
diff --git a/gdb/symtab.c b/gdb/symtab.c
index 05943cf..c9feee6 100644
--- a/gdb/symtab.c
+++ b/gdb/symtab.c
@@ -4200,7 +4200,8 @@ expand_partial_symbol_name (const char *name, void *user_data)
 
 VEC (char_ptr) *
 default_make_symbol_completion_list_break_on (char *text, char *word,
-					      const char *break_on)
+					      const char *break_on,
+					      enum type_code code)
 {
   /* Problem: All of the symbols have to be copied because readline
      frees them.  I'm not going to worry about this; hopefully there
@@ -4307,13 +4308,18 @@ default_make_symbol_completion_list_break_on (char *text, char *word,
      anything that isn't a text symbol (everything else will be
      handled by the psymtab code above).  */
 
-  ALL_MSYMBOLS (objfile, msymbol)
-  {
-    QUIT;
-    COMPLETION_LIST_ADD_SYMBOL (msymbol, sym_text, sym_text_len, text, word);
+  if (code == TYPE_CODE_UNDEF)
+    {
+      ALL_MSYMBOLS (objfile, msymbol)
+	{
+	  QUIT;
+	  COMPLETION_LIST_ADD_SYMBOL (msymbol, sym_text, sym_text_len, text,
+				      word);
 
-    completion_list_objc_symbol (msymbol, sym_text, sym_text_len, text, word);
-  }
+	  completion_list_objc_symbol (msymbol, sym_text, sym_text_len, text,
+				       word);
+	}
+    }
 
   /* Search upwards from currently selected frame (so that we can
      complete on local vars).  Also catch fields of types defined in
@@ -4330,10 +4336,17 @@ default_make_symbol_completion_list_break_on (char *text, char *word,
 
 	ALL_BLOCK_SYMBOLS (b, iter, sym)
 	  {
-	    COMPLETION_LIST_ADD_SYMBOL (sym, sym_text, sym_text_len, text,
-					word);
-	    completion_list_add_fields (sym, sym_text, sym_text_len, text,
-					word);
+	    if (code == TYPE_CODE_UNDEF)
+	      {
+		COMPLETION_LIST_ADD_SYMBOL (sym, sym_text, sym_text_len, text,
+					    word);
+		completion_list_add_fields (sym, sym_text, sym_text_len, text,
+					    word);
+	      }
+	    else if (SYMBOL_DOMAIN (sym) == STRUCT_DOMAIN
+		     && TYPE_CODE (SYMBOL_TYPE (sym)) == code)
+	      COMPLETION_LIST_ADD_SYMBOL (sym, sym_text, sym_text_len, text,
+					  word);
 	  }
 
 	/* Stop when we encounter an enclosing function.  Do not stop for
@@ -4346,13 +4359,16 @@ default_make_symbol_completion_list_break_on (char *text, char *word,
 
   /* Add fields from the file's types; symbols will be added below.  */
 
-  if (surrounding_static_block != NULL)
-    ALL_BLOCK_SYMBOLS (surrounding_static_block, iter, sym)
-      completion_list_add_fields (sym, sym_text, sym_text_len, text, word);
+  if (code == TYPE_CODE_UNDEF)
+    {
+      if (surrounding_static_block != NULL)
+	ALL_BLOCK_SYMBOLS (surrounding_static_block, iter, sym)
+	  completion_list_add_fields (sym, sym_text, sym_text_len, text, word);
 
-  if (surrounding_global_block != NULL)
-      ALL_BLOCK_SYMBOLS (surrounding_global_block, iter, sym)
-	completion_list_add_fields (sym, sym_text, sym_text_len, text, word);
+      if (surrounding_global_block != NULL)
+	ALL_BLOCK_SYMBOLS (surrounding_global_block, iter, sym)
+	  completion_list_add_fields (sym, sym_text, sym_text_len, text, word);
+    }
 
   /* Go through the symtabs and check the externs and statics for
      symbols which match.  */
@@ -4363,7 +4379,10 @@ default_make_symbol_completion_list_break_on (char *text, char *word,
     b = BLOCKVECTOR_BLOCK (BLOCKVECTOR (s), GLOBAL_BLOCK);
     ALL_BLOCK_SYMBOLS (b, iter, sym)
       {
-	COMPLETION_LIST_ADD_SYMBOL (sym, sym_text, sym_text_len, text, word);
+	if (code == TYPE_CODE_UNDEF
+	    || (SYMBOL_DOMAIN (sym) == STRUCT_DOMAIN
+		&& TYPE_CODE (SYMBOL_TYPE (sym)) == code))
+	  COMPLETION_LIST_ADD_SYMBOL (sym, sym_text, sym_text_len, text, word);
       }
   }
 
@@ -4373,11 +4392,17 @@ default_make_symbol_completion_list_break_on (char *text, char *word,
     b = BLOCKVECTOR_BLOCK (BLOCKVECTOR (s), STATIC_BLOCK);
     ALL_BLOCK_SYMBOLS (b, iter, sym)
       {
-	COMPLETION_LIST_ADD_SYMBOL (sym, sym_text, sym_text_len, text, word);
+	if (code == TYPE_CODE_UNDEF
+	    || (SYMBOL_DOMAIN (sym) == STRUCT_DOMAIN
+		&& TYPE_CODE (SYMBOL_TYPE (sym)) == code))
+	  COMPLETION_LIST_ADD_SYMBOL (sym, sym_text, sym_text_len, text, word);
       }
   }
 
-  if (current_language->la_macro_expansion == macro_expansion_c)
+  /* Skip macros if we are completing a struct tag -- arguable but
+     usually what is expected.  */
+  if (current_language->la_macro_expansion == macro_expansion_c
+      && code == TYPE_CODE_UNDEF)
     {
       struct macro_scope *scope;
 
@@ -4405,9 +4430,10 @@ default_make_symbol_completion_list_break_on (char *text, char *word,
 }
 
 VEC (char_ptr) *
-default_make_symbol_completion_list (char *text, char *word)
+default_make_symbol_completion_list (char *text, char *word,
+				     enum type_code code)
 {
-  return default_make_symbol_completion_list_break_on (text, word, "");
+  return default_make_symbol_completion_list_break_on (text, word, "", code);
 }
 
 /* Return a vector of all symbols (regardless of class) which begin by
@@ -4417,7 +4443,21 @@ default_make_symbol_completion_list (char *text, char *word)
 VEC (char_ptr) *
 make_symbol_completion_list (char *text, char *word)
 {
-  return current_language->la_make_symbol_completion_list (text, word);
+  return current_language->la_make_symbol_completion_list (text, word,
+							   TYPE_CODE_UNDEF);
+}
+
+/* Like make_symbol_completion_list, but only return STRUCT_DOMAIN
+   symbols whose type code is CODE.  */
+
+VEC (char_ptr) *
+make_symbol_completion_type (char *text, char *word, enum type_code code)
+{
+  gdb_assert (code == TYPE_CODE_UNION
+	      || code == TYPE_CODE_STRUCT
+	      || code == TYPE_CODE_CLASS
+	      || code == TYPE_CODE_ENUM);
+  return current_language->la_make_symbol_completion_list (text, word, code);
 }
 
 /* Like make_symbol_completion_list, but suitable for use as a
diff --git a/gdb/symtab.h b/gdb/symtab.h
index 0d24cb5..a181da0 100644
--- a/gdb/symtab.h
+++ b/gdb/symtab.h
@@ -23,6 +23,7 @@
 
 #include "vec.h"
 #include "gdb_vecs.h"
+#include "gdbtypes.h"
 
 /* Opaque declarations.  */
 struct ui_file;
@@ -1182,9 +1183,13 @@ extern void forget_cached_source_info (void);
 extern void select_source_symtab (struct symtab *);
 
 extern VEC (char_ptr) *default_make_symbol_completion_list_break_on
-  (char *text, char *word, const char *break_on);
-extern VEC (char_ptr) *default_make_symbol_completion_list (char *, char *);
+  (char *text, char *word, const char *break_on,
+   enum type_code code);
+extern VEC (char_ptr) *default_make_symbol_completion_list (char *, char *,
+							    enum type_code);
 extern VEC (char_ptr) *make_symbol_completion_list (char *, char *);
+extern VEC (char_ptr) *make_symbol_completion_type (char *, char *,
+						    enum type_code);
 extern VEC (char_ptr) *make_symbol_completion_list_fn (struct cmd_list_element *,
 						       char *, char *);
 
diff --git a/gdb/testsuite/gdb.base/break1.c b/gdb/testsuite/gdb.base/break1.c
index af4f2b0..1f0654e 100644
--- a/gdb/testsuite/gdb.base/break1.c
+++ b/gdb/testsuite/gdb.base/break1.c
@@ -29,6 +29,23 @@ struct some_struct
 
 struct some_struct values[50];
 
+/* Some definitions for tag completion.  */
+enum some_enum { VALUE };
+
+enum some_enum some_enum_global;
+
+union some_union
+{
+  int f1;
+  double f2;
+};
+
+union some_union some_union_global;
+
+/* A variable with a name "similar" to the above struct, to test that
+   tag completion works ok.  */
+int some_variable;
+
 /* The following functions do nothing useful.  They are included
    simply as places to try setting breakpoints at.  They are
    explicitly "one-line functions" to verify that this case works
diff --git a/gdb/testsuite/gdb.base/completion.exp b/gdb/testsuite/gdb.base/completion.exp
index c8edc98..006670d 100644
--- a/gdb/testsuite/gdb.base/completion.exp
+++ b/gdb/testsuite/gdb.base/completion.exp
@@ -700,6 +700,14 @@ gdb_test "complete sav" "save" "test non-deprecated completion"
 gdb_test "complete save-t" "save-tracepoints" "test deprecated completion"
 
 
+#
+# Tag name completion.
+#
+
+gdb_test "complete ptype struct some_" "ptype struct some_struct"
+gdb_test "complete ptype enum some_" "ptype enum some_enum"
+gdb_test "complete ptype union some_" "ptype union some_union"
+
 # Restore globals modified in this test...
 set timeout $oldtimeout1
 
-- 
1.7.7.6



More information about the Gdb-patches mailing list