From 2b9ffaa6075ef9047c048b5ebbc9e8cb08ac6366 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Fri, 12 May 2017 16:29:00 -0400
Subject: [PATCH 11/14] FIXME: convert to linked-list representation to allow
 for reparenting childs (towards accessing return type location)

---
 gcc/ast.c                              | 142 +++++++++++++++++++++++++++++----
 gcc/ast.def                            |   7 +-
 gcc/ast.h                              |  17 +++-
 gcc/cp/cp-tree.h                       |   3 +
 gcc/cp/decl.c                          |   5 +-
 gcc/cp/parser.c                        |  86 +++++++++++++++++---
 gcc/cp/typeck.c                        |  68 +++++++++++++++-
 gcc/testsuite/g++.dg/bad-return-type.C | 135 +++++++++++++++++++++++++++++++
 gcc/testsuite/gcc.dg/bad-return-type.c |   9 ---
 9 files changed, 427 insertions(+), 45 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/bad-return-type.C
 delete mode 100644 gcc/testsuite/gcc.dg/bad-return-type.c

diff --git a/gcc/ast.c b/gcc/ast.c
index 2519c6a..40e949f 100644
--- a/gcc/ast.c
+++ b/gcc/ast.c
@@ -33,7 +33,8 @@ static const char *ast_kind_to_name (enum ast_kind kind);
 /* FIXME.  */
 
 ast_node::ast_node (enum ast_kind kind, location_t start)
-: m_kind (kind), m_parent (NULL), m_children (),
+: m_kind (kind), m_parent (NULL), m_first_child (NULL), m_last_child (NULL),
+  m_prev_sibling (NULL), m_next_sibling (NULL),
   m_start (start), m_finish (UNKNOWN_LOCATION), m_tree (NULL_TREE)
 {
 }
@@ -43,10 +44,89 @@ ast_node::ast_node (enum ast_kind kind, location_t start)
 void
 ast_node::add_child (ast_node *child)
 {
-  m_children.safe_push (child);
+  gcc_assert (child->m_prev_sibling == NULL);
+  gcc_assert (child->m_next_sibling == NULL);
+  gcc_assert (child->m_parent == NULL);
+
+  if (m_last_child)
+    {
+      m_last_child->m_next_sibling = child;
+      child->m_prev_sibling = m_last_child;
+    }
+  else
+    {
+      gcc_assert (m_first_child == NULL);
+      m_first_child = child;
+    }
+
+  m_last_child = child;
   child->m_parent = this;
 }
 
+
+/* Convert OLD to an orphan, and take over parenthood of NEW,
+   putting NEW in OLD's place.  */
+// FIXME: motivated by the fixup in C++ parser of
+// block-declaration => function-definition
+
+void
+ast_node::replace_child (ast_node *old, ast_node *new_)
+{
+  gcc_assert (old);
+  gcc_assert (old->m_parent == this);
+  gcc_assert (new_);
+  assert_invariants ();
+  old->assert_invariants ();
+  new_->assert_invariants ();
+
+  ast_node *old_prev_sibling = old->m_prev_sibling;
+  ast_node *old_next_sibling = old->m_next_sibling;
+
+  old->make_orphan ();
+  new_->make_orphan ();
+
+  new_->m_prev_sibling = old_prev_sibling;
+  new_->m_next_sibling = old_next_sibling;
+
+  if (old_prev_sibling == NULL)
+    m_first_child = new_;
+  if (old_next_sibling == NULL)
+    m_last_child = new_;
+
+  assert_invariants ();
+  old->assert_invariants ();
+  new_->assert_invariants ();
+}
+
+/* FIXME.  */
+
+void
+ast_node::make_orphan ()
+{
+  assert_invariants ();
+
+  if (m_parent)
+    {
+      if (m_prev_sibling)
+	{
+	  m_prev_sibling->m_next_sibling = m_next_sibling;
+	  m_prev_sibling = NULL;
+	}
+      else
+	m_parent->m_first_child = m_next_sibling;
+      if (m_next_sibling)
+	{
+	  m_next_sibling->m_prev_sibling = m_prev_sibling;
+	  m_next_sibling = NULL;
+	}
+      else
+	m_parent->m_last_child = m_prev_sibling;
+    }
+  m_parent = NULL;
+
+  assert_invariants ();
+}
+
 /* FIXME.  */
 
 void
@@ -82,9 +162,6 @@ void
 ast_node::dump (pretty_printer *pp, const char *prefix,
 		const ast_node *parent, bool is_last_child) const
 {
-  unsigned i;
-  ast_node *child;
-
   if (parent)
     {
       // FIXME: colorize hierachical ASCII art
@@ -124,12 +201,15 @@ ast_node::dump (pretty_printer *pp, const char *prefix,
   if (m_tree)
     // FIXME: format code:
     // FIXME: add get_tree_code_name to both
-#if 1
-    pp_printf (pp, " tree: %p %qs", (void *)m_tree,
+#if 0
+    pp_printf (pp, " tree: %p %r<%s>%R", (void *)m_tree,
+	       "warning", // FIXME: dedicated color code?
 	       get_tree_code_name (TREE_CODE (m_tree)));
 #else
     // seems to work for C++:
-    pp_printf (pp, " tree: %p %qE", (void *)m_tree, m_tree);
+    pp_printf (pp, " tree: %p %r<%s>%R %qE", (void *)m_tree,
+	       "warning", // FIXME: dedicated color code?
+	       get_tree_code_name (TREE_CODE (m_tree)), m_tree);
 #endif
 
   /* Show source code.  */
@@ -152,9 +232,9 @@ ast_node::dump (pretty_printer *pp, const char *prefix,
     new_prefix = ACONCAT ((prefix, is_last_child ? "  " : "|  ", NULL));
   else
     new_prefix = prefix;
-  FOR_EACH_VEC_ELT (m_children, i, child)
+  for (ast_node *child = m_first_child; child; child = child->m_next_sibling)
     {
-      bool is_last_child = (i == m_children.length () - 1);
+      bool is_last_child = child == m_last_child;
       child->dump (pp, new_prefix, this, is_last_child);
     }
 }
@@ -180,9 +260,7 @@ ast_node::get_range () const
 ast_node *
 ast_node::get_first_child_of_kind (enum ast_kind kind) const
 {
-  int i;
-  ast_node *child;
-  FOR_EACH_VEC_ELT (m_children, i, child)
+  for (ast_node *child = m_first_child; child; child = child->m_next_sibling)
     if (kind == child->m_kind)
       return child;
   return NULL;
@@ -192,9 +270,7 @@ void
 ast_node::get_children_of_kind (auto_vec<ast_node *> &out,
 				enum ast_kind kind) const
 {
-  int i;
-  ast_node *child;
-  FOR_EACH_VEC_ELT (m_children, i, child)
+  for (ast_node *child = m_first_child; child; child = child->m_next_sibling)
     if (kind == child->m_kind)
       out.safe_push (child);
 }
@@ -202,6 +278,40 @@ ast_node::get_children_of_kind (auto_vec<ast_node *> &out,
 /* FIXME.  */
 
 ast_node *
+ast_node::get_ancestor_of_kind (enum ast_kind kind) const
+{
+  for (ast_node *iter = m_parent; iter; iter = iter->m_parent)
+    if (kind == iter->m_kind)
+      return iter;
+  return NULL;
+}
+
+/* FIXME.  */
+
+void
+ast_node::assert_invariants () const
+{
+  if (m_parent)
+    {
+      if (m_next_sibling)
+	gcc_assert (m_parent);
+      else
+	gcc_assert (m_parent->m_last_child == this);
+      if (m_prev_sibling)
+	gcc_assert (m_parent);
+      else
+	gcc_assert (m_parent->m_first_child == this);
+    }
+  else
+    {
+      gcc_assert (m_next_sibling == NULL);
+      gcc_assert (m_prev_sibling == NULL);
+    }
+}
+
+/* FIXME.  */
+
+ast_node *
 ast_get_node_for_tree (tree t)
 {
   if (!t)
diff --git a/gcc/ast.def b/gcc/ast.def
index 6ca7814..1492193 100644
--- a/gcc/ast.def
+++ b/gcc/ast.def
@@ -27,12 +27,16 @@ DEF_AST_NODE (AST_CLASS_SPECIFIER, "class-specifier")
 DEF_AST_NODE (AST_DECLARATION, "declaration")
 DEF_AST_NODE (AST_DECLARATION_SEQ, "declaration-seq")
 DEF_AST_NODE (AST_DECLARATOR, "declarator")
+DEF_AST_NODE (AST_DECL_SPECIFIER, "decl-specifier")
 DEF_AST_NODE (AST_DECL_SPECIFIER_SEQ, "decl-specifier-seq")
 DEF_AST_NODE (AST_DIRECT_DECLARATOR, "direct-declarator")
 DEF_AST_NODE (AST_EXCEPTION_DECLARATION, "exception-declaration")
+DEF_AST_NODE (AST_EXPLICIT_INSTANTIATION, "explicit-instantiation")
+DEF_AST_NODE (AST_EXPLICIT_SPECIALIZATION, "explicit-specialization")
 DEF_AST_NODE (AST_EXPRESSION, "expression")
 DEF_AST_NODE (AST_EXPRESSION_LIST, "expression-list")
 DEF_AST_NODE (AST_EXTERNAL_DECLARATION, "external-declaration")
+DEF_AST_NODE (AST_FUNCTION_DEFINITION, "function-definition")
 DEF_AST_NODE (AST_FUNCTION_TRY_BLOCK, "function-try-block")
 DEF_AST_NODE (AST_HANDLER, "handler")
 DEF_AST_NODE (AST_HANDLER_SEQ, "handler-seq")
@@ -49,12 +53,11 @@ DEF_AST_NODE (AST_PRIMARY_EXPRESSION, "primary-expression")
 DEF_AST_NODE (AST_SIMPLE_DECLARATION, "simple-declaration")
 DEF_AST_NODE (AST_TEMPLATE_DECLARATION, "template-declaration")
 DEF_AST_NODE (AST_TEMPLATE_PARAMETER_LIST, "template-parameter-list")
-DEF_AST_NODE (AST_EXPLICIT_INSTANTIATION, "explicit-instantiation")
-DEF_AST_NODE (AST_EXPLICIT_SPECIALIZATION, "explicit-specialization")
 DEF_AST_NODE (AST_THROW_EXPRESSION, "throw-expression")
 DEF_AST_NODE (AST_TOKEN, "token")
 DEF_AST_NODE (AST_TRANSLATION_UNIT, "translation-unit")
 DEF_AST_NODE (AST_TRY_BLOCK, "try-block")
 DEF_AST_NODE (AST_TYPE_ID_LIST, "type-id-list")
+DEF_AST_NODE (AST_TYPE_SPECIFIER, "type-specifier")
 DEF_AST_NODE (AST_UNARY_EXPRESSION, "unary-expression")
 DEF_AST_NODE (AST_UNQUALIFIED_ID, "unqualified-id")
diff --git a/gcc/ast.h b/gcc/ast.h
index 5a93aab..b1f3f3c 100644
--- a/gcc/ast.h
+++ b/gcc/ast.h
@@ -36,10 +36,15 @@ class ast_node
 public:
   ast_node (enum ast_kind kind, location_t start);
 
-  /* Modification.  */
+  /* Structural manipulation.  */
   void add_child (ast_node *child);
+  void replace_child (ast_node *old, ast_node *new_);
+  void make_orphan ();
+
+  /* Modification.  */
   void set_finish (location_t loc) { m_finish = loc; }
   void set_tree (tree t);
+  void set_kind (enum ast_kind kind) { m_kind = kind; }
 
   /* Dumping.  */
   void dump (FILE *) const;
@@ -59,10 +64,18 @@ public:
   void get_children_of_kind (auto_vec<ast_node *> &out,
 			     enum ast_kind kind) const;
 
+  ast_node *get_ancestor_of_kind (enum ast_kind kind) const;
+
+private:
+  void assert_invariants () const;
+
 private:
   enum ast_kind m_kind;
   ast_node *m_parent;
-  auto_vec <ast_node *> m_children;
+  ast_node *m_first_child;
+  ast_node *m_last_child;
+  ast_node *m_prev_sibling;
+  ast_node *m_next_sibling;
 #if 1
   location_t m_start;
   location_t m_finish;
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index c4e7a8c..428c389 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7099,6 +7099,9 @@ extern tree cp_ubsan_maybe_instrument_downcast	(location_t, tree, tree, tree);
 extern tree cp_ubsan_maybe_instrument_cast_to_vbase (location_t, tree, tree);
 extern void cp_ubsan_maybe_initialize_vtbl_ptrs (tree);
 
+// FIXME
+extern void attempt_to_highlight_return_type (tree fndecl);
+
 /* -- end of C++ */
 
 #endif /* ! GCC_CP_TREE_H */
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index d46a631..9aaac37 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -15713,8 +15713,9 @@ finish_function (int flags)
       && !DECL_DESTRUCTOR_P (fndecl)
       && targetm.warn_func_return (fndecl))
     {
-      warning (OPT_Wreturn_type,
- 	       "no return statement in function returning non-void");
+      if (warning (OPT_Wreturn_type,
+		   "no return statement in function returning non-void"))
+	attempt_to_highlight_return_type (current_function_decl);
       TREE_NO_WARNING (fndecl) = 1;
     }
 
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 80bdc58..8676dfc 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -1368,6 +1368,7 @@ public:
 private:
   cp_parser *m_parser;
   cp_expr *m_expr_ptr;
+  ast_node *m_parent;
 };
 
 /* FIXME.  */
@@ -1383,38 +1384,50 @@ private:
 #define AST_ADD_NEXT_TOKEN(PARSER) \
   add_next_token (PARSER)
 
+#define ADD_NODE_FOR_NEXT_TOKEN(KIND) \
+  add_node_for_next_token (parser, (KIND))
+
 #define AST_SET_TREE(TREE_NODE) \
   tmp_ast_node.set_tree (TREE_NODE);
 
-static void
-add_next_token (cp_parser *parser)
+/* FIXME.  */
+
+static ast_node *
+add_node_for_next_token (cp_parser *parser, enum ast_kind kind)
 {
   if (!flag_capture_ast)
-    return;
+    return NULL;
 
   cp_token *token = cp_lexer_peek_token (parser->lexer);
   ast_node *parent = parser->ast_current_node;
-  ast_node *node = new ast_node (AST_TOKEN, token->location);
+  ast_node *node = new ast_node (kind, token->location);
   parent->add_child (node);
-  // FIXME: get a meaningful string for the token
   node->set_finish (token->location);
+  return node;
+}
+
+static void
+add_next_token (cp_parser *parser)
+{
+  add_node_for_next_token (parser, AST_TOKEN);
+  // FIXME: get a meaningful string for the token
 }
 
 /* FIXME.  */
 
 auto_ast_node::auto_ast_node (cp_parser *parser, enum ast_kind kind,
                               cp_expr *expr_ptr)
-: m_parser (parser), m_expr_ptr (expr_ptr)
+: m_parser (parser), m_expr_ptr (expr_ptr),
+  m_parent (parser->ast_current_node)
 {
   if (!flag_capture_ast)
     return;
 
   cp_token *first_token = cp_lexer_peek_token (parser->lexer);
-  ast_node *parent = parser->ast_current_node;
   ast_node *node = new ast_node (kind, first_token->location);
   parser->ast_current_node = node;
-  if (parent)
-    parent->add_child (node);
+  if (m_parent)
+    m_parent->add_child (node);
   else
     parser->ast_root_node = node;
 }
@@ -1443,7 +1456,9 @@ auto_ast_node::~auto_ast_node ()
   if (m_expr_ptr)
     node->set_tree (m_expr_ptr->get_value ());
 
-  m_parser->ast_current_node = node->get_parent ();
+  /* Use stashed parent, rather than the curent node's parent
+     to allow for reparenting within the tree.  */
+  m_parser->ast_current_node = m_parent;
 }
 
 void
@@ -13375,6 +13390,7 @@ cp_parser_decl_specifier_seq (cp_parser* parser,
 	  else
 	    {
 	      ds = ds_friend;
+	      ADD_NODE_FOR_NEXT_TOKEN (AST_DECL_SPECIFIER);
 	      /* Consume the token.  */
 	      cp_lexer_consume_token (parser->lexer);
 	    }
@@ -13382,6 +13398,7 @@ cp_parser_decl_specifier_seq (cp_parser* parser,
 
         case RID_CONSTEXPR:
 	  ds = ds_constexpr;
+	  ADD_NODE_FOR_NEXT_TOKEN (AST_DECL_SPECIFIER);
           cp_lexer_consume_token (parser->lexer);
           break;
 
@@ -13404,6 +13421,7 @@ cp_parser_decl_specifier_seq (cp_parser* parser,
 	       typedef  */
 	case RID_TYPEDEF:
 	  ds = ds_typedef;
+	  ADD_NODE_FOR_NEXT_TOKEN (AST_DECL_SPECIFIER);
 	  /* Consume the token.  */
 	  cp_lexer_consume_token (parser->lexer);
 	  /* A constructor declarator cannot appear in a typedef.  */
@@ -13428,6 +13446,7 @@ cp_parser_decl_specifier_seq (cp_parser* parser,
 	case RID_AUTO:
           if (cxx_dialect == cxx98) 
             {
+	      ADD_NODE_FOR_NEXT_TOKEN (AST_DECL_SPECIFIER);
 	      /* Consume the token.  */
 	      cp_lexer_consume_token (parser->lexer);
 
@@ -13449,6 +13468,7 @@ cp_parser_decl_specifier_seq (cp_parser* parser,
 	case RID_STATIC:
 	case RID_EXTERN:
 	case RID_MUTABLE:
+	  ADD_NODE_FOR_NEXT_TOKEN (AST_DECL_SPECIFIER);
 	  /* Consume the token.  */
 	  cp_lexer_consume_token (parser->lexer);
           cp_parser_set_storage_class (parser, decl_specs, token->keyword,
@@ -13457,6 +13477,7 @@ cp_parser_decl_specifier_seq (cp_parser* parser,
 	case RID_THREAD:
 	  /* Consume the token.  */
 	  ds = ds_thread;
+          ADD_NODE_FOR_NEXT_TOKEN (AST_DECL_SPECIFIER);
 	  cp_lexer_consume_token (parser->lexer);
 	  break;
 
@@ -16547,6 +16568,8 @@ cp_parser_type_specifier (cp_parser* parser,
 			  int* declares_class_or_enum,
 			  bool* is_cv_qualifier)
 {
+  AST_NODE (parser, AST_TYPE_SPECIFIER);
+
   tree type_spec = NULL_TREE;
   cp_token *token;
   enum rid keyword;
@@ -19113,6 +19136,48 @@ strip_declarator_types (tree type, cp_declarator *declarator)
   return type;
 }
 
+/* Subroutine for cp_parser_init_declarator for once
+   we know we're dealing with a function-definition.
+
+   We're below a:
+   + PARENT
+     `-block-declaration
+       `-simple-declaration
+         |-decl-specifier-seq
+         |-declarator
+
+  Convert the simple-declaration to a function-definition,
+  and lose the block-declaration, resulting in:
+
+   + PARENT
+     `-function-definition
+       |-decl-specifier-seq
+       |-declarator.  */
+
+static void
+update_ast_for_function_definition (cp_parser *parser)
+{
+  if (!CURRENT_AST_NODE)
+    return;
+
+  ast_node *block_decl
+    = CURRENT_AST_NODE->get_ancestor_of_kind (AST_BLOCK_DECLARATION);
+
+  if (!block_decl)
+    /* We might have a template-declaration.  */
+    return;
+
+  ast_node *simple_decl
+    = block_decl->get_first_child_of_kind (AST_SIMPLE_DECLARATION);
+  gcc_assert (simple_decl);
+
+  ast_node *parent = block_decl->get_parent ();
+  gcc_assert (parent);
+
+  simple_decl->set_kind (AST_FUNCTION_DEFINITION);
+  parent->replace_child (block_decl, simple_decl);
+}
+
 /* Declarators [gram.dcl.decl] */
 
 /* Parse an init-declarator.
@@ -19329,6 +19394,7 @@ cp_parser_init_declarator (cp_parser* parser,
 		      "on a function-definition");
 	  /* This is a function-definition.  */
 	  *function_definition_p = true;
+          update_ast_for_function_definition (parser);
 
 	  /* Parse the function definition.  */
 	  if (member_p)
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index 1abb50f..a5c7e41 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -37,6 +37,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "c-family/c-ubsan.h"
 #include "params.h"
 #include "gcc-rich-location.h"
+#include "ast.h"
 
 static tree cp_build_addr_expr_strict (tree, tsubst_flags_t);
 static tree cp_build_function_call (tree, tree, tsubst_flags_t);
@@ -8778,6 +8779,59 @@ maybe_warn_about_returning_address_of_local (tree retval)
   return false;
 }
 
+/* FIXME.  */
+
+static location_t
+get_location_of_return_type (tree fndecl)
+{
+  ast_node *node = ast_get_node_for_tree (fndecl);
+  if (!node)
+    return UNKNOWN_LOCATION;
+
+  /* We have a direct-declarator within a function-definition,
+     or a member-declaration.
+     Locate the function-definition or member-declaration.  */
+  ast_node *iter;
+  for (iter = node; iter; iter = iter->get_parent ())
+    if (iter->get_kind () == AST_FUNCTION_DEFINITION
+	|| iter->get_kind () == AST_MEMBER_DECLARATION)
+      break;
+  if (!iter)
+    return UNKNOWN_LOCATION;
+
+  /* Locate the decl specifiers within the function-definition
+     or member-declaration.  */
+  ast_node *dss = iter->get_first_child_of_kind (AST_DECL_SPECIFIER_SEQ);
+  if (!dss)
+    return UNKNOWN_LOCATION;
+
+  /* Locate the type-specifier within the decl specifiers.  It ought to
+     be "void".  */
+  ast_node *ts = dss->get_first_child_of_kind (AST_TYPE_SPECIFIER);
+  if (!ts)
+    return UNKNOWN_LOCATION;
+
+  return ts->get_range ();
+}
+
+/* FIXME.  */
+
+void
+attempt_to_highlight_return_type (tree fndecl)
+{
+  location_t ret_type_loc
+    = get_location_of_return_type (fndecl);
+  if (ret_type_loc == UNKNOWN_LOCATION)
+    return;
+
+  tree result = DECL_RESULT (fndecl);
+  tree return_type = TREE_TYPE (result);
+
+  inform (ret_type_loc,
+	  "the return type was declared as %qT here", return_type);
+}
+
+
 /* Check that returning RETVAL from the current function is valid.
    Return an expression explicitly showing all conversions required to
    change RETVAL into the function return type, and to assign it to
@@ -8903,8 +8957,11 @@ check_return_expr (tree retval, bool *no_warning)
   if (!retval && fn_returns_value_p)
     {
       if (functype != error_mark_node)
-	permerror (input_location, "return-statement with no value, in "
-		   "function returning %qT", valtype);
+	{
+	  if (permerror (input_location, "return-statement with no value, in "
+			 "function returning %qT", valtype))
+	    attempt_to_highlight_return_type (current_function_decl);
+	}
       /* Remember that this function did return.  */
       current_function_returns_value = 1;
       /* And signal caller that TREE_NO_WARNING should be set on the
@@ -8922,8 +8979,11 @@ check_return_expr (tree retval, bool *no_warning)
 	   its side-effects.  */
 	  finish_expr_stmt (retval);
       else
-	permerror (input_location, "return-statement with a value, in function "
-		   "returning 'void'");
+	{
+	  if (permerror (input_location, "return-statement with a value,"
+			 " in function returning %<void%>"))
+	    attempt_to_highlight_return_type (current_function_decl);
+	}
       current_function_returns_null = 1;
 
       /* There's really no value to return, after all.  */
diff --git a/gcc/testsuite/g++.dg/bad-return-type.C b/gcc/testsuite/g++.dg/bad-return-type.C
new file mode 100644
index 0000000..bea7734
--- /dev/null
+++ b/gcc/testsuite/g++.dg/bad-return-type.C
@@ -0,0 +1,135 @@
+/* Verify that we can highlight the return type for various kinds
+   of bogus return.  */
+
+// { dg-options "-fdiagnostics-show-caret -fcapture-ast -Wreturn-type" }
+
+void test_1 (void) // { dg-line void_line }
+{
+  return 42; // { dg-error "return-statement with a value, in function returning 'void'" }
+  /* { dg-begin-multiline-output "" }
+   return 42;
+          ^~
+     { dg-end-multiline-output "" } */
+  // { dg-message "the return type was declared as 'void' here" "" { target *-*-* } void_line }
+  /* { dg-begin-multiline-output "" }
+ void test_1 (void)
+ ^~~~
+     { dg-end-multiline-output "" } */
+}
+
+/* As before, but with different whitespace.  */
+
+void // { dg-line void_line_2 }
+test_2 (void)
+{
+  return 42; // { dg-error "return-statement with a value, in function returning 'void'" }
+  /* { dg-begin-multiline-output "" }
+   return 42;
+          ^~
+     { dg-end-multiline-output "" } */
+  // { dg-message "the return type was declared as 'void' here" "" { target *-*-* } void_line_2 }
+  /* { dg-begin-multiline-output "" }
+ void
+ ^~~~
+     { dg-end-multiline-output "" } */
+}
+
+/* As before, but with "extern".  */
+
+extern void test_3 (void) // { dg-line void_line_3 }
+{
+  return 42; // { dg-error "return-statement with a value, in function returning 'void'" }
+  /* { dg-begin-multiline-output "" }
+   return 42;
+          ^~
+     { dg-end-multiline-output "" } */
+  // { dg-message "the return type was declared as 'void' here" "" { target *-*-* } void_line_3 }
+  /* { dg-begin-multiline-output "" }
+ extern void test_3 (void)
+        ^~~~
+     { dg-end-multiline-output "" } */
+}
+
+/* Type mismatch for non-void return.  */
+
+extern int test_4 (void) // { dg-line void_line_4 }
+{
+  return; // { dg-error "return-statement with no value, in function returning 'int'" }
+  /* { dg-begin-multiline-output "" }
+   return;
+   ^~~~~~
+     { dg-end-multiline-output "" } */
+  // { dg-message "the return type was declared as 'int' here" "" { target *-*-* } void_line_4 }
+  /* { dg-begin-multiline-output "" }
+ extern int test_4 (void)
+        ^~~
+     { dg-end-multiline-output "" } */
+}
+
+/* Falling off the end of a non-void function.  */
+
+extern int test_5 (void) // { dg-line void_line_5 }
+{
+} // { dg-warning "no return statement in function returning non-void" }
+/* { dg-begin-multiline-output "" }
+ }
+ ^
+   { dg-end-multiline-output "" } */
+// { dg-message "the return type was declared as 'int' here" "" { target *-*-* } void_line_5 }
+/* { dg-begin-multiline-output "" }
+ extern int test_5 (void)
+        ^~~
+   { dg-end-multiline-output "" } */
+
+/* Member function.  */
+
+struct test_6
+{
+  void member (void) // { dg-line void_line_6 }
+  {
+    return 42; // { dg-error "return-statement with a value, in function returning 'void'" }
+    /* { dg-begin-multiline-output "" }
+     return 42;
+            ^~
+       { dg-end-multiline-output "" } */
+    // { dg-message "the return type was declared as 'void' here" "" { target *-*-* } void_line_6 }
+    /* { dg-begin-multiline-output "" }
+   void member (void)
+   ^~~~
+       { dg-end-multiline-output "" } */
+  }
+};
+
+/* Function template.  */
+// TODO: highlight "void" for these
+template <typename T>
+void test_7 (T t)
+{
+  return 42; // { dg-error "return-statement with a value, in function returning 'void'" }
+/* { dg-begin-multiline-output "" }
+   return 42;
+          ^~
+   { dg-end-multiline-output "" } */
+}
+
+/* Class template.  */
+
+template <typename T>
+struct test_8
+{
+  void member (T t) // { dg-line void_line_8 }
+  {
+    return 42; // { dg-error "return-statement with a value, in function returning 'void'" }
+    /* { dg-begin-multiline-output "" }
+     return 42;
+            ^~
+       { dg-end-multiline-output "" } */
+    // { dg-message "the return type was declared as 'void' here" "" { target *-*-* } void_line_8 }
+    /* { dg-begin-multiline-output "" }
+   void member (T t)
+   ^~~~
+       { dg-end-multiline-output "" } */
+  }
+};
+
+// TODO: complex return type e.g. const char *
diff --git a/gcc/testsuite/gcc.dg/bad-return-type.c b/gcc/testsuite/gcc.dg/bad-return-type.c
deleted file mode 100644
index bb27a94..0000000
--- a/gcc/testsuite/gcc.dg/bad-return-type.c
+++ /dev/null
@@ -1,9 +0,0 @@
-int test (void)
-{
-  return "hello world";
-}
-
-void test (void)
-{
-  return 42;
-}
-- 
1.8.5.3

