public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH 03/17] Core of BLT implementation
  2017-07-24 19:31 [PATCH 00/17] RFC: New source-location representation; Language Server Protocol David Malcolm
@ 2017-07-24 19:31 ` David Malcolm
  2017-09-01 17:32   ` Jeff Law
  2017-07-24 19:31 ` [PATCH 15/17] Language Server Protocol: add lsp::server abstract base class David Malcolm
                   ` (18 subsequent siblings)
  19 siblings, 1 reply; 38+ messages in thread
From: David Malcolm @ 2017-07-24 19:31 UTC (permalink / raw)
  To: gcc-patches; +Cc: David Malcolm

This patch implements the core of the new "blt" type: an optional
"on-the-side" hierarchical recording of source ranges, associated
with grammar productions, with a sparse mapping to our regular
"tree" type.

Caveats:
* the name is a placeholder (see the comment in blt.h)
* I'm ignoring memory management for now (e.g. how do these get freed?
  possible a custom arena, or an obstack within a blt_context or
  somesuch)
* similarly, I've not attempted any optimization yet

gcc/ChangeLog:
	* Makefile.in (OBJS): Add blt.o.
	* blt.c: New file.
	* blt.def: New file.
	* blt.h: New file.

gcc/c-family/ChangeLog:
	* c.opt (fblt): New option.
	(fdump-blt): New option.

gcc/ChangeLog:
	* selftest-run-tests.c (selftest::run_tests): Call
	selftest::blt_c_tests.
	* selftest.h (selftest::blt_c_tests): New decl.
---
 gcc/Makefile.in          |   1 +
 gcc/blt.c                | 768 +++++++++++++++++++++++++++++++++++++++++++++++
 gcc/blt.def              |  87 ++++++
 gcc/blt.h                | 147 +++++++++
 gcc/c-family/c.opt       |   8 +
 gcc/selftest-run-tests.c |   1 +
 gcc/selftest.h           |   1 +
 7 files changed, 1013 insertions(+)
 create mode 100644 gcc/blt.c
 create mode 100644 gcc/blt.def
 create mode 100644 gcc/blt.h

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index efca916..519ada0 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1219,6 +1219,7 @@ OBJS = \
 	auto-profile.o \
 	bb-reorder.o \
 	bitmap.o \
+	blt.o \
 	bt-load.o \
 	builtins.o \
 	caller-save.o \
diff --git a/gcc/blt.c b/gcc/blt.c
new file mode 100644
index 0000000..1216964
--- /dev/null
+++ b/gcc/blt.c
@@ -0,0 +1,768 @@
+/* Extra location information.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+
+   This file is part of GCC.
+
+   GCC is free software; you can redistribute it and/or modify it
+   under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3, or (at your option)
+   any later version.
+
+   GCC is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tree.h"
+#include "pretty-print.h"
+#include "diagnostic.h"
+#include "diagnostic-color.h"
+#include "blt.h"
+#include "selftest.h"
+
+typedef hash_map <tree, blt_node *> tree_to_blt_map_t;
+static tree_to_blt_map_t *tree_to_blt_map;
+
+static const char *blt_kind_to_name (enum blt_kind kind);
+
+/* blt_node's ctor.  */
+
+blt_node::blt_node (enum blt_kind kind, location_t start)
+: 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)
+{
+}
+
+/* Add CHILD to this blt_node as its final child.
+   CHILD must be an orphan.  */
+
+void
+blt_node::add_child (blt_node *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
+blt_node::replace_child (blt_node *old, blt_node *new_)
+{
+  gcc_assert (old);
+  gcc_assert (old->m_parent == this);
+  gcc_assert (new_);
+  assert_invariants ();
+  old->assert_invariants ();
+  new_->assert_invariants ();
+
+  blt_node *old_prev_sibling = old->m_prev_sibling;
+  blt_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 ();
+}
+
+/* Convert this node to an orphan.  */
+
+void
+blt_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 ();
+}
+
+/* Set the tree associated with this blt_node to be T.  */
+// FIXME: what if it's called more than once
+
+void
+blt_node::set_tree (tree t)
+{
+  m_tree = t;
+
+  /* Add to mapping.  */
+  if (!t)
+    return;
+
+  if (!tree_to_blt_map)
+    tree_to_blt_map = new tree_to_blt_map_t ();
+  tree_to_blt_map->put (t, this);
+}
+
+/* Dump this blt_node and its descendants to FILE.  */
+
+void
+blt_node::dump (FILE *file) const
+{
+  pretty_printer pp;
+  pp_show_color (&pp) = pp_show_color (global_dc->printer);
+  pp_format_decoder (&pp) = pp_format_decoder (global_dc->printer);
+  pp.buffer->stream = file;
+  dump (&pp, "", NULL, true);
+  pp_flush (&pp);
+}
+
+/* Dump this blt_node and its descendants to PP.
+   PREFIX is used for all lines, providing an ascii-art representation
+   of the tree structure; PARENT and IS_LAST_CHILD are used to control
+   this representation; PARENT should be NULL if printing this node as
+   the top-level node within this dump (for dumping sub-trees).  */
+
+void
+blt_node::dump (pretty_printer *pp, const char *prefix,
+		const blt_node *parent, bool is_last_child) const
+{
+  if (parent)
+    {
+      /* Colorized hierachical ASCII art.  */
+      const char *tail = is_last_child ? "`-" : "|-";
+      pp_printf (pp, "%r%s%s%R", "note", prefix, tail);
+      // FIXME: dedicated color code?
+    }
+  pp_printf (pp, "%r%s%R %p", "error", blt_kind_to_name (m_kind),
+	     (const void *)this); // FIXME: dedicated color code?
+
+  location_t parent_start_loc = parent ? parent->m_start : UNKNOWN_LOCATION;
+  location_t parent_finish_loc = parent ? parent->m_finish : UNKNOWN_LOCATION;
+
+  /* Dump location information, eliding commonality with parent.  */
+  {
+    pp_printf (pp, " %s<", colorize_start (pp_show_color (pp), "warning"));
+    // FIXME: dedicated color code?
+
+    expanded_location el_start = expand_location (m_start);
+    expanded_location el_parent = expand_location (parent_start_loc);
+
+    if (el_start.file != el_parent.file)
+      /* We don't use %qs or %< and %> here, to avoid affecting
+	 colorization.  */
+      pp_printf (pp, "%s:", el_start.file);
+    pp_printf (pp, "%i:%i", el_start.line, el_start.column);
+    if (m_finish != m_start)
+      {
+	pp_printf (pp, "-");
+	expanded_location el_finish = expand_location (m_finish);
+	if (el_finish.file && el_finish.file != el_start.file)
+	  pp_printf (pp, "%s:", el_finish.file);
+	pp_printf (pp, "%i:%i", el_finish.line, el_finish.column);
+      }
+
+    pp_printf (pp, ">%s", colorize_stop (pp_show_color (pp)));
+  }
+
+  /* TODO: print any tree associatee with this blt_node.  */
+#if 0
+  if (m_tree)
+    // FIXME: which format code should be used?
+    // FIXME: add get_tree_code_name to both
+#if 1
+    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 %r<%s>%R %qE", (void *)m_tree,
+	       "warning", // FIXME: dedicated color code?
+	       get_tree_code_name (TREE_CODE (m_tree)),
+	       m_tree);
+#endif
+#endif
+
+  const char *new_prefix;
+  if (parent)
+    new_prefix = ACONCAT ((prefix, is_last_child ? "  " : "|  ", NULL));
+  else
+    new_prefix = prefix;
+
+  /* Show source code.  */
+  if (m_start != parent_start_loc || m_finish != parent_finish_loc)
+    {
+      location_t range = get_range ();
+      rich_location richloc (line_table, range);
+      diagnostic_context dc;
+      memset (&dc, 0, sizeof (dc));
+      dc.printer = pp;
+      dc.show_caret = true;
+      dc.caret_chars[0] = '^';
+      diagnostic_set_caret_max_width (&dc, pp_line_cutoff (pp));
+      const char *saved_prefix = pp->prefix;
+      const char *source_prefix;
+      const char *begin_color = colorize_start (pp_show_color (pp), "note");
+      const char *end_color = colorize_stop (pp_show_color (pp));
+      source_prefix
+	= ACONCAT ((begin_color, new_prefix, "|:", end_color, NULL));
+      pp_set_prefix (pp, source_prefix);
+      pp_prefixing_rule (pp) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
+      dc.show_ruler_p = true;
+      diagnostic_show_locus (&dc, &richloc, DK_NOTE);
+      pp_prefixing_rule (pp) = DIAGNOSTICS_SHOW_PREFIX_NEVER;
+      pp_set_prefix (pp, saved_prefix);
+      /* diagnostic_show_locus expects to add a newline at the start
+	 and ends with a newline.  */
+    }
+  else
+    pp_newline (pp);
+
+  for (blt_node *child = m_first_child; child; child = child->m_next_sibling)
+    {
+      bool is_last_child = child == m_last_child;
+      child->dump (pp, new_prefix, this, is_last_child);
+    }
+}
+
+/* Get a human-readable name for this node, e.g. "translation-unit".  */
+
+const char *
+blt_node::get_name () const
+{
+  return blt_kind_to_name (m_kind);
+}
+
+/* Get the range of source locations covered by this blt_node.  */
+
+location_t
+blt_node::get_range () const
+{
+  return make_location (m_start, m_start, m_finish);
+}
+
+/* Get the first child of KIND, or NULL.  */
+
+blt_node *
+blt_node::get_first_child_of_kind (enum blt_kind kind) const
+{
+  for (blt_node *child = m_first_child; child; child = child->m_next_sibling)
+    if (kind == child->m_kind)
+      return child;
+  return NULL;
+}
+
+/* Populate OUT with all children of KIND.  */
+
+void
+blt_node::get_children_of_kind (auto_vec<blt_node *> &out,
+				enum blt_kind kind) const
+{
+  for (blt_node *child = m_first_child; child; child = child->m_next_sibling)
+    if (kind == child->m_kind)
+      out.safe_push (child);
+}
+
+/* Get the index of NEEDLE within this node if is a child, or -1 otherwise.  */
+
+int
+blt_node::get_index_of_child (blt_node *needle) const
+{
+  int idx = 0;
+  for (blt_node *child = m_first_child; child; child = child->m_next_sibling)
+    {
+      if (child == needle)
+	return idx;
+      idx++;
+    }
+  return -1;
+}
+
+/* Get the most recent ancestor of this node of KIND, or NULL if there
+   aren't any.  */
+
+blt_node *
+blt_node::get_ancestor_of_kind (enum blt_kind kind) const
+{
+  for (blt_node *iter = m_parent; iter; iter = iter->m_parent)
+    if (kind == iter->m_kind)
+      return iter;
+  return NULL;
+}
+
+/* Find the deepest descendant of this node (or node itself)
+   satisfying PRED, or NULL if there aren't any.  */
+
+blt_node *
+blt_node::find_descendant_satisfying (blt_predicate &pred)
+{
+  /* First, try to find within children, so that we get the deepest node
+     matching the criterion.  */
+  for (blt_node *child = m_first_child; child; child = child->m_next_sibling)
+    {
+      blt_node *within_child = child->find_descendant_satisfying (pred);
+      if (within_child)
+	return within_child;
+    }
+
+  /* Try within this node.  */
+  if (pred.satisfied_by_node_p (*this))
+    return this;
+
+  return NULL;
+}
+
+/* Subclass of blt_predicate for identifying nodes containing the
+   given source location.  */
+
+class contains_location_predicate : public blt_predicate
+{
+ public:
+  contains_location_predicate (const char *filename, int line, int character)
+  : m_filename (filename), m_line (line), m_character (character) {}
+
+  bool satisfied_by_node_p (const blt_node &node) FINAL OVERRIDE
+  {
+    return node.contains_location_p (m_filename, m_line, m_character);
+  }
+
+ private:
+  const char *m_filename;
+  int m_line;
+  int m_character;
+};
+
+/* Find the deepest descendant of this node (or the node itself) at
+   the given source location, or NULL if there aren't any.  */
+
+blt_node *
+blt_node::get_descendant_at_location (const char *filename, int line,
+				      int character)
+{
+  contains_location_predicate pred (filename, line, character);
+  return find_descendant_satisfying (pred);
+}
+
+/* Return true iff this node contains the given source location.  */
+
+bool
+blt_node::contains_location_p (const char *filename, int row, int column) const
+{
+  expanded_location el_start = expand_location (m_start);
+  expanded_location el_finish = expand_location (m_finish);
+
+  if (0 != strcmp (filename, el_start.file))
+      return false;
+  if (0 != strcmp (filename, el_finish.file))
+      return false;
+
+  /* FIXME: share this with diagnostic-show-locus.c:layout_range.  */
+
+  gcc_assert (el_start.line <= el_finish.line);
+  /* ...but the equivalent isn't true for the columns;
+     consider example B in the comment above.  */
+
+  if (row < el_start.line)
+    /* Points before the first line of the range are
+       outside it (corresponding to line 01 in example A
+       and lines 01 and 02 in example B above).  */
+    return false;
+
+  if (row == el_start.line)
+    /* On same line as start of range (corresponding
+       to line 02 in example A and line 03 in example B).  */
+    {
+      if (column < el_start.column)
+	/* Points on the starting line of the range, but
+	   before the column in which it begins.  */
+	return false;
+
+      if (row < el_finish.line)
+	/* This is a multiline range; the point
+	   is within it (corresponds to line 03 in example B
+	   from column 14 onwards) */
+	return true;
+      else
+	{
+	  /* This is a single-line range.  */
+	  gcc_assert (row == el_finish.line);
+	  return column <= el_finish.column;
+	}
+    }
+
+  /* The point is in a line beyond that containing the
+     start of the range: lines 03 onwards in example A,
+     and lines 04 onwards in example B.  */
+  gcc_assert (row > el_start.line);
+
+  if (row > el_finish.line)
+    /* The point is beyond the final line of the range
+       (lines 03 onwards in example A, and lines 06 onwards
+       in example B).  */
+    return false;
+
+  if (row < el_finish.line)
+    {
+      /* The point is in a line that's fully within a multiline
+	 range (e.g. line 04 in example B).  */
+      gcc_assert (el_start.line < el_finish.line);
+      return true;
+    }
+
+  gcc_assert (row ==  el_finish.line);
+
+  return column <= el_finish.column;
+}
+
+/* In a debug build, assert that basic invariants hold.  */
+
+void
+blt_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);
+    }
+}
+
+/* Given tree node T, get the assocated blt_node, if any, or NULL.  */
+
+blt_node *
+blt_get_node_for_tree (tree t)
+{
+  if (!t)
+    return NULL;
+  if (!tree_to_blt_map)
+    return NULL;
+  blt_node **slot = tree_to_blt_map->get (t);
+  if (!slot)
+    return NULL;
+  return *slot;
+}
+
+/* Given tree node T, set the assocated blt_node if it has not been
+   set already.
+
+   If it has been set, don't change it; multiple tree nodes can
+   reference an blt_node *, but an blt_node * references
+   at most one tree node (e.g. C++ template instantiations
+   can lead to multiple FUNCTION_DECL tree nodes from one blt_node).  */
+
+void
+blt_set_node_for_tree (tree t, blt_node *node)
+{
+  if (!t)
+    return;
+  if (!node)
+    return;
+
+  if (node->get_tree () == NULL)
+    node->set_tree (t);
+  else
+    {
+      /* Don't attempt to change; multiple tree nodes can
+	 reference an blt_node *, but an blt_node * references
+	 at most one tree node (e.g. template instantiations).  */
+      gcc_assert (tree_to_blt_map);
+      tree_to_blt_map->get_or_insert (t) = node;
+    }
+}
+
+/* The table of names for enum blt_kind.  */
+
+static const char * const blt_kind_names[] = {
+#define DEF_BLT_NODE(ENUM_NAME, PRETTY_NAME) PRETTY_NAME,
+#include "blt.def"
+#undef DEF_BLT_NODE
+};
+
+/* Get a human-readable name for this blt_kind, e.g. "translation-unit".  */
+
+static const char *
+blt_kind_to_name (enum blt_kind kind)
+{
+  return blt_kind_names[kind];
+}
+
+/* Dump NODE to stderr.  */
+
+DEBUG_FUNCTION void
+debug (blt_node *node)
+{
+  node->dump (stderr);
+}
+
+/* Global singleton.  */
+// FIXME: do we need this?  it's been useful when debugging.
+
+blt_node *the_blt_root_node;
+
+\f
+#if CHECKING_P
+
+namespace selftest {
+
+/* Selftests.  */
+
+/* Helper function for making a blt_node.  */
+
+static blt_node *
+make_blt_node (blt_node *parent, enum blt_kind kind,
+	       location_t start, location_t finish)
+{
+  blt_node *new_node = new blt_node (kind, start);
+  new_node->set_finish (finish);
+  if (parent)
+    parent->add_child (new_node);
+  return new_node;
+}
+
+/* Verify that blt_node basic operations work as expected.  */
+
+static void
+test_basic_ops (const line_table_case &case_)
+{
+  /* The primary goal of blt_node is location-tracking, so let's track
+     some locations.  */
+
+  /* Create a tempfile and write some text to it.
+     ...0000000001111111111222222222233333333334444444444.
+     ...1234567890123456789012345678901234567890123456789.  */
+  const char *content
+    = ("/* Some comment.  */\n" /* line 1.  */
+       "struct point {\n"       /* line 2.  */
+       " double x;\n"           /* line 3.  */
+       " double y;\n"           /* line 4.  */
+       " double z;\n"           /* line 5.  */
+       "};\n");                 /* line 6.  */
+  temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
+  line_table_test ltt (case_);
+
+  const line_map_ordinary *ord_map
+    = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
+					   tmp.get_filename (), 0));
+
+  linemap_line_start (line_table, 1, 100);
+
+#define LOC(ROW, COL) \
+  (linemap_position_for_line_and_column (line_table, ord_map, (ROW), (COL)))
+
+  const location_t final_line_end = LOC (6, 2);
+
+  /* Don't attempt to run the tests if column data might be unavailable.  */
+  if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
+    return;
+
+  blt_node *tu, *ext_decl, *decl_specs, *type_spec, *s_or_u_spec,
+    *s_contents,
+    *s_decl_x, /* *decl_specs_x, */ /* *decl_x, */
+    *s_decl_y, *decl_specs_y, *decl_y,
+    *s_decl_z, /* *decl_specs_z, */ *decl_z;
+
+  /* The block structure here shows the intended hierarchy.  */
+  {
+    tu = make_blt_node (NULL, BLT_TRANSLATION_UNIT, LOC (1, 1), LOC (6, 2));
+    {
+      ext_decl = make_blt_node (tu, BLT_EXTERNAL_DECLARATION,
+				LOC (2, 1), LOC (6, 2));
+      {
+	decl_specs = make_blt_node (ext_decl, BLT_DECLARATION_SPECIFIERS,
+				    LOC (2, 1), LOC (6, 1));
+	{
+	  type_spec = make_blt_node (decl_specs, BLT_TYPE_SPECIFIER,
+				     LOC (2, 1), LOC (6, 1));
+	  {
+	    s_or_u_spec
+	      = make_blt_node (type_spec, BLT_STRUCT_OR_UNION_SPECIFIER,
+			       LOC (2, 1), LOC (6, 1));
+	    {
+	      s_contents = make_blt_node (s_or_u_spec, BLT_STRUCT_CONTENTS,
+					  LOC (2, 14), LOC (6, 1));
+	      {
+		s_decl_x = make_blt_node (s_contents, BLT_STRUCT_DECLARATION,
+					  LOC (3, 2), LOC (3, 9));
+		{
+		  /*decl_specs_x = */
+		  make_blt_node (s_decl_x, BLT_DECLARATION_SPECIFIERS,
+				 LOC (3, 2), LOC (3, 7));
+		  /* decl_x = */ make_blt_node (s_decl_x, BLT_DECLARATOR,
+						LOC (3, 9), LOC (3, 9));
+		}
+	      }
+	      {
+		s_decl_y = make_blt_node (s_contents, BLT_STRUCT_DECLARATION,
+					  LOC (4, 2), LOC (4, 9));
+		{
+		  decl_specs_y
+		    = make_blt_node (s_decl_y, BLT_DECLARATION_SPECIFIERS,
+				     LOC (4, 2), LOC (4, 7));
+		  decl_y = make_blt_node (s_decl_y, BLT_DECLARATOR,
+					  LOC (4, 9), LOC (4, 9));
+		}
+	      }
+	      {
+		s_decl_z = make_blt_node (s_contents, BLT_STRUCT_DECLARATION,
+					  LOC (5, 2), LOC (5, 9));
+		{
+		  /* decl_specs_z = */
+		  make_blt_node (s_decl_z, BLT_DECLARATION_SPECIFIERS,
+				 LOC (5, 2), LOC (5, 7));
+		  decl_z = make_blt_node (s_decl_z, BLT_DECLARATOR,
+					  LOC (5, 9), LOC (5, 9));
+		}
+	      }
+	    }
+	  }
+	}
+      }
+    }
+  }
+
+  if (0)
+    tu->dump (stderr);
+
+  ASSERT_EQ (BLT_TRANSLATION_UNIT, tu->get_kind ());
+  ASSERT_EQ (BLT_STRUCT_DECLARATION, s_decl_z->get_kind ());
+
+  ASSERT_STREQ ("translation-unit", tu->get_name ());
+  ASSERT_STREQ ("struct-declaration", s_decl_z->get_name ());
+
+  ASSERT_EQ (NULL, tu->get_parent ());
+  ASSERT_EQ (s_contents, s_decl_z->get_parent ());
+
+  /* Location access.  */
+  ASSERT_EQ (LOC (2, 1), ext_decl->get_start ());
+  ASSERT_EQ (LOC (6, 2), ext_decl->get_finish ());
+  location_t range = ext_decl->get_range ();
+  ASSERT_EQ (LOC (2, 1), get_start (range));
+  ASSERT_EQ (LOC (6, 2), get_finish (range));
+
+  /* blt_node::get_tree.  */
+  // TODO
+
+  /* blt_node::get_first_child_of_kind.  */
+  ASSERT_EQ (ext_decl, tu->get_first_child_of_kind (BLT_EXTERNAL_DECLARATION));
+  ASSERT_EQ (NULL, tu->get_first_child_of_kind (BLT_TRANSLATION_UNIT));
+  ASSERT_EQ (decl_z,  s_decl_z->get_first_child_of_kind (BLT_DECLARATOR));
+
+  /* blt_node::get_children_of_kind.  */
+  auto_vec<blt_node *> fields;
+  s_contents->get_children_of_kind (fields, BLT_STRUCT_DECLARATION);
+  ASSERT_EQ (3, fields.length ());
+  ASSERT_EQ (s_decl_x, fields[0]);
+  ASSERT_EQ (s_decl_y, fields[1]);
+  ASSERT_EQ (s_decl_z, fields[2]);
+
+  /* blt_node::get_index_of_child.  */
+  ASSERT_EQ (0, s_contents->get_index_of_child (s_decl_x));
+  ASSERT_EQ (1, s_contents->get_index_of_child (s_decl_y));
+  ASSERT_EQ (2, s_contents->get_index_of_child (s_decl_z));
+  ASSERT_EQ (-1, s_contents->get_index_of_child (tu));
+
+  /* blt_node::get_ancestor_of_kind.  */
+  ASSERT_EQ (tu, s_decl_z->get_ancestor_of_kind (BLT_TRANSLATION_UNIT));
+  ASSERT_EQ (NULL, tu->get_ancestor_of_kind (BLT_TRANSLATION_UNIT));
+
+  /* blt_node::get_descendant_at_location.  */
+  ASSERT_EQ (decl_specs_y,
+	     tu->get_descendant_at_location (tmp.get_filename (), 4, 4));
+  ASSERT_EQ (decl_y,
+	     tu->get_descendant_at_location (tmp.get_filename (), 4, 9));
+  ASSERT_EQ (tu,
+	     tu->get_descendant_at_location (tmp.get_filename (), 1, 1));
+  ASSERT_EQ (NULL,
+	     tu->get_descendant_at_location (tmp.get_filename (), 7, 1));
+
+  /* blt_node::contains_location_p.  */
+  /* s_contents ought to range from LOC (2, 14) to LOC (6, 1).  */
+  ASSERT_FALSE (s_contents->contains_location_p (tmp.get_filename (), 1, 14));
+  ASSERT_FALSE (s_contents->contains_location_p (tmp.get_filename (), 2, 13));
+  ASSERT_TRUE (s_contents->contains_location_p (tmp.get_filename (), 2, 14));
+  ASSERT_TRUE (s_contents->contains_location_p (tmp.get_filename (), 3, 1));
+  ASSERT_FALSE (s_contents->contains_location_p ("not-the-filename", 3, 1));
+  ASSERT_TRUE (s_contents->contains_location_p (tmp.get_filename (), 6, 1));
+  ASSERT_FALSE (s_contents->contains_location_p (tmp.get_filename (), 6, 2));
+
+#undef LOC
+}
+
+/* Verify that we can wrap cpp tokens.  */
+
+static void
+test_cpp_tokens ()
+{
+  blt_node *plus_node = new blt_node (BLT_TOKEN_OP_PLUS, UNKNOWN_LOCATION);
+  ASSERT_STREQ ("+-token", plus_node->get_name ());
+
+  blt_node *name_node = new blt_node (BLT_TOKEN_TK_NAME, UNKNOWN_LOCATION);
+  ASSERT_STREQ ("NAME-token", name_node->get_name ());
+}
+
+/* Run all of the selftests within this file.  */
+
+void
+blt_c_tests ()
+{
+  for_each_line_table_case (test_basic_ops);
+
+  test_cpp_tokens ();
+}
+
+} // namespace selftest
+
+#endif /* #if CHECKING_P */
diff --git a/gcc/blt.def b/gcc/blt.def
new file mode 100644
index 0000000..ba248b7
--- /dev/null
+++ b/gcc/blt.def
@@ -0,0 +1,87 @@
+/* Bonus location trees.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+
+   This file is part of GCC.
+
+   GCC is free software; you can redistribute it and/or modify it
+   under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3, or (at your option)
+   any later version.
+
+   GCC is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+/* The first set of DEF_BLT_NODE corresponds to enum cpp_ttype.  */
+
+#ifndef TTYPE_TABLE
+#error TTYPE_TABLE not defined
+#endif
+
+#define OP(e, s) DEF_BLT_NODE (BLT_TOKEN_OP_##e, s "-token")
+#define TK(e, s) DEF_BLT_NODE (BLT_TOKEN_TK_##e, #e "-token")
+
+TTYPE_TABLE
+
+#undef OP
+#undef TK
+
+DEF_BLT_NODE (BLT_KEYWORD, "keyword")
+
+/* Additional nodes kinds, beyond enum cpp_ttype.
+   These are named after non-terminals within the C and C++ grammar.
+   They're just IDs; they don't need to mean anything for other languages.  */
+
+DEF_BLT_NODE (BLT_ASM_SPECIFICATION, "asm-specification")
+DEF_BLT_NODE (BLT_ASSIGNMENT_EXPRESSION, "assignment-expression")
+DEF_BLT_NODE (BLT_BLOCK_DECLARATION, "block-declaration")
+DEF_BLT_NODE (BLT_CLASS_HEAD, "class-head")
+DEF_BLT_NODE (BLT_CLASS_KEY, "class-key")
+DEF_BLT_NODE (BLT_CLASS_SPECIFIER, "class-specifier")
+DEF_BLT_NODE (BLT_CV_QUALIFIER, "cv-qualifier")
+DEF_BLT_NODE (BLT_CV_QUALIFIER_SEQ, "cv-qualifier-seq")
+DEF_BLT_NODE (BLT_DECLARATION, "declaration")
+DEF_BLT_NODE (BLT_DECLARATION_SEQ, "declaration-seq")
+DEF_BLT_NODE (BLT_DECLARATION_SPECIFIERS, "declaration-specifiers")
+DEF_BLT_NODE (BLT_DECLARATOR, "declarator")
+DEF_BLT_NODE (BLT_DECL_SPECIFIER, "decl-specifier")
+DEF_BLT_NODE (BLT_DECL_SPECIFIER_SEQ, "decl-specifier-seq")
+DEF_BLT_NODE (BLT_DIRECT_DECLARATOR, "direct-declarator")
+DEF_BLT_NODE (BLT_EXCEPTION_DECLARATION, "exception-declaration")
+DEF_BLT_NODE (BLT_EXPLICIT_INSTANTIATION, "explicit-instantiation")
+DEF_BLT_NODE (BLT_EXPLICIT_SPECIALIZATION, "explicit-specialization")
+DEF_BLT_NODE (BLT_EXPRESSION, "expression")
+DEF_BLT_NODE (BLT_EXPRESSION_LIST, "expression-list")
+DEF_BLT_NODE (BLT_EXTERNAL_DECLARATION, "external-declaration")
+DEF_BLT_NODE (BLT_FUNCTION_DEFINITION, "function-definition")
+DEF_BLT_NODE (BLT_FUNCTION_TRY_BLOCK, "function-try-block")
+DEF_BLT_NODE (BLT_HANDLER, "handler")
+DEF_BLT_NODE (BLT_HANDLER_SEQ, "handler-seq")
+DEF_BLT_NODE (BLT_IDENTIFIER, "identifier")
+DEF_BLT_NODE (BLT_ID_EXPRESSION, "id-expression")
+DEF_BLT_NODE (BLT_MEMBER_DECLARATION, "member-declaration")
+DEF_BLT_NODE (BLT_NONEMPTY_EXPR_LIST, "nonempty-expr-list")
+DEF_BLT_NODE (BLT_PARAMETER_DECLARATION, "parameter-declaration")
+DEF_BLT_NODE (BLT_PARAMETER_DECLARATION_CLAUSE, "parameter-declaration-clause")
+DEF_BLT_NODE (BLT_PARAMETER_DECLARATION_LIST, "parameter-declaration-list")
+DEF_BLT_NODE (BLT_PARAMETER_LIST, "parameter-list")
+DEF_BLT_NODE (BLT_POSTFIX_EXPRESSION, "postfix-expression")
+DEF_BLT_NODE (BLT_PRIMARY_EXPRESSION, "primary-expression")
+DEF_BLT_NODE (BLT_SIMPLE_DECLARATION, "simple-declaration")
+DEF_BLT_NODE (BLT_STRUCT_CONTENTS, "struct-contents")
+DEF_BLT_NODE (BLT_STRUCT_DECLARATION, "struct-declaration")
+DEF_BLT_NODE (BLT_STRUCT_OR_UNION_SPECIFIER, "struct-or-union-specifier")
+DEF_BLT_NODE (BLT_TEMPLATE_DECLARATION, "template-declaration")
+DEF_BLT_NODE (BLT_TEMPLATE_PARAMETER_LIST, "template-parameter-list")
+DEF_BLT_NODE (BLT_THROW_EXPRESSION, "throw-expression")
+DEF_BLT_NODE (BLT_TRANSLATION_UNIT, "translation-unit")
+DEF_BLT_NODE (BLT_TRY_BLOCK, "try-block")
+DEF_BLT_NODE (BLT_TYPE_ID_LIST, "type-id-list")
+DEF_BLT_NODE (BLT_TYPE_SPECIFIER, "type-specifier")
+DEF_BLT_NODE (BLT_UNARY_EXPRESSION, "unary-expression")
+DEF_BLT_NODE (BLT_UNQUALIFIED_ID, "unqualified-id")
diff --git a/gcc/blt.h b/gcc/blt.h
new file mode 100644
index 0000000..107f169
--- /dev/null
+++ b/gcc/blt.h
@@ -0,0 +1,147 @@
+/* Bonus location tree information.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+
+   This file is part of GCC.
+
+   GCC is free software; you can redistribute it and/or modify it
+   under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3, or (at your option)
+   any later version.
+
+   GCC is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_BLT_H
+#define GCC_BLT_H
+
+/* Sometimes we need additional location information.
+
+   The "tree" type represents a node within an abstract syntax tree,
+   but this is sometimes too abstract - sometimes we want the locations
+   of the clauses and tokens that are abstracted away by the frontends.
+
+   In theory we could generate the full parse tree ("concrete syntax tree"),
+   showing every production followed to parse the input, but it is likely
+   to be unwieldy: large and difficult to navigate.
+
+   So this file implements a middle-ground: an additional tree of parse
+   information, more concrete than "tree", but not the full parse tree.
+
+   There is a partial mapping between "tree" and blt_node: a blt_node
+   can reference a tree, and a tree can reference a blt_node (though
+   typically the mapping is very sparse; most don't).  This allows us
+   to go from e.g. a function_decl in the tree world and navigate
+   pertinent parts of the syntax that was used to declare it.
+
+   Working title = "BLT" as a backronym for "bonus location tree" (but
+   actually a reference to a sandwich) - doesn't clash with anything else
+   in the source tree ("Parse Tree" would be "PT" which clashes with
+   "points-to", and "Concrete Syntax Tree" would be "CST" which clashes
+   with our abbreviation for "constant").  */
+
+#include "cpplib.h"
+
+/* An ID for a kind of node within the tree.  */
+
+enum blt_kind
+{
+#define DEF_BLT_NODE(ENUM_NAME, PRETTY_NAME) ENUM_NAME,
+#include "blt.def"
+#undef DEF_BLT_NODE
+};
+
+class blt_node;
+
+/* Would use a lambda.  */
+
+class blt_predicate
+{
+ public:
+  virtual bool satisfied_by_node_p (const blt_node &node) = 0;
+};
+
+/* A syntax node: either a token, a non-terminal, or a simplified set of
+   non-terminals.  */
+
+class blt_node
+{
+public:
+  blt_node (enum blt_kind kind, location_t start);
+
+  /* Structural manipulation.  */
+  void add_child (blt_node *child);
+  void replace_child (blt_node *old, blt_node *new_);
+  void make_orphan ();
+
+  /* Modification.  */
+
+  void set_finish (location_t loc) { m_finish = ::get_finish (loc); }
+  void set_tree (tree t);
+  void set_kind (enum blt_kind kind) { m_kind = kind; }
+
+  /* Dumping.  */
+  void dump (FILE *) const;
+  void dump (pretty_printer *pp, const char *prefix,
+	     const blt_node *parent, bool is_last_child) const;
+
+  /* Accessors.  */
+  enum blt_kind get_kind () const { return m_kind; }
+  const char *get_name () const;
+  blt_node *get_parent () const { return m_parent; }
+  location_t get_start () const { return m_start; }
+  location_t get_finish () const { return m_finish; }
+  location_t get_range () const;
+  tree get_tree () const { return m_tree; }
+
+  blt_node *get_first_child_of_kind (enum blt_kind kind) const;
+  void get_children_of_kind (auto_vec<blt_node *> &out,
+			     enum blt_kind kind) const;
+
+  int get_index_of_child (blt_node *needle) const;
+
+  blt_node *get_ancestor_of_kind (enum blt_kind kind) const;
+
+  blt_node *find_descendant_satisfying (blt_predicate &pred);
+
+  blt_node *get_descendant_at_location (const char *filename, int line,
+					int character);
+
+  bool contains_location_p (const char *filename, int line,
+			    int character) const;
+
+private:
+  void assert_invariants () const;
+
+private:
+  enum blt_kind m_kind;
+  blt_node *m_parent;
+  blt_node *m_first_child;
+  blt_node *m_last_child;
+  blt_node *m_prev_sibling;
+  blt_node *m_next_sibling;
+#if 1
+  location_t m_start;
+  location_t m_finish;
+#else
+  // Tokens are currently released after lexing...
+  cp_token *m_first_token;
+  cp_token *m_last_token;
+#endif
+  tree m_tree;
+};
+
+extern blt_node *blt_get_node_for_tree (tree);
+extern void blt_set_node_for_tree (tree, blt_node *);
+
+extern void DEBUG_FUNCTION debug (blt_node *);
+
+/* FIXME.  */
+extern blt_node *the_blt_root_node;
+
+#endif  /* GCC_BLT_H  */
diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index e0ad3ab..6f15b5d 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -1209,6 +1209,10 @@ fasm
 C ObjC C++ ObjC++ Var(flag_no_asm, 0)
 Recognize the \"asm\" keyword.
 
+fblt
+C ObjC C++ ObjC++ Var(flag_blt)
+Capture additional location information.  FIXME: name.
+
 ; Define extra predefined macros for use in libgcc.
 fbuilding-libgcc
 C ObjC C++ ObjC++ Undocumented Var(flag_building_libgcc)
@@ -1382,6 +1386,10 @@ fdump-ada-spec-slim
 C ObjC C++ ObjC++ RejectNegative Var(flag_dump_ada_spec_slim)
 Write all declarations as Ada code for the given file only.
 
+fdump-blt
+C ObjC C++ ObjC++ Var(flag_dump_blt)
+Dump the extra location information from -fblt.  FIXME: name.
+
 felide-constructors
 C++ ObjC++ Var(flag_elide_constructors) Init(1)
 
diff --git a/gcc/selftest-run-tests.c b/gcc/selftest-run-tests.c
index 30e476d..01e3504 100644
--- a/gcc/selftest-run-tests.c
+++ b/gcc/selftest-run-tests.c
@@ -66,6 +66,7 @@ selftest::run_tests ()
   sreal_c_tests ();
   fibonacci_heap_c_tests ();
   typed_splay_tree_c_tests ();
+  blt_c_tests ();
 
   /* Mid-level data structures.  */
   input_c_tests ();
diff --git a/gcc/selftest.h b/gcc/selftest.h
index 0572fef..583bdb2 100644
--- a/gcc/selftest.h
+++ b/gcc/selftest.h
@@ -171,6 +171,7 @@ extern const char *path_to_selftest_files;
 /* Declarations for specific families of tests (by source file), in
    alphabetical order.  */
 extern void bitmap_c_tests ();
+extern void blt_c_tests ();
 extern void diagnostic_c_tests ();
 extern void diagnostic_show_locus_c_tests ();
 extern void edit_context_c_tests ();
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH 13/17] Add http-server.h and http-server.c
  2017-07-24 19:31 [PATCH 00/17] RFC: New source-location representation; Language Server Protocol David Malcolm
                   ` (7 preceding siblings ...)
  2017-07-24 19:31 ` [PATCH 17/17] Language Server Protocol: work-in-progess on testsuite David Malcolm
@ 2017-07-24 19:31 ` David Malcolm
  2017-07-24 19:38 ` [PATCH 07/17] C++: use BLT to highlight parameter of callee decl for mismatching types David Malcolm
                   ` (10 subsequent siblings)
  19 siblings, 0 replies; 38+ messages in thread
From: David Malcolm @ 2017-07-24 19:31 UTC (permalink / raw)
  To: gcc-patches; +Cc: David Malcolm

This patch implements an abstract class of HTTP server, as a subclass
of the server class implemented in the previous patch.

gcc/ChangeLog:
	* Makefile.in (OBJS): Add http-server.o.
	* http-server.c: New file.
	* http-server.h: New file.
	* selftest-run-tests.c (selftest::run_tests): Call
	selftest::http_server_c_tests.
	* selftest.h (selftest::http_server_c_tests): New decl.
---
 gcc/Makefile.in          |   1 +
 gcc/http-server.c        | 358 +++++++++++++++++++++++++++++++++++++++++++++++
 gcc/http-server.h        | 101 +++++++++++++
 gcc/selftest-run-tests.c |   1 +
 gcc/selftest.h           |   1 +
 5 files changed, 462 insertions(+)
 create mode 100644 gcc/http-server.c
 create mode 100644 gcc/http-server.h

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 4e60bc0..0c361f1 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1332,6 +1332,7 @@ OBJS = \
 	hsa-regalloc.o \
 	hsa-brig.o \
 	hsa-dump.o \
+	http-server.o \
 	hw-doloop.o \
 	hwint.o \
 	ifcvt.o \
diff --git a/gcc/http-server.c b/gcc/http-server.c
new file mode 100644
index 0000000..88c1c17
--- /dev/null
+++ b/gcc/http-server.c
@@ -0,0 +1,358 @@
+/* HTTP server implementation.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "http-server.h"
+#include "pretty-print.h"
+#include "selftest.h"
+#include "diagnostic.h"
+
+/* class http::message.  */
+
+/* Set the content of this message.  */
+
+void
+http::message::set_content (size_t length, const char *content)
+{
+  m_length = length;
+  m_buffer = xstrndup (content, length);
+}
+
+/* class http::request : public http::message.  */
+
+/* http::request's constructor.  */
+
+http::request::request ()
+: message (), m_parsing_body (false), m_pending_data (),
+  m_content_length (-1), m_header_map ()
+{
+}
+
+/* http::request's destructor.  */
+
+http::request::~request()
+{
+  for (header_map_t::iterator it = m_header_map.begin ();
+       it != m_header_map.end (); ++it)
+    {
+      free (const_cast <char *>((*it).first));
+      free (const_cast <char *>((*it).second));
+    }
+}
+
+/* Access the given header within this request, or NULL if not present.  */
+
+const char *
+http::request::get_header (const char *header) const
+{
+  char **slot = const_cast <request*> (this)->m_header_map.get (header);
+  if (slot)
+    return *slot;
+  return NULL;
+}
+
+/* Consume up to LENGTH bytes from BUF.
+   Return the number of bytes consumed.  */
+
+size_t
+http::request::parse_buffer (size_t length, const char *buf)
+{
+  size_t idx;
+  for (idx = 0; idx < length; idx++)
+    {
+      if (consume_octet (buf[idx]))
+	return idx + 1;
+    }
+  return idx;
+}
+
+/* Parse CH.  Return true if the request has finished parsing, or false
+   if more data is expected.  */
+
+bool
+http::request::consume_octet (char ch)
+{
+  if (m_parsing_body)
+    return consume_body_octet (ch);
+  else
+    return consume_header_octet (ch);
+}
+
+/* Parse CH within the headers.  Return true if the request has finished
+   parsing, or false if more data is expected.  */
+
+bool
+http::request::consume_header_octet (char ch)
+{
+  /* We're parsing the headers.  */
+  m_pending_data.safe_push (ch);
+  /* Detect "\r\n".  */
+  size_t len = m_pending_data.length ();
+  if (len >= 2 && m_pending_data[len - 2] == '\r' && ch == '\n')
+    {
+      /* Is this a blank line?  If so, then we're done with headers.  */
+      if (len == 2)
+	{
+	  m_pending_data.truncate (0);
+	  const char *content_length_str = get_header ("Content-Length");
+	  if (content_length_str)
+	    {
+	      m_content_length = atoi (content_length_str);
+	      // FIXME: error-handling for non-int values
+	      /* We're not yet finished; we must read the body.  */
+	      m_parsing_body = true;
+	      return false;
+	    }
+	  else
+	    /* If there was no Content-Length, we have nothing
+	       more to read.  */
+	    return true;
+	}
+      else
+	{
+	  /* Otherwise we have a header (or the initial verb line).  */
+	  parse_header (m_pending_data.length () - 2, &m_pending_data[0]);
+	  m_pending_data.truncate (0);
+	  /* We're not yet finished.  */
+	  return false;
+	}
+    }
+
+  /* Not yet finished.  */
+  return false;
+}
+
+/* Parse CH within the body.  Return true if the request has finished
+   parsing, or false if more data is expected.  */
+
+bool
+http::request::consume_body_octet (char ch)
+{
+  /* Accumulate data until we've seen Content-Length octets.  */
+  gcc_assert (m_content_length > 0);
+  m_pending_data.safe_push (ch);
+  if (m_pending_data.length () == m_content_length)
+    {
+      set_content (m_content_length, &m_pending_data[0]);
+      m_pending_data.truncate (0);
+      /* We've finished parsing this request.  */
+      return true;
+    }
+  else
+    /* We're not yet finished.  */
+    return false;
+}
+
+/* FIXME.  */
+
+void
+http::request::parse_header (size_t length, const char *buf)
+{
+  /* header-field   = field-name ":" OWS field-value OWS  */
+  for (size_t colon_idx = 0; colon_idx + 1 < length; colon_idx++)
+    // FIXME: whitespace after colon is optional
+    // FIXME: optional trailing whitespace after header value
+    if (buf[colon_idx] == ':' && buf[colon_idx + 1] == ' ')
+      {
+	char *key = xstrndup (buf, colon_idx);
+	char *value = xstrndup (buf + colon_idx + 2,
+				length - (colon_idx + 2));
+	m_header_map.put (key, value);
+	return;
+      }
+  // FIXME: error-handling
+}
+
+/* class http::response : public http::message.  */
+
+/* Generate a string form of this response.
+   The caller is responsible for freeing it.  */
+
+char *
+http::response::to_str () const
+{
+  pretty_printer pp;
+  pp_string (&pp, "HTTP/1.1 200 OK\r\n");
+  pp_printf (&pp, "Content-Length: %i\r\n", (int)get_content_length ());
+  pp_string (&pp, "\r\n");
+  if (get_content ())
+    pp_string (&pp, get_content ());
+  return xstrdup (pp_formatted_text (&pp));
+}
+
+/* Implementation of ::server::on_read for http::server.
+   Read up to LENGTH bytes from BUF, and potentially call
+   on_http_request for any requests seen.
+   Write any responses back to FD.  */
+
+void
+http::server::on_read (file_descriptor fd, size_t length, const char *buf)
+{
+  if (m_verbose)
+    inform (UNKNOWN_LOCATION, "received http request: %qs",
+	    buf); // FIXME respect length
+
+  /* FIXME: this assumes we have a full request i.e. two "\r\n\r\n"
+     If we don't we should read more until we do.  */
+  size_t req_start = 0;
+  while (req_start < length)
+    {
+      request req;
+      req_start += req.parse_buffer (length, buf);
+      http::response resp;
+      on_http_request (req, resp);
+      char *resp_str = resp.to_str ();
+      if (1)
+	inform (UNKNOWN_LOCATION, "sending http response: %qs",
+		resp_str);
+      write (fd.m_fd, resp_str, strlen (resp_str));
+      free (resp_str);
+    }
+}
+
+#if CHECKING_P
+
+namespace selftest {
+
+/* Selftests.  */
+
+/* Verify that we can parse an HTTP request.  */
+
+static void
+test_parse_request ()
+{
+  const char *in = ("POST /jsonrpc HTTP/1.1\r\n"
+		    "Host: localhost:4000\r\n"
+		    "Content-Length: 12\r\n"
+		    "content-type: application/json\r\n"
+		    "Accept-Encoding: gzip, deflate, compress\r\n"
+		    "Accept: */*\r\n"
+		    "User-Agent: test-user-agent\r\n"
+		    "\r\n"
+		    "test-content");
+  http::request r;
+  size_t consumed = r.parse_buffer (strlen (in), in);
+  ASSERT_EQ (consumed, strlen (in));
+  ASSERT_STREQ ("test-user-agent", r.get_header ("User-Agent"));
+  ASSERT_STREQ ("12", r.get_header ("Content-Length"));
+  ASSERT_EQ (NULL, r.get_header ("Not-A-Header"));
+  ASSERT_EQ (12, r.get_content_length ());
+  ASSERT_TRUE (0 == strncmp ("test-content", r.get_content (), 12));
+}
+
+/* Verify that we can split up the parsing of a request at arbitrary
+   places.  */
+
+static void
+test_parse_split_request ()
+{
+  const char *in = ("POST /jsonrpc HTTP/1.1\r\n"
+		    "Host: localhost:4000\r\n"
+		    "Content-Length: 12\r\n"
+		    "content-type: application/json\r\n"
+		    "Accept-Encoding: gzip, deflate, compress\r\n"
+		    "Accept: */*\r\n"
+		    "User-Agent: test-user-agent\r\n"
+		    "\r\n"
+		    "test-content");
+  for (size_t split = 0; split < strlen (in); split++)
+    {
+      http::request r;
+
+      size_t consumed_1 = r.parse_buffer (split, in);
+      ASSERT_EQ (consumed_1, split);
+
+      size_t consumed_2 = r.parse_buffer (strlen (in) - split, in + split);
+      ASSERT_EQ (consumed_2, strlen (in) - split);
+
+      ASSERT_STREQ ("test-user-agent", r.get_header ("User-Agent"));
+      ASSERT_STREQ ("12", r.get_header ("Content-Length"));
+      ASSERT_EQ (NULL, r.get_header ("Not-A-Header"));
+      ASSERT_EQ (12, r.get_content_length ());
+      ASSERT_TRUE (0 == strncmp ("test-content", r.get_content (), 12));
+    }
+}
+
+/* Verify that we can parse multiple requests out of one buffer,
+   honoring the Content-Length headers.  */
+
+static void
+test_parse_multiple_requests ()
+{
+  const char *in = ("POST /test HTTP/1.1\r\n"
+		    "Content-Length: 25\r\n"
+		    "\r\n"
+		    "This is the first request"
+		    "POST /test HTTP/1.1\r\n"
+		    "Content-Length: 26\r\n"
+		    "\r\n"
+		    "This is the second request");
+  http::request r1;
+  size_t consumed_1 = r1.parse_buffer (strlen (in), in);
+  ASSERT_EQ (68, consumed_1);
+  ASSERT_STREQ ("25", r1.get_header ("Content-Length"));
+  ASSERT_EQ (25, r1.get_content_length ());
+  ASSERT_TRUE (0 == strncmp ("This is the first request",
+			     r1.get_content (), 25));
+
+  http::request r2;
+  size_t consumed_2 = r2.parse_buffer (strlen (in) - consumed_1,
+				       in + consumed_1);
+  ASSERT_EQ (69, consumed_2);
+  ASSERT_STREQ ("26", r2.get_header ("Content-Length"));
+  ASSERT_EQ (26, r2.get_content_length ());
+  ASSERT_TRUE (0 == strncmp ("This is the second request",
+			     r2.get_content (), 26));
+
+  ASSERT_EQ (strlen (in), consumed_1 + consumed_2);
+}
+
+/* Verify http::response::to_str.  */
+
+static void
+test_emit_response ()
+{
+  http::response r;
+  const char *msg = "hello world";
+  r.set_content (strlen (msg), msg);
+
+  char *str = r.to_str ();
+  ASSERT_STREQ ("HTTP/1.1 200 OK\r\n"
+		"Content-Length: 11\r\n"
+		"\r\n"
+		"hello world", str);
+  free (str);
+}
+
+/* Run all of the selftests within this file.  */
+
+void
+http_server_c_tests ()
+{
+  test_parse_request ();
+  test_parse_split_request ();
+  test_parse_multiple_requests ();
+  test_emit_response ();
+}
+
+} // namespace selftest
+
+#endif /* #if CHECKING_P */
diff --git a/gcc/http-server.h b/gcc/http-server.h
new file mode 100644
index 0000000..25d542d
--- /dev/null
+++ b/gcc/http-server.h
@@ -0,0 +1,101 @@
+/* HTTP server implementation.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_HTTP_SERVER_H
+#define GCC_HTTP_SERVER_H
+
+#include "server.h"
+
+namespace http {
+
+/* http::message is a base class for encapsulating a collection of
+   octets received or to be sent.  */
+
+class message
+{
+ public:
+  message () : m_length (0), m_buffer (NULL) {}
+  ~message () { free (m_buffer); }
+
+  size_t get_content_length () const { return m_length; }
+  const char *get_content () const { return m_buffer; }
+
+  void set_content (size_t length, const char *content);
+
+ private:
+  size_t m_length;
+  char *m_buffer;
+};
+
+/* http::request encapsulates an HTTP request.  */
+
+class request : public message
+{
+ public:
+  request ();
+  ~request();
+
+  const char *get_header (const char *header) const;
+  size_t parse_buffer (size_t length, const char *buf);
+
+ private:
+  bool consume_octet (char ch);
+  bool consume_header_octet (char ch);
+  bool consume_body_octet (char ch);
+  void parse_header (size_t sz, const char *buf);
+
+  bool m_parsing_body;
+  auto_vec<char> m_pending_data;
+  size_t m_content_length;
+
+  typedef hash_map <char *, char *,
+    simple_hashmap_traits<nofree_string_hash, char *> > header_map_t;
+  header_map_t m_header_map;
+};
+
+/* http::response encapsulates an HTTP response.  */
+
+class response : public message
+{
+ public:
+  char *to_str () const;
+};
+
+/* Subclass of ::server than expects an HTTP-like protocol,
+   with header lines ended by '\r\n', then a '\r\n' line, then
+   the content.  */
+
+class server : public ::server
+{
+ public:
+  server (bool verbose) : m_verbose (verbose) {}
+
+  void on_read (file_descriptor fd, size_t length,
+		const char *buf) OVERRIDE FINAL;
+
+  virtual void on_http_request (const http::request &request,
+				http::response &response) = 0;
+
+ private:
+  bool m_verbose;
+};
+
+} // namespace http
+
+#endif  /* GCC_HTTP_SERVER_H  */
diff --git a/gcc/selftest-run-tests.c b/gcc/selftest-run-tests.c
index a8668f4..7e75680 100644
--- a/gcc/selftest-run-tests.c
+++ b/gcc/selftest-run-tests.c
@@ -68,6 +68,7 @@ selftest::run_tests ()
   typed_splay_tree_c_tests ();
   blt_c_tests ();
   json_c_tests ();
+  http_server_c_tests ();
 
   /* Mid-level data structures.  */
   input_c_tests ();
diff --git a/gcc/selftest.h b/gcc/selftest.h
index 281658b..ad4e957 100644
--- a/gcc/selftest.h
+++ b/gcc/selftest.h
@@ -183,6 +183,7 @@ extern void gimple_c_tests ();
 extern void ggc_tests_c_tests ();
 extern void hash_map_tests_c_tests ();
 extern void hash_set_tests_c_tests ();
+extern void http_server_c_tests ();
 extern void input_c_tests ();
 extern void json_c_tests ();
 extern void pretty_print_c_tests ();
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH 00/17] RFC: New source-location representation; Language Server Protocol
@ 2017-07-24 19:31 David Malcolm
  2017-07-24 19:31 ` [PATCH 03/17] Core of BLT implementation David Malcolm
                   ` (19 more replies)
  0 siblings, 20 replies; 38+ messages in thread
From: David Malcolm @ 2017-07-24 19:31 UTC (permalink / raw)
  To: gcc-patches; +Cc: David Malcolm

We currently capture some source location information in the
frontends, but there are many kinds of source entity for which we *don't*
retain the location information after the initial parse.

For example, in the C/C++ frontends:

* we don't capture the locations of the individual parameters
  within e.g. an extern function declaration, so we can't underline
  the pertinent param when there's a mismatching type in a call to
  that function decl.

* we don't capture the locations of attributes of a function,
  so we can't underline these if they're wrong (e.g. a "noreturn" on a
  function that does in fact return).

* we don't retain the locations of things like close parens and
  semicolons for after parsing, so we can't offer fix-it hints for
  adding new attributes, or, say the C++11 "override" feature.

* we can't at present implement many kinds of useful "cousins" of a
  compiler on top of the GCC codebase (e.g. code refactoring tools,
  code reformatting tools, IDE support daemons, etc), since much of the
  useful location information is discarded at parse time.

This patch kit implements:

(a) a new, optional, representation of this location information,
    enabled by a command-line flag

(b) improvements to various diagnostics to use this location information
    if it's present, falling back to the status-quo (less accurate)
    source locations otherwise

(b) a gcc-based implementation of Microsoft's Language Server Protocol,
      https://github.com/Microsoft/language-server-protocol
    allowing IDEs to connect to a gcc-based LSP server, making RPC
    calls to query it for things like "where is this struct declared?".
    This last part is very much just a proof-of-concept.


================================
(a) The new location information
================================

Our existing "tree" type represents a node within an abstract syntax tree,
but this is sometimes too abstract - sometimes we want the locations
of the clauses and tokens that were abstracted away by the frontends.

In theory we could generate the full parse tree ("concrete syntax tree"),
showing every production followed to parse the input, but it is likely
to be unwieldy: large and difficult to navigate.

(aside: I found myself re-reading the Dragon book to refresh my mind
on exactly what an AST vs a CST is; I also found this blog post to be
very useful:
  http://eli.thegreenplace.net/2009/02/16/abstract-vs-concrete-syntax-trees )

So the patch kit implements a middle-ground: an additional tree of parse
information, much more concrete than our "tree" type, but not quite the
full parse tree.

My working title for this system is "BLT" (and hence "class blt_node").
I could claim that this is a acronym for "bonus location tree" (but
it's actually a reference to a sandwich) - it needs a name, and that
name needs to not clash with anything else in the source tree.
"Parse Tree" would be "PT" which clashes with "points-to", and
"Concrete Syntax Tree" would be "CST" which clashes with our abbreviation
for "constant".  ("BLT" popped into my mind somewhere between "AST"
and "CST"; ideas for better names welcome).

blt_nodes form a tree-like structure; a blt_node has a "kind",
identifying the terminal or nonterminal it corresponds to
(e.g. BLT_TRANSLATION_UNIT or BLT_DECLARATION_SPECIFIERS).
This is just an enum, but allows for language-specific traversals,
without introducing significant language-specific features in
the shared "gcc" dir (it's just an enum of IDs).

There is a partial mapping between "tree" and blt_node: a blt_node
can reference a tree, and a tree can reference a blt_node, though
typically the mapping is very sparse; most don't.  This allows us
to go from e.g. a function_decl in the "tree" world and navigate to
pertinent parts of the syntax that was used to declare it.

All of this is enabled by a new "-fblt" command-line option; in the
absense of -fblt, almost all of it becomes close to no-ops, and the
relevant diagnostics fall back to using less accurate location
information.

So it's a kind of optional, "on-the-side" record of how we parsed
the source, with a sparse relationship to our tree type.

The patch kit implements it for the C and C++ frontends.

An example of a BLT dump for a C file can be seen here:
  https://dmalcolm.fedorapeople.org/gcc/2017-07-24/fdump-blt.html
It shows the tree structure using indentation (and colorization);
source locations are printed, and, for each node where the
location is different from the parent, the pertinent source range
is printed and underlined inline.
(BTW, does the colorization of dumps look useful for other
dump formats?  similarly for the ASCII art for printing hierarchies)


=====================
(b) Examples of usage
=====================

Patches 6-10 in the kit update various diagnostics to use
the improved location information where available:

* C and C++: highlighting the pertinent parameter of a function
  decl when there's a mismatched type in a call

* C and C++: highlighting the return type in the function defn
  when compaining about mismatch in the body (e.g. highlighting
  the "void" when we see a "return EXPR;" within a void function).

* C++: add a fix-it hint to -Wsuggest-override

I have plenty of ideas for other uses of this infrastructure
(but which aren't implemented yet), e.g.:

* C++: highlight the "const" token (or suggest a fix-it hint)
  when you have a missing "const" on the *definition* of a member
  function that was declared as "const" (I make this mistake
  all the time).

* C++: add a fix-it hint to -Wsuggest-final-methods

* highlight bogus attributes

* add fix-it hints suggesting missing attributes

...etc, plus those "cousins of a compiler" ideas mentioned above.

Any other ideas?


============================
(c) Language Server Protocol
============================

The later parts of the patch kit implement a proof-of-concept
LSP server, making use of the extended location information,
exposing it to IDEs.

LSP is an RPC protocol layered on top of JSON-RPC (and hence JSON
and HTTP):
  https://github.com/Microsoft/language-server-protocol
so the patch kit implements a set of classes to support
this (including a barebones HTTP server running inside cc1), and
a toy IDE written in PyGTK to test it.


=======
Caveats
=======

* There are plenty of FIXMEs and TODOs in the patch kit.

* I've entirely ignored tentative parsing in the C++ frontend for now.

* I haven't attempted to optimize it at all yet (so no performance
  measurements yet).

* How much of the syntax tree ought to be captured?  I've focussed on the
  stuff outside of function bodies, since we don't currently capture that
  well, but to do "proper" IDE support we'd want to capture things more
  deeply.  (I experimented with using it to fix some of our missing
  location information for things like uses of constants and variables
  as arguments at callsites, but it quickly turned into a much more
  invasive patch).

* The LSP implementation is a just a proof-of-concept, to further
  motivate capturing the extra data.  Turning it into a "proper" LSP
  server implementation would be a *lot* more work, and I'm unlikely to
  actually do that (but maybe someone on the list wants to take this on?)

I've successfully bootstrapped&regrtested the combination of the patches
on x86_64-pc-linux-gnu; takes -fself-test from 39458 passes to 41574;
adds 30 PASS results to gcc.sum; adds 182 PASS results to g++.sum.

Thoughts?
Dave

David Malcolm (17):
  Add param-type-mismatch.c/C testcases as a baseline
  diagnostics: support prefixes within diagnostic_show_locus
  Core of BLT implementation
  C frontend: capture BLT information
  C++ frontend: capture BLT information
  C: use BLT to highlight parameter of callee decl for mismatching types
  C++: use BLT to highlight parameter of callee decl for mismatching
    types
  C: highlight return types when complaining about mismatches
  C++: highlight return types when complaining about mismatches
  C++: provide fix-it hints in -Wsuggest-override
  Add JSON implementation
  Add server.h and server.c
  Add http-server.h and http-server.c
  Add implementation of JSON-RPC
  Language Server Protocol: add lsp::server abstract base class
  Language Server Protocol: proof-of-concept GCC implementation
  Language Server Protocol: work-in-progess on testsuite

 gcc/Makefile.in                                    |    7 +
 gcc/blt.c                                          |  768 ++++++++
 gcc/blt.def                                        |   87 +
 gcc/blt.h                                          |  147 ++
 gcc/c-family/c-opts.c                              |    2 +-
 gcc/c-family/c.opt                                 |    8 +
 gcc/c/c-decl.c                                     |   13 +-
 gcc/c/c-parser.c                                   |  241 ++-
 gcc/c/c-tree.h                                     |    6 +-
 gcc/c/c-typeck.c                                   |  120 +-
 gcc/common.opt                                     |    4 +
 gcc/cp/call.c                                      |   79 +-
 gcc/cp/class.c                                     |   23 +-
 gcc/cp/cp-tree.h                                   |    7 +
 gcc/cp/decl.c                                      |   32 +-
 gcc/cp/parser.c                                    |  369 +++-
 gcc/cp/parser.h                                    |    7 +
 gcc/cp/pt.c                                        |    8 +
 gcc/cp/typeck.c                                    |   70 +-
 gcc/diagnostic-show-locus.c                        |   94 +-
 gcc/diagnostic.c                                   |    5 +-
 gcc/http-server.c                                  |  358 ++++
 gcc/http-server.h                                  |  101 ++
 gcc/json-rpc.c                                     |  486 +++++
 gcc/json-rpc.h                                     |   94 +
 gcc/json.c                                         | 1914 ++++++++++++++++++++
 gcc/json.h                                         |  214 +++
 gcc/lsp-main.c                                     |  168 ++
 gcc/lsp-main.h                                     |   25 +
 gcc/lsp.c                                          |  291 +++
 gcc/lsp.h                                          |  210 +++
 gcc/selftest-run-tests.c                           |    5 +
 gcc/selftest.h                                     |    5 +
 gcc/server.c                                       |  152 ++
 gcc/server.h                                       |   46 +
 gcc/testsuite/g++.dg/bad-return-type.C             |  135 ++
 .../g++.dg/diagnostic/param-type-mismatch.C        |  159 ++
 gcc/testsuite/g++.dg/warn/Wsuggest-override.C      |   12 +-
 gcc/testsuite/gcc.dg/bad-return-type.c             |   67 +
 gcc/testsuite/gcc.dg/lsp/lsp.py                    |  125 ++
 gcc/testsuite/gcc.dg/lsp/test.c                    |   12 +
 gcc/testsuite/gcc.dg/lsp/test.py                   |   28 +
 gcc/testsuite/gcc.dg/lsp/toy-ide.py                |  111 ++
 gcc/testsuite/gcc.dg/param-type-mismatch.c         |   60 +
 .../plugin/diagnostic_plugin_test_show_locus.c     |    1 +
 gcc/toplev.c                                       |    4 +
 46 files changed, 6772 insertions(+), 108 deletions(-)
 create mode 100644 gcc/blt.c
 create mode 100644 gcc/blt.def
 create mode 100644 gcc/blt.h
 create mode 100644 gcc/http-server.c
 create mode 100644 gcc/http-server.h
 create mode 100644 gcc/json-rpc.c
 create mode 100644 gcc/json-rpc.h
 create mode 100644 gcc/json.c
 create mode 100644 gcc/json.h
 create mode 100644 gcc/lsp-main.c
 create mode 100644 gcc/lsp-main.h
 create mode 100644 gcc/lsp.c
 create mode 100644 gcc/lsp.h
 create mode 100644 gcc/server.c
 create mode 100644 gcc/server.h
 create mode 100644 gcc/testsuite/g++.dg/bad-return-type.C
 create mode 100644 gcc/testsuite/g++.dg/diagnostic/param-type-mismatch.C
 create mode 100644 gcc/testsuite/gcc.dg/bad-return-type.c
 create mode 100644 gcc/testsuite/gcc.dg/lsp/lsp.py
 create mode 100644 gcc/testsuite/gcc.dg/lsp/test.c
 create mode 100644 gcc/testsuite/gcc.dg/lsp/test.py
 create mode 100644 gcc/testsuite/gcc.dg/lsp/toy-ide.py
 create mode 100644 gcc/testsuite/gcc.dg/param-type-mismatch.c

-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH 17/17] Language Server Protocol: work-in-progess on testsuite
  2017-07-24 19:31 [PATCH 00/17] RFC: New source-location representation; Language Server Protocol David Malcolm
                   ` (6 preceding siblings ...)
  2017-07-24 19:31 ` [PATCH 01/17] Add param-type-mismatch.c/C testcases as a baseline David Malcolm
@ 2017-07-24 19:31 ` David Malcolm
  2017-07-24 19:31 ` [PATCH 13/17] Add http-server.h and http-server.c David Malcolm
                   ` (11 subsequent siblings)
  19 siblings, 0 replies; 38+ messages in thread
From: David Malcolm @ 2017-07-24 19:31 UTC (permalink / raw)
  To: gcc-patches; +Cc: David Malcolm

This file adds the beginnings of a testsuite for the LSP
implementation.

The test cases are implemented in Python; they aren't currently
wired up to DejaGnu (I've been invoking them manually).

There's an automated test case, and a PyGTK UI.

Both require the language server to be manually started
(see the comments).

gcc/testsuite/ChangeLog:
	* gcc.dg/lsp/lsp.py: New file.
	* gcc.dg/lsp/test.c: New test file.
	* gcc.dg/lsp/test.py: New test case.
	* gcc.dg/lsp/toy-ide.py: New file.
---
 gcc/testsuite/gcc.dg/lsp/lsp.py     | 125 ++++++++++++++++++++++++++++++++++++
 gcc/testsuite/gcc.dg/lsp/test.c     |  12 ++++
 gcc/testsuite/gcc.dg/lsp/test.py    |  28 ++++++++
 gcc/testsuite/gcc.dg/lsp/toy-ide.py | 111 ++++++++++++++++++++++++++++++++
 4 files changed, 276 insertions(+)
 create mode 100644 gcc/testsuite/gcc.dg/lsp/lsp.py
 create mode 100644 gcc/testsuite/gcc.dg/lsp/test.c
 create mode 100644 gcc/testsuite/gcc.dg/lsp/test.py
 create mode 100644 gcc/testsuite/gcc.dg/lsp/toy-ide.py

diff --git a/gcc/testsuite/gcc.dg/lsp/lsp.py b/gcc/testsuite/gcc.dg/lsp/lsp.py
new file mode 100644
index 0000000..56468da
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/lsp/lsp.py
@@ -0,0 +1,125 @@
+# A python module for implementing LSP clients
+
+import json
+
+import jsonrpc # pip install json-rpc
+import requests
+
+# Various types to model the LSP interface
+
+class Position:
+    def __init__(self, line, character):
+        self.line = line
+        self.character = character
+
+    def __repr__(self):
+        return 'Position(%r, %r)' % (self.line, self.character)
+
+    def __eq__(self, other):
+        return self.__dict__ == other.__dict__
+
+    def to_json(self):
+        return {'line': self.line, 'character': self.character}
+
+    @staticmethod
+    def from_json(js):
+        return Position(js['line'], js['character'])
+
+class Range:
+    def __init__(self, start, end):
+        self.start = start
+        self.end = end
+
+    def __repr__(self):
+        return 'Range(%r, %r)' % (self.start, self.end)
+
+    def __eq__(self, other):
+        return self.__dict__ == other.__dict__
+
+    @staticmethod
+    def from_json(js):
+        return Range(Position.from_json(js['start']),
+                     Position.from_json(js['end']))
+
+class Location:
+    def __init__(self, uri, range):
+        self.uri = uri
+        self.range = range
+
+    def __repr__(self):
+        return 'Location(%r, %r)' % (self.uri, self.range)
+
+    def __eq__(self, other):
+        return self.__dict__ == other.__dict__
+
+    @staticmethod
+    def from_json(js):
+        return Location(js['uri'], Range.from_json(js['range']))
+
+    def dump(self, msg):
+        print('%s:%i:%i: %s' % (self.uri, self.range.start.line,
+                                self.range.start.character, msg))
+        # FIXME: underline
+        # linecache uses 1-based line numbers, whereas LSP uses
+        # 0-based line numbers
+        import linecache
+        line = linecache.getline(self.uri, self.range.start.line + 1)
+        print('line: %r' % line)
+
+class TextDocumentIdentifier:
+    def __init__(self, uri):
+        self.uri = uri
+
+    def __eq__(self, other):
+        return self.__dict__ == other.__dict__
+
+    def to_json(self):
+        return {'uri': self.uri}
+
+class TextDocumentPositionParams:
+    def __init__(self, textDocument, position):
+        self.textDocument = textDocument
+        self.position = position
+
+    def __eq__(self, other):
+        return self.__dict__ == other.__dict__
+
+    def to_json(self):
+        return {"textDocument" : self.textDocument.to_json(),
+                "position" : self.position.to_json()}
+
+# A wrapper for making LSP calls against a server
+
+class Proxy:
+    def __init__(self, url):
+        self.url = url
+        self.next_id = 0
+
+    def make_request(self, method, params):
+        json_req = {"method": method,
+                    "params": params,
+                    "jsonrpc": "2.0",
+                    "id": self.next_id}
+        self.next_id += 1
+        return json_req
+
+    def post_request(self, method, params):
+        payload = self.make_request(method, params)
+        headers = {'content-type': 'application/json'}
+        response = requests.post(self.url, data=json.dumps(payload),
+                                 headers=headers)
+        print('response: %r' % response)
+        print('response.json(): %r' % response.json())
+        return response.json()
+
+    def goto_definition(self, textDocument, position):
+        params = TextDocumentPositionParams(textDocument, position)
+        json_resp = self.post_request('textDocument/definition',
+                                      params.to_json())
+        print(json_resp)
+        # Expect either a Location or a list of Location
+        if isinstance(json_resp, list):
+            return [Location.from_json(item) for item in json_resp]
+        else:
+            return Location.from_json(json_resp)
+
diff --git a/gcc/testsuite/gcc.dg/lsp/test.c b/gcc/testsuite/gcc.dg/lsp/test.c
new file mode 100644
index 0000000..bb80bd0
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/lsp/test.c
@@ -0,0 +1,12 @@
+/* */
+
+struct foo
+{
+  int color;
+  int shape;
+};
+
+int test (struct foo *ptr)
+{
+  ptr->
+}
diff --git a/gcc/testsuite/gcc.dg/lsp/test.py b/gcc/testsuite/gcc.dg/lsp/test.py
new file mode 100644
index 0000000..c7f0067
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/lsp/test.py
@@ -0,0 +1,28 @@
+from lsp import Proxy, TextDocumentIdentifier, Position, Range, Location
+
+def main():
+    # This assumes that we're running this:
+    # (hardcoding the particular source file for now):
+    # ./xgcc -B. -c ../../src/gcc/testsuite/gcc.dg/lsp/test.c \
+    #    -flsp=4000 -fblt -wrapper gdb,--args
+
+    url = "http://localhost:4000/jsonrpc"
+    proxy = Proxy(url)
+
+    # FIXME: filename/uri (currently a particular relative location)
+    FILENAME = '../../src/gcc/testsuite/gcc.dg/lsp/test.c'
+
+    # Ask for the location of a usage of "struct foo" (0-based lines)
+    result = proxy.goto_definition(TextDocumentIdentifier(FILENAME),
+                                   Position(8, 16))
+
+    # We expect to get back the location of where "struct foo" is defined
+    print(result)
+    # FIXME: filename/uri (currently a particular relative location)
+    assert result == [Location(FILENAME,
+                               Range(Position(2, 0), Position(6, 1)))]
+    for loc in result:
+        loc.dump("definition")
+
+if __name__ == "__main__":
+    main()
diff --git a/gcc/testsuite/gcc.dg/lsp/toy-ide.py b/gcc/testsuite/gcc.dg/lsp/toy-ide.py
new file mode 100644
index 0000000..ff1d2e2
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/lsp/toy-ide.py
@@ -0,0 +1,111 @@
+# A toy IDE implemented in PyGTK
+
+import pygtk
+pygtk.require('2.0')
+import gtk
+import gtksourceview2
+#help(gtksourceview2)
+
+import lsp
+
+class ToyIde:
+    def delete_event(self, widget, event, data=None):
+        return False
+
+    def destroy(self, widget, data=None):
+        gtk.main_quit()
+
+    def quit_cb(self, b):
+        gtk.main_quit()
+
+    def get_cursor_position(self):
+        """Get the position of the cursor within the buffer
+        as an lsp.Position"""
+        mark = self.buf.get_insert()
+        print(mark)
+        iter = self.buf.get_iter_at_mark(mark)
+        print(iter)
+
+        print('line: %r' % iter.get_line()) # 0-based line
+        print('line_offset: %r' % iter.get_line_offset()) # 0-based offse
+
+        return lsp.Position(iter.get_line(), iter.get_line_offset())
+
+    def goto_definition_cb(self, b):
+        print "goto_definition_cb"
+
+        # FIXME: need to sort out paths between the LSP server and client
+        FILENAME = '../../src/gcc/testsuite/gcc.dg/lsp/test.c'
+
+        locs = self.lsp.goto_definition(lsp.TextDocumentIdentifier(FILENAME),
+                                        self.get_cursor_position())
+        print(locs)
+        if len(locs) == 1:
+            loc = locs[0]
+            # Both lsp.Range and gtk.TextBuffer.select_range are inclusive
+            # on the start, exclusive on the end-point
+            self.buf.select_range(
+                self.buf.get_iter_at_line_offset(loc.range.start.line,
+                                                 loc.range.start.character),
+                self.buf.get_iter_at_line_offset(loc.range.end.line,
+                                                 loc.range.end.character))
+
+    def __init__(self, path):
+        self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
+        self.window.connect("delete_event", self.delete_event)
+        self.window.connect("destroy", self.destroy)
+        self.window.set_size_request(640, 480)
+        vbox = gtk.VBox()
+        self.window.add(vbox)
+
+        uimanager = gtk.UIManager()
+        accelgroup = uimanager.get_accel_group()
+        self.window.add_accel_group(accelgroup)
+
+        actiongroup = gtk.ActionGroup('UIManagerExample')
+        self.actiongroup = actiongroup
+        actiongroup.add_actions([('Quit', gtk.STOCK_QUIT, '_Quit', None,
+                                  'Quit', self.quit_cb),
+                                 ('File', None, '_File'),
+                                 ('Test', None, '_Test'),
+                                 ('GotoDefinition', None, 'Goto _Definition',
+                                  None, 'Goto the definition of this thing',
+                                  self.goto_definition_cb)
+                             ])
+        actiongroup.get_action('Quit').set_property('short-label', '_Quit')
+        uimanager.insert_action_group(actiongroup, 0)
+
+        merge_id = uimanager.add_ui_from_string("""
+ <ui>
+    <menubar name="MenuBar">
+      <menu action="File">
+        <menuitem action="Quit"/>
+      </menu>
+      <menu action="Test">
+        <menuitem action="GotoDefinition"/>
+      </menu>
+    </menubar>
+  </ui>
+""")
+        menubar = uimanager.get_widget('/MenuBar')
+        vbox.pack_start(menubar, False)
+
+        self.buf = gtksourceview2.Buffer()
+
+        with open(path) as f:
+            text = f.read()
+        self.buf.set_text(text)
+
+        self.sv = gtksourceview2.View(buffer=self.buf)
+        self.sv.set_show_line_numbers(True)
+
+        vbox.add(self.sv)
+        self.window.show_all()
+
+        self.lsp = lsp.Proxy('http://localhost:4000/jsonrpc')
+
+    def main(self):
+        gtk.main()
+
+ide = ToyIde("gcc/testsuite/gcc.dg/lsp/test.c")
+ide.main()
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH 12/17] Add server.h and server.c
  2017-07-24 19:31 [PATCH 00/17] RFC: New source-location representation; Language Server Protocol David Malcolm
                   ` (2 preceding siblings ...)
  2017-07-24 19:31 ` [PATCH 02/17] diagnostics: support prefixes within diagnostic_show_locus David Malcolm
@ 2017-07-24 19:31 ` David Malcolm
  2017-07-26 14:35   ` Oleg Endo
  2017-09-01 17:09   ` Jeff Law
  2017-07-24 19:31 ` [PATCH 10/17] C++: provide fix-it hints in -Wsuggest-override David Malcolm
                   ` (15 subsequent siblings)
  19 siblings, 2 replies; 38+ messages in thread
From: David Malcolm @ 2017-07-24 19:31 UTC (permalink / raw)
  To: gcc-patches; +Cc: David Malcolm

This patch adds a "server" abstract base class for listening
on a port, for use by the later patches to implement an LSP server.

It's largely adapted from examples in glibc's docs.  I suspect that
I've introduced platform-specific assumptions (and that it may need some
extra configure tests for the extra functionality), but this part of
the kit is just a proof-of-concept.

gcc/ChangeLog:
	* Makefile.in (OBJS): Add server.o.
	* server.c: New file.
	* server.h: New file.
---
 gcc/Makefile.in |   1 +
 gcc/server.c    | 152 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 gcc/server.h    |  46 +++++++++++++++++
 3 files changed, 199 insertions(+)
 create mode 100644 gcc/server.c
 create mode 100644 gcc/server.h

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 77ecbd6..4e60bc0 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1454,6 +1454,7 @@ OBJS = \
 	sel-sched.o \
 	selftest-rtl.o \
 	selftest-run-tests.o \
+	server.o \
 	sese.o \
 	shrink-wrap.o \
 	simplify-rtx.o \
diff --git a/gcc/server.c b/gcc/server.c
new file mode 100644
index 0000000..871a95f
--- /dev/null
+++ b/gcc/server.c
@@ -0,0 +1,152 @@
+/* Abstract server implementation.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "server.h"
+
+/* adapted from GNU libc docs.  */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+
+#define MAXMSG  512
+
+static int
+make_socket (uint16_t port)
+{
+  int sock;
+  struct sockaddr_in name;
+
+  /* Create the socket. */
+  sock = socket (PF_INET, SOCK_STREAM, 0);
+  if (sock < 0)
+    {
+      perror ("socket");
+      exit (EXIT_FAILURE);
+    }
+
+  /* Give the socket a name. */
+  name.sin_family = AF_INET;
+  name.sin_port = htons (port);
+  name.sin_addr.s_addr = htonl (INADDR_ANY);
+  if (bind (sock, (struct sockaddr *) &name, sizeof (name)) < 0)
+    {
+      perror ("bind");
+      exit (EXIT_FAILURE);
+    }
+
+  return sock;
+}
+
+/* Serve on PORT.  */
+
+void
+server::serve (int port)
+{
+  int sock;
+  fd_set active_fd_set, read_fd_set;
+  int i;
+  struct sockaddr_in clientname;
+  socklen_t size;
+
+  /* Create the socket and set it up to accept connections. */
+  sock = make_socket (port);
+  if (listen (sock, 1) < 0)
+    {
+      perror ("listen");
+      exit (EXIT_FAILURE);
+    }
+
+  /* Initialize the set of active sockets. */
+  FD_ZERO (&active_fd_set);
+  FD_SET (sock, &active_fd_set);
+
+  while (1)
+    {
+      /* Block until input arrives on one or more active sockets. */
+      read_fd_set = active_fd_set;
+      if (select (FD_SETSIZE, &read_fd_set, NULL, NULL, NULL) < 0)
+	{
+	  perror ("select");
+	  exit (EXIT_FAILURE);
+	}
+
+      /* Service all the sockets with input pending. */
+      for (i = 0; i < FD_SETSIZE; ++i)
+	if (FD_ISSET (i, &read_fd_set))
+	  {
+	    if (i == sock)
+	      {
+		/* Connection request on original socket. */
+		int new_;
+		size = sizeof (clientname);
+		new_ = accept (sock,
+			       (struct sockaddr *) &clientname,
+			       &size);
+		if (new_ < 0)
+		  {
+		    perror ("accept");
+		    exit (EXIT_FAILURE);
+		  }
+		fprintf (stderr,
+			 "Server: connect from host %s, port %hd.\n",
+			 inet_ntoa (clientname.sin_addr),
+			 ntohs (clientname.sin_port));
+		FD_SET (new_, &active_fd_set);
+	      }
+	    else
+	      {
+		/* Data arriving on an already-connected socket. */
+		if (read_from_client (file_descriptor (i)) < 0)
+		  {
+		    close (i);
+		    FD_CLR (i, &active_fd_set);
+		  }
+	      }
+	  }
+    }
+}
+
+int
+server::read_from_client (file_descriptor fd)
+{
+  char buffer[MAXMSG];
+  int nbytes;
+
+  nbytes = read (fd.m_fd, buffer, MAXMSG);
+  if (nbytes < 0)
+    {
+      /* Read error. */
+      perror ("read");
+      exit (EXIT_FAILURE);
+    }
+  else if (nbytes == 0)
+    /* End-of-file. */
+    return -1;
+  else
+    {
+      /* Data read. */
+      on_read (fd, nbytes, buffer);
+      return 0;
+    }
+}
diff --git a/gcc/server.h b/gcc/server.h
new file mode 100644
index 0000000..3bcf9f6e
--- /dev/null
+++ b/gcc/server.h
@@ -0,0 +1,46 @@
+/* Abstract server implementation.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_SERVER_H
+#define GCC_SERVER_H
+
+/* Wrapper aroung "int" for file descriptors.  */
+
+struct file_descriptor
+{
+  explicit file_descriptor (int fd) : m_fd (fd) {}
+
+  int m_fd;
+};
+
+/* Abstract base class for implementing a server.  */
+
+class server
+{
+ public:
+  virtual ~server () {}
+  void serve (int port);
+
+  virtual void on_read (file_descriptor fd, size_t length, const char *buf) = 0;
+
+ private:
+  int read_from_client (file_descriptor fd);
+};
+
+#endif  /* GCC_SERVER_H  */
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH 02/17] diagnostics: support prefixes within diagnostic_show_locus
  2017-07-24 19:31 [PATCH 00/17] RFC: New source-location representation; Language Server Protocol David Malcolm
  2017-07-24 19:31 ` [PATCH 03/17] Core of BLT implementation David Malcolm
  2017-07-24 19:31 ` [PATCH 15/17] Language Server Protocol: add lsp::server abstract base class David Malcolm
@ 2017-07-24 19:31 ` David Malcolm
  2017-09-01 17:11   ` Jeff Law
  2017-07-24 19:31 ` [PATCH 12/17] Add server.h and server.c David Malcolm
                   ` (16 subsequent siblings)
  19 siblings, 1 reply; 38+ messages in thread
From: David Malcolm @ 2017-07-24 19:31 UTC (permalink / raw)
  To: gcc-patches; +Cc: David Malcolm

This patch reworks diagnostic_show_locus so that it respects
the prefix of the pretty_printer.

This is used later in the patch kit for embedding source dumps
within a hierarchical tree-like dump, by using the ASCII art for the
tree as the prefix.

gcc/c-family/ChangeLog:
	* c-opts.c (c_diagnostic_finalizer): Move call to
	pp_destroy_prefix to before call to diagnostic_show_locus.

gcc/ChangeLog:
	* diagnostic-show-locus.c (layout::print_source_line):
	Call pp_emit_prefix.
	(layout::should_print_annotation_line_p): Likewise.
	(layout::print_leading_fixits): Likewise.
	(layout::print_trailing_fixits): Likewise, for first correction.
	(layout::print_trailing_fixits): Replace call to move_to_column
	with a call to print_newline, to avoid emitting a prefix after
	last line.
	(layout::move_to_column): Call pp_emit_prefix.
	(layout::show_ruler): Likewise.
	(diagnostic_show_locus): Remove save/restore of prefix.
	(selftest::test_diagnostic_context::test_diagnostic_context): Set
	prefixing rule to DIAGNOSTICS_SHOW_PREFIX_NEVER.
	(selftest::test_one_liner_fixit_remove): Verify the interaction of
	pp_set_prefix with rulers and fix-it hints.
	* diagnostic.c (default_diagnostic_start_span_fn): Don't modify
	the prefix.

gcc/testsuite/ChangeLog:
	* gcc.dg/plugin/diagnostic_plugin_test_show_locus.c
	(custom_diagnostic_finalizer): Reset pp_indentation of printer.
---
 gcc/c-family/c-opts.c                              |  2 +-
 gcc/diagnostic-show-locus.c                        | 94 ++++++++++++++++++----
 gcc/diagnostic.c                                   |  5 +-
 .../plugin/diagnostic_plugin_test_show_locus.c     |  1 +
 4 files changed, 82 insertions(+), 20 deletions(-)

diff --git a/gcc/c-family/c-opts.c b/gcc/c-family/c-opts.c
index 1657e7a..a0b86c2 100644
--- a/gcc/c-family/c-opts.c
+++ b/gcc/c-family/c-opts.c
@@ -163,11 +163,11 @@ static void
 c_diagnostic_finalizer (diagnostic_context *context,
 			diagnostic_info *diagnostic)
 {
+  pp_destroy_prefix (context->printer);
   diagnostic_show_locus (context, diagnostic->richloc, diagnostic->kind);
   /* By default print macro expansion contexts in the diagnostic
      finalizer -- for tokens resulting from macro expansion.  */
   virt_loc_aware_diagnostic_finalizer (context, diagnostic);
-  pp_destroy_prefix (context->printer);
   pp_flush (context->printer);
 }
 
diff --git a/gcc/diagnostic-show-locus.c b/gcc/diagnostic-show-locus.c
index b0e72e7..eb9b7b4 100644
--- a/gcc/diagnostic-show-locus.c
+++ b/gcc/diagnostic-show-locus.c
@@ -1146,6 +1146,8 @@ layout::print_source_line (int row, const char *line, int line_width,
 							   line_width);
   line += m_x_offset;
 
+  pp_emit_prefix (m_pp);
+
   pp_space (m_pp);
   int first_non_ws = INT_MAX;
   int last_non_ws = 0;
@@ -1214,6 +1216,8 @@ layout::should_print_annotation_line_p (int row) const
 void
 layout::print_annotation_line (int row, const line_bounds lbounds)
 {
+  pp_emit_prefix (m_pp);
+
   int x_bound = get_x_bound_for_row (row, m_exploc.column,
 				     lbounds.m_last_non_ws);
 
@@ -1274,6 +1278,8 @@ layout::print_leading_fixits (int row)
 
       if (hint->affects_line_p (m_exploc.file, row))
 	{
+	  pp_emit_prefix (m_pp);
+
 	  /* Printing the '+' with normal colorization
 	     and the inserted line with "insert" colorization
 	     helps them stand out from each other, and from
@@ -1695,6 +1701,9 @@ layout::print_trailing_fixits (int row)
 
   FOR_EACH_VEC_ELT (corrections.m_corrections, i, c)
     {
+      if (i == 0)
+	pp_emit_prefix (m_pp);
+
       /* For now we assume each fixit hint can only touch one line.  */
       if (c->insertion_p ())
 	{
@@ -1739,7 +1748,8 @@ layout::print_trailing_fixits (int row)
     }
 
   /* Add a trailing newline, if necessary.  */
-  move_to_column (&column, 0);
+  if (column > 0)
+    print_newline ();
 }
 
 /* Disable any colorization and emit a newline.  */
@@ -1835,8 +1845,8 @@ layout::get_x_bound_for_row (int row, int caret_column,
 }
 
 /* Given *COLUMN as an x-coordinate, print spaces to position
-   successive output at DEST_COLUMN, printing a newline if necessary,
-   and updating *COLUMN.  */
+   successive output at DEST_COLUMN, printing a newline if necessary
+   (and, if so, any prefix), and updating *COLUMN.  */
 
 void
 layout::move_to_column (int *column, int dest_column)
@@ -1846,6 +1856,7 @@ layout::move_to_column (int *column, int dest_column)
     {
       print_newline ();
       *column = 0;
+      pp_emit_prefix (m_pp);
     }
 
   while (*column < dest_column)
@@ -1864,6 +1875,7 @@ layout::show_ruler (int max_column) const
   /* Hundreds.  */
   if (max_column > 99)
     {
+      pp_emit_prefix (m_pp);
       pp_space (m_pp);
       for (int column = 1 + m_x_offset; column <= max_column; column++)
 	if (0 == column % 10)
@@ -1874,6 +1886,7 @@ layout::show_ruler (int max_column) const
     }
 
   /* Tens.  */
+  pp_emit_prefix (m_pp);
   pp_space (m_pp);
   for (int column = 1 + m_x_offset; column <= max_column; column++)
     if (0 == column % 10)
@@ -1883,6 +1896,7 @@ layout::show_ruler (int max_column) const
   pp_newline (m_pp);
 
   /* Units.  */
+  pp_emit_prefix (m_pp);
   pp_space (m_pp);
   for (int column = 1 + m_x_offset; column <= max_column; column++)
     pp_character (m_pp, '0' + (column % 10));
@@ -1961,9 +1975,6 @@ diagnostic_show_locus (diagnostic_context * context,
 
   context->last_location = loc;
 
-  const char *saved_prefix = pp_get_prefix (context->printer);
-  pp_set_prefix (context->printer, NULL);
-
   layout layout (context, richloc, diagnostic_kind);
   for (int line_span_idx = 0; line_span_idx < layout.get_num_line_spans ();
        line_span_idx++)
@@ -1978,8 +1989,6 @@ diagnostic_show_locus (diagnostic_context * context,
       for (int row = line_span->get_first_line (); row <= last_line; row++)
 	layout.print_line (row);
     }
-
-  pp_set_prefix (context->printer, saved_prefix);
 }
 
 #if CHECKING_P
@@ -1997,6 +2006,7 @@ class test_diagnostic_context : public diagnostic_context
   test_diagnostic_context ()
   {
     diagnostic_initialize (this, 0);
+    pp_prefixing_rule (printer) = DIAGNOSTICS_SHOW_PREFIX_NEVER;
     show_caret = true;
     show_column = true;
     start_span = start_span_cb;
@@ -2140,23 +2150,75 @@ test_one_liner_fixit_insert_after ()
 		pp_formatted_text (dc.printer));
 }
 
-/* Removal fix-it hint: removal of the ".field". */
+/* Removal fix-it hint: removal of the ".field".
+   Also verify the interaction of pp_set_prefix with rulers and
+   fix-it hints.  */
 
 static void
 test_one_liner_fixit_remove ()
 {
-  test_diagnostic_context dc;
   location_t start = linemap_position_for_column (line_table, 10);
   location_t finish = linemap_position_for_column (line_table, 15);
   location_t dot = make_location (start, start, finish);
   rich_location richloc (line_table, dot);
   richloc.add_fixit_remove ();
-  diagnostic_show_locus (&dc, &richloc, DK_ERROR);
-  ASSERT_STREQ ("\n"
-		" foo = bar.field;\n"
-		"          ^~~~~~\n"
-		"          ------\n",
-		pp_formatted_text (dc.printer));
+
+  /* Normal.  */
+  {
+    test_diagnostic_context dc;
+    diagnostic_show_locus (&dc, &richloc, DK_ERROR);
+    ASSERT_STREQ ("\n"
+		  " foo = bar.field;\n"
+		  "          ^~~~~~\n"
+		  "          ------\n",
+		  pp_formatted_text (dc.printer));
+  }
+
+  /* Test of adding a prefix.  */
+  {
+    test_diagnostic_context dc;
+    pp_prefixing_rule (dc.printer) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
+    pp_set_prefix (dc.printer, "TEST PREFIX:");
+    diagnostic_show_locus (&dc, &richloc, DK_ERROR);
+    ASSERT_STREQ ("\n"
+		  "TEST PREFIX: foo = bar.field;\n"
+		  "TEST PREFIX:          ^~~~~~\n"
+		  "TEST PREFIX:          ------\n",
+		  pp_formatted_text (dc.printer));
+  }
+
+  /* Normal, with ruler.  */
+  {
+    test_diagnostic_context dc;
+    dc.show_ruler_p = true;
+    dc.caret_max_width = 104;
+    diagnostic_show_locus (&dc, &richloc, DK_ERROR);
+    ASSERT_STREQ ("\n"
+		  "          0         0         0         0         0         0         0         0         0         1    \n"
+		  "          1         2         3         4         5         6         7         8         9         0    \n"
+		  " 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234\n"
+		  " foo = bar.field;\n"
+		  "          ^~~~~~\n"
+		  "          ------\n",
+		  pp_formatted_text (dc.printer));
+  }
+
+  /* Test of adding a prefix, with ruler.  */
+  {
+    test_diagnostic_context dc;
+    dc.show_ruler_p = true;
+    dc.caret_max_width = 50;
+    pp_prefixing_rule (dc.printer) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
+    pp_set_prefix (dc.printer, "TEST PREFIX:");
+    diagnostic_show_locus (&dc, &richloc, DK_ERROR);
+    ASSERT_STREQ ("\n"
+		  "TEST PREFIX:          1         2         3         4         5\n"
+		  "TEST PREFIX: 12345678901234567890123456789012345678901234567890\n"
+		  "TEST PREFIX: foo = bar.field;\n"
+		  "TEST PREFIX:          ^~~~~~\n"
+		  "TEST PREFIX:          ------\n",
+		  pp_formatted_text (dc.printer));
+  }
 }
 
 /* Replace fix-it hint: replacing "field" with "m_field". */
diff --git a/gcc/diagnostic.c b/gcc/diagnostic.c
index bbf5f5c..2415baa 100644
--- a/gcc/diagnostic.c
+++ b/gcc/diagnostic.c
@@ -612,9 +612,8 @@ void
 default_diagnostic_start_span_fn (diagnostic_context *context,
 				  expanded_location exploc)
 {
-  pp_set_prefix (context->printer,
-		 diagnostic_get_location_text (context, exploc));
-  pp_string (context->printer, "");
+  pp_string (context->printer,
+	     diagnostic_get_location_text (context, exploc));
   pp_newline (context->printer);
 }
 
diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_show_locus.c b/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_show_locus.c
index 0a8eeba..9eaa0af 100644
--- a/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_show_locus.c
+++ b/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_show_locus.c
@@ -133,6 +133,7 @@ custom_diagnostic_finalizer (diagnostic_context *context,
   bool old_show_color = pp_show_color (context->printer);
   if (force_show_locus_color)
     pp_show_color (context->printer) = true;
+  pp_indentation (context->printer) = 0;
   diagnostic_show_locus (context, diagnostic->richloc, diagnostic->kind);
   pp_show_color (context->printer) = old_show_color;
 
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH 01/17] Add param-type-mismatch.c/C testcases as a baseline
  2017-07-24 19:31 [PATCH 00/17] RFC: New source-location representation; Language Server Protocol David Malcolm
                   ` (5 preceding siblings ...)
  2017-07-24 19:31 ` [PATCH 11/17] Add JSON implementation David Malcolm
@ 2017-07-24 19:31 ` David Malcolm
  2017-07-24 19:56   ` Eric Gallager
  2017-09-01 17:00   ` Jeff Law
  2017-07-24 19:31 ` [PATCH 17/17] Language Server Protocol: work-in-progess on testsuite David Malcolm
                   ` (12 subsequent siblings)
  19 siblings, 2 replies; 38+ messages in thread
From: David Malcolm @ 2017-07-24 19:31 UTC (permalink / raw)
  To: gcc-patches; +Cc: David Malcolm

Amongst other things, this patch kit aims to improve our handling
of mismatched types within function calls.

For example, given:

  extern int callee_1 (int one, const char *two, float three);

  int test_1 (int first, int second, float third)
  {
    return callee_1 (first, second, third);
  }

the C++ FE currently reports:

  error: invalid conversion from 'int' to 'const char*' [-fpermissive]
   return callee_1 (first, second, third);
                                        ^
  note:   initializing argument 2 of 'int callee_1(int, const char*, float)'
   extern int callee_1 (int one, const char *two, float three);
              ^~~~~~~~

when it ought to underline the pertinent parts of the code:

  error: invalid conversion from 'int' to 'const char*' [-fpermissive]
   return callee_1 (first, second, third);
                           ^~~~~~
  note:   initializing argument 2 of 'int callee_1(int, const char*, float)'
   extern int callee_1 (int one, const char *two, float three);
                                 ^~~~~~~~~~~~~~~

The C FE currently does better, underlining the pertinent argument at
the callsite (due to the "vec<location_t> arg_loc" passed around
when the call is created); but, like the C++ frontend, it doesn't
underline the pertinent parameter at the decl of the callee.

This patch adds a pair of test cases to exercise various cases of
this in the C and C++ frontends, to establish a baseline for how
we currently handle these; the "TODO" comments in the test cases
note the aspects where we could do better (some of which are fixed
by this kit).

Patches 6 and 7 of the kit fix the parameter highlighting, for
C and C++ respectively (making use of -fblt).

gcc/testsuite/ChangeLog:
	* g++.dg/diagnostic/param-type-mismatch.C: New test acse.
	* gcc.dg/param-type-mismatch.c: New test case.
---
 .../g++.dg/diagnostic/param-type-mismatch.C        | 162 +++++++++++++++++++++
 gcc/testsuite/gcc.dg/param-type-mismatch.c         |  63 ++++++++
 2 files changed, 225 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/diagnostic/param-type-mismatch.C
 create mode 100644 gcc/testsuite/gcc.dg/param-type-mismatch.c

diff --git a/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch.C b/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch.C
new file mode 100644
index 0000000..864ead1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch.C
@@ -0,0 +1,162 @@
+// { dg-options "-fdiagnostics-show-caret" }
+
+/* A collection of calls where argument 2 is of the wrong type.
+
+   TODO: we should put the caret and underline for the diagnostic
+   at the second argument, rather than the close paren.
+
+   TODO: we should highlight the second parameter of the callee, rather
+   than its name.  */
+
+/* decl, with argname.  */
+
+extern int callee_1 (int one, const char *two, float three); // { dg-line callee_1 }
+
+int test_1 (int first, int second, float third)
+{
+  return callee_1 (first, second, third); // { dg-error "invalid conversion from 'int' to 'const char\\*'" }
+  /* { dg-begin-multiline-output "" }
+   return callee_1 (first, second, third);
+                                        ^
+     { dg-end-multiline-output "" } */
+  // { dg-message "initializing argument 2 of 'int callee_1\\(int, const char\\*, float\\)'" "" { target *-*-* } callee_1 }
+  /* { dg-begin-multiline-output "" }
+ extern int callee_1 (int one, const char *two, float three);
+            ^~~~~~~~
+     { dg-end-multiline-output "" } */
+}
+
+/* decl, without argname.  */
+
+extern int callee_2 (int, const char *, float); // { dg-line callee_2 }
+
+int test_2 (int first, int second, float third)
+{
+  return callee_2 (first, second, third); // { dg-error "invalid conversion from 'int' to 'const char\\*'" }
+  /* { dg-begin-multiline-output "" }
+   return callee_2 (first, second, third);
+                                        ^
+     { dg-end-multiline-output "" } */
+  // { dg-message "initializing argument 2 of 'int callee_2\\(int, const char\\*, float\\)'" "" { target *-*-* } callee_2 }
+  /* { dg-begin-multiline-output "" }
+ extern int callee_2 (int, const char *, float);
+            ^~~~~~~~
+     { dg-end-multiline-output "" } */
+}
+
+/* defn, with argname.  */
+
+static int callee_3 (int one, const char *two, float three) // { dg-line callee_3 }
+{
+  return callee_2 (one, two, three);
+}
+
+int test_3 (int first, int second, float third)
+{
+  return callee_3 (first, second, third); // { dg-error "invalid conversion from 'int' to 'const char\\*'" }
+  /* { dg-begin-multiline-output "" }
+   return callee_3 (first, second, third);
+                                        ^
+     { dg-end-multiline-output "" } */
+  // { dg-message "initializing argument 2 of 'int callee_3\\(int, const char\\*, float\\)'" "" { target *-*-* } callee_3 }
+  /* { dg-begin-multiline-output "" }
+ static int callee_3 (int one, const char *two, float three)
+            ^~~~~~~~
+     { dg-end-multiline-output "" } */
+}
+
+/* static member, with argname.  */
+
+struct s4 { static int member_1 (int one, const char *two, float three); };
+
+int test_4 (int first, int second, float third)
+{
+  return s4::member_1 (first, second, third); // { dg-error "invalid conversion from 'int' to 'const char\\*'" }
+  /* { dg-begin-multiline-output "" }
+   return s4::member_1 (first, second, third);
+                                            ^
+     { dg-end-multiline-output "" } */
+  /* { dg-begin-multiline-output "" }
+ struct s4 { static int member_1 (int one, const char *two, float three); };
+                        ^~~~~~~~
+     { dg-end-multiline-output "" } */
+}
+
+/* non-static member, with argname.  */
+
+struct s5 { int member_1 (int one, const char *two, float three); };
+
+int test_5 (int first, int second, float third)
+{
+  s5 inst;
+  return inst.member_1 (first, second, third); // { dg-error "invalid conversion from 'int' to 'const char\\*'" }
+  /* { dg-begin-multiline-output "" }
+   return inst.member_1 (first, second, third);
+                                             ^
+     { dg-end-multiline-output "" } */
+  /* { dg-begin-multiline-output "" }
+ struct s5 { int member_1 (int one, const char *two, float three); };
+                 ^~~~~~~~
+     { dg-end-multiline-output "" } */
+}
+
+/* Template function.  */
+
+template <typename T>
+int test_6 (int one, T two, float three);
+
+int test_6 (int first, int second, float third)
+{
+  return test_6 <const char *> (first, second, third); // { dg-error "no matching function" }
+  /* { dg-begin-multiline-output "" }
+   return test_6 <const char *> (first, second, third);
+                                                     ^
+     { dg-end-multiline-output "" } */
+  /* { dg-begin-multiline-output "" }
+   return test_6 <const char *> (first, second, third);
+                                                     ^
+     { dg-end-multiline-output "" } */
+  /* { dg-begin-multiline-output "" }
+ int test_6 (int one, T two, float three);
+     ^~~~~~
+     { dg-end-multiline-output "" } */
+}
+
+/* Template class, static function.  */
+
+template <typename T>
+struct s7 { static int member_1 (int one, T two, float three); };
+
+int test_7 (int first, int second, float third)
+{
+  return s7 <const char *>::member_1 (first, second, third); // { dg-error "invalid conversion from 'int' to 'const char\\*'" }
+  /* { dg-begin-multiline-output "" }
+   return s7 <const char *>::member_1 (first, second, third);
+                                                           ^
+     { dg-end-multiline-output "" } */
+  /* { dg-begin-multiline-output "" }
+ struct s7 { static int member_1 (int one, T two, float three); };
+                        ^~~~~~~~
+     { dg-end-multiline-output "" } */
+}
+
+/* Template class, non-static function.  */
+
+template <typename T>
+struct s8 { int member_1 (int one, T two, float three); };
+
+int test_8 (int first, int second, float third)
+{
+  s8 <const char *> inst;
+  return inst.member_1 (first, second, third); // { dg-error "invalid conversion from 'int' to 'const char\\*'" }
+  /* { dg-begin-multiline-output "" }
+   return inst.member_1 (first, second, third);
+                                             ^
+     { dg-end-multiline-output "" } */
+  /* { dg-begin-multiline-output "" }
+ struct s8 { int member_1 (int one, T two, float three); };
+                 ^~~~~~~~
+     { dg-end-multiline-output "" } */
+}
+
+// TODO: template callsite
diff --git a/gcc/testsuite/gcc.dg/param-type-mismatch.c b/gcc/testsuite/gcc.dg/param-type-mismatch.c
new file mode 100644
index 0000000..70ea0bc
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/param-type-mismatch.c
@@ -0,0 +1,63 @@
+/* { dg-options "-fdiagnostics-show-caret" }  */
+
+/* A collection of calls where argument 2 is of the wrong type.
+
+   TODO: we should highlight the second parameter of the callee, rather
+   than its name.  */
+
+/* decl, with argname.  */
+
+extern int callee_1 (int one, const char *two, float three); /* { dg-line callee_1 } */
+
+int test_1 (int first, int second, float third)
+{
+  return callee_1 (first, second, third); /* { dg-warning "passing argument 2 of 'callee_1' makes pointer from integer without a cast" }  */
+  /* { dg-begin-multiline-output "" }
+   return callee_1 (first, second, third);
+                           ^~~~~~
+     { dg-end-multiline-output "" } */
+  /* { dg-message "expected 'const char \\*' but argument is of type 'int'" "" { target *-*-* } callee_1 } */
+  /* { dg-begin-multiline-output "" }
+ extern int callee_1 (int one, const char *two, float three);
+            ^~~~~~~~
+     { dg-end-multiline-output "" } */
+}
+
+/* decl, without argname.  */
+
+extern int callee_2 (int, const char *, float); /* { dg-line callee_2 } */
+
+int test_2 (int first, int second, float third)
+{
+  return callee_2 (first, second, third); /* { dg-warning "passing argument 2 of 'callee_2' makes pointer from integer without a cast" } */
+  /* { dg-begin-multiline-output "" }
+   return callee_2 (first, second, third);
+                           ^~~~~~
+     { dg-end-multiline-output "" } */
+  /* { dg-message "expected 'const char \\*' but argument is of type 'int'" "" { target *-*-* } callee_2 } */
+  /* { dg-begin-multiline-output "" }
+ extern int callee_2 (int, const char *, float);
+            ^~~~~~~~
+     { dg-end-multiline-output "" } */
+}
+
+/* defn, with argname.  */
+
+static int callee_3 (int one, const char *two, float three) /* { dg-line callee_3 } */
+{
+  return callee_2 (one, two, three);
+}
+
+int test_3 (int first, int second, float third)
+{
+  return callee_3 (first, second, third); // { dg-warning "passing argument 2 of 'callee_3' makes pointer from integer without a cast" }
+  /* { dg-begin-multiline-output "" }
+   return callee_3 (first, second, third);
+                           ^~~~~~
+     { dg-end-multiline-output "" } */
+  /* { dg-message "expected 'const char \\*' but argument is of type 'int'" "" { target *-*-* } callee_3 } */
+  /* { dg-begin-multiline-output "" }
+ static int callee_3 (int one, const char *two, float three)
+            ^~~~~~~~
+     { dg-end-multiline-output "" } */
+}
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH 10/17] C++: provide fix-it hints in -Wsuggest-override
  2017-07-24 19:31 [PATCH 00/17] RFC: New source-location representation; Language Server Protocol David Malcolm
                   ` (3 preceding siblings ...)
  2017-07-24 19:31 ` [PATCH 12/17] Add server.h and server.c David Malcolm
@ 2017-07-24 19:31 ` David Malcolm
  2017-07-24 19:31 ` [PATCH 11/17] Add JSON implementation David Malcolm
                   ` (14 subsequent siblings)
  19 siblings, 0 replies; 38+ messages in thread
From: David Malcolm @ 2017-07-24 19:31 UTC (permalink / raw)
  To: gcc-patches; +Cc: David Malcolm

This patch to the C++ frontend extends -Wsuggest-override so that the
suggestions contain fix-it hints, provided that it can get at the correct
location.  Doing so requires the blt_node infrastructure.

gcc/cp/ChangeLog:
	* class.c: Include "gcc-rich-location.h" and "blt.h".
	(check_for_override): When suggesting functions that can be marked
	"override", if the location is available via BLT nodes, add a
	fix-it hint.

gcc/testsuite/ChangeLog:
	* g++.dg/warn/Wsuggest-override.C: Add -fdiagnostics-show-caret and
	-fblt to the options.  Update expected output to show the fix-it
	hints.
---
 gcc/cp/class.c                                | 23 +++++++++++++++++++++--
 gcc/testsuite/g++.dg/warn/Wsuggest-override.C | 12 +++++++++++-
 2 files changed, 32 insertions(+), 3 deletions(-)

diff --git a/gcc/cp/class.c b/gcc/cp/class.c
index 0336ae5..6d0644d 100644
--- a/gcc/cp/class.c
+++ b/gcc/cp/class.c
@@ -37,6 +37,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "gimplify.h"
 #include "intl.h"
 #include "asan.h"
+#include "gcc-rich-location.h"
+#include "blt.h"
 
 /* Id for dumping the class hierarchy.  */
 int class_dump_id;
@@ -3024,8 +3026,25 @@ check_for_override (tree decl, tree ctype)
       overrides_found = true;
       if (warn_override && !DECL_OVERRIDE_P (decl)
 	  && !DECL_DESTRUCTOR_P (decl))
-	warning_at (DECL_SOURCE_LOCATION (decl), OPT_Wsuggest_override,
-		    "%qD can be marked override", decl);
+	{
+	  gcc_rich_location richloc (DECL_SOURCE_LOCATION (decl));
+	  /* Attempt to suggest adding " override" after the declarator
+	     (and before the semicolon).  */
+	  blt_node *asnode = blt_get_node_for_tree (decl);
+	  if (asnode)
+	    {
+	      /* Expect a member-declaration containing a
+		 decl-specifier-seq declarator token(;).
+		 We want the declarator.  */
+	      blt_node *declarator
+		= asnode->get_first_child_of_kind (BLT_DECLARATOR);
+	      if (declarator)
+		richloc.add_fixit_insert_after (declarator->get_finish (),
+						" override");
+	    }
+	  warning_at_rich_loc (&richloc, OPT_Wsuggest_override,
+			       "%qD can be marked override", decl);
+	}
     }
 
   if (DECL_VIRTUAL_P (decl))
diff --git a/gcc/testsuite/g++.dg/warn/Wsuggest-override.C b/gcc/testsuite/g++.dg/warn/Wsuggest-override.C
index f820f4b..8f5c133 100644
--- a/gcc/testsuite/g++.dg/warn/Wsuggest-override.C
+++ b/gcc/testsuite/g++.dg/warn/Wsuggest-override.C
@@ -1,5 +1,5 @@
 // { dg-do compile }
-// { dg-options "-std=c++11 -Wsuggest-override" }
+// { dg-options "-std=c++11 -Wsuggest-override -fdiagnostics-show-caret -fblt" }
 struct A
 {
 	A();
@@ -16,8 +16,18 @@ struct B : A
 	B();
 	virtual ~B();
 	virtual void f(); // { dg-warning "can be marked override" }
+/* { dg-begin-multiline-output "" }
+  virtual void f();
+               ^
+                   override
+   { dg-end-multiline-output "" } */
 virtual int bar() override;
 int c();
 operator int();
 virtual operator float(); // { dg-warning "can be marked override" }
+/* { dg-begin-multiline-output "" }
+ virtual operator float();
+         ^~~~~~~~
+                          override
+   { dg-end-multiline-output "" } */
 };
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH 15/17] Language Server Protocol: add lsp::server abstract base class
  2017-07-24 19:31 [PATCH 00/17] RFC: New source-location representation; Language Server Protocol David Malcolm
  2017-07-24 19:31 ` [PATCH 03/17] Core of BLT implementation David Malcolm
@ 2017-07-24 19:31 ` David Malcolm
  2017-07-27  4:14   ` Trevor Saunders
  2017-07-27  7:55   ` Richard Biener
  2017-07-24 19:31 ` [PATCH 02/17] diagnostics: support prefixes within diagnostic_show_locus David Malcolm
                   ` (17 subsequent siblings)
  19 siblings, 2 replies; 38+ messages in thread
From: David Malcolm @ 2017-07-24 19:31 UTC (permalink / raw)
  To: gcc-patches; +Cc: David Malcolm

This patch adds an lsp::server abstract base class for implementing
servers for the Language Server Protocol:
  https://github.com/Microsoft/language-server-protocol

along with supporting classes mirroring those from the protocol
description.

The public specification of the protocol uses CamelCase, and so these
classes use CamelCase, to mirror the protocol definition.

Only a small subset of the protocol is implemented, enough for
a proof-of-concept.

Ideally much/all of this would be autogenerated from the
protocol definition.

The patch also implements an ::lsp::jsonrpc_server subclass of
::jsonrpc::server, handling the marshalling from JSON-RPC to
an lsp::server instance, expressing them as vfunc calls.

gcc/ChangeLog:
	* Makefile.in (OBJS): Add lsp.o.
	* lsp.c: New file.
	* lsp.h: New file.
	* selftest-run-tests.c (selftest::run_tests): Call
	selftest::lsp_c_tests.
	* selftest.h (selftest::lsp_c_tests): New decl.
---
 gcc/Makefile.in          |   1 +
 gcc/lsp.c                | 291 +++++++++++++++++++++++++++++++++++++++++++++++
 gcc/lsp.h                | 210 ++++++++++++++++++++++++++++++++++
 gcc/selftest-run-tests.c |   1 +
 gcc/selftest.h           |   1 +
 5 files changed, 504 insertions(+)
 create mode 100644 gcc/lsp.c
 create mode 100644 gcc/lsp.h

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 1f9050c..e5120c2 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1382,6 +1382,7 @@ OBJS = \
 	loop-iv.o \
 	loop-unroll.o \
 	lower-subreg.o \
+	lsp.o \
 	lra.o \
 	lra-assigns.o \
 	lra-coalesce.o \
diff --git a/gcc/lsp.c b/gcc/lsp.c
new file mode 100644
index 0000000..3b79794
--- /dev/null
+++ b/gcc/lsp.c
@@ -0,0 +1,291 @@
+/* Language Server Protocol implementation.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "json.h"
+#include "http-server.h"
+#include "json-rpc.h"
+#include "lsp.h"
+#include "selftest.h"
+
+using namespace jsonrpc;
+using namespace lsp;
+
+// TODO: autogenerate the interface binding/marshalling/demarshalling code
+// from an interface description.
+
+#define SET_VALUE(LHS, VALUE, NAME)				\
+  do {								\
+    if (!(VALUE)->get_value_by_key ((NAME), (LHS), out_err))	\
+      return result;						\
+  } while (0)
+
+#define SET_NUMBER(LHS, VALUE, NAME)				\
+  do {								\
+    if (!(VALUE)->get_int_by_key ((NAME), (LHS), out_err))	\
+      return result;						\
+  } while (0)
+
+#define SET_STRING(LHS, VALUE, NAME)				\
+  do {								\
+    if (!(VALUE)->get_string_by_key ((NAME), (LHS), out_err))	\
+      return result;						\
+  } while (0)
+
+Position
+Position::from_json (const json::value *params,
+		     char *&out_err)
+{
+  Position result;
+  SET_NUMBER (result.line, params, "line");
+  SET_NUMBER (result.character, params, "character");
+  return result;
+}
+
+TextDocumentIdentifier
+TextDocumentIdentifier::from_json (const json::value *params,
+				   char *&out_err)
+{
+  TextDocumentIdentifier result;
+  SET_STRING (result.uri, params, "uri");
+  return result;
+}
+
+TextDocumentItem
+TextDocumentItem::from_json (const json::value *params,
+			     char *&out_err)
+{
+  TextDocumentItem result;
+  SET_STRING (result.uri, params, "uri");
+  SET_STRING (result.languageId, params, "languageId");
+  SET_NUMBER (result.version, params, "version");
+  SET_STRING (result.text, params, "text");
+  return result;
+}
+
+DidOpenTextDocumentParams
+DidOpenTextDocumentParams::from_json (const json::value *params,
+				      char *&out_err)
+{
+  DidOpenTextDocumentParams result;
+  // FIXME: error-handling
+  const json::value *text_document;
+  SET_VALUE (text_document, params, "textDocument");
+  result.textDocument
+    = TextDocumentItem::from_json (text_document, out_err);
+  return result;
+}
+
+DidChangeTextDocumentParams
+DidChangeTextDocumentParams::from_json (const json::value */*params*/,
+					char *&/*out_err*/)
+{
+  DidChangeTextDocumentParams result;
+
+  // FIXME
+  return result;
+}
+
+TextDocumentPositionParams
+TextDocumentPositionParams::from_json (const json::value *params,
+				       char *&out_err)
+{
+  TextDocumentPositionParams result;
+  const json::value *text_document;
+  const json::value *position;
+  SET_VALUE (text_document, params, "textDocument");
+  SET_VALUE (position, params, "position");
+  result.textDocument
+    = TextDocumentIdentifier::from_json (text_document, out_err);
+  result.position
+    = Position::from_json (position, out_err);
+  return result;
+}
+
+/* struct Position.  */
+
+json::value *
+Position::to_json () const
+{
+  json::object *result = new json::object ();
+  result->set ("line", new json::number (line));
+  result->set ("character", new json::number (character));
+  return result;
+}
+
+/* struct Range.  */
+
+json::value *
+Range::to_json () const
+{
+  json::object *result = new json::object ();
+  result->set ("start", start.to_json ());
+  result->set ("end", end.to_json ());
+  return result;
+}
+
+/* struct Location.  */
+
+json::value *
+Location::to_json () const
+{
+  json::object *result = new json::object ();
+  result->set ("uri", new json::string (uri));
+  result->set ("range", range.to_json ());
+  return result;
+}
+
+/* class lsp::jsonrpc_server : public ::jsonrpc::server.  */
+
+json::value *
+lsp::jsonrpc_server::dispatch (const char *method, const json::value *params,
+			       const json::value *id)
+{
+  if (0 == strcmp (method, "initialize"))
+    return do_initialize (id, params);
+  if (0 == strcmp (method, "textDocument/didOpen"))
+    return do_text_document_did_open (params);
+  if (0 == strcmp (method, "textDocument/didChange"))
+    return do_text_document_did_change (params);
+  if (0 == strcmp (method, "textDocument/definition"))
+    return do_text_document_definition (params);
+  return make_method_not_found (id, method);
+}
+
+json::value *
+lsp::jsonrpc_server::do_initialize (const json::value *id,
+				    const json::value */*params*/)
+{
+  // FIXME: for now, ignore params
+
+  json::object *server_caps = new json::object ();
+  json::object *result = new json::object ();
+  result->set ("capabilities", server_caps);
+  return make_success (id, result);
+}
+
+static json::value *
+make_invalid_params_and_free_msg (const json::value *id, char *msg)
+{
+  json::value *err = make_invalid_params (id, msg);
+  free (msg);
+  return err;
+}
+
+json::value *
+lsp::jsonrpc_server::do_text_document_did_open (const json::value *params)
+{
+  char *err = NULL;
+  DidOpenTextDocumentParams p
+    = DidOpenTextDocumentParams::from_json (params, err);
+  if (err)
+    return make_invalid_params_and_free_msg (NULL, err); // though we ought not to return non-NULL for a notification
+
+  m_inner.do_text_document_did_open (p);
+  return NULL; // notification, so no response
+}
+
+json::value *
+lsp::jsonrpc_server::do_text_document_did_change (const json::value *params)
+{
+  char *err = NULL;
+  DidChangeTextDocumentParams p
+    = DidChangeTextDocumentParams::from_json (params, err);
+  if (err)
+    return make_invalid_params_and_free_msg (NULL, err); // though we ought not to return non-NULL for a notification
+
+  m_inner.do_text_document_did_change (p);
+
+  return NULL; // notification, so no response
+}
+
+json::value *
+lsp::jsonrpc_server::do_text_document_definition (const json::value *params)
+{
+  char *err = NULL;
+  TextDocumentPositionParams p
+    = TextDocumentPositionParams::from_json (params, err);
+  if (err)
+    return make_invalid_params_and_free_msg (NULL, err);
+
+  auto_vec<Location> out;
+  m_inner.do_text_document_definition (p, out);
+
+  json::array *result = new json::array ();
+  unsigned i;
+  Location *loc;
+  FOR_EACH_VEC_ELT (out, i, loc)
+    result->append (loc->to_json ());
+  return result;
+}
+
+#if CHECKING_P
+
+namespace selftest {
+
+/* Selftests.  */
+
+static void
+test_simple ()
+{
+  noop_server noop;
+  lsp::jsonrpc_server js (false, noop);
+  const char *init_request
+    = ("{\"jsonrpc\": \"2.0\", \"method\": \"initialize\","
+       " \"params\": [42, 23], \"id\": 1}"); // FIXME
+  json::value *init_response = js.handle_request_string (init_request);
+  //const json::value *result = assert_is_success (response, 1);
+  //ASSERT_EQ (19, result->as_number ()->get ());
+  delete init_response;
+
+  const char *did_open_note
+    = ("{\"jsonrpc\": \"2.0\", \"method\": \"textDocument/didOpen\","
+       " \"params\": {"
+       " \"textDocument\": "
+       " { \"uri\": \"DocumentUri goes here\","
+         " \"languageId\": \"c++\","
+         " \"version\": 0,"
+         " \"text\": \"/* Initial content.  */\"}}}");
+  json::value *did_open_response = js.handle_request_string (did_open_note);
+  delete did_open_response;
+
+  const char *did_change_note
+    = ("{\"jsonrpc\": \"2.0\", \"method\": \"textDocument/didChange\","
+       " \"params\": {"
+         " \"textDocument\": {\"version\": 1},"
+         " \"contentChanges\": [{\"text\": \"/* Hello world.  */\"}]"
+       "}}"); // FIXME
+  json::value *did_change_response = js.handle_request_string (did_change_note);
+  delete did_change_response;
+}
+
+
+/* Run all of the selftests within this file.  */
+
+void
+lsp_c_tests ()
+{
+  test_simple ();
+}
+
+} // namespace selftest
+
+#endif /* #if CHECKING_P */
diff --git a/gcc/lsp.h b/gcc/lsp.h
new file mode 100644
index 0000000..d264cbf
--- /dev/null
+++ b/gcc/lsp.h
@@ -0,0 +1,210 @@
+/* Language Server Protocol implementation.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_LSP_H
+#define GCC_LSP_H
+
+namespace lsp {
+
+typedef const char *DocumentUri;
+
+/* Interfaces from the protocol specification (which uses camel case).  */
+
+/* Note that LSP uses 0-based lines and characters, whereas GCC uses
+   1-based lines and columns.  */
+
+struct Position
+{
+  Position ()
+  : line (0), character (0) {}
+
+  static Position from_json (const json::value *params,
+			     char *&out_err);
+  json::value *to_json () const;
+
+  int line;
+  int character;
+};
+
+struct Range
+{
+  Range ()
+  : start (), end () {}
+
+  json::value *to_json () const;
+
+  Position start;
+  Position end;
+};
+
+struct Location
+{
+  Location ()
+  : uri (NULL), range () {}
+
+  json::value *to_json () const;
+
+  DocumentUri uri;
+  Range range;
+};
+
+// Exceptions would be nicer than passing around the out_err
+
+// TODO: autogenerate the interface binding/marshalling/demarshalling code
+// from an interface description.
+
+struct TextDocumentIdentifier
+{
+  TextDocumentIdentifier ()
+  : uri (NULL) {}
+
+  static TextDocumentIdentifier from_json (const json::value *params,
+					   char *&out_err);
+
+  DocumentUri uri;
+};
+
+struct TextDocumentItem
+{
+  TextDocumentItem ()
+  : uri (NULL), languageId (NULL), version (0), text (NULL)
+  {}
+
+  static TextDocumentItem from_json (const json::value *params,
+				     char *&out_err);
+
+  DocumentUri uri;
+  const char *languageId;
+  int version;
+  const char *text;
+};
+
+struct DidOpenTextDocumentParams
+{
+  DidOpenTextDocumentParams ()
+  : textDocument () {}
+
+  static DidOpenTextDocumentParams from_json (const json::value *params,
+					      char *&out_err);
+
+  TextDocumentItem textDocument;
+};
+
+struct DidChangeTextDocumentParams
+{
+ public:
+  static DidChangeTextDocumentParams from_json (const json::value *params,
+						char *&out_err);
+
+ private:
+#if 0
+  VersionedTextDocumentIdentifier textDocument;
+  auto_vec<TextDocumentContentChangeEvent> contentChanges;
+#endif
+};
+
+struct TextDocumentPositionParams
+{
+  TextDocumentPositionParams ()
+  : textDocument (), position () {}
+
+  static TextDocumentPositionParams from_json (const json::value *params,
+					       char *&out_err);
+
+  TextDocumentIdentifier textDocument;
+  Position position;
+};
+
+/* An abstract base class for implementing the LSP as vfunc calls,
+   avoiding dealing with JSON.  */
+
+class server
+{
+ public:
+  virtual ~server () {}
+
+  virtual void
+  do_text_document_did_open (const DidOpenTextDocumentParams &p) = 0;
+
+  virtual void
+  do_text_document_did_change (const DidChangeTextDocumentParams &p) = 0;
+
+  virtual void
+  do_text_document_definition (const TextDocumentPositionParams &p,
+			       vec<Location> &out) = 0;
+};
+
+/* A concrete subclass of lsp::server that implements everything as a no-op.  */
+
+class noop_server : public server
+{
+  void
+  do_text_document_did_open (const DidOpenTextDocumentParams &) OVERRIDE
+  {
+    // no-op
+  }
+
+  void
+  do_text_document_did_change (const DidChangeTextDocumentParams &) OVERRIDE
+  {
+    // no-op
+  }
+
+  void
+  do_text_document_definition (const TextDocumentPositionParams &,
+			       vec<Location> &) OVERRIDE
+  {
+    // no-op
+  }
+};
+
+/* A jsonrpc::server subclass that decodes incoming JSON-RPC requests
+   and dispatches them to an lsp::server instance as vfunc calls,
+   marshalling the inputs/outputs to/from JSON objects.  */
+
+class jsonrpc_server : public ::jsonrpc::server
+{
+ public:
+  jsonrpc_server (bool verbose, ::lsp::server &inner)
+  : server (verbose), m_inner (inner) {}
+
+  json::value *
+  dispatch (const char *method, const json::value *params,
+	    const json::value *id) FINAL OVERRIDE;
+
+ private:
+  json::value *
+  do_initialize (const json::value *id, const json::value *params);
+
+  json::value *
+  do_text_document_did_open (const json::value *params);
+
+  json::value *
+  do_text_document_did_change (const json::value *params);
+
+  json::value *
+  do_text_document_definition (const json::value *params);
+
+ private:
+  ::lsp::server &m_inner;
+};
+
+} // namespace lsp
+
+#endif  /* GCC_LSP_H  */
diff --git a/gcc/selftest-run-tests.c b/gcc/selftest-run-tests.c
index 35ab965..5209aa8 100644
--- a/gcc/selftest-run-tests.c
+++ b/gcc/selftest-run-tests.c
@@ -70,6 +70,7 @@ selftest::run_tests ()
   json_c_tests ();
   http_server_c_tests ();
   json_rpc_c_tests ();
+  lsp_c_tests ();
 
   /* Mid-level data structures.  */
   input_c_tests ();
diff --git a/gcc/selftest.h b/gcc/selftest.h
index 2312fb2..1655125 100644
--- a/gcc/selftest.h
+++ b/gcc/selftest.h
@@ -187,6 +187,7 @@ extern void http_server_c_tests ();
 extern void input_c_tests ();
 extern void json_c_tests ();
 extern void json_rpc_c_tests ();
+extern void lsp_c_tests ();
 extern void pretty_print_c_tests ();
 extern void read_rtl_function_c_tests ();
 extern void rtl_tests_c_tests ();
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH 11/17] Add JSON implementation
  2017-07-24 19:31 [PATCH 00/17] RFC: New source-location representation; Language Server Protocol David Malcolm
                   ` (4 preceding siblings ...)
  2017-07-24 19:31 ` [PATCH 10/17] C++: provide fix-it hints in -Wsuggest-override David Malcolm
@ 2017-07-24 19:31 ` David Malcolm
  2017-09-01 17:06   ` Jeff Law
  2017-07-24 19:31 ` [PATCH 01/17] Add param-type-mismatch.c/C testcases as a baseline David Malcolm
                   ` (13 subsequent siblings)
  19 siblings, 1 reply; 38+ messages in thread
From: David Malcolm @ 2017-07-24 19:31 UTC (permalink / raw)
  To: gcc-patches; +Cc: David Malcolm

This patch is the first part of the Language Server Protocol
server proof-of-concept.

It adds support to gcc for reading and writing JSON,
based on DOM-like trees of "json::value" instances.

gcc/ChangeLog:
	* Makefile.in (OBJS): Add json.o.
	* json.c: New file.
	* json.h: New file.
	* selftest-run-tests.c (selftest::run_tests): Call json_c_tests.
	* selftest.h (selftest::json_c_tests): New decl.
---
 gcc/Makefile.in          |    1 +
 gcc/json.c               | 1914 ++++++++++++++++++++++++++++++++++++++++++++++
 gcc/json.h               |  214 ++++++
 gcc/selftest-run-tests.c |    1 +
 gcc/selftest.h           |    1 +
 5 files changed, 2131 insertions(+)
 create mode 100644 gcc/json.c
 create mode 100644 gcc/json.h

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 519ada0..77ecbd6 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1369,6 +1369,7 @@ OBJS = \
 	ira-color.o \
 	ira-emit.o \
 	ira-lives.o \
+	json.o \
 	jump.o \
 	langhooks.o \
 	lcm.o \
diff --git a/gcc/json.c b/gcc/json.c
new file mode 100644
index 0000000..9f8b456
--- /dev/null
+++ b/gcc/json.c
@@ -0,0 +1,1914 @@
+/* JSON parsing
+   Copyright (C) 2017 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "json.h"
+#include "pretty-print.h"
+#include "math.h"
+#include "selftest.h"
+
+using namespace json;
+
+/* class json::value.  */
+
+/* Generate a char * for this json::value tree.
+   The returned value must be freed by the caller.  */
+
+char *
+value::to_str () const
+{
+  pretty_printer pp;
+  print (&pp);
+  return xstrdup (pp_formatted_text (&pp));
+}
+
+/* Dump this json::value tree to OUTF.
+   No formatting is done.  There are no guarantees about the order
+   in which the key/value pairs of json::objects are printed.  */
+
+void
+value::dump (FILE *outf) const
+{
+  pretty_printer pp;
+  pp_buffer (&pp)->stream = outf;
+  print (&pp);
+  pp_flush (&pp);
+}
+
+/* If this json::value is a json::object, return it,
+   otherwise return NULL.  */
+
+const object *
+value::as_object () const
+{
+  if (get_kind () != JSON_OBJECT)
+    return NULL;
+  return static_cast <const object *> (this);
+}
+
+/* If this json::value is a json::array, return it,
+   otherwise return NULL.  */
+
+const array *
+value::as_array () const
+{
+  if (get_kind () != JSON_ARRAY)
+    return NULL;
+  return static_cast <const array *> (this);
+}
+
+/* If this json::value is a json::number, return it,
+   otherwise return NULL.  */
+
+const number *
+value::as_number () const
+{
+  if (get_kind () != JSON_NUMBER)
+    return NULL;
+  return static_cast <const number *> (this);
+}
+
+/* If this json::value is a json::string, return it,
+   otherwise return NULL.  */
+
+const string *
+value::as_string () const
+{
+  if (get_kind () != JSON_STRING)
+    return NULL;
+  return static_cast <const string *> (this);
+}
+
+/* Attempt to get the value of a key/value pair from this value
+   as if THIS value were an object.
+
+   If THIS is not a json::object, return write an error message to OUT_ERR
+   (which must be freed by the caller) and return false.
+
+   Otherwise write the value ptr (possibly NULL) to OUT_VALUE and
+   return true.  */
+
+bool
+value::get_optional_value_by_key (const char *name, const value *&out_value,
+				  char *&out_err) const
+{
+  const json::object *obj = as_object ();
+  if (!obj)
+    {
+      out_err = xstrdup ("not an object");
+      return false;
+    }
+  out_value = obj->get (name);
+  return true;
+}
+
+/* Attempt to get a string value of a key/value pair from this value
+   as if THIS value were an object.
+
+   If THIS is a json::object, and KEY is either not present, is a string,
+   or is the "null" JSON literal, then return true, and write to OUT_VALUE.
+   If a string, then the ptr is written to OUT_VALUE, otherwise NULL
+   is written to OUT_VALUE.
+
+   If THIS is not a json::object, or KEY is not a string/"null",
+   return false and write an error message to OUT_ERR
+   (which must be freed by the caller).  */
+
+bool
+value::get_optional_string_by_key (const char *name, const char *&out_value,
+				   char *&out_err) const
+{
+  const json::value *v;
+  if (!get_optional_value_by_key (name, v, out_err))
+    return false;
+  if (v && v->get_kind () != JSON_NULL)
+    {
+      const json::string *s = v->as_string ();
+      if (!s)
+	{
+	  out_err = xasprintf ("not a string: \"%s\"", name);
+	  return false;
+	}
+      out_value = s->get_string ();
+      return true;
+    }
+  else
+    {
+      out_value = NULL;
+      return true;
+    }
+}
+
+/* Attempt to get lookup the value of a key/value pair from this value
+   as if this value were an object.
+
+   To succeed, THIS must be a json::object, and it must have a key named
+   NAME.
+
+   On success, return true and write the value to OUT_VALUE.
+   On failure, return false and write an error message to OUT_ERR
+   (which must be freed by the caller).  */
+
+bool
+value::get_value_by_key (const char *name, const value *&out_value,
+			 char *&out_err) const
+{
+  const json::object *obj = as_object ();
+  if (!obj)
+    {
+      out_err = xstrdup ("not an object");
+      return false;
+    }
+  const json::value *v = obj->get (name);
+  if (!v)
+    {
+      out_err = xasprintf ("missing attribute: \"%s\"", name);
+      return false;
+    }
+  out_value = v;
+  return true;
+}
+
+/* As value::get_value_by_key, but the value must be a number;
+   if successful, write it as an int to OUT_VALUE.  */
+
+bool
+value::get_int_by_key (const char *name, int &out_value, char *&out_err) const
+{
+  const json::value *v;
+  if (!get_value_by_key (name, v, out_err))
+    return false;
+  const json::number *n = v->as_number ();
+  if (!n)
+    {
+      out_err = xasprintf ("not a number: \"%s\"", name);
+      return false;
+    }
+  out_value = n->get ();
+  return true;
+}
+
+/* As value::get_value_by_key, but the value must be a string;
+   if successful, write it as const char * to OUT_VALUE.  */
+
+bool
+value::get_string_by_key (const char *name, const char *&out_value,
+			  char *&out_err) const
+{
+  const json::value *v;
+  if (!get_value_by_key (name, v, out_err))
+    return false;
+  const json::string *s = v->as_string ();
+  if (!s)
+    {
+      out_err = xasprintf ("not a string: \"%s\"", name);
+      return false;
+    }
+  out_value = s->get_string ();
+  return true;
+}
+
+/* As value::get_value_by_key, but the value must be an array;
+   if successful, write it as a json::array * to OUT_VALUE.  */
+
+bool
+value::get_array_by_key (const char *name, const array *&out_value,
+			 char *&out_err) const
+{
+  const json::value *v;
+  if (!get_value_by_key (name, v, out_err))
+    return false;
+  const json::array *arr = v->as_array ();
+  if (!arr)
+    {
+      out_err = xasprintf ("not an array: \"%s\"", name);
+      return false;
+    }
+  out_value = arr;
+  return true;
+}
+
+/* class json::object, a subclass of json::value, representing
+   an unordered collection of key/value pairs.  */
+
+/* json:object's dtor.  */
+
+object::~object ()
+{
+  for (map_t::iterator it = m_map.begin (); it != m_map.end (); ++it)
+    {
+      free (const_cast <char *>((*it).first));
+      delete ((*it).second);
+    }
+}
+
+/* Implementation of json::value::print for json::object.  */
+
+void
+object::print (pretty_printer *pp) const
+{
+  /* Note that the order is not guaranteed.  */
+  pp_character (pp, '{');
+  for (map_t::iterator it = m_map.begin (); it != m_map.end (); ++it)
+    {
+      if (it != m_map.begin ())
+	pp_string (pp, ", ");
+      const char *key = const_cast <char *>((*it).first);
+      value *value = (*it).second;
+      pp_printf (pp, "\"%s\": ", key); // FIXME: escaping?
+      value->print (pp);
+    }
+  pp_character (pp, '}');
+}
+
+/* Implementation of json::value::clone for json::object.  */
+
+value *
+object::clone () const
+{
+  object *other = new object ();
+  for (map_t::iterator it = m_map.begin (); it != m_map.end (); ++it)
+    {
+      const char *key = const_cast <char *>((*it).first);
+      value *value = (*it).second;
+      other->set (key, value->clone ());
+    }
+  return other;
+}
+
+/* Get the json::value * for KEY, or NULL if the key is not present.  */
+
+value *
+object::get (const char *key) const
+{
+  value **slot = const_cast <object*> (this)->m_map.get (key);
+  if (slot)
+    return *slot;
+  return NULL;
+}
+
+/* As object::get (KEY), but return NULL if the value of the key
+   is the "null" JSON literal.  */
+
+value *
+object::get_if_nonnull (const char *key) const
+{
+  value *result = get (key);
+  if (!result)
+    return NULL;
+  if (result->get_kind () == JSON_NULL)
+    return NULL;
+  return result;
+}
+
+/* Set the json::value * for KEY, taking ownership of VALUE
+   (and taking a copy of KEY if necessary).  */
+
+void
+object::set (const char *key, value *v)
+{
+  value **ptr = m_map.get (key);
+  if (ptr)
+    {
+      /* If the key is already present, delete the existing value
+	 and overwrite it.  */
+      delete *ptr;
+      *ptr = v;
+    }
+  else
+    /* If the key wasn't already present, take a copy of the key,
+       and store the value.  */
+    m_map.put (xstrdup (key), v);
+}
+
+/* class json::array, a subclass of json::value, representing
+   an ordered collection of values.  */
+
+/* json::array's dtor.  */
+
+array::~array ()
+{
+  unsigned i;
+  value *v;
+  FOR_EACH_VEC_ELT (m_elements, i, v)
+    delete v;
+}
+
+/* Implementation of json::value::print for json::array.  */
+
+void
+array::print (pretty_printer *pp) const
+{
+  pp_character (pp, '[');
+  unsigned i;
+  value *v;
+  FOR_EACH_VEC_ELT (m_elements, i, v)
+    {
+      if (i)
+	pp_string (pp, ", ");
+      v->print (pp);
+    }
+  pp_character (pp, ']');
+}
+
+/* Implementation of json::value::clone for json::array.  */
+
+value *
+array::clone () const
+{
+  array *other = new array ();
+  unsigned i;
+  value *v;
+  FOR_EACH_VEC_ELT (m_elements, i, v)
+    other->append (v->clone ());
+  return other;
+}
+
+/* class json::number, a subclass of json::value, wrapping a double.  */
+
+/* Implementation of json::value::print for json::number.  */
+
+void
+number::print (pretty_printer *pp) const
+{
+  char tmp[1024];
+  snprintf (tmp, sizeof (tmp), "%g", m_value);
+  pp_string (pp, tmp);
+}
+
+/* Implementation of json::value::clone for json::number.  */
+
+value *
+number::clone () const
+{
+  return new number (m_value);
+}
+
+/* class json::string, a subclass of json::value.  */
+
+void
+string::print (pretty_printer *pp) const
+{
+  pp_character (pp, '"');
+  for (const char *ptr = m_utf8; *ptr; ptr++)
+    {
+      char ch = *ptr;
+      switch (ch)
+	{
+	case '"':
+	  pp_string (pp, "\\\"");
+	  break;
+	case '\\':
+	  pp_string (pp, "\\n");
+	  break;
+	case '\b':
+	  pp_string (pp, "\\b");
+	  break;
+	case '\f':
+	  pp_string (pp, "\\f");
+	  break;
+	case '\n':
+	  pp_string (pp, "\\n");
+	  break;
+	case '\r':
+	  pp_string (pp, "\\r");
+	  break;
+	case '\t':
+	  pp_string (pp, "\\t");
+	  break;
+
+	default:
+	  pp_character (pp, ch);
+	}
+    }
+  pp_character (pp, '"');
+}
+
+/* Implementation of json::value::clone for json::string.  */
+
+value *
+string::clone () const
+{
+  return new string (m_utf8);
+}
+
+/* class json::literal, a subclass of json::value.  */
+
+/* Implementation of json::value::print for json::literal.  */
+
+void
+literal::print (pretty_printer *pp) const
+{
+  switch (m_kind)
+    {
+    case JSON_TRUE:
+      pp_string (pp, "true");
+      break;
+    case JSON_FALSE:
+      pp_string (pp, "false");
+      break;
+    case JSON_NULL:
+      pp_string (pp, "null");
+      break;
+    default:
+      gcc_unreachable ();
+    }
+}
+
+/* Implementation of json::value::clone for json::literal.  */
+
+value *
+literal::clone () const
+{
+  return new literal (m_kind);
+}
+
+\f
+/* Declarations relating to parsing JSON, all within an
+   anonymous namespace.  */
+
+namespace {
+
+/* A typedef representing a single unicode character.  */
+
+typedef unsigned unichar;
+
+/* An enum for discriminating different kinds of JSON token.  */
+
+enum token_id
+{
+  TOK_ERROR,
+
+  TOK_EOF,
+
+  /* Punctuation.  */
+  TOK_OPEN_SQUARE,
+  TOK_OPEN_CURLY,
+  TOK_CLOSE_SQUARE,
+  TOK_CLOSE_CURLY,
+  TOK_COLON,
+  TOK_COMMA,
+
+  /* Literal names.  */
+  TOK_TRUE,
+  TOK_FALSE,
+  TOK_NULL,
+
+  TOK_STRING,
+  TOK_NUMBER
+};
+
+/* Human-readable descriptions of enum token_id.  */
+
+static const char *token_id_name[] = {
+  "error",
+  "EOF",
+  "'['",
+  "'{'",
+  "']'",
+  "'}'",
+  "':'",
+  "','",
+  "'true'",
+  "'false'",
+  "'null'",
+  "string",
+  "number"
+};
+
+/* Tokens within the JSON lexer.  */
+
+struct token
+{
+  /* The kind of token.  */
+  enum token_id id;
+
+  /* The location of this token within the unicode
+     character stream.  */
+  int index;
+
+  union
+  {
+    /* Value for TOK_ERROR and TOK_STRING.  */
+    char *string;
+
+    /* Value for TOK_NUMBER.  */
+    double number;
+  } u;
+};
+
+/* A class for lexing JSON.  */
+
+class lexer
+{
+ public:
+  lexer ();
+  ~lexer ();
+  bool add_utf8 (size_t length, const char *utf8_buf, char **err_out);
+
+  const token *peek ();
+  void consume ();
+
+ private:
+  bool get_char (unichar &out);
+  void unget_char ();
+  static void dump_token (FILE *outf, const token *tok);
+  void lex_token (token *out);
+  void lex_string (token *out);
+  void lex_number (token *out, unichar first_char);
+  bool rest_of_literal (const char *suffix);
+
+ private:
+  auto_vec<unichar> m_buffer;
+  int m_next_char_idx;
+
+  static const int MAX_TOKENS = 1;
+  token m_next_tokens[MAX_TOKENS];
+  int m_num_next_tokens;
+};
+
+/* A class for parsing JSON.  */
+
+class parser
+{
+ public:
+  parser (char **err_out);
+  bool add_utf8 (size_t length, const char *utf8_buf, char **err_out);
+  value *parse_value (int depth);
+  object *parse_object (int depth);
+  array *parse_array (int depth);
+
+  bool seen_error_p () const { return *m_err_out; }
+  void require_eof ();
+
+ private:
+  void require (enum token_id tok_id);
+  void error_at (int, const char *, ...) ATTRIBUTE_PRINTF_3;
+
+ private:
+  lexer m_lexer;
+  char **m_err_out;
+};
+
+} // anonymous namespace for parsing implementation
+
+/* Parser implementation.  */
+
+/* lexer's ctor.  */
+
+lexer::lexer ()
+: m_buffer (), m_next_char_idx (0), m_num_next_tokens (0)
+{
+}
+
+/* lexer's dtor.  */
+
+lexer::~lexer ()
+{
+  while (m_num_next_tokens > 0)
+    consume ();
+}
+
+/* Peek the next token.  */
+
+const token *
+lexer::peek ()
+{
+  if (m_num_next_tokens == 0)
+    {
+      lex_token (&m_next_tokens[0]);
+      m_num_next_tokens++;
+    }
+  return &m_next_tokens[0];
+}
+
+/* Consume the next token.  */
+
+void
+lexer::consume ()
+{
+  if (m_num_next_tokens == 0)
+    peek ();
+
+  gcc_assert (m_num_next_tokens > 0);
+  gcc_assert (m_num_next_tokens <= MAX_TOKENS);
+
+  if (0)
+    {
+      fprintf (stderr, "consuming token: ");
+      dump_token (stderr, &m_next_tokens[0]);
+      fprintf (stderr, "\n");
+    }
+
+  if (m_next_tokens[0].id == TOK_ERROR
+      || m_next_tokens[0].id == TOK_STRING)
+    free (m_next_tokens[0].u.string);
+
+  m_num_next_tokens--;
+  memmove (&m_next_tokens[0], &m_next_tokens[1],
+	   sizeof (token) * m_num_next_tokens);
+}
+
+/* Add LENGTH bytes of UTF-8 encoded text from UTF8_BUF to this lexer's
+   buffer.  */
+
+bool
+lexer::add_utf8 (size_t length, const char *utf8_buf, char **err_out)
+{
+  /* FIXME: adapted from charset.c:one_utf8_to_cppchar.  */
+  static const uchar masks[6] = { 0x7F, 0x1F, 0x0F, 0x07, 0x03, 0x01 };
+  static const uchar patns[6] = { 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
+
+  const uchar *inbuf = (const unsigned char *) (utf8_buf);
+  const uchar **inbufp = &inbuf;
+  size_t *inbytesleftp = &length;
+
+  while (length > 0)
+    {
+      unichar c;
+      const uchar *inbuf = *inbufp;
+      size_t nbytes, i;
+
+      c = *inbuf;
+      if (c < 0x80)
+	{
+	  m_buffer.safe_push (c);
+	  *inbytesleftp -= 1;
+	  *inbufp += 1;
+	  continue;
+	}
+
+      /* The number of leading 1-bits in the first byte indicates how many
+	 bytes follow.  */
+      for (nbytes = 2; nbytes < 7; nbytes++)
+	if ((c & ~masks[nbytes-1]) == patns[nbytes-1])
+	  goto found;
+      *err_out = xstrdup ("ill-formed UTF-8 sequence");
+      return false;
+    found:
+
+      if (*inbytesleftp < nbytes)
+	{
+	  *err_out = xstrdup ("ill-formed UTF-8 sequence");
+	  return false;
+	}
+
+      c = (c & masks[nbytes-1]);
+      inbuf++;
+      for (i = 1; i < nbytes; i++)
+	{
+	  unichar n = *inbuf++;
+	  if ((n & 0xC0) != 0x80)
+	    {
+	      *err_out = xstrdup ("ill-formed UTF-8 sequence");
+	      return false;
+	    }
+	  c = ((c << 6) + (n & 0x3F));
+	}
+
+      /* Make sure the shortest possible encoding was used.  */
+      if ((   c <=      0x7F && nbytes > 1)
+	  || (c <=     0x7FF && nbytes > 2)
+	  || (c <=    0xFFFF && nbytes > 3)
+	  || (c <=  0x1FFFFF && nbytes > 4)
+	  || (c <= 0x3FFFFFF && nbytes > 5))
+	{
+	  *err_out = xstrdup ("ill-formed UTF-8:"
+			      " shortest possible encoding not used");
+	  return false;
+	}
+
+      /* Make sure the character is valid.  */
+      if (c > 0x7FFFFFFF || (c >= 0xD800 && c <= 0xDFFF))
+	{
+	  *err_out = xstrdup ("ill-formed UTF-8: invalid character");
+	  return false;
+	}
+
+      m_buffer.safe_push (c);
+      *inbufp = inbuf;
+      *inbytesleftp -= nbytes;
+    }
+  return true;
+}
+
+/* Attempt to get the next unicode character from this lexer's buffer.
+   If successful, write it to OUT and return true.
+   Otherwise, return false.  */
+
+bool
+lexer::get_char (unichar &out)
+{
+  if (m_next_char_idx >= (int)m_buffer.length ())
+    return false;
+
+  out = m_buffer[m_next_char_idx++];
+  return true;
+}
+
+/* FIXME.  */
+
+void
+lexer::unget_char ()
+{
+  --m_next_char_idx;
+}
+
+/* Print a textual representation of TOK to OUTF.
+   This is intended for debugging the lexer and parser,
+   rather than for user-facing output.  */
+
+void
+lexer::dump_token (FILE *outf, const token *tok)
+{
+  switch (tok->id)
+    {
+    case TOK_ERROR:
+      fprintf (outf, "TOK_ERROR (\"%s\")", tok->u.string);
+      break;
+
+    case TOK_EOF:
+      fprintf (outf, "TOK_EOF");
+      break;
+
+    case TOK_OPEN_SQUARE:
+      fprintf (outf, "TOK_OPEN_SQUARE");
+      break;
+
+    case TOK_OPEN_CURLY:
+      fprintf (outf, "TOK_OPEN_CURLY");
+      break;
+
+    case TOK_CLOSE_SQUARE:
+      fprintf (outf, "TOK_CLOSE_SQUARE");
+      break;
+
+    case TOK_CLOSE_CURLY:
+      fprintf (outf, "TOK_CLOSE_CURLY");
+      break;
+
+    case TOK_COLON:
+      fprintf (outf, "TOK_COLON");
+      break;
+
+    case TOK_COMMA:
+      fprintf (outf, "TOK_COMMA");
+      break;
+
+    case TOK_TRUE:
+      fprintf (outf, "TOK_TRUE");
+      break;
+
+    case TOK_FALSE:
+      fprintf (outf, "TOK_FALSE");
+      break;
+
+    case TOK_NULL:
+      fprintf (outf, "TOK_NULL");
+      break;
+
+    case TOK_STRING:
+      fprintf (outf, "TOK_STRING (\"%s\")", tok->u.string);
+      break;
+
+    case TOK_NUMBER:
+      fprintf (outf, "TOK_NUMBER (%f)", tok->u.number);
+      break;
+
+    default:
+      gcc_unreachable ();
+      break;
+    }
+}
+
+/* Attempt to lex the input buffer, writing the next token to OUT.
+   On errors, TOK_ERROR (or TOK_EOF) is written to OUT.  */
+
+void
+lexer::lex_token (token *out)
+{
+  /* Skip to next non-whitespace char.  */
+  unichar next_char;
+  while (1)
+    {
+      out->index = m_next_char_idx;
+      if (!get_char (next_char))
+	{
+	  out->id = TOK_EOF;
+	  return;
+	}
+      if (next_char != ' '
+	  && next_char != '\t'
+	  && next_char != '\n'
+	  && next_char != '\r')
+	break;
+    }
+
+  switch (next_char)
+    {
+    case '[':
+      out->id = TOK_OPEN_SQUARE;
+      break;
+
+    case '{':
+      out->id = TOK_OPEN_CURLY;
+      break;
+
+    case ']':
+      out->id = TOK_CLOSE_SQUARE;
+      break;
+
+    case '}':
+      out->id = TOK_CLOSE_CURLY;
+      break;
+
+    case ':':
+      out->id = TOK_COLON;
+      break;
+
+    case ',':
+      out->id = TOK_COMMA;
+      break;
+
+    case '"':
+      lex_string (out);
+      break;
+
+    case '-':
+    case '0':
+    case '1':
+    case '2':
+    case '3':
+    case '4':
+    case '5':
+    case '6':
+    case '7':
+    case '8':
+    case '9':
+      lex_number (out, next_char);
+      break;
+
+    case 't':
+      /* Handle literal "true".  */
+      if (rest_of_literal ("rue"))
+	{
+	  out->id = TOK_TRUE;
+	  break;
+	}
+      else
+	goto err;
+
+    case 'f':
+      /* Handle literal "false".  */
+      if (rest_of_literal ("alse"))
+	{
+	  out->id = TOK_FALSE;
+	  break;
+	}
+      else
+	goto err;
+
+    case 'n':
+      /* Handle literal "null".  */
+      if (rest_of_literal ("ull"))
+	{
+	  out->id = TOK_NULL;
+	  break;
+	}
+      else
+	goto err;
+
+    err:
+    default:
+      out->id = TOK_ERROR;
+      out->u.string = xasprintf ("unexpected character: %c", next_char);
+      break;
+    }
+}
+
+/* Having consumed an open-quote character from the lexer's buffer, attempt
+   to lex the rest of a JSON string, writing the result to OUT (or TOK_ERROR)
+   if an error occurred.
+   (ECMA-404 section 9; RFC 7159 section 7).  */
+
+void
+lexer::lex_string (token *out)
+{
+  auto_vec<unichar> content;
+  bool still_going = true;
+  while (still_going)
+    {
+      unichar uc;
+      if (!get_char (uc))
+	{
+	  out->id = TOK_ERROR;
+	  out->u.string = xstrdup ("EOF within string");
+	  return;
+	}
+      switch (uc)
+	{
+	case '"':
+	  still_going = false;
+	  break;
+	case '\\':
+	  {
+	    unichar next_char;
+	    if (!get_char (next_char))
+	      {
+		out->id = TOK_ERROR;
+		out->u.string = xstrdup ("EOF within string");;
+		return;
+	      }
+	    switch (next_char)
+	      {
+	      case '"':
+	      case '\\':
+	      case '/':
+		content.safe_push (next_char);
+		break;
+
+	      case 'b':
+		content.safe_push ('\b');
+		break;
+
+	      case 'f':
+		content.safe_push ('\f');
+		break;
+
+	      case 'n':
+		content.safe_push ('\n');
+		break;
+
+	      case 'r':
+		content.safe_push ('\r');
+		break;
+
+	      case 't':
+		content.safe_push ('\t');
+		break;
+
+	      case 'u':
+		{
+		  unichar result = 0;
+		  for (int i = 0; i < 4; i++)
+		    {
+		      unichar hexdigit;
+		      if (!get_char (hexdigit))
+			{
+			  out->id = TOK_ERROR;
+			  out->u.string = xstrdup ("EOF within string");
+			  return;
+			}
+		      result <<= 4;
+		      if (hexdigit >= '0' && hexdigit <= '9')
+			result += hexdigit - '0';
+		      else if (hexdigit >= 'a' && hexdigit <= 'f')
+			result += (hexdigit - 'a') + 10;
+		      else if (hexdigit >= 'A' && hexdigit <= 'F')
+			result += (hexdigit - 'A') + 10;
+		      else
+			{
+			  out->id = TOK_ERROR;
+			  out->u.string = xstrdup ("bogus hex char");
+			  return;
+			}
+		    }
+		  content.safe_push (result);
+		}
+		break;
+
+	      default:
+		out->id = TOK_ERROR;
+		out->u.string = xstrdup ("unrecognized escape char");
+		return;
+	      }
+	  }
+	  break;
+
+	default:
+	  /* Reject unescaped control characters U+0000 through U+001F
+	     (ECMA-404 section 9 para 1; RFC 7159 section 7 para 1).  */
+	  if (uc <= 0x1f)
+	    {
+		out->id = TOK_ERROR;
+		out->u.string = xstrdup ("unescaped control char");
+		return;
+	    }
+
+	  /* Otherwise, add regular unicode code point.  */
+	  content.safe_push (uc);
+	  break;
+	}
+    }
+
+  out->id = TOK_STRING;
+
+  auto_vec<char> utf8_buf;
+  // FIXME: adapted from libcpp/charset.c:one_cppchar_to_utf8
+  for (unsigned i = 0; i < content.length (); i++)
+    {
+      static const uchar masks[6] =  { 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
+      static const uchar limits[6] = { 0x80, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE };
+      size_t nbytes;
+      uchar buf[6], *p = &buf[6];
+      unichar c = content[i];
+
+      nbytes = 1;
+      if (c < 0x80)
+	*--p = c;
+      else
+	{
+	  do
+	    {
+	      *--p = ((c & 0x3F) | 0x80);
+	      c >>= 6;
+	      nbytes++;
+	    }
+	  while (c >= 0x3F || (c & limits[nbytes-1]));
+	  *--p = (c | masks[nbytes-1]);
+	}
+
+      while (p < &buf[6])
+	utf8_buf.safe_push (*p++);
+    }
+
+  out->u.string = XNEWVEC (char, utf8_buf.length () + 1);
+  for (unsigned i = 0; i < utf8_buf.length (); i++)
+    out->u.string[i] = utf8_buf[i];
+  out->u.string[utf8_buf.length ()] = '\0';
+
+  // FIXME: leaks?  have a json_context do the allocation
+}
+
+/* Having consumed FIRST_CHAR, an initial digit or '-' character from
+   the lexer's buffer attempt to lex the rest of a JSON number, writing
+   the result to OUT (or TOK_ERROR) if an error occurred.
+   (ECMA-404 section 8; RFC 7159 section 6).  */
+
+void
+lexer::lex_number (token *out, unichar first_char)
+{
+  bool negate = false;
+  double value = 0.0;
+  if (first_char == '-')
+    {
+      negate = true;
+      if (!get_char (first_char))
+	{
+	  out->id = TOK_ERROR;
+	  out->u.string = xstrdup ("expected digit");
+	  return;
+	}
+    }
+
+  if (first_char == '0')
+    value = 0.0;
+  else if (!ISDIGIT (first_char))
+    {
+      out->id = TOK_ERROR;
+      out->u.string = xstrdup ("expected digit");
+      return;
+    }
+  else
+    {
+      /* Got a nonzero digit; expect zero or more digits.  */
+      value = first_char - '0';
+      while (1)
+	{
+	  unichar uc;
+	  if (!get_char (uc))
+	    break;
+	  if (ISDIGIT (uc))
+	    {
+	      value *= 10;
+	      value += uc -'0';
+	      continue;
+	    }
+	  else
+	    {
+	      unget_char ();
+	      break;
+	    }
+	}
+    }
+
+  /* Optional '.', followed by one or more decimals.  */
+  unichar next_char;
+  if (get_char (next_char))
+    {
+      if (next_char == '.')
+	{
+	  /* Parse decimal digits.  */
+	  bool had_digit = false;
+	  // FIXME: does this lose too much precision?
+	  double digit_factor = 0.1;
+	  while (get_char (next_char))
+	    {
+	      if (!ISDIGIT (next_char))
+		{
+		  unget_char ();
+		  break;
+		}
+	      value += (next_char - '0') * digit_factor;
+	      digit_factor *= 0.1;
+	      had_digit = true;
+	    }
+	  if (!had_digit)
+	    {
+	      out->id = TOK_ERROR;
+	      out->u.string = xstrdup ("expected digit");
+	      return;
+	    }
+	}
+      else
+	unget_char ();
+    }
+
+  /* Parse 'e' and 'E'.  */
+  unichar exponent_char;
+  if (get_char (exponent_char))
+    {
+      if (exponent_char == 'e' || exponent_char == 'E')
+	{
+	  /* Optional +/-.  */
+	  unichar sign_char;
+	  int exponent = 0;
+	  bool negate_exponent = false;
+	  bool had_exponent_digit = false;
+	  if (!get_char (sign_char))
+	    {
+	      out->id = TOK_ERROR;
+	      out->u.string = xstrdup ("EOF within exponent");
+	      return;
+	    }
+	  if (sign_char == '-')
+	    negate_exponent = true;
+	  else if (sign_char == '+')
+	    ;
+	  else if (ISDIGIT (sign_char))
+	    {
+	      exponent = sign_char - '0';
+	      had_exponent_digit = true;
+	    }
+	  else
+	    {
+	      out->id = TOK_ERROR;
+	      out->u.string
+		= xstrdup ("expected '-','+' or digit within exponent");
+	      return;
+	    }
+
+	  /* One or more digits (we might have seen the digit above,
+	     though).  */
+	  while (1)
+	    {
+	      unichar uc;
+	      if (!get_char (uc))
+		break;
+	      if (ISDIGIT (uc))
+		{
+		  exponent *= 10;
+		  exponent += uc -'0';
+		  had_exponent_digit = true;
+		  continue;
+		}
+	      else
+		{
+		  unget_char ();
+		  break;
+		}
+	    }
+	  if (!had_exponent_digit)
+	    {
+	      out->id = TOK_ERROR;
+	      out->u.string = xstrdup ("expected digit within exponent");
+	      return;
+	    }
+	  if (negate_exponent)
+	    exponent = -exponent;
+	  /* FIXME: better way to do this?  */
+	  value = value * pow (10, exponent);
+	}
+      else
+	unget_char ();
+    }
+
+  if (negate)
+    value = -value;
+
+  out->id = TOK_NUMBER;
+  out->u.number = value;
+}
+
+/* Determine if the next characters to be lexed match SUFFIX.
+   SUFFIX must be pure ASCII.
+   If so, consume the characters and return true.
+   Otherwise, return false.  */
+
+bool
+lexer::rest_of_literal (const char *suffix)
+{
+  int suffix_idx = 0;
+  int buf_idx = m_next_char_idx;
+  while (1)
+    {
+      if (suffix[suffix_idx] == '\0')
+	{
+	  m_next_char_idx += suffix_idx;
+	  return true;
+	}
+      if (buf_idx >= (int)m_buffer.length ())
+	return false;
+      /* This assumes that suffix is ASCII.  */
+      if (m_buffer[buf_idx] != (unichar)suffix[suffix_idx])
+	return false;
+      buf_idx++;
+      suffix_idx++;
+    }
+}
+
+/* parser's ctor.  */
+
+parser::parser (char **err_out)
+: m_lexer (), m_err_out (err_out)
+{
+  gcc_assert (err_out);
+  gcc_assert (*err_out == NULL);
+  *err_out = NULL;
+}
+
+/* Add LENGTH bytes of UTF-8 encoded text from UTF8_BUF to this parser's
+   lexer's buffer.  */
+
+bool
+parser::add_utf8 (size_t length, const char *utf8_buf, char **err_out)
+{
+  return m_lexer.add_utf8 (length, utf8_buf, err_out);
+}
+
+/* Parse a JSON value (object, array, number, string, or literal).
+   (ECMA-404 section 5; RFC 7159 section 3).  */
+
+value *
+parser::parse_value (int depth)
+{
+  const token *tok = m_lexer.peek ();
+
+  /* Avoid stack overflow with deeply-nested inputs; RFC 7159 section 9
+     states: "An implementation may set limits on the maximum depth
+     of nesting.".
+
+     Ideally we'd avoid this limit (e.g. by rewriting parse_value,
+     parse_object, and parse_array into a single function with a vec of
+     state).  */
+  const int MAX_DEPTH = 100;
+  if (depth >= MAX_DEPTH)
+    {
+      error_at (tok->index, "maximum nesting depth exceeded: %i", MAX_DEPTH);
+      return NULL;
+    }
+
+  switch (tok->id)
+    {
+    case TOK_OPEN_CURLY:
+      return parse_object (depth);
+
+    case TOK_STRING:
+      {
+	string *result = new string (tok->u.string);
+	m_lexer.consume ();
+	return result;
+      }
+
+    case TOK_OPEN_SQUARE:
+      return parse_array (depth);
+
+    case TOK_NUMBER:
+      {
+	number *result = new number (tok->u.number);
+	m_lexer.consume ();
+	return result;
+      }
+
+    case TOK_TRUE:
+      {
+	literal *result = new literal (JSON_TRUE);
+	m_lexer.consume ();
+	return result;
+      }
+
+    case TOK_FALSE:
+      {
+	literal *result = new literal (JSON_FALSE);
+	m_lexer.consume ();
+	return result;
+      }
+
+    case TOK_NULL:
+      {
+	literal *result = new literal (JSON_NULL);
+	m_lexer.consume ();
+	return result;
+      }
+
+    default:
+      error_at (tok->index, "unexpected token: %s", token_id_name[tok->id]);
+      return NULL;
+    }
+}
+
+/* Parse a JSON object.
+   (ECMA-404 section 6; RFC 7159 section 4).  */
+
+object *
+parser::parse_object (int depth)
+{
+  require (TOK_OPEN_CURLY);
+
+  object *result = new object ();
+
+  const token *tok = m_lexer.peek ();
+  if (tok->id == TOK_CLOSE_CURLY)
+    {
+      require (TOK_CLOSE_CURLY);
+      return result;
+    }
+  if (tok->id != TOK_STRING)
+    {
+      error_at (tok->index, "expected string for object key");
+      return result;
+    }
+  while (!seen_error_p ())
+    {
+      tok = m_lexer.peek ();
+      if (tok->id != TOK_STRING)
+	{
+	  error_at (tok->index, "expected string for object key");
+	  return result;
+	}
+      char *key = xstrdup (tok->u.string);
+      m_lexer.consume ();
+
+      require (TOK_COLON);
+
+      value *v = parse_value (depth + 1);
+      if (!v)
+	{
+	  free (key);
+	  return result;
+	}
+      /* We don't enforce uniqueness for keys.  */
+      result->set (key, v);
+      free (key);
+
+      tok = m_lexer.peek ();
+      if (tok->id == TOK_COMMA)
+	{
+	  m_lexer.consume ();
+	  continue;
+	}
+      else
+	{
+	  require (TOK_CLOSE_CURLY);
+	  break;
+	}
+    }
+  return result;
+}
+
+/* Parse a JSON array.
+   (ECMA-404 section 7; RFC 7159 section 5).  */
+
+array *
+parser::parse_array (int depth)
+{
+  require (TOK_OPEN_SQUARE);
+
+  array *result = new array ();
+
+  const token *tok = m_lexer.peek ();
+  if (tok->id == TOK_CLOSE_SQUARE)
+    {
+      m_lexer.consume ();
+      return result;
+    }
+
+  while (!seen_error_p ())
+    {
+      value *v = parse_value (depth + 1);
+      if (!v)
+	return result;
+
+      result->append (v);
+
+      tok = m_lexer.peek ();
+      if (tok->id == TOK_COMMA)
+	{
+	  m_lexer.consume ();
+	  continue;
+	}
+      else
+	{
+	  require (TOK_CLOSE_SQUARE);
+	  break;
+	}
+    }
+
+  return result;
+}
+
+/* Require an EOF, or fail if there is surplus input.  */
+
+void
+parser::require_eof ()
+{
+  require (TOK_EOF);
+}
+
+/* Consume the next token, issuing an error if it is not of kind TOK_ID.  */
+
+void
+parser::require (enum token_id tok_id)
+{
+  const token *tok = m_lexer.peek ();
+  if (tok->id != tok_id)
+    {
+      if (tok->id == TOK_ERROR)
+	error_at (tok->index, "expected %s; got bad token: %s",
+		  token_id_name[tok_id], tok->u.string);
+      else
+	error_at (tok->index, "expected %s; got %s", token_id_name[tok_id],
+		  token_id_name[tok->id]);
+    }
+  m_lexer.consume ();
+}
+
+/* Issue a parsing error.  If this is the first error that has occurred on
+   the parser, store it within the parser's m_err_out (the buffer will
+   eventually need to be free by the caller of the parser).
+   Otherwise the error is discarded.
+
+   TODO: maybe provide a callback so that client code can print all errors?  */
+
+void
+parser::error_at (int index, const char *fmt, ...)
+{
+  va_list ap;
+  va_start (ap, fmt);
+  char *formatted = xvasprintf (fmt, ap);
+  va_end (ap);
+
+  char *msg_with_index = xasprintf ("error at index %i: %s",
+				    index, formatted);
+  free (formatted);
+
+  if (0)
+    fprintf (stderr, "%s\n", msg_with_index);
+  if (*m_err_out == NULL)
+    *m_err_out = msg_with_index;
+  else
+    free (msg_with_index);
+}
+
+/* Attempt to parse the UTF-8 encoded buffer at UTF8_BUF
+   of the given LENGTH.
+   If successful, return a non-NULL json::value *.
+   if there was a problem, return NULL and write an error
+   message to err_out, which must be freed by the caller.  */
+
+value *
+json::parse_utf8_string (size_t length, const char *utf8_buf,
+			 char **err_out)
+{
+  gcc_assert (err_out);
+  gcc_assert (*err_out == NULL);
+
+  parser p (err_out);
+  if (!p.add_utf8 (length, utf8_buf, err_out))
+    return NULL;
+  value *result = p.parse_value (0);
+  if (!p.seen_error_p ())
+    p.require_eof ();
+  if (p.seen_error_p ())
+    {
+      gcc_assert (*err_out);
+      delete result;
+      return NULL;
+    }
+  return result;
+}
+
+/* Attempt to parse the nil-terminated UTF-8 encoded buffer at
+   UTF8_BUF.
+   If successful, return a non-NULL json::value *.
+   if there was a problem, return NULL and write an error
+   message to err_out, which must be freed by the caller.  */
+
+value *
+json::parse_utf8_string (const char *utf8, char **err_out)
+{
+  return parse_utf8_string (strlen (utf8), utf8, err_out);
+}
+
+\f
+#if CHECKING_P
+
+namespace selftest {
+
+/* Selftests.  */
+
+/* Verify that JV->to_str () equals EXPECTED_JSON.  */
+
+static void
+assert_to_str_eq (const char *expected_json, json::value *jv)
+{
+  char *json = jv->to_str ();
+  ASSERT_STREQ (expected_json, json);
+  free (json);
+}
+
+/* FIXME.  */
+
+static void
+test_parse_string ()
+{
+  char *err = NULL;
+  json::value *jv = parse_utf8_string ("\"foo\"", &err);
+  ASSERT_EQ (NULL, err);
+  ASSERT_EQ (JSON_STRING, jv->get_kind ());
+  ASSERT_STREQ ("foo", ((json::string *)jv)->get_string ());
+  assert_to_str_eq ("\"foo\"", jv);
+
+  json::value *clone = jv->clone ();
+  ASSERT_EQ (JSON_STRING, clone->get_kind ());
+  ASSERT_STREQ ("foo", ((json::string *)clone)->get_string ());
+  assert_to_str_eq ("\"foo\"", clone);
+  delete clone;
+  delete jv;
+
+  const char *contains_quotes = "\"before \\\"quoted\\\" after\"";
+  jv = parse_utf8_string (contains_quotes, &err);
+  ASSERT_EQ (NULL, err);
+  ASSERT_EQ (JSON_STRING, jv->get_kind ());
+  ASSERT_STREQ ("before \"quoted\" after", ((json::string *)jv)->get_string ());
+  assert_to_str_eq (contains_quotes, jv);
+  delete jv;
+
+  /* Test of non-ASCII input.  This string is the Japanese word "mojibake",
+     written as C octal-escaped UTF-8.  */
+  const char *mojibake = (/* Opening quote.  */
+			  "\""
+			  /* U+6587 CJK UNIFIED IDEOGRAPH-6587
+			     UTF-8: 0xE6 0x96 0x87
+			     C octal escaped UTF-8: \346\226\207.  */
+			  "\346\226\207"
+			  /* U+5B57 CJK UNIFIED IDEOGRAPH-5B57
+			     UTF-8: 0xE5 0xAD 0x97
+			     C octal escaped UTF-8: \345\255\227.  */
+			  "\345\255\227"
+			 /* U+5316 CJK UNIFIED IDEOGRAPH-5316
+			      UTF-8: 0xE5 0x8C 0x96
+			      C octal escaped UTF-8: \345\214\226.  */
+			  "\345\214\226"
+			 /* U+3051 HIRAGANA LETTER KE
+			      UTF-8: 0xE3 0x81 0x91
+			      C octal escaped UTF-8: \343\201\221.  */
+			  "\343\201\221"
+			  /* Closing quote.  */
+			  "\"");
+  jv = parse_utf8_string (mojibake, &err);
+  ASSERT_EQ (NULL, err);
+  ASSERT_EQ (JSON_STRING, jv->get_kind ());
+  /* Result of get_string should be UTF-8 encoded, without quotes.  */
+  ASSERT_STREQ ("\346\226\207" "\345\255\227" "\345\214\226" "\343\201\221",
+		((json::string *)jv)->get_string ());
+  /* Result of dump should be UTF-8 encoded, with quotes.  */
+  assert_to_str_eq (mojibake, jv);
+  delete jv;
+
+  /* Test of \u-escaped unicode.  This is "mojibake" again, as above.  */
+  const char *escaped_unicode = "\"\\u6587\\u5b57\\u5316\\u3051\"";
+  jv = parse_utf8_string (escaped_unicode, &err);
+  ASSERT_EQ (NULL, err);
+  ASSERT_EQ (JSON_STRING, jv->get_kind ());
+  /* Result of get_string should be UTF-8 encoded, without quotes.  */
+  ASSERT_STREQ ("\346\226\207" "\345\255\227" "\345\214\226" "\343\201\221",
+		((json::string *)jv)->get_string ());
+  /* Result of dump should be UTF-8 encoded, with quotes.  */
+  assert_to_str_eq (mojibake, jv);
+  delete jv;
+}
+
+/* FIXME.  */
+
+static void
+test_parse_number ()
+{
+  json::value *jv, *clone;
+
+  char *err = NULL;
+  jv = parse_utf8_string ("42", &err);
+  ASSERT_EQ (NULL, err);
+  ASSERT_EQ (JSON_NUMBER, jv->get_kind ());
+  ASSERT_EQ (42.0, ((json::number *)jv)->get ());
+  assert_to_str_eq ("42", jv);
+  clone = jv->clone ();
+  ASSERT_EQ (JSON_NUMBER, clone->get_kind ());
+  delete clone;
+  delete jv;
+
+  /* Negative number.  */
+  jv = parse_utf8_string ("-17", &err);
+  ASSERT_EQ (NULL, err);
+  ASSERT_EQ (JSON_NUMBER, jv->get_kind ());
+  ASSERT_EQ (-17.0, ((json::number *)jv)->get ());
+  assert_to_str_eq ("-17", jv);
+  delete jv;
+
+  /* Decimal.  */
+  jv = parse_utf8_string ("3.141", &err);
+  ASSERT_EQ (NULL, err);
+  ASSERT_EQ (JSON_NUMBER, jv->get_kind ());
+  ASSERT_EQ (3.141, ((json::number *)jv)->get ());
+  assert_to_str_eq ("3.141", jv);
+  delete jv;
+
+  /* Exponents.  */
+  jv = parse_utf8_string ("3.141e+0", &err);
+  ASSERT_EQ (NULL, err);
+  ASSERT_EQ (JSON_NUMBER, jv->get_kind ());
+  ASSERT_EQ (3.141, ((json::number *)jv)->get ());
+  assert_to_str_eq ("3.141", jv);
+  delete jv;
+
+  jv = parse_utf8_string ("42e2", &err);
+  ASSERT_EQ (NULL, err);
+  ASSERT_EQ (JSON_NUMBER, jv->get_kind ());
+  ASSERT_EQ (4200, ((json::number *)jv)->get ());
+  assert_to_str_eq ("4200", jv);
+  delete jv;
+
+  jv = parse_utf8_string ("42e-1", &err);
+  ASSERT_EQ (NULL, err);
+  ASSERT_EQ (JSON_NUMBER, jv->get_kind ());
+  ASSERT_EQ (4.2, ((json::number *)jv)->get ());
+  assert_to_str_eq ("4.2", jv);
+  delete jv;
+
+}
+
+/* FIXME.  */
+
+static void
+test_parse_array ()
+{
+  json::value *jv, *clone;
+
+  char *err = NULL;
+  jv = parse_utf8_string ("[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]", &err);
+  ASSERT_EQ (NULL, err);
+  ASSERT_EQ (JSON_ARRAY, jv->get_kind ());
+  json::array *arr = static_cast <json::array *> (jv);
+  ASSERT_EQ (10, arr->get_length ());
+  for (int i = 0; i < 10; i++)
+    {
+      json::value *element = arr->get (i);
+      ASSERT_EQ (JSON_NUMBER, element->get_kind ());
+      ASSERT_EQ (i, ((json::number *)element)->get ());
+    }
+  assert_to_str_eq ("[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]", jv);
+
+  clone = jv->clone ();
+  ASSERT_EQ (JSON_ARRAY, clone->get_kind ());
+  arr = static_cast <json::array *> (clone);
+  ASSERT_EQ (10, arr->get_length ());
+  for (int i = 0; i < 10; i++)
+    {
+      json::value *element = arr->get (i);
+      ASSERT_EQ (JSON_NUMBER, element->get_kind ());
+      ASSERT_EQ (i, ((json::number *)element)->get ());
+    }
+  assert_to_str_eq ("[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]", clone);
+  delete clone;
+
+  delete jv;
+}
+
+/* FIXME.  */
+
+static void
+test_parse_object ()
+{
+  char *err = NULL;
+  json::value *jv
+    = parse_utf8_string ("{\"foo\": \"bar\", \"baz\": [42, null]}", &err);
+  ASSERT_EQ (NULL, err);
+  ASSERT_TRUE (jv != NULL);
+  ASSERT_EQ (JSON_OBJECT, jv->get_kind ());
+  json::object *jo = static_cast <json::object *> (jv);
+
+  json::value *foo_value = jo->get ("foo");
+  ASSERT_TRUE (foo_value != NULL);
+  ASSERT_EQ (JSON_STRING, foo_value->get_kind ());
+  ASSERT_STREQ ("bar", ((json::string *)foo_value)->get_string ());
+
+  json::value *baz_value = jo->get ("baz");
+  ASSERT_TRUE (baz_value != NULL);
+  ASSERT_EQ (JSON_ARRAY, baz_value->get_kind ());
+
+  json::array *baz_array = (json::array *)baz_value;
+  ASSERT_EQ (2, baz_array->get_length ());
+  ASSERT_EQ (42, baz_array->get (0)->as_number ()->get ());
+  ASSERT_EQ (JSON_NULL, baz_array->get (1)->get_kind ());
+
+  // TODO: error-handling
+  // TODO: partial document
+
+  /* We can't use assert_to_str_eq since ordering is not guaranteed.  */
+
+  json::value *clone = jv->clone ();
+  ASSERT_EQ (JSON_OBJECT, clone->get_kind ());
+  ASSERT_EQ (JSON_STRING, clone->as_object ()->get ("foo")->get_kind ());
+  delete clone;
+
+  delete jv;
+}
+
+/* Verify that the literals "true", "false" and "null" are parsed,
+   dumped, and are clonable.  */
+
+static void
+test_parse_literals ()
+{
+  json::value *jv, *clone;
+  char *err = NULL;
+  jv = parse_utf8_string ("true", &err);
+  ASSERT_EQ (NULL, err);
+  ASSERT_TRUE (jv != NULL);
+  ASSERT_EQ (JSON_TRUE, jv->get_kind ());
+  assert_to_str_eq ("true", jv);
+  clone = jv->clone ();
+  ASSERT_EQ (JSON_TRUE, clone->get_kind ());
+  delete clone;
+  delete jv;
+
+  jv = parse_utf8_string ("false", &err);
+  ASSERT_EQ (NULL, err);
+  ASSERT_TRUE (jv != NULL);
+  ASSERT_EQ (JSON_FALSE, jv->get_kind ());
+  assert_to_str_eq ("false", jv);
+  clone = jv->clone ();
+  ASSERT_EQ (JSON_FALSE, clone->get_kind ());
+  delete clone;
+  delete jv;
+
+  jv = parse_utf8_string ("null", &err);
+  ASSERT_EQ (NULL, err);
+  ASSERT_TRUE (jv != NULL);
+  ASSERT_EQ (JSON_NULL, jv->get_kind ());
+  assert_to_str_eq ("null", jv);
+  clone = jv->clone ();
+  ASSERT_EQ (JSON_NULL, clone->get_kind ());
+  delete clone;
+  delete jv;
+}
+
+/* FIXME.  */
+
+static void
+test_parse_jsonrpc ()
+{
+  char *err = NULL;
+  const char *request
+    = ("{\"jsonrpc\": \"2.0\", \"method\": \"subtract\","
+       " \"params\": [42, 23], \"id\": 1}");
+  json::value *jv = parse_utf8_string (request, &err);
+  ASSERT_EQ (NULL, err);
+  ASSERT_TRUE (jv != NULL);
+  delete jv;
+}
+
+/* FIXME.  */
+
+static void
+test_parse_empty_object ()
+{
+  char *err = NULL;
+  json::value *jv = parse_utf8_string ("{}", &err);
+  ASSERT_EQ (NULL, err);
+  ASSERT_TRUE (jv != NULL);
+  ASSERT_EQ (JSON_OBJECT, jv->get_kind ());
+  assert_to_str_eq ("{}", jv);
+  delete jv;
+}
+
+/* FIXME.  */
+
+static void
+test_error_empty_string ()
+{
+  char *err = NULL;
+  json::value *jv = parse_utf8_string ("", &err);
+  ASSERT_STREQ ("error at index 0: unexpected token: EOF", err);
+  ASSERT_TRUE (jv == NULL);
+  free (err);
+}
+
+/* FIXME.  */
+
+static void
+test_error_missing_comma ()
+{
+  char *err = NULL;
+  /*                  01234567.  */
+  const char *json = "[0, 1 2]";
+  json::value *jv = parse_utf8_string (json, &err);
+  ASSERT_STREQ ("error at index 6: expected ']'; got number",
+		err);
+  // FIXME: unittest the lexer?
+  ASSERT_TRUE (jv == NULL);
+  free (err);
+}
+
+/* Run all of the selftests within this file.  */
+
+void
+json_c_tests ()
+{
+  test_parse_string ();
+  test_parse_number ();
+  test_parse_array ();
+  test_parse_object ();
+  test_parse_literals ();
+  test_parse_jsonrpc ();
+  test_parse_empty_object ();
+  test_error_empty_string ();
+  test_error_missing_comma ();
+
+  /* FIXME: tests for roundtripping (noting that we don't preserve
+     object key ordering).  */
+
+  /* FIXME: cloning.  */
+}
+
+} // namespace selftest
+
+#endif /* #if CHECKING_P */
diff --git a/gcc/json.h b/gcc/json.h
new file mode 100644
index 0000000..aedf84a
--- /dev/null
+++ b/gcc/json.h
@@ -0,0 +1,214 @@
+/* JSON parsing
+   Copyright (C) 2017 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_JSON_H
+#define GCC_JSON_H
+
+/* Implementation of JSON, a lightweight data-interchange format.
+
+   See http://www.json.org/
+   and http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf
+   and https://tools.ietf.org/html/rfc7159
+
+   Supports parsing text into a DOM-like tree of json::value *, dumping
+   json::value * to text.  */
+
+namespace json
+{
+
+/* Forward decls of json::value and its subclasses (using indentation
+   to denote inheritance.  */
+
+class value;
+  class object;
+  class array;
+  class number;
+  class string;
+  class literal;
+
+/* An enum for discriminating the subclasses of json::value.  */
+
+enum kind
+{
+  /* class json::object.  */
+  JSON_OBJECT,
+
+  /* class json::array.  */
+  JSON_ARRAY,
+
+  /* class json::number.  */
+  JSON_NUMBER,
+
+  /* class json::string.  */
+  JSON_STRING,
+
+  /* class json::literal uses these three values to identify the
+     particular literal.  */
+  JSON_TRUE,
+  JSON_FALSE,
+  JSON_NULL
+};
+
+/* Base class of JSON value.  */
+
+class value
+{
+ public:
+  virtual ~value () {}
+  virtual enum kind get_kind () const = 0;
+  virtual void print (pretty_printer *pp) const = 0;
+
+  /* Create a deep copy of the value, returning a value which must be
+     deleted by the caller.  */
+  virtual value *clone () const = 0;
+
+  char *to_str () const;
+  void dump (FILE *) const;
+
+  /* Methods for dynamically casting a value to one of the subclasses,
+     returning NULL if the value is of the wrong kind.  */
+  const object *as_object () const;
+  const array *as_array () const;
+  const number *as_number () const;
+  const string *as_string () const;
+
+  /* Convenience accessors for attempting to perform key/value lookups
+     on this value as if it were an json::object.
+
+     On success, return true and write the value to OUT_VALUE.
+     On failure, return false and write an error message to OUT_ERR
+     (which must be freed by the caller).  */
+  bool get_value_by_key (const char *name, const value *&out_value,
+			 char *&out_err) const;
+  bool get_int_by_key (const char *name, int &out_value, char *&out_err) const;
+  bool get_string_by_key (const char *name, const char *&out_value,
+			  char *&out_err) const;
+  bool get_array_by_key (const char *name, const array *&out_value,
+			 char *&out_err) const;
+
+  /* As above, but the key is optional.  THIS must still be an object,
+     though.  */
+  bool get_optional_value_by_key (const char *name, const value *&out_value,
+				  char *&out_err) const;
+  bool get_optional_string_by_key (const char *name, const char *&out_value,
+				   char *&out_err) const;
+};
+
+/* Subclass of value for objects: an unordered collection of
+   key/value pairs.  */
+
+class object : public value
+{
+ public:
+  ~object ();
+
+  enum kind get_kind () const FINAL OVERRIDE { return JSON_OBJECT; }
+  void print (pretty_printer *pp) const FINAL OVERRIDE;
+  value *clone () const FINAL OVERRIDE;
+
+  value *get (const char *key) const;
+  value *get_if_nonnull (const char *key) const;
+
+  void set (const char *key, value *v);
+
+ private:
+  typedef hash_map <char *, value *,
+    simple_hashmap_traits<nofree_string_hash, value *> > map_t;
+  map_t m_map;
+};
+
+/* Subclass of value for arrays.  */
+
+class array : public value
+{
+ public:
+  ~array ();
+
+  enum kind get_kind () const FINAL OVERRIDE { return JSON_ARRAY; }
+  void print (pretty_printer *pp) const FINAL OVERRIDE;
+  value *clone () const FINAL OVERRIDE;
+
+  unsigned get_length () const { return m_elements.length (); }
+  value *get (int idx) const { return m_elements[idx]; }
+  void append (value *v) { m_elements.safe_push (v); }
+
+ private:
+  auto_vec<value *> m_elements;
+};
+
+/* Subclass of value for numbers.  */
+
+class number : public value
+{
+ public:
+  number (double value) : m_value (value) {}
+
+  enum kind get_kind () const FINAL OVERRIDE { return JSON_NUMBER; }
+  void print (pretty_printer *pp) const FINAL OVERRIDE;
+  value *clone () const FINAL OVERRIDE;
+
+  double get () const { return m_value; }
+
+ private:
+  double m_value;
+};
+
+/* Subclass of value for strings.  */
+
+class string : public value
+{
+ public:
+  string (const char *utf8) : m_utf8 (xstrdup (utf8)) {}
+  ~string () { free (m_utf8); }
+
+  enum kind get_kind () const FINAL OVERRIDE { return JSON_STRING; }
+  void print (pretty_printer *pp) const FINAL OVERRIDE;
+  value *clone () const FINAL OVERRIDE;
+
+  const char *get_string () const { return m_utf8; }
+
+ private:
+  char *m_utf8;
+};
+
+/* Subclass of value for the three JSON literals "true", "false",
+   and "null".  */
+
+class literal : public value
+{
+ public:
+  literal (enum kind kind) : m_kind (kind) {}
+
+  enum kind get_kind () const FINAL OVERRIDE { return m_kind; }
+  void print (pretty_printer *pp) const FINAL OVERRIDE;
+  value *clone () const FINAL OVERRIDE;
+
+ private:
+  enum kind m_kind;
+};
+
+/* Declarations for parsing JSON to a json::value * tree.  */
+
+extern value *parse_utf8_string (size_t length, const char *utf8_buf,
+				 char **err_out);
+extern value *parse_utf8_string (const char *utf8, char **err_out);
+
+} // namespace json
+
+#endif  /* GCC_JSON_H  */
diff --git a/gcc/selftest-run-tests.c b/gcc/selftest-run-tests.c
index 01e3504..a8668f4 100644
--- a/gcc/selftest-run-tests.c
+++ b/gcc/selftest-run-tests.c
@@ -67,6 +67,7 @@ selftest::run_tests ()
   fibonacci_heap_c_tests ();
   typed_splay_tree_c_tests ();
   blt_c_tests ();
+  json_c_tests ();
 
   /* Mid-level data structures.  */
   input_c_tests ();
diff --git a/gcc/selftest.h b/gcc/selftest.h
index 583bdb2..281658b 100644
--- a/gcc/selftest.h
+++ b/gcc/selftest.h
@@ -184,6 +184,7 @@ extern void ggc_tests_c_tests ();
 extern void hash_map_tests_c_tests ();
 extern void hash_set_tests_c_tests ();
 extern void input_c_tests ();
+extern void json_c_tests ();
 extern void pretty_print_c_tests ();
 extern void read_rtl_function_c_tests ();
 extern void rtl_tests_c_tests ();
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH 14/17] Add implementation of JSON-RPC
  2017-07-24 19:31 [PATCH 00/17] RFC: New source-location representation; Language Server Protocol David Malcolm
                   ` (9 preceding siblings ...)
  2017-07-24 19:38 ` [PATCH 07/17] C++: use BLT to highlight parameter of callee decl for mismatching types David Malcolm
@ 2017-07-24 19:38 ` David Malcolm
  2017-07-24 19:38 ` [PATCH 06/17] C: use BLT to highlight parameter of callee decl for mismatching types David Malcolm
                   ` (8 subsequent siblings)
  19 siblings, 0 replies; 38+ messages in thread
From: David Malcolm @ 2017-07-24 19:38 UTC (permalink / raw)
  To: gcc-patches; +Cc: David Malcolm

This patch adds an abstract base class for implementing
JSON-RPC 2.0 servers, along with a class for wiring them
up to the HTTP server class implemented in the previous
patch, supporting the serving of JSON-RPC over HTTP on a port.

gcc/ChangeLog:
	* Makefile.in (OBJS): Add json-rpc.o.
	* json-rpc.c: New file.
	* json-rpc.h: New file.
	* selftest-run-tests.c (selftest::run_tests): Call
	selftest::json_rpc_c_tests.
	* selftest.h (selftest::json_rpc_c_tests.): New decl.
---
 gcc/Makefile.in          |   1 +
 gcc/json-rpc.c           | 486 +++++++++++++++++++++++++++++++++++++++++++++++
 gcc/json-rpc.h           |  94 +++++++++
 gcc/selftest-run-tests.c |   1 +
 gcc/selftest.h           |   1 +
 5 files changed, 583 insertions(+)
 create mode 100644 gcc/json-rpc.c
 create mode 100644 gcc/json-rpc.h

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 0c361f1..1f9050c 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1371,6 +1371,7 @@ OBJS = \
 	ira-emit.o \
 	ira-lives.o \
 	json.o \
+	json-rpc.o \
 	jump.o \
 	langhooks.o \
 	lcm.o \
diff --git a/gcc/json-rpc.c b/gcc/json-rpc.c
new file mode 100644
index 0000000..1cc0049
--- /dev/null
+++ b/gcc/json-rpc.c
@@ -0,0 +1,486 @@
+/* JSON-RPC server implementation.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "json.h"
+#include "http-server.h"
+#include "json-rpc.h"
+#include "selftest.h"
+
+/* Create a JSON-RPC 2.0 response object, setting "id" to a copy
+   of ID if it is non-NULL.  */
+
+json::object *
+jsonrpc::make_response (const json::value *id)
+{
+  json::object *response = new json::object ();
+  response->set ("jsonrpc", new json::string ("2.0"));
+  if (id)
+    response->set ("id", id->clone ());
+  return response;
+}
+
+/* Create a JSON-RPC 2.0 error object, setting:
+   - "id" to a copy of ID if it is non-NULL,
+   - error.code to CODE,
+   - error.message to MESSAGE.  */
+
+json::object *
+jsonrpc::make_failure (int code, const char *message, const json::value *id)
+{
+  json::object *failure = make_response (id);
+  json::object *error = new json::object ();
+  failure->set ("error", error);
+
+  error->set ("code", new json::number (code));
+  error->set ("message", new json::string (message));
+  return failure;
+}
+
+/* Create a JSON-RPC 2.0 "parse error" object with error.message as MSG.  */
+
+json::value *
+jsonrpc::make_parse_error (const char *msg)
+{
+  return make_failure (PARSE_ERROR, msg, NULL);
+}
+
+/* Create a JSON-RPC 2.0 "invalid request" error object with the given ID.  */
+
+json::value *
+jsonrpc::make_invalid_request (const json::value *id)
+{
+  return make_failure (INVALID_REQUEST, "invalid request", id);
+}
+
+/* Create a JSON-RPC 2.0 "method not found" error object for a method named
+   METHOD.  */
+
+json::value *
+jsonrpc::make_method_not_found (const json::value *id, const char *method)
+{
+  const char *msg = ACONCAT (("method not found: ", method, NULL));
+  return make_failure (METHOD_NOT_FOUND, msg, id);
+}
+
+/* Create a JSON-RPC 2.0 "invalid params" error object with error.message as MSG
+   if non-NULL, or "invalid params" otherwise.  */
+
+json::value *
+jsonrpc::make_invalid_params (const json::value *id, const char *msg)
+{
+  if (msg == NULL)
+    msg = "invalid params";
+  return make_failure (INVALID_PARAMS, msg, id);
+}
+
+/* Create a JSON-RPC 2.0 response object for request ID, with result
+   as RESULT, taking ownership of RESULT.  */
+
+json::value *
+jsonrpc::make_success (const json::value *id, json::value *result)
+{
+  json::object *success = make_response (id);
+  success->set ("result", result);
+  return success;
+}
+
+/* Public interface for handling JSON-RPC requests.
+   Takes a borrowed pointer to REQUEST.
+   Returns a new pointer to a result object.
+   Wrapper around handle_request_1, adding support for dumping.  */
+
+json::value *
+jsonrpc::server::handle_request (json::value *request)
+{
+  if (m_verbose)
+    {
+      fprintf (stderr, "request: ");
+      request->dump (stderr);
+      fprintf (stderr, "\n");
+    }
+
+  json::value *response = handle_request_1 (request);
+
+  if (m_verbose)
+    {
+      fprintf (stderr, "response: ");
+      if (response)
+	response->dump (stderr);
+      else
+	fprintf (stderr, "NULL");
+      fprintf (stderr, "\n");
+    }
+
+  return response;
+}
+
+/* Private subroutine of handle_request.
+   Takes a borrowed pointer to REQUEST.
+   Returns a new pointer to a result object.
+   Performs various error-checking, then calls the "dispatch" vfunc.  */
+
+json::value *
+jsonrpc::server::handle_request_1 (json::value *request)
+{
+  // FIXME: handle batch requests
+  if (request->get_kind () != json::JSON_OBJECT)
+    return make_parse_error ("not an object");
+
+  json::object *reqobj = static_cast <json::object *> (request);
+
+  /* Lookup id.  */
+  json::value *id = reqobj->get ("id");
+
+  /* If no id, then it's a notification.  */
+
+  /* TODO: validate non-NULL id.  */
+
+  /* Check version.  */
+  json::value *version = reqobj->get ("jsonrpc");
+  if (!version)
+    return make_invalid_request (id);
+  const json::string *version_string = version->as_string ();
+  if (!version_string)
+    return make_failure (INVALID_REQUEST, "jsonrpc was not 2.0", id);
+  if (0 != strcmp (version_string->get_string (), "2.0"))
+    return make_failure (INVALID_REQUEST, "jsonrpc was not 2.0", id);
+
+  json::value *method = reqobj->get ("method");
+  if (!method)
+    return make_failure (INVALID_REQUEST, "no method", id);
+  const json::string *method_str = method->as_string ();
+  if (!method_str)
+    return make_failure (INVALID_REQUEST, "method was not a string", id);
+
+  const json::value *params = reqobj->get ("params");
+  return dispatch (method_str->get_string (), params, id);
+}
+
+/* Parse the given buffer as JSON-RPC and attempt to dispatch the call.
+   Returns a new pointer to a result object.  */
+
+json::value *
+jsonrpc::server::handle_request (size_t sz, const char *buf)
+{
+  char *err = NULL;
+  json::value *request = json::parse_utf8_string (sz, buf, &err);
+  if (!request)
+    {
+      gcc_assert (err);
+      json::value *response = make_parse_error (err);
+      free (err);
+      return response;
+    }
+  gcc_assert (err == NULL);
+  json::value *response = handle_request (request);
+  delete request;
+  return response;
+}
+
+/* Parse the given UTF-8 encoded 0-terminated string as JSON-RPC and
+   attempt to dispatch the call.
+   Returns a new pointer to a result object.
+
+   Helper function for writing selftests.  */
+
+json::value *
+jsonrpc::server::handle_request_string (const char *utf8)
+{
+  return handle_request (strlen (utf8), utf8);
+}
+
+/* class ::jsonrpc::http_server : public ::http::server.  */
+
+/* jsonrpc::http_server's ctor.  */
+
+jsonrpc::http_server::http_server (jsonrpc::server &json_handler)
+: ::http::server (true), m_json_handler (json_handler)
+{}
+
+/* Handle HTTP requests by parsing the content as JSON-RPC, and delegating
+   them to a ::jsonrpc::server.  */
+
+void
+jsonrpc::http_server::on_http_request (const http::request &http_request,
+				       http::response &http_response)
+{
+  size_t length = http_request.get_content_length ();
+  const char *buf = http_request.get_content ();
+  if (0)
+    fprintf (stderr, "got content: '%.*s'\n", (int)length, buf);
+  json::value *json_response = m_json_handler.handle_request (length, buf);
+  char *response_str = json_response->to_str ();
+  delete json_response;
+  http_response.set_content (strlen (response_str), response_str);
+  free (response_str);
+}
+
+#if CHECKING_P
+
+namespace selftest {
+
+/* Selftests.  */
+
+/* Assert that RESPONSE is a non-NULL response for ID.
+   Verify that the jsonrpc value is "2.0".
+   Return RESPONSE, cast to an object *.  */
+
+static const json::object *
+assert_is_response (const json::value *response, int id)
+{
+  ASSERT_TRUE (response != NULL);
+  const json::object *obj = response->as_object ();
+  ASSERT_TRUE (obj != NULL);
+  json::value *jsonrpc = obj->get ("jsonrpc");
+  ASSERT_TRUE (jsonrpc != NULL);
+  ASSERT_STREQ ("2.0", jsonrpc->as_string ()->get_string ());
+  ASSERT_EQ (id, obj->get ("id")->as_number ()->get ());
+  return obj;
+}
+
+/* Assert that RESPONSE is a non-NULL response, with no ID.
+   Verify that the jsonrpc value is "2.0".
+   Return RESPONSE, cast to an object *.  */
+
+static const json::object *
+assert_is_response (const json::value *response)
+{
+  ASSERT_TRUE (response != NULL);
+  const json::object *obj = response->as_object ();
+  ASSERT_TRUE (obj != NULL);
+  json::value *jsonrpc = obj->get ("jsonrpc");
+  ASSERT_TRUE (jsonrpc != NULL);
+  ASSERT_STREQ ("2.0", jsonrpc->as_string ()->get_string ());
+  return obj;
+}
+
+/* Assert that RESPONSE is a non-NULL successful response for ID.
+   Verify that the jsonrpc value is "2.0".
+   Return the "result" value.  */
+
+static const json::value *
+assert_is_success (const json::value *response, int id)
+{
+  const json::object *obj = assert_is_response (response, id);
+  ASSERT_EQ (NULL, obj->get ("error"));
+  return obj->get ("result");
+}
+
+/* Assert that RESPONSE is a non-NULL failure response for ID.
+   Verify that the jsonrpc value is "2.0".
+   Verify that CODE and MESSAGE match the given values.  */
+
+static void
+assert_is_failure (const json::value *response, int code, const char *message,
+		   int id)
+{
+  const json::object *obj = assert_is_response (response, id);
+  ASSERT_EQ (NULL, obj->get ("result"));
+  const json::object *err = obj->get ("error")->as_object ();
+  ASSERT_EQ (code, err->get ("code")->as_number ()->get ());
+  ASSERT_STREQ (message, err->get ("message")->as_string ()->get_string ());
+}
+
+/* Assert that RESPONSE is a non-NULL failure response, with no ID.
+   Verify that the jsonrpc value is "2.0".
+   Verify that CODE and MESSAGE match the given values.  */
+
+static void
+assert_is_failure (const json::value *response, int code, const char *message)
+{
+  const json::object *obj = assert_is_response (response);
+  ASSERT_EQ (NULL, obj->get ("result"));
+  const json::object *err = obj->get ("error")->as_object ();
+  ASSERT_EQ (code, err->get ("code")->as_number ()->get ());
+  ASSERT_STREQ (message, err->get ("message")->as_string ()->get_string ());
+}
+
+using namespace jsonrpc;
+
+namespace {
+
+/* A concrete subclass of jsonrpc::server for use by the selftests.  */
+
+class test_server : public jsonrpc::server
+{
+ public:
+  test_server (bool verbose) : server (verbose) {}
+
+  json::value *
+  dispatch (const char *method, const json::value *params,
+	    const json::value *id) FINAL OVERRIDE
+  {
+    if (0 == strcmp (method, "subtract"))
+      return do_subtract (params, id);
+    return make_method_not_found (id, method);
+  }
+
+  json::value *
+  do_subtract (const json::value *params, const json::value *id)
+  {
+    const json::array *positional = params->as_array ();
+    if (!positional)
+      return make_invalid_params (id, "params was not an array");
+    if (positional->get_length () != 2)
+      return make_invalid_params (id, "length of params was not 2");
+
+    const json::number *arg0 = positional->get (0)->as_number ();
+    if (!arg0)
+      return make_invalid_params (id, "param 0 was not a number");
+    const json::number *arg1 = positional->get (1)->as_number ();
+    if (!arg1)
+      return make_invalid_params (id, "param 1 was not a number");
+
+    double result = arg0->get () - arg1->get ();
+
+    return make_success (id, new json::number (result));
+  }
+};
+
+} // anonymous namespace
+
+/* Verify that a simple JSON-RPC call (of "subtract") works.  */
+
+static void
+test_simple ()
+{
+  test_server s (false);
+  const char *request
+    = ("{\"jsonrpc\": \"2.0\", \"method\": \"subtract\","
+       " \"params\": [42, 23], \"id\": 1}");
+  json::value *response = s.handle_request_string (request);
+  const json::value *result = assert_is_success (response, 1);
+  ASSERT_EQ (19, result->as_number ()->get ());
+  delete response;
+}
+
+/* Verify that jsonrpc::server::make_invalid_params works.  */
+
+static void
+test_bad_params ()
+{
+  test_server s (false);
+  const char *request
+    = ("{\"jsonrpc\": \"2.0\", \"method\": \"subtract\","
+       " \"params\": [42, \"0\"], \"id\": 1}");
+  json::value *response = s.handle_request_string (request);
+  assert_is_failure (response, INVALID_PARAMS, "param 1 was not a number", 1);
+  delete response;
+}
+
+/* Verify that jsonrpc::server enforces JSON-RPC 2.0.  */
+
+static void
+test_bad_version ()
+{
+  test_server s (false);
+  const char *request
+    = ("{\"jsonrpc\": \"1.0\", \"method\": \"subtract\","
+       " \"params\": [42, 23], \"id\": 1}");
+  json::value *response = s.handle_request_string (request);
+  assert_is_failure (response, INVALID_REQUEST, "jsonrpc was not 2.0", 1);
+  delete response;
+}
+
+/* Verify that jsonrpc::server handles non-string methods.  */
+
+static void
+test_method_not_a_string ()
+{
+  test_server s (false);
+  const char *request
+    = ("{\"jsonrpc\": \"2.0\", \"method\": 1,"
+       " \"params\": \"bar\", \"id\": 1}");
+  json::value *response = s.handle_request_string (request);
+  assert_is_failure (response, INVALID_REQUEST, "method was not a string", 1);
+  delete response;
+}
+
+/* Verify that jsonrpc::server::make_method_not_found works.  */
+
+static void
+test_method_not_found ()
+{
+  test_server s (false);
+  const char *request
+    = ("{\"jsonrpc\": \"2.0\", \"method\": \"does_not_exist\","
+       " \"params\": [42, 23], \"id\": 1}");
+  json::value *response = s.handle_request_string (request);
+  assert_is_failure (response, METHOD_NOT_FOUND,
+    "method not found: does_not_exist", 1);
+  delete response;
+}
+
+/* Verify that jsonrpc::server::handle_request_string gracefully
+   handles malformed JSON.  */
+
+static void
+test_malformed_json ()
+{
+  test_server s (false);
+  const char *request = "{";
+  json::value *response = s.handle_request_string (request);
+  assert_is_failure (response, PARSE_ERROR,
+    "error at index 1: expected string for object key");
+  delete response;
+}
+
+/* Verify that jsonrpc::http_server can handle JSON-RPC within HTTP,
+   delegating to a jsonrpc::server.  */
+
+static void
+test_http_server ()
+{
+  http::request req;
+  const char *in = ("POST /jsonrpc HTTP/1.1\r\n"
+		    "Content-Length: 69\r\n"
+		    "\r\n"
+		    "{\"jsonrpc\": \"2.0\", \"method\": \"subtract\","
+		    " \"params\": [42, 23], \"id\": 1}");
+  size_t consumed = req.parse_buffer (strlen (in), in);
+  ASSERT_EQ (strlen (in), consumed);
+
+  test_server s (false);
+  http_server http_server (s);
+  http::response resp;
+  http_server.on_http_request (req, resp);
+  ASSERT_STREQ ("{\"jsonrpc\": \"2.0\", \"id\": 1, \"result\": 19}",
+		resp.get_content ()); // FIXME: length?
+}
+
+/* Run all of the selftests within this file.  */
+
+void
+json_rpc_c_tests ()
+{
+  test_simple ();
+  test_bad_params ();
+  test_bad_version ();
+  test_method_not_a_string ();
+  test_method_not_found ();
+  test_malformed_json ();
+  test_http_server ();
+}
+
+} // namespace selftest
+
+#endif /* #if CHECKING_P */
diff --git a/gcc/json-rpc.h b/gcc/json-rpc.h
new file mode 100644
index 0000000..85f7370
--- /dev/null
+++ b/gcc/json-rpc.h
@@ -0,0 +1,94 @@
+/* JSON-RPC.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_JSON_RPC_H
+#define GCC_JSON_RPC_H
+
+namespace jsonrpc
+{
+
+/* jsonrpc::server is an abstract base class for implementing
+   a JSON-RPC 2.0 server.  */
+
+class server
+{
+ public:
+  server (bool verbose) : m_verbose (verbose) {}
+  virtual ~server () {}
+
+  json::value *handle_request (json::value *);
+  json::value *handle_request (size_t sz, const char *buf);
+  json::value *handle_request_string (const char *utf8);
+
+  virtual json::value *dispatch (const char *method,
+				 const json::value *params,
+				 const json::value *id) = 0;
+
+ private:
+  json::value *handle_request_1 (json::value *);
+
+  bool m_verbose;
+};
+
+/* The standard JSON-RPC error codes.  */
+
+const int PARSE_ERROR = -32700;
+const int INVALID_REQUEST = -32600;
+const int METHOD_NOT_FOUND = -32601;
+const int INVALID_PARAMS = -32602;
+const int INTERNAL_ERROR = -32603;
+
+/* Helper functions for creating responses.  */
+
+extern json::object *make_response (const json::value *id);
+extern json::object *make_failure (int code, const char *message,
+				   const json::value *id);
+
+extern json::value *make_parse_error (const char *msg);
+
+extern json::value *make_invalid_request (const json::value *id);
+
+extern json::value *make_method_not_found (const json::value *id,
+					   const char *method);
+
+extern json::value *make_invalid_params (const json::value *id,
+					 const char *msg);
+
+extern json::value *make_success (const json::value *id, json::value *result);
+
+
+/* A subclass of ::http::server that handles HTTP requests
+   by parsing the content as JSON-RPC, and delegating them to
+   a ::jsonrpc::server.  */
+
+class http_server : public ::http::server
+{
+ public:
+  http_server (::jsonrpc::server &json_handler);
+
+  void on_http_request (const http::request &http_request,
+			http::response &http_response) FINAL OVERRIDE;
+
+ private:
+  ::jsonrpc::server &m_json_handler;
+};
+
+} // namespace jsonrpc
+
+#endif  /* GCC_JSON_RPC_H  */
diff --git a/gcc/selftest-run-tests.c b/gcc/selftest-run-tests.c
index 7e75680..35ab965 100644
--- a/gcc/selftest-run-tests.c
+++ b/gcc/selftest-run-tests.c
@@ -69,6 +69,7 @@ selftest::run_tests ()
   blt_c_tests ();
   json_c_tests ();
   http_server_c_tests ();
+  json_rpc_c_tests ();
 
   /* Mid-level data structures.  */
   input_c_tests ();
diff --git a/gcc/selftest.h b/gcc/selftest.h
index ad4e957..2312fb2 100644
--- a/gcc/selftest.h
+++ b/gcc/selftest.h
@@ -186,6 +186,7 @@ extern void hash_set_tests_c_tests ();
 extern void http_server_c_tests ();
 extern void input_c_tests ();
 extern void json_c_tests ();
+extern void json_rpc_c_tests ();
 extern void pretty_print_c_tests ();
 extern void read_rtl_function_c_tests ();
 extern void rtl_tests_c_tests ();
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH 07/17] C++: use BLT to highlight parameter of callee decl for mismatching types
  2017-07-24 19:31 [PATCH 00/17] RFC: New source-location representation; Language Server Protocol David Malcolm
                   ` (8 preceding siblings ...)
  2017-07-24 19:31 ` [PATCH 13/17] Add http-server.h and http-server.c David Malcolm
@ 2017-07-24 19:38 ` David Malcolm
  2017-07-24 19:38 ` [PATCH 14/17] Add implementation of JSON-RPC David Malcolm
                   ` (9 subsequent siblings)
  19 siblings, 0 replies; 38+ messages in thread
From: David Malcolm @ 2017-07-24 19:38 UTC (permalink / raw)
  To: gcc-patches; +Cc: David Malcolm

This patch to the C++ frontend uses the BLT infrastructure to improve:

  extern int callee_1 (int one, const char *two, float three);

  int test_1 (int first, int second, float third)
  {
    return callee_1 (first, second, third);
  }

from:

  error: invalid conversion from 'int' to 'const char*' [-fpermissive]
   return callee_1 (first, second, third);
                                        ^
  note:   initializing argument 2 of 'int callee_1(int, const char*, float)'
   extern int callee_1 (int one, const char *two, float three);
              ^~~~~~~~

to:

  error: invalid conversion from 'int' to 'const char*' [-fpermissive]
   return callee_1 (first, second, third);
                                        ^
  note:   initializing argument 2 of 'int callee_1(int, const char*, float)'
   extern int callee_1 (int one, const char *two, float three);
                                 ^~~~~~~~~~~~~~~

by locating the pertinent parameter within the decl, by traversing the
BLT tree (if present).

gcc/cp/ChangeLog:
	* call.c: Include "blt.h".
	(get_blt_node_for_function_decl): New function.
	(get_fndecl_argument_location): New function.
	(convert_like_real): Use the above when determining the location
	for the note about a mismatching argument.

gcc/testsuite/ChangeLog:
	* g++.dg/diagnostic/param-type-mismatch.C: Add -fblt to the
	options.  Update expected output to show that the pertinent
	callee parameters are underlined.
---
 gcc/cp/call.c                                      | 79 +++++++++++++++++++++-
 .../g++.dg/diagnostic/param-type-mismatch.C        | 21 +++---
 2 files changed, 86 insertions(+), 14 deletions(-)

diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index fac6b6c..99693df 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -39,6 +39,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "langhooks.h"
 #include "c-family/c-objc.h"
 #include "internal-fn.h"
+#include "blt.h"
 
 /* The various kinds of conversion.  */
 
@@ -6590,6 +6591,77 @@ maybe_print_user_conv_context (conversion *convs)
 	}
 }
 
+/* Attempt to locate the blt_node for FNDECL, or return NULL
+   if not found (or blt was not enabled).  */
+
+static blt_node *
+get_blt_node_for_function_decl (tree fndecl)
+{
+  if (!flag_blt)
+    return NULL;
+
+  blt_node *node = blt_get_node_for_tree (fndecl);
+  if (node)
+    return node;
+
+  tree template_decl = DECL_TI_TEMPLATE (fndecl);
+  if (template_decl)
+    {
+      node = blt_get_node_for_tree (template_decl);
+      if (node)
+        return node;
+    }
+
+  return NULL;
+}
+
+/* Attempt to locate the parameter with the given index within
+   FNDECL, returning DECL_SOURCE_LOCATION (fndecl) if it can't
+   be found (or blt was not enabled).  */
+
+static location_t
+get_fndecl_argument_location (tree fndecl, int argnum)
+{
+  location_t loc = DECL_SOURCE_LOCATION (fndecl);
+  blt_node *node = get_blt_node_for_function_decl (fndecl);
+  if (!node)
+    return loc;
+
+  /* For "member-declaration", expect the "direct-declarator" to be
+     inside a "declarator".  */
+  if (node->get_kind () == BLT_MEMBER_DECLARATION)
+    {
+      node = node->get_first_child_of_kind (BLT_DECLARATOR);
+      if (!node)
+        return loc;
+      node = node->get_first_child_of_kind (BLT_DIRECT_DECLARATOR);
+      if (!node)
+        return loc;
+    }
+
+  if (node->get_kind () != BLT_DIRECT_DECLARATOR)
+    return loc;
+
+  blt_node *pdc
+    = node->get_first_child_of_kind (BLT_PARAMETER_DECLARATION_CLAUSE);
+  if (!pdc)
+    return loc;
+
+  blt_node *pdl = pdc->get_first_child_of_kind (BLT_PARAMETER_DECLARATION_LIST);
+  if (!pdl)
+    return loc;
+
+  auto_vec<blt_node *> params;
+  pdl->get_children_of_kind (params, BLT_PARAMETER_DECLARATION);
+
+  if (argnum >= (int)params.length ())
+    return loc;
+
+  blt_node *param = params[argnum];
+  return param->get_range ();
+}
+
+
 /* Perform the conversions in CONVS on the expression EXPR.  FN and
    ARGNUM are used for diagnostics.  ARGNUM is zero based, -1
    indicates the `this' argument of a method.  INNER is nonzero when
@@ -6691,8 +6763,11 @@ convert_like_real (conversion *convs, tree expr, tree fn, int argnum,
 	complained = permerror (loc, "invalid conversion from %qH to %qI",
 				TREE_TYPE (expr), totype);
       if (complained && fn)
-	inform (DECL_SOURCE_LOCATION (fn),
-		"  initializing argument %P of %qD", argnum, fn);
+        {
+          location_t decl_loc = get_fndecl_argument_location (fn, argnum);
+          inform (decl_loc,
+                  "  initializing argument %P of %qD", argnum, fn);
+        }
 
       return cp_convert (totype, expr, complain);
     }
diff --git a/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch.C b/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch.C
index 864ead1..5c89098 100644
--- a/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch.C
+++ b/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch.C
@@ -1,12 +1,9 @@
-// { dg-options "-fdiagnostics-show-caret" }
+// { dg-options "-fdiagnostics-show-caret -fblt" }
 
 /* A collection of calls where argument 2 is of the wrong type.
 
    TODO: we should put the caret and underline for the diagnostic
-   at the second argument, rather than the close paren.
-
-   TODO: we should highlight the second parameter of the callee, rather
-   than its name.  */
+   at the second argument, rather than the close paren.  */
 
 /* decl, with argname.  */
 
@@ -22,7 +19,7 @@ int test_1 (int first, int second, float third)
   // { dg-message "initializing argument 2 of 'int callee_1\\(int, const char\\*, float\\)'" "" { target *-*-* } callee_1 }
   /* { dg-begin-multiline-output "" }
  extern int callee_1 (int one, const char *two, float three);
-            ^~~~~~~~
+                               ^~~~~~~~~~~~~~~
      { dg-end-multiline-output "" } */
 }
 
@@ -40,7 +37,7 @@ int test_2 (int first, int second, float third)
   // { dg-message "initializing argument 2 of 'int callee_2\\(int, const char\\*, float\\)'" "" { target *-*-* } callee_2 }
   /* { dg-begin-multiline-output "" }
  extern int callee_2 (int, const char *, float);
-            ^~~~~~~~
+                           ^~~~~~~~~~~~
      { dg-end-multiline-output "" } */
 }
 
@@ -61,7 +58,7 @@ int test_3 (int first, int second, float third)
   // { dg-message "initializing argument 2 of 'int callee_3\\(int, const char\\*, float\\)'" "" { target *-*-* } callee_3 }
   /* { dg-begin-multiline-output "" }
  static int callee_3 (int one, const char *two, float three)
-            ^~~~~~~~
+                               ^~~~~~~~~~~~~~~
      { dg-end-multiline-output "" } */
 }
 
@@ -78,7 +75,7 @@ int test_4 (int first, int second, float third)
      { dg-end-multiline-output "" } */
   /* { dg-begin-multiline-output "" }
  struct s4 { static int member_1 (int one, const char *two, float three); };
-                        ^~~~~~~~
+                                           ^~~~~~~~~~~~~~~
      { dg-end-multiline-output "" } */
 }
 
@@ -96,7 +93,7 @@ int test_5 (int first, int second, float third)
      { dg-end-multiline-output "" } */
   /* { dg-begin-multiline-output "" }
  struct s5 { int member_1 (int one, const char *two, float three); };
-                 ^~~~~~~~
+                                    ^~~~~~~~~~~~~~~
      { dg-end-multiline-output "" } */
 }
 
@@ -136,7 +133,7 @@ int test_7 (int first, int second, float third)
      { dg-end-multiline-output "" } */
   /* { dg-begin-multiline-output "" }
  struct s7 { static int member_1 (int one, T two, float three); };
-                        ^~~~~~~~
+                                           ^~~~~
      { dg-end-multiline-output "" } */
 }
 
@@ -155,7 +152,7 @@ int test_8 (int first, int second, float third)
      { dg-end-multiline-output "" } */
   /* { dg-begin-multiline-output "" }
  struct s8 { int member_1 (int one, T two, float three); };
-                 ^~~~~~~~
+                                    ^~~~~
      { dg-end-multiline-output "" } */
 }
 
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH 06/17] C: use BLT to highlight parameter of callee decl for mismatching types
  2017-07-24 19:31 [PATCH 00/17] RFC: New source-location representation; Language Server Protocol David Malcolm
                   ` (10 preceding siblings ...)
  2017-07-24 19:38 ` [PATCH 14/17] Add implementation of JSON-RPC David Malcolm
@ 2017-07-24 19:38 ` David Malcolm
  2017-07-24 19:39 ` [PATCH 08/17] C: highlight return types when complaining about mismatches David Malcolm
                   ` (7 subsequent siblings)
  19 siblings, 0 replies; 38+ messages in thread
From: David Malcolm @ 2017-07-24 19:38 UTC (permalink / raw)
  To: gcc-patches; +Cc: David Malcolm

This patch to the C frontend uses the BLT infrastructure to improve:

  extern int callee_1 (int one, const char *two, float three);

  int test_1 (int first, int second, float third)
  {
    return callee_1 (first, second, third);
  }

from:

  warning: passing argument 2 of 'callee_1' makes pointer from integer without a cast [-Wint-conversion]
   return callee_1 (first, second, third);
                           ^~~~~~
  note: expected 'const char *' but argument is of type 'int'
   extern int callee_1 (int one, const char *two, float three);
              ^~~~~~~~

to:

  warning: passing argument 2 of 'callee_1' makes pointer from integer without a cast [-Wint-conversion]
   return callee_1 (first, second, third);
                           ^~~~~~
  note: expected 'const char *' but argument is of type 'int'
   extern int callee_1 (int one, const char *two, float three);
                                 ^~~~~~~~~~~~~~~

by locating the pertinent parameter within the decl, by traversing the
BLT tree (if present).

gcc/c/ChangeLog:
	* c-typeck.c: Include "blt.h".
	(get_fndecl_argument_location): New function.
	(convert_for_assignment): Replace calls to "inform" with...
	(inform_for_arg): ...this new function, attempting to highlight
	the pertinent parameter.

gcc/testsuite/ChangeLog:
	* gcc.dg/param-type-mismatch.c: Add -fblt to options.  Update
	expected underlining to show the pertinent parameters.
---
 gcc/c/c-typeck.c                           | 63 ++++++++++++++++++++++++------
 gcc/testsuite/gcc.dg/param-type-mismatch.c | 13 +++---
 2 files changed, 56 insertions(+), 20 deletions(-)

diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c
index 4d067e9..c37cc33 100644
--- a/gcc/c/c-typeck.c
+++ b/gcc/c/c-typeck.c
@@ -51,6 +51,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "spellcheck-tree.h"
 #include "gcc-rich-location.h"
 #include "asan.h"
+#include "blt.h"
 
 /* Possible cases of implicit bad conversions.  Used to select
    diagnostic messages in convert_for_assignment.  */
@@ -6112,6 +6113,53 @@ maybe_warn_string_init (location_t loc, tree type, struct c_expr expr)
 		  "array initialized from parenthesized string constant");
 }
 
+/* Attempt to locate the parameter with the given index within FNDECL,
+   returning LOC if it can't be found (or blt was not enabled).  */
+
+static location_t
+get_fndecl_argument_location (tree fundecl, int argnum, location_t loc)
+{
+  blt_node *node = blt_get_node_for_tree (fundecl);
+  if (!node)
+    return loc;
+
+  if (node->get_kind () != BLT_DIRECT_DECLARATOR)
+    return loc;
+
+  /* We expect a parameter-list containing parameter-declaration.  */
+  node = node->get_first_child_of_kind (BLT_PARAMETER_LIST);
+  if (!node)
+    return loc;
+
+  auto_vec<blt_node *> params;
+  node->get_children_of_kind (params, BLT_PARAMETER_DECLARATION);
+
+  if (argnum >= (int)params.length ())
+    return loc;
+
+  blt_node *param = params[argnum];
+  return param->get_range ();
+}
+
+/* Issue a note about a mismatching argument for parameter PARMNUM
+   to FUNDECL, for types EXPECTED_TYPE and ACTUAL_TYPE.
+   Attempt to issue the note at the pertinent parameter of the decl;
+   failing that issue it at the location of FUNDECL; failing that
+   issue it at PLOC.  */
+
+static void
+inform_for_arg (tree fundecl, location_t ploc, int parmnum,
+		tree expected_type, tree actual_type)
+{
+  location_t loc = (fundecl && !DECL_IS_BUILTIN (fundecl)
+		    ? DECL_SOURCE_LOCATION (fundecl) : ploc);
+  loc = get_fndecl_argument_location (fundecl, parmnum - 1, loc);
+
+  inform (loc,
+	  "expected %qT but argument is of type %qT",
+	  expected_type, actual_type);
+}
+
 /* Convert value RHS to type TYPE as preparation for an assignment to
    an lvalue of type TYPE.  If ORIGTYPE is not NULL_TREE, it is the
    original type of RHS; this differs from TREE_TYPE (RHS) for enum
@@ -6183,10 +6231,7 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type,
       {                                                                  \
       case ic_argpass:                                                   \
         if (pedwarn (PLOC, OPT, AR, parmnum, rname))			 \
-          inform ((fundecl && !DECL_IS_BUILTIN (fundecl))	         \
-		  ? DECL_SOURCE_LOCATION (fundecl) : PLOC,		 \
-                  "expected %qT but argument is of type %qT",            \
-                  type, rhstype);                                        \
+          inform_for_arg (fundecl, (PLOC), parmnum, type, rhstype);	\
         break;                                                           \
       case ic_assign:                                                    \
         pedwarn (LOCATION, OPT, AS);                                     \
@@ -6212,10 +6257,7 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type,
       {                                                                  \
       case ic_argpass:                                                   \
         if (pedwarn (PLOC, OPT, AR, parmnum, rname, QUALS))		 \
-          inform ((fundecl && !DECL_IS_BUILTIN (fundecl))	         \
-		  ? DECL_SOURCE_LOCATION (fundecl) : PLOC,		 \
-                  "expected %qT but argument is of type %qT",            \
-                  type, rhstype);                                        \
+          inform_for_arg (fundecl, (PLOC), parmnum, type, rhstype);	\
         break;                                                           \
       case ic_assign:                                                    \
         pedwarn (LOCATION, OPT, AS, QUALS);				 \
@@ -6241,10 +6283,7 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type,
       {                                                                  \
       case ic_argpass:                                                   \
         if (warning_at (PLOC, OPT, AR, parmnum, rname, QUALS))           \
-          inform ((fundecl && !DECL_IS_BUILTIN (fundecl))                \
-                  ? DECL_SOURCE_LOCATION (fundecl) : PLOC,               \
-                  "expected %qT but argument is of type %qT",            \
-                  type, rhstype);                                        \
+          inform_for_arg (fundecl, (PLOC), parmnum, type, rhstype);      \
         break;                                                           \
       case ic_assign:                                                    \
         warning_at (LOCATION, OPT, AS, QUALS);                           \
diff --git a/gcc/testsuite/gcc.dg/param-type-mismatch.c b/gcc/testsuite/gcc.dg/param-type-mismatch.c
index 70ea0bc..eb3169b 100644
--- a/gcc/testsuite/gcc.dg/param-type-mismatch.c
+++ b/gcc/testsuite/gcc.dg/param-type-mismatch.c
@@ -1,9 +1,6 @@
-/* { dg-options "-fdiagnostics-show-caret" }  */
+/* { dg-options "-fdiagnostics-show-caret -fblt" }  */
 
-/* A collection of calls where argument 2 is of the wrong type.
-
-   TODO: we should highlight the second parameter of the callee, rather
-   than its name.  */
+/* A collection of calls where argument 2 is of the wrong type.  */
 
 /* decl, with argname.  */
 
@@ -19,7 +16,7 @@ int test_1 (int first, int second, float third)
   /* { dg-message "expected 'const char \\*' but argument is of type 'int'" "" { target *-*-* } callee_1 } */
   /* { dg-begin-multiline-output "" }
  extern int callee_1 (int one, const char *two, float three);
-            ^~~~~~~~
+                               ^~~~~~~~~~~~~~~
      { dg-end-multiline-output "" } */
 }
 
@@ -37,7 +34,7 @@ int test_2 (int first, int second, float third)
   /* { dg-message "expected 'const char \\*' but argument is of type 'int'" "" { target *-*-* } callee_2 } */
   /* { dg-begin-multiline-output "" }
  extern int callee_2 (int, const char *, float);
-            ^~~~~~~~
+                           ^~~~~~~~~~~~
      { dg-end-multiline-output "" } */
 }
 
@@ -58,6 +55,6 @@ int test_3 (int first, int second, float third)
   /* { dg-message "expected 'const char \\*' but argument is of type 'int'" "" { target *-*-* } callee_3 } */
   /* { dg-begin-multiline-output "" }
  static int callee_3 (int one, const char *two, float three)
-            ^~~~~~~~
+                               ^~~~~~~~~~~~~~~
      { dg-end-multiline-output "" } */
 }
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH 09/17] C++: highlight return types when complaining about mismatches
  2017-07-24 19:31 [PATCH 00/17] RFC: New source-location representation; Language Server Protocol David Malcolm
                   ` (12 preceding siblings ...)
  2017-07-24 19:39 ` [PATCH 08/17] C: highlight return types when complaining about mismatches David Malcolm
@ 2017-07-24 19:39 ` David Malcolm
  2017-07-24 19:39 ` [PATCH 04/17] C frontend: capture BLT information David Malcolm
                   ` (5 subsequent siblings)
  19 siblings, 0 replies; 38+ messages in thread
From: David Malcolm @ 2017-07-24 19:39 UTC (permalink / raw)
  To: gcc-patches; +Cc: David Malcolm

This patch to the C++ frontend uses the BLT infrastructure to highlight
the return type of a function when complaining about code within the
function that doesn't match it.

For example, given:

  error: return-statement with a value, in function returning 'void' [-fpermissive]
     return 42;
            ^~

the patch adds this note:

  note: the return type was declared as 'void' here
   void test_1 (void)
   ^~~~

gcc/cp/ChangeLog:
	* cp-tree.h (attempt_to_highlight_return_type): New decl.
	* decl.c (finish_function): Call attempt_to_highlight_return_type
	when complaining about missing return statements in non-void
	functions.
	* typeck.c: Include "blt.h".
	(get_location_of_return_type): New function.
	(attempt_to_highlight_return_type): New function.
	(check_return_expr): Call attempt_to_highlight_return_type when
	complaining about return statements that mismatch the "void-ness"
	of the function.

gcc/testsuite/ChangeLog:
	* g++.dg/bad-return-type.C: New test case.
---
 gcc/cp/cp-tree.h                       |   2 +
 gcc/cp/decl.c                          |   5 +-
 gcc/cp/typeck.c                        |  70 ++++++++++++++++-
 gcc/testsuite/g++.dg/bad-return-type.C | 135 +++++++++++++++++++++++++++++++++
 4 files changed, 206 insertions(+), 6 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/bad-return-type.C

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 16d8a48..ec83f78 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7146,6 +7146,8 @@ extern tree finish_left_unary_fold_expr      (tree, int);
 extern tree finish_right_unary_fold_expr     (tree, int);
 extern tree finish_binary_fold_expr          (tree, tree, int);
 
+extern void attempt_to_highlight_return_type (tree fndecl);
+
 /* in typeck2.c */
 extern void require_complete_eh_spec_types	(tree, tree);
 extern void cxx_incomplete_type_diagnostic	(location_t, const_tree,
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index bcf305c..84a275f 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -15643,8 +15643,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/typeck.c b/gcc/cp/typeck.c
index 316d57f..054dca5 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -38,6 +38,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "params.h"
 #include "gcc-rich-location.h"
 #include "asan.h"
+#include "blt.h"
 
 static tree cp_build_addr_expr_strict (tree, tsubst_flags_t);
 static tree cp_build_function_call (tree, tree, tsubst_flags_t);
@@ -8869,6 +8870,61 @@ maybe_warn_about_returning_address_of_local (tree retval)
   return false;
 }
 
+/* Attempt to use BLT locate the return type within FNDECL, returning
+   UNKNOWN_LOCATION if there is a problem (or if BLT is disabled).  */
+
+static location_t
+get_location_of_return_type (tree fndecl)
+{
+  blt_node *node = blt_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.  */
+  blt_node *iter;
+  for (iter = node; iter; iter = iter->get_parent ())
+    if (iter->get_kind () == BLT_FUNCTION_DEFINITION
+	|| iter->get_kind () == BLT_MEMBER_DECLARATION)
+      break;
+  if (!iter)
+    return UNKNOWN_LOCATION;
+
+  /* Locate the decl specifiers within the function-definition
+     or member-declaration.  */
+  blt_node *dss = iter->get_first_child_of_kind (BLT_DECL_SPECIFIER_SEQ);
+  if (!dss)
+    return UNKNOWN_LOCATION;
+
+  /* Locate the type-specifier within the decl specifiers.  It ought to
+     be "void".  */
+  blt_node *ts = dss->get_first_child_of_kind (BLT_TYPE_SPECIFIER);
+  if (!ts)
+    return UNKNOWN_LOCATION;
+
+  return ts->get_range ();
+}
+
+/* Attempt to locate the return type within FNDECL; if successful,
+   emit a note highlighting it.  */
+
+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
@@ -8994,8 +9050,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
@@ -9013,8 +9072,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..e62eb0a
--- /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 -fblt -Wreturn-type" }
+
+void test_1 (void) // { dg-line return_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 *-*-* } return_line }
+  /* { dg-begin-multiline-output "" }
+ void test_1 (void)
+ ^~~~
+     { dg-end-multiline-output "" } */
+}
+
+/* As before, but with different whitespace.  */
+
+void // { dg-line return_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 *-*-* } return_line_2 }
+  /* { dg-begin-multiline-output "" }
+ void
+ ^~~~
+     { dg-end-multiline-output "" } */
+}
+
+/* As before, but with "extern".  */
+
+extern void test_3 (void) // { dg-line return_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 *-*-* } return_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 return_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 *-*-* } return_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 return_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 *-*-* } return_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 return_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 *-*-* } return_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 return_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 *-*-* } return_line_8 }
+    /* { dg-begin-multiline-output "" }
+   void member (T t)
+   ^~~~
+       { dg-end-multiline-output "" } */
+  }
+};
+
+// TODO: complex return type e.g. const char *
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH 08/17] C: highlight return types when complaining about mismatches
  2017-07-24 19:31 [PATCH 00/17] RFC: New source-location representation; Language Server Protocol David Malcolm
                   ` (11 preceding siblings ...)
  2017-07-24 19:38 ` [PATCH 06/17] C: use BLT to highlight parameter of callee decl for mismatching types David Malcolm
@ 2017-07-24 19:39 ` David Malcolm
  2017-07-24 19:39 ` [PATCH 09/17] C++: " David Malcolm
                   ` (6 subsequent siblings)
  19 siblings, 0 replies; 38+ messages in thread
From: David Malcolm @ 2017-07-24 19:39 UTC (permalink / raw)
  To: gcc-patches; +Cc: David Malcolm

This patch to the C frontend uses the BLT infrastructure to highlight
the return type of a function when complaining about code within the
function that doesn't match it.

For example, given:

  warning: 'return' with a value, in function returning void
     return 42;
            ^~
  note: declared here
   void test_1 (void)
        ^~~~~~

the patch converts the location and wording of the note to highlight
the return type:

  warning: 'return' with a value, in function returning void
     return 42;
            ^~
  note: the return type was declared as 'void' here
   void test_1 (void)
   ^~~~

gcc/c/ChangeLog:
	* c-typeck.c (get_location_of_return_type): New function.
	(attempt_to_highlight_return_type): New function.
	(c_finish_return): Convert "inform" calls to calls to
	attempt_to_highlight_return_type.

gcc/testsuite/ChangeLog:
	* gcc.dg/bad-return-type.c: New test case.
---
 gcc/c/c-typeck.c                       | 57 +++++++++++++++++++++++++++--
 gcc/testsuite/gcc.dg/bad-return-type.c | 67 ++++++++++++++++++++++++++++++++++
 2 files changed, 120 insertions(+), 4 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/bad-return-type.c

diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c
index c37cc33..7362246 100644
--- a/gcc/c/c-typeck.c
+++ b/gcc/c/c-typeck.c
@@ -9884,6 +9884,57 @@ c_finish_goto_ptr (location_t loc, tree expr)
   return add_stmt (t);
 }
 
+/* Attempt to use BLT locate the return type within FNDECL, returning
+   UNKNOWN_LOCATION if there is a problem (or if BLT is disabled).  */
+
+static location_t
+get_location_of_return_type (tree fndecl)
+{
+  blt_node *node = blt_get_node_for_tree (fndecl);
+  if (!node)
+    return UNKNOWN_LOCATION;
+
+  /* We have a direct-declarator.
+     Go up two to find the external-declaration.  */
+  blt_node *ext_decl = node->get_ancestor_of_kind (BLT_EXTERNAL_DECLARATION);
+  if (!ext_decl)
+    return UNKNOWN_LOCATION;
+
+  /* Locate the declaration-specifiers within the direct-declarator.  */
+  blt_node *dss
+    = ext_decl->get_first_child_of_kind (BLT_DECLARATION_SPECIFIERS);
+  if (!dss)
+    return UNKNOWN_LOCATION;
+
+  /* Locate the type-specifier within the decl specifiers.  */
+  blt_node *ts = dss->get_first_child_of_kind (BLT_TYPE_SPECIFIER);
+  if (!ts)
+    return UNKNOWN_LOCATION;
+
+  /* FIXME: we want just the return type, not "extern" etc.  */
+  return ts->get_range ();
+}
+
+/* Attempt to locate the return type within FNDECL; if successful,
+   emit a note highlighting the return type; otherwise emit a note
+   highlighting the decl.  */
+
+static 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)
+    inform (DECL_SOURCE_LOCATION (fndecl), "declared here");
+  else
+    {
+      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);
+    }
+}
+
+
 /* Generate a C `return' statement.  RETVAL is the expression for what
    to return, or a null pointer for `return;' with no value.  LOC is
    the location of the return statement, or the location of the expression,
@@ -9956,8 +10007,7 @@ c_finish_return (location_t loc, tree retval, tree origtype)
 	       "%<return%> with no value, in function returning non-void");
 	  no_warning = true;
 	  if (warned_here)
-	    inform (DECL_SOURCE_LOCATION (current_function_decl),
-		    "declared here");
+	    attempt_to_highlight_return_type (current_function_decl);
 	}
     }
   else if (valtype == NULL_TREE || TREE_CODE (valtype) == VOID_TYPE)
@@ -9973,8 +10023,7 @@ c_finish_return (location_t loc, tree retval, tree origtype)
 	  (xloc, OPT_Wpedantic, "ISO C forbids "
 	   "%<return%> with expression, in function returning void");
       if (warned_here)
-	inform (DECL_SOURCE_LOCATION (current_function_decl),
-		"declared here");
+	attempt_to_highlight_return_type (current_function_decl);
     }
   else
     {
diff --git a/gcc/testsuite/gcc.dg/bad-return-type.c b/gcc/testsuite/gcc.dg/bad-return-type.c
new file mode 100644
index 0000000..095b767
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/bad-return-type.c
@@ -0,0 +1,67 @@
+/* Verify that we can highlight the return type for various kinds
+   of bogus return.  */
+
+/* { dg-options "-fdiagnostics-show-caret -fblt -Wreturn-type" } */
+
+void test_1 (void) // { dg-line return_line }
+{
+  return 42; // { dg-warning "'return' 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 *-*-* } return_line } */
+  /* { dg-begin-multiline-output "" }
+ void test_1 (void)
+ ^~~~
+     { dg-end-multiline-output "" } */
+}
+
+/* As before, but with different whitespace.  */
+
+void // { dg-line return_line_2 }
+test_2 (void)
+{
+  return 42; // { dg-warning "'return' 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 *-*-* } return_line_2 } */
+  /* { dg-begin-multiline-output "" }
+ void
+ ^~~~
+     { dg-end-multiline-output "" } */
+}
+
+/* As before, but with "extern".  */
+
+extern void test_3 (void) // { dg-line return_line_3 }
+{
+  return 42; // { dg-warning "'return' 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 *-*-* } return_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 return_line_4 }
+{
+  return; // { dg-warning "'return' with no value, in function returning non-void" }
+  /* { dg-begin-multiline-output "" }
+   return;
+   ^~~~~~
+     { dg-end-multiline-output "" } */
+  /* { dg-message "the return type was declared as 'int' here" "" { target *-*-* } return_line_4 } */
+  /* { dg-begin-multiline-output "" }
+ extern int test_4 (void)
+        ^~~
+     { dg-end-multiline-output "" } */
+}
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH 04/17] C frontend: capture BLT information
  2017-07-24 19:31 [PATCH 00/17] RFC: New source-location representation; Language Server Protocol David Malcolm
                   ` (13 preceding siblings ...)
  2017-07-24 19:39 ` [PATCH 09/17] C++: " David Malcolm
@ 2017-07-24 19:39 ` David Malcolm
  2017-07-27 19:58   ` Martin Sebor
  2017-07-24 19:40 ` [PATCH 05/17] C++ " David Malcolm
                   ` (4 subsequent siblings)
  19 siblings, 1 reply; 38+ messages in thread
From: David Malcolm @ 2017-07-24 19:39 UTC (permalink / raw)
  To: gcc-patches; +Cc: David Malcolm

This patch extends the C frontend so that it optionally builds a BLT tree,
by using an auto_blt_node class within the recursive descent through the
parser, so that its ctor/dtors build the blt_node hierarchy; this is
rapidly (I hope) rejected in the no -fblt case, so that by default, no
blt nodes are created and it's (I hope) close to a no-op.

gcc/c/ChangeLog:
	* c-decl.c: Include "blt.h".
	(build_array_declarator): Initialize bltnode field of new
	declarator.
	(start_decl): Associate this decl with any bltnode of the
	declarator.
	(start_function): Likewise.
	(build_attrs_declarator): Initialize bltnode field of new
	declarator.
	(build_function_declarator): Add bltnode param, and use it to
	initialize the bltnode field of the new declarator.
	(build_id_declarator): Likewise.
	(make_pointer_declarator): Likewise.
	* c-parser.c: Include "blt.h".
	(struct c_parser): Add fields "last_token_location",
	"blt_root_node", "blt_current_node".
	(class auto_blt_node): New class.
	(AUTO_BLT_NODE): New macro.
	(CURRENT_BLT_NODE): New macro.
	(auto_blt_node::auto_blt_node): New ctor.
	(auto_blt_node::~auto_blt_node): New dtor.
	(auto_blt_node::set_tree): New method.
	(c_parser_consume_token): Update last_token_location.
	(c_parser_translation_unit): Add AUTO_BLT_NODE.
	(c_parser_external_declaration): Likewise.
	(c_parser_declspecs): Likewise, in multiple places.
	(c_parser_struct_or_union_specifier): Likewise.  Set the tree
	on the struct-contents and the struct-or-union-specifier.
	(c_parser_struct_declaration): Add AUTO_BLT_NODE.
	(c_parser_declarator): Likewise.
	(c_parser_direct_declarator): Likewise.  Set up the bltnode of
	the declarator.
	(c_parser_direct_declarator_inner): Pass the bltnode to the
	declarator.
	(c_parser_parms_declarator): Add AUTO_BLT_NODE.
	(c_parser_parameter_declaration): Likewise.
	(c_parser_expr_no_commas): Likewise.
	(c_parser_expression): Likewise.
	(c_parser_expr_list): Likewise.
	(c_parse_file): Handle -fdump-blt; set the_blt_root_node.
	* c-tree.h (class blt_node): Add forward decl.
	(struct c_declarator): Add "bltnode" field.
	(build_function_declarator): Add blt_node * param.
---
 gcc/c/c-decl.c   |  13 ++-
 gcc/c/c-parser.c | 241 ++++++++++++++++++++++++++++++++++++++++++++++---------
 gcc/c/c-tree.h   |   6 +-
 3 files changed, 221 insertions(+), 39 deletions(-)

diff --git a/gcc/c/c-decl.c b/gcc/c/c-decl.c
index 50da185..ff27e55 100644
--- a/gcc/c/c-decl.c
+++ b/gcc/c/c-decl.c
@@ -54,6 +54,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "spellcheck-tree.h"
 #include "gcc-rich-location.h"
 #include "asan.h"
+#include "blt.h"
 
 /* In grokdeclarator, distinguish syntactic contexts of declarators.  */
 enum decl_context
@@ -4503,6 +4504,7 @@ build_array_declarator (location_t loc,
 	}
       current_scope->had_vla_unspec = true;
     }
+  declarator->bltnode = NULL;
   return declarator;
 }
 
@@ -4627,6 +4629,8 @@ start_decl (struct c_declarator *declarator, struct c_declspecs *declspecs,
 			 deprecated_state);
   if (!decl || decl == error_mark_node)
     return NULL_TREE;
+  if (declarator->bltnode)
+    declarator->bltnode->set_tree (decl);
 
   if (expr)
     add_stmt (fold_convert (void_type_node, expr));
@@ -8527,6 +8531,8 @@ start_function (struct c_declspecs *declspecs, struct c_declarator *declarator,
 
   decl1 = grokdeclarator (declarator, declspecs, FUNCDEF, true, NULL,
 			  &attributes, NULL, NULL, DEPRECATED_NORMAL);
+  if (declarator->bltnode)
+    declarator->bltnode->set_tree (decl1);
   invoke_plugin_callbacks (PLUGIN_START_PARSE_FUNCTION, decl1);
 
   /* If the declarator is not suitable for a function definition,
@@ -9645,6 +9651,7 @@ build_attrs_declarator (tree attrs, struct c_declarator *target)
   ret->kind = cdk_attrs;
   ret->declarator = target;
   ret->u.attrs = attrs;
+  ret->bltnode = NULL;
   return ret;
 }
 
@@ -9653,12 +9660,14 @@ build_attrs_declarator (tree attrs, struct c_declarator *target)
 
 struct c_declarator *
 build_function_declarator (struct c_arg_info *args,
-			   struct c_declarator *target)
+			   struct c_declarator *target,
+			   blt_node *bltnode)
 {
   struct c_declarator *ret = XOBNEW (&parser_obstack, struct c_declarator);
   ret->kind = cdk_function;
   ret->declarator = target;
   ret->u.arg_info = args;
+  ret->bltnode = bltnode;
   return ret;
 }
 
@@ -9674,6 +9683,7 @@ build_id_declarator (tree ident)
   ret->u.id = ident;
   /* Default value - may get reset to a more precise location. */
   ret->id_loc = input_location;
+  ret->bltnode = NULL;
   return ret;
 }
 
@@ -9700,6 +9710,7 @@ make_pointer_declarator (struct c_declspecs *type_quals_attrs,
   ret->kind = cdk_pointer;
   ret->declarator = itarget;
   ret->u.pointer_quals = quals;
+  ret->bltnode = NULL;
   return ret;
 }
 
diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c
index f8fbc92..119ae23 100644
--- a/gcc/c/c-parser.c
+++ b/gcc/c/c-parser.c
@@ -65,6 +65,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "read-rtl-function.h"
 #include "run-rtl-passes.h"
 #include "intl.h"
+#include "blt.h"
 
 /* We need to walk over decls with incomplete struct/union/enum types
    after parsing the whole translation unit.
@@ -206,8 +207,115 @@ struct GTY(()) c_parser {
   /* Buffer to hold all the tokens from parsing the vector attribute for the
      SIMD-enabled functions (formerly known as elemental functions).  */
   vec <c_token, va_gc> *cilk_simd_fn_tokens;
+
+  location_t last_token_location;
+  blt_node * GTY((skip)) blt_root_node;
+  blt_node * GTY((skip)) blt_current_node;
+};
+
+/* A RAII-style class for optionally building a concrete parse tree (or
+   at least, something close to it) as the recursive descent parser runs.
+
+   Close to a no-op if -fblt is not selected.  */
+
+class auto_blt_node
+{
+public:
+  auto_blt_node (c_parser* parser, enum blt_kind kind);
+  ~auto_blt_node ();
+
+  void set_tree (tree node);
+
+private:
+  c_parser *m_parser;
 };
 
+/* RAII-style construction of the blt_node tree: push a blt_node of KIND
+   onto the current stack of blt_nodes, popping it when it goes out
+   of scope.  */
+
+#define AUTO_BLT_NODE(PARSER, KIND) \
+  auto_blt_node tmp_blt_node ((PARSER), (KIND))
+
+/* The blt_node currently being constructed (the macro assumes that "parser"
+   exists in the current scope).  */
+
+#define CURRENT_BLT_NODE (parser->blt_current_node)
+
+/* auto_blt_node's constructor.
+
+   If -fblt was not enabled, return immediately.
+
+   Otherwise push a new blt_node of KIND as a child of the previous top
+   of the blt stack, effectively constructing a tree.
+
+   Set the new blt_node's start location to that of the next token
+   within PARSER.  */
+
+auto_blt_node::auto_blt_node (c_parser *parser, enum blt_kind kind)
+{
+  if (!flag_blt)
+    return;
+
+  /* Do this here rather than as an initializer
+     to avoid doing work when -fblt is not set.  */
+  m_parser = parser;
+
+  c_token *first_token = c_parser_peek_token (parser);
+  blt_node *parent = parser->blt_current_node;
+  blt_node *node = new blt_node (kind, first_token->location);
+  parser->blt_current_node = node;
+  if (parent)
+    parent->add_child (node);
+  else
+    parser->blt_root_node = node;
+}
+
+/* auto_blt_node's destructor.
+
+   If -fblt was not enabled, return immediately.
+
+   Otherwise, pop the current blt_node from the stack,
+   and set its finish location to that of the last
+   token that was consumed.  */
+
+auto_blt_node::~auto_blt_node ()
+{
+  if (!flag_blt)
+    return;
+
+  blt_node *node = m_parser->blt_current_node;
+  node->set_finish (m_parser->last_token_location);
+
+  if (0)
+    {
+      location_t start = node->get_start ();
+      location_t finish = node->get_finish ();
+      location_t range = make_location (start, start, finish);
+      inform (range, "%qs", node->get_name ());
+    }
+
+#if 0
+  if (m_expr_ptr)
+    node->set_tree (m_expr_ptr->get_value ());
+#endif
+
+  m_parser->blt_current_node = node->get_parent ();
+}
+
+/* Set the current blt_node's tree to be TREE_NODE.
+   Do nothing if -fblt is not set.  */
+
+void
+auto_blt_node::set_tree (tree tree_node)
+{
+  if (!flag_blt)
+    return;
+
+  blt_node *node = m_parser->blt_current_node;
+  node->set_tree (tree_node);
+}
+
 /* Return a pointer to the Nth token in PARSERs tokens_buf.  */
 
 c_token *
@@ -770,6 +878,7 @@ c_parser_consume_token (c_parser *parser)
   gcc_assert (parser->tokens[0].type != CPP_EOF);
   gcc_assert (!parser->in_pragma || parser->tokens[0].type != CPP_PRAGMA_EOL);
   gcc_assert (parser->error || parser->tokens[0].type != CPP_PRAGMA);
+  parser->last_token_location = parser->tokens[0].location;
   if (parser->tokens != &parser->tokens_buf[0])
     parser->tokens++;
   else if (parser->tokens_avail == 2)
@@ -1336,6 +1445,8 @@ static void c_parser_parse_rtl_body (c_parser *parser, char *start_with_pass);
 static void
 c_parser_translation_unit (c_parser *parser)
 {
+  AUTO_BLT_NODE (parser, BLT_TRANSLATION_UNIT);
+
   if (c_parser_next_token_is (parser, CPP_EOF))
     {
       pedwarn (c_parser_peek_token (parser)->location, OPT_Wpedantic,
@@ -1388,6 +1499,8 @@ c_parser_translation_unit (c_parser *parser)
 static void
 c_parser_external_declaration (c_parser *parser)
 {
+  AUTO_BLT_NODE (parser, BLT_EXTERNAL_DECLARATION);
+
   int ext;
   switch (c_parser_peek_token (parser)->type)
     {
@@ -2378,6 +2491,8 @@ c_parser_declspecs (c_parser *parser, struct c_declspecs *specs,
 		    bool alignspec_ok, bool auto_type_ok,
 		    enum c_lookahead_kind la)
 {
+  AUTO_BLT_NODE (parser, BLT_DECLARATION_SPECIFIERS);
+
   bool attrs_ok = start_attr_ok;
   bool seen_type = specs->typespec_kind != ctsk_none;
 
@@ -2427,6 +2542,8 @@ c_parser_declspecs (c_parser *parser, struct c_declspecs *specs,
 	  if (seen_type || !c_parser_next_tokens_start_typename (parser, la))
 	    break;
 
+	  AUTO_BLT_NODE (parser, BLT_TYPE_SPECIFIER);
+
 	  /* Now at an unknown typename (C_ID_ID), a C_ID_TYPENAME or
 	     a C_ID_CLASSNAME.  */
 	  c_parser_consume_token (parser);
@@ -2524,48 +2641,61 @@ c_parser_declspecs (c_parser *parser, struct c_declspecs *specs,
 	case RID_INT_N_1:
 	case RID_INT_N_2:
 	case RID_INT_N_3:
-	  if (!typespec_ok)
-	    goto out;
-	  attrs_ok = true;
-	  seen_type = true;
-	  if (c_dialect_objc ())
-	    parser->objc_need_raw_identifier = true;
-	  t.kind = ctsk_resword;
-	  t.spec = c_parser_peek_token (parser)->value;
-	  t.expr = NULL_TREE;
-	  t.expr_const_operands = true;
-	  declspecs_add_type (loc, specs, t);
-	  c_parser_consume_token (parser);
+	  {
+	    if (!typespec_ok)
+	      goto out;
+
+	    AUTO_BLT_NODE (the_parser, BLT_TYPE_SPECIFIER);
+	    attrs_ok = true;
+	    seen_type = true;
+	    if (c_dialect_objc ())
+	      parser->objc_need_raw_identifier = true;
+	    t.kind = ctsk_resword;
+	    t.spec = c_parser_peek_token (parser)->value;
+	    t.expr = NULL_TREE;
+	    t.expr_const_operands = true;
+	    declspecs_add_type (loc, specs, t);
+	    c_parser_consume_token (parser);
+	  }
 	  break;
 	case RID_ENUM:
-	  if (!typespec_ok)
-	    goto out;
-	  attrs_ok = true;
-	  seen_type = true;
-	  t = c_parser_enum_specifier (parser);
-          invoke_plugin_callbacks (PLUGIN_FINISH_TYPE, t.spec);
-	  declspecs_add_type (loc, specs, t);
+	  {
+	    if (!typespec_ok)
+	      goto out;
+	    AUTO_BLT_NODE (the_parser, BLT_TYPE_SPECIFIER);
+	    attrs_ok = true;
+	    seen_type = true;
+	    t = c_parser_enum_specifier (parser);
+	    invoke_plugin_callbacks (PLUGIN_FINISH_TYPE, t.spec);
+	    declspecs_add_type (loc, specs, t);
+	  }
 	  break;
 	case RID_STRUCT:
 	case RID_UNION:
-	  if (!typespec_ok)
-	    goto out;
-	  attrs_ok = true;
-	  seen_type = true;
-	  t = c_parser_struct_or_union_specifier (parser);
-          invoke_plugin_callbacks (PLUGIN_FINISH_TYPE, t.spec);
-	  declspecs_add_type (loc, specs, t);
+	  {
+	    if (!typespec_ok)
+	      goto out;
+	    AUTO_BLT_NODE (the_parser, BLT_TYPE_SPECIFIER);
+	    attrs_ok = true;
+	    seen_type = true;
+	    t = c_parser_struct_or_union_specifier (parser);
+	    invoke_plugin_callbacks (PLUGIN_FINISH_TYPE, t.spec);
+	    declspecs_add_type (loc, specs, t);
+	  }
 	  break;
 	case RID_TYPEOF:
-	  /* ??? The old parser rejected typeof after other type
-	     specifiers, but is a syntax error the best way of
-	     handling this?  */
-	  if (!typespec_ok || seen_type)
-	    goto out;
-	  attrs_ok = true;
-	  seen_type = true;
-	  t = c_parser_typeof_specifier (parser);
-	  declspecs_add_type (loc, specs, t);
+	  {
+	    /* ??? The old parser rejected typeof after other type
+	       specifiers, but is a syntax error the best way of
+	       handling this?  */
+	    if (!typespec_ok || seen_type)
+	      goto out;
+	    AUTO_BLT_NODE (the_parser, BLT_TYPE_SPECIFIER);
+	    attrs_ok = true;
+	    seen_type = true;
+	    t = c_parser_typeof_specifier (parser);
+	    declspecs_add_type (loc, specs, t);
+	  }
 	  break;
 	case RID_ATOMIC:
 	  /* C parser handling of Objective-C constructs needs
@@ -2863,6 +2993,8 @@ c_parser_enum_specifier (c_parser *parser)
 static struct c_typespec
 c_parser_struct_or_union_specifier (c_parser *parser)
 {
+  AUTO_BLT_NODE (parser, BLT_STRUCT_OR_UNION_SPECIFIER);
+
   struct c_typespec ret;
   tree attrs;
   tree ident = NULL_TREE;
@@ -2898,6 +3030,8 @@ c_parser_struct_or_union_specifier (c_parser *parser)
     {
       /* Parse a struct or union definition.  Start the scope of the
 	 tag before parsing components.  */
+      AUTO_BLT_NODE (parser, BLT_STRUCT_CONTENTS);
+
       struct c_struct_parse_info *struct_info;
       tree type = start_struct (struct_loc, code, ident, &struct_info);
       tree postfix_attrs;
@@ -3006,6 +3140,10 @@ c_parser_struct_or_union_specifier (c_parser *parser)
       ret.expr = NULL_TREE;
       ret.expr_const_operands = true;
       timevar_pop (TV_PARSE_STRUCT);
+
+      if (CURRENT_BLT_NODE)
+	CURRENT_BLT_NODE->set_tree (ret.spec);
+
       return ret;
     }
   else if (!ident)
@@ -3017,7 +3155,12 @@ c_parser_struct_or_union_specifier (c_parser *parser)
       ret.expr_const_operands = true;
       return ret;
     }
+
   ret = parser_xref_tag (ident_loc, code, ident);
+
+  if (CURRENT_BLT_NODE)
+    CURRENT_BLT_NODE->set_tree (ret.spec);
+
   return ret;
 }
 
@@ -3056,6 +3199,8 @@ c_parser_struct_or_union_specifier (c_parser *parser)
 static tree
 c_parser_struct_declaration (c_parser *parser)
 {
+  AUTO_BLT_NODE (parser, BLT_STRUCT_DECLARATION);
+
   struct c_declspecs *specs;
   tree prefix_attrs;
   tree all_prefix_attrs;
@@ -3393,6 +3538,8 @@ struct c_declarator *
 c_parser_declarator (c_parser *parser, bool type_seen_p, c_dtr_syn kind,
 		     bool *seen_id)
 {
+  AUTO_BLT_NODE (parser, BLT_DECLARATOR);
+
   /* Parse any initial pointer part.  */
   if (c_parser_next_token_is (parser, CPP_MULT))
     {
@@ -3419,6 +3566,8 @@ static struct c_declarator *
 c_parser_direct_declarator (c_parser *parser, bool type_seen_p, c_dtr_syn kind,
 			    bool *seen_id)
 {
+  AUTO_BLT_NODE (parser, BLT_DIRECT_DECLARATOR);
+
   /* The direct declarator must start with an identifier (possibly
      omitted) or a parenthesized declarator (possibly abstract).  In
      an ordinary declarator, initial parentheses must start a
@@ -3462,6 +3611,7 @@ c_parser_direct_declarator (c_parser *parser, bool type_seen_p, c_dtr_syn kind,
     {
       struct c_declarator *inner
 	= build_id_declarator (c_parser_peek_token (parser)->value);
+      inner->bltnode = CURRENT_BLT_NODE;
       *seen_id = true;
       inner->id_loc = c_parser_peek_token (parser)->location;
       c_parser_consume_token (parser);
@@ -3498,7 +3648,8 @@ c_parser_direct_declarator (c_parser *parser, bool type_seen_p, c_dtr_syn kind,
 	    {
 	      inner
 		= build_function_declarator (args,
-					     build_id_declarator (NULL_TREE));
+					     build_id_declarator (NULL_TREE),
+					     CURRENT_BLT_NODE);
 	      return c_parser_direct_declarator_inner (parser, *seen_id,
 						       inner);
 	    }
@@ -3646,7 +3797,7 @@ c_parser_direct_declarator_inner (c_parser *parser, bool id_present,
 	return NULL;
       else
 	{
-	  inner = build_function_declarator (args, inner);
+	  inner = build_function_declarator (args, inner, CURRENT_BLT_NODE);
 	  return c_parser_direct_declarator_inner (parser, id_present, inner);
 	}
     }
@@ -3661,6 +3812,8 @@ c_parser_direct_declarator_inner (c_parser *parser, bool id_present,
 static struct c_arg_info *
 c_parser_parms_declarator (c_parser *parser, bool id_list_ok, tree attrs)
 {
+  AUTO_BLT_NODE (parser, BLT_PARAMETER_LIST);
+
   push_scope ();
   declare_parm_level ();
   /* If the list starts with an identifier, it is an identifier list.
@@ -3832,6 +3985,8 @@ c_parser_parms_list_declarator (c_parser *parser, tree attrs, tree expr)
 static struct c_parm *
 c_parser_parameter_declaration (c_parser *parser, tree attrs)
 {
+  AUTO_BLT_NODE (parser, BLT_PARAMETER_DECLARATION);
+
   struct c_declspecs *specs;
   struct c_declarator *declarator;
   tree prefix_attrs;
@@ -6422,6 +6577,8 @@ static struct c_expr
 c_parser_expr_no_commas (c_parser *parser, struct c_expr *after,
 			 tree omp_atomic_lhs)
 {
+  AUTO_BLT_NODE (parser, BLT_ASSIGNMENT_EXPRESSION);
+
   struct c_expr lhs, rhs, ret;
   enum tree_code code;
   location_t op_location, exp_location;
@@ -8655,6 +8812,8 @@ c_parser_postfix_expression_after_primary (c_parser *parser,
 static struct c_expr
 c_parser_expression (c_parser *parser)
 {
+  AUTO_BLT_NODE (parser, BLT_EXPRESSION);
+
   location_t tloc = c_parser_peek_token (parser)->location;
   struct c_expr expr;
   expr = c_parser_expr_no_commas (parser, NULL);
@@ -8742,6 +8901,8 @@ c_parser_expr_list (c_parser *parser, bool convert_p, bool fold_p,
 		    vec<location_t> *locations,
 		    unsigned int *literal_zero_mask)
 {
+  AUTO_BLT_NODE (parser, BLT_NONEMPTY_EXPR_LIST);
+
   vec<tree, va_gc> *ret;
   vec<tree, va_gc> *orig_types;
   struct c_expr expr;
@@ -18166,6 +18327,12 @@ c_parse_file (void)
     using_eh_for_cleanups ();
 
   c_parser_translation_unit (the_parser);
+
+  if (flag_blt && flag_dump_blt)
+    the_parser->blt_root_node->dump (stderr);
+
+  the_blt_root_node = the_parser->blt_root_node;
+
   the_parser = NULL;
 }
 
diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h
index a8197eb..9455fcf 100644
--- a/gcc/c/c-tree.h
+++ b/gcc/c/c-tree.h
@@ -23,6 +23,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "c-family/c-common.h"
 #include "diagnostic.h"
 
+class blt_node;
+
 /* struct lang_identifier is private to c-decl.c, but langhooks.c needs to
    know how big it is.  This is sanity-checked in c-decl.c.  */
 #define C_SIZEOF_STRUCT_LANG_IDENTIFIER \
@@ -451,6 +453,7 @@ struct c_declarator {
     /* For attributes.  */
     tree attrs;
   } u;
+  blt_node *bltnode;
 };
 
 /* A type name.  */
@@ -577,7 +580,8 @@ extern struct c_parm *build_c_parm (struct c_declspecs *, tree,
 extern struct c_declarator *build_attrs_declarator (tree,
 						    struct c_declarator *);
 extern struct c_declarator *build_function_declarator (struct c_arg_info *,
-						       struct c_declarator *);
+						       struct c_declarator *,
+						       blt_node *);
 extern struct c_declarator *build_id_declarator (tree);
 extern struct c_declarator *make_pointer_declarator (struct c_declspecs *,
 						     struct c_declarator *);
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH 05/17] C++ frontend: capture BLT information
  2017-07-24 19:31 [PATCH 00/17] RFC: New source-location representation; Language Server Protocol David Malcolm
                   ` (14 preceding siblings ...)
  2017-07-24 19:39 ` [PATCH 04/17] C frontend: capture BLT information David Malcolm
@ 2017-07-24 19:40 ` David Malcolm
  2017-07-24 19:41 ` [PATCH 16/17] Language Server Protocol: proof-of-concept GCC implementation David Malcolm
                   ` (3 subsequent siblings)
  19 siblings, 0 replies; 38+ messages in thread
From: David Malcolm @ 2017-07-24 19:40 UTC (permalink / raw)
  To: gcc-patches; +Cc: David Malcolm

This patch extends the C++ frontend so that it optionally builds a BLT tree,
by using an auto_blt_node class within the recursive descent through the
parser, so that its ctor/dtors build the blt_node hierarchy; this is
rapidly (I hope) rejected in the no -fblt case, so that by default, no
blt nodes are created.

This version of the parser captures a blt_node for every token consumed by
cp_parser_require, so there are a lot of leaf nodes in the resulting tree.

Caveat: currently the patch blithely ignores "tentative parsing" (for
the sake of posting this kit sooner rather than later).

All of this is largely a no-op if -fblt is not provided.

gcc/cp/ChangeLog:
	* cp-tree.h (class blt_node): Forward decl.
	(struct cp_parameter_declarator): Add "bltnode" field.
	(struct cp_declarator): Likewise.
	* decl.c: Include "blt.h"
	(grokdeclarator): Rename to...
	(grokdeclarator_1): ...this.
	(grokdeclarator): Reintroduce by calling grokdeclarator_1,
	and associating any bltnode in the declarator with the new tree.
	* parser.c: Include "blt.h".
	(class auto_blt_node): New class.
	(AUTO_BLT_NODE): New macro.
	AUTO_BLT_NODE_WITH_RETURN): New macro.
	(CURRENT_BLT_NODE): New macro.
	(BLT_ADD_NEXT_TOKEN): New macro.
	(ADD_NODE_FOR_NEXT_TOKEN): New macro.
	(BLT_SET_TREE): New macro.
	(add_node_for_next_token): New functions.
	(auto_blt_node::auto_blt_node): New ctor.
	(auto_blt_node::~auto_blt_node): New dtor.
	(auto_blt_node::set_tree): New method.
	(make_declarator): Initialize the bltnode field.
	(make_parameter_declarator): Add new "bltnode" param.
	(cp_parser_identifier): Add AUTO_BLT_NODE.
	(cp_parser_translation_unit): Likewise.
	(cp_parser_primary_expression): Likewise.
	(cp_parser_id_expression): Likewise.
	(cp_parser_unqualified_id): Likewise.
	(cp_parser_postfix_expression): Likewise; add BLT_ADD_NEXT_TOKEN.
	(cp_parser_parenthesized_expression_list): Add AUTO_BLT_NODE.
	(cp_parser_unary_expression): Add AUTO_BLT_NODE.
	(cp_parser_assignment_expression): Likewise.
	(cp_parser_declaration_seq_opt): Likewise.
	(cp_parser_declaration): Likewise.
	(cp_parser_block_declaration): Likewise.
	(cp_parser_simple_declaration): Likewise.
	(cp_parser_decl_specifier_seq): Likewise; add
	ADD_NODE_FOR_NEXT_TOKEN in various places.
	(cp_parser_template_declaration): Add AUTO_BLT_NODE.
	(cp_parser_template_parameter_list): Likewise.
	(cp_parser_explicit_instantiation): Likewise.
	(cp_parser_explicit_specialization): Likewise.
	(cp_parser_type_specifier): Likewise; add ADD_NODE_FOR_NEXT_TOKEN
	in various places.
	(update_blt_for_function_definition): New function.
	(cp_parser_init_declarator): Call
	update_blt_for_function_definition.
	(cp_parser_declarator): Add AUTO_BLT_NODE.
	(cp_parser_direct_declarator): Likewise; add BLT_ADD_NEXT_TOKEN;
	set the bltnode of the declarator.
	(cp_parser_cv_qualifier_seq_opt): Add AUTO_BLT_NODE; add
	ADD_NODE_FOR_NEXT_TOKEN.
	(cp_parser_parameter_declaration_clause): Add AUTO_BLT_NODE; add
	BLT_ADD_NEXT_TOKEN in various places.  Add BLT_SET_TREE.
	(cp_parser_parameter_declaration_list): Add AUTO_BLT_NODE; add
	BLT_SET_TREE in various places.
	(cp_parser_parameter_declaration): Add AUTO_BLT_NODE; pass
	CURRENT_BLT_NODE to make_parameter_declarator.
	(cp_parser_class_specifier_1): Add AUTO_BLT_NODE; add BLT_SET_TREE.
	(cp_parser_class_head): Add AUTO_BLT_NODE.
	(cp_parser_class_key): Likewise.
	(cp_parser_member_declaration): Likewise.  Ensure decl is
	initialized, and associate it with the blt_node.
	(cp_parser_type_id_list): Add AUTO_BLT_NODE.
	(cp_parser_try_block): Likewise.
	(cp_parser_function_try_block): Likewise.
	(cp_parser_handler_seq): Likewise.
	(cp_parser_handler): Likewise.
	(cp_parser_exception_declaration): Likewise.
	(cp_parser_throw_expression): Likewise.
	(cp_parser_asm_specification_opt): Likewise.
	(cp_parser_require): Call BLT_ADD_NEXT_TOKEN.
	(c_parse_file): Implement -fdump-blt.  Set the_blt_root_node.
	* parser.h (class blt_node): Forward decl.
	(struct cp_parser): Add fields "blt_root_node" and "blt_current_node".
	* pt.c: Include "blt.h".
	(build_template_decl): Associate any blt_node for the decl with the
	template instantation.
---
 gcc/cp/cp-tree.h |   5 +
 gcc/cp/decl.c    |  27 +++-
 gcc/cp/parser.c  | 369 ++++++++++++++++++++++++++++++++++++++++++++++++++++---
 gcc/cp/parser.h  |   7 ++
 gcc/cp/pt.c      |   8 ++
 5 files changed, 394 insertions(+), 22 deletions(-)

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 0da8a5c..16d8a48 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -25,6 +25,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "hard-reg-set.h"
 #include "function.h"
 
+class blt_node;
+
 /* In order for the format checking to accept the C++ front end
    diagnostic framework extensions, you must include this file before
    diagnostic-core.h, not after.  We override the definition of GCC_DIAG_STYLE
@@ -5699,6 +5701,8 @@ struct cp_parameter_declarator {
   tree default_argument;
   /* True iff this is a template parameter pack.  */
   bool template_parameter_pack_p;
+  /* The blt_node for this parameter, if any.  */
+  blt_node *bltnode;
 };
 
 /* A declarator.  */
@@ -5774,6 +5778,7 @@ struct cp_declarator {
       bool rvalue_ref;
     } reference;
   } u;
+  blt_node *bltnode;
 };
 
 /* A level of template instantiation.  */
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index 5b8e6a2..bcf305c 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -52,6 +52,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "builtins.h"
 #include "gimplify.h"
 #include "asan.h"
+#include "blt.h"
 
 /* Possible cases of bad specifiers type used by bad_specifiers. */
 enum bad_spec_place {
@@ -9876,12 +9877,12 @@ name_unnamed_type (tree type, tree decl)
    declarator, in cases like "struct S;"), or the ERROR_MARK_NODE if an
    error occurs. */
 
-tree
-grokdeclarator (const cp_declarator *declarator,
-		cp_decl_specifier_seq *declspecs,
-		enum decl_context decl_context,
-		int initialized,
-		tree* attrlist)
+static tree
+grokdeclarator_1 (const cp_declarator *declarator,
+		  cp_decl_specifier_seq *declspecs,
+		  enum decl_context decl_context,
+		  int initialized,
+		  tree* attrlist)
 {
   tree type = NULL_TREE;
   int longlong = 0;
@@ -12349,6 +12350,20 @@ grokdeclarator (const cp_declarator *declarator,
     return decl;
   }
 }
+
+tree
+grokdeclarator (const cp_declarator *declarator,
+		cp_decl_specifier_seq *declspecs,
+		enum decl_context decl_context,
+		int initialized,
+		tree* attrlist)
+{
+  tree result = grokdeclarator_1 (declarator, declspecs, decl_context,
+				  initialized, attrlist);
+  if (declarator && declarator->bltnode)
+    declarator->bltnode->set_tree (result);
+  return result;
+}
 \f
 /* Subroutine of start_function.  Ensure that each of the parameter
    types (as listed in PARMS) is complete, as is required for a
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 88d0b2b..5e0b34e 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -43,6 +43,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "cp-cilkplus.h"
 #include "gcc-rich-location.h"
 #include "tree-iterator.h"
+#include "blt.h"
 
 \f
 /* The lexer.  */
@@ -1348,6 +1349,181 @@ cp_token_cache_new (cp_token *first, cp_token *last)
   return cache;
 }
 
+\f
+
+/* A RAII-style class for optionally building a concrete parse tree (or
+   at least, something close to it) as the recursive descent parser runs.
+
+   Close to a no-op if -fblt is not selected.  */
+
+class auto_blt_node
+{
+public:
+  auto_blt_node (cp_parser* parser, enum blt_kind kind,
+                 cp_expr *expr_ptr = NULL);
+  ~auto_blt_node ();
+
+  void set_tree (tree node);
+
+private:
+  cp_parser *m_parser;
+  cp_expr *m_expr_ptr;
+  blt_node *m_parent;
+};
+
+/* RAII-style construction of the blt_node tree: push a blt_node of KIND
+   onto the current stack of blt_nodes, popping it when it goes out
+   of scope.  */
+
+#define AUTO_BLT_NODE(PARSER, KIND) \
+  auto_blt_node tmp_blt_node ((PARSER), (KIND))
+
+/* As above, but with an addition cp_expr *.
+   If non-NULL, then the node's tree will be set to that of the tree
+   within the cp_expr when the AUTO_BLT_NODE_WITH_RETURN goes out of
+   scope.  */
+
+#define AUTO_BLT_NODE_WITH_RETURN(PARSER, KIND, ADDR_OF_CP_EXPR) \
+  auto_blt_node tmp_blt_node ((PARSER), (KIND), (ADDR_OF_CP_EXPR))
+
+/* The blt_node currently being constructed (the macro assumes that "parser"
+   exists in the current scope).  */
+
+#define CURRENT_BLT_NODE (parser->blt_current_node)
+
+/* Peek the next token within PARSER and a child node for it to the
+   current blt_node.  */
+
+#define BLT_ADD_NEXT_TOKEN(PARSER) \
+  add_node_for_next_token (PARSER)
+
+/* Peek the next token within "parser" and a child node for it to the
+   current blt_node, using KIND as the kind.  */
+
+#define ADD_NODE_FOR_NEXT_TOKEN(KIND) \
+  add_node_for_next_token (parser, (KIND))
+
+/* Set the tree node for the current AUTO_BLT_NODE.  */
+
+#define BLT_SET_TREE(TREE_NODE) \
+  tmp_blt_node.set_tree (TREE_NODE);
+
+/* Peek the next token within PARSER and a child node for it to the
+   current blt_node.  */
+
+static blt_node *
+add_node_for_next_token (cp_parser *parser)
+{
+  if (!flag_blt)
+    return NULL;
+
+  cp_token *token = cp_lexer_peek_token (parser->lexer);
+  blt_node *parent = parser->blt_current_node;
+  enum blt_kind kind = (enum blt_kind)token->type;
+  blt_node *node = new blt_node (kind, token->location);
+  parent->add_child (node);
+  node->set_finish (token->location);
+  return node;
+}
+
+/* As above, but use KIND as the kind of the new blt_node.  */
+
+static blt_node *
+add_node_for_next_token (cp_parser *parser, enum blt_kind kind)
+{
+  if (!flag_blt)
+    return NULL;
+
+  cp_token *token = cp_lexer_peek_token (parser->lexer);
+  blt_node *parent = parser->blt_current_node;
+  blt_node *node = new blt_node (kind, token->location);
+  parent->add_child (node);
+  node->set_finish (token->location);
+  return node;
+}
+
+/* auto_blt_node's constructor.
+
+   If -fblt was not enabled, return immediately.
+
+   Otherwise push a new blt_node of KIND as a child of the previous top
+   of the blt stack, effectively constructing a tree.
+
+   Set the new blt_node's start location to that of the next token
+   within PARSER.  */
+
+auto_blt_node::auto_blt_node (cp_parser *parser, enum blt_kind kind,
+                              cp_expr *expr_ptr)
+{
+  if (!flag_blt)
+    return;
+
+  /* Do this here rather than as an initializer list
+     to avoid doing work when -fblt is not set.  */
+  m_parser = parser;
+  m_expr_ptr = expr_ptr;
+  m_parent = parser->blt_current_node;
+
+  cp_token *first_token = cp_lexer_peek_token (parser->lexer);
+  blt_node *node = new blt_node (kind, first_token->location);
+  parser->blt_current_node = node;
+  if (m_parent)
+    m_parent->add_child (node);
+  else
+    parser->blt_root_node = node;
+}
+
+/* auto_blt_node's destructor.
+
+   If -fblt was not enabled, return immediately.
+
+   Otherwise, pop the current blt_node from the stack,
+   and set its finish location to that of the last
+   token that was consumed.
+
+   If m_expr_ptr was set, set the blt_node's tree to be that
+   of the m_expr_ptr.  */
+
+auto_blt_node::~auto_blt_node ()
+{
+  if (!flag_blt)
+    return;
+
+  blt_node *node = m_parser->blt_current_node;
+
+  cp_token_position prev
+    = cp_lexer_previous_token_position (m_parser->lexer);
+  node->set_finish (cp_lexer_token_at (m_parser->lexer, prev)->location);
+
+  if (0)
+    {
+      location_t start = node->get_start ();
+      location_t finish = node->get_finish ();
+      location_t range = make_location (start, start, finish);
+      inform (range, "%qs", node->get_name ());
+    }
+
+  if (m_expr_ptr)
+    node->set_tree (m_expr_ptr->get_value ());
+
+  /* Use stashed parent, rather than the curent node's parent
+     to allow for reparenting within the tree.  */
+  m_parser->blt_current_node = m_parent;
+}
+
+/* Set the tree node for the current blt_node within this AUTO_BLT_NODE.  */
+
+void
+auto_blt_node::set_tree (tree tree_node)
+{
+  if (!flag_blt)
+    return;
+
+  blt_node *node = m_parser->blt_current_node;
+  node->set_tree (tree_node);
+}
+
+
 /* Diagnose if #pragma omp declare simd isn't followed immediately
    by function declaration or definition.  */
 
@@ -1456,6 +1632,7 @@ make_declarator (cp_declarator_kind kind)
   declarator->declarator = NULL;
   declarator->parameter_pack_p = false;
   declarator->id_loc = UNKNOWN_LOCATION;
+  declarator->bltnode = NULL;
 
   return declarator;
 }
@@ -1691,7 +1868,8 @@ cp_parameter_declarator *
 make_parameter_declarator (cp_decl_specifier_seq *decl_specifiers,
 			   cp_declarator *declarator,
 			   tree default_argument,
-			   bool template_parameter_pack_p = false)
+			   bool template_parameter_pack_p = false,
+			   blt_node *bltnode = NULL)
 {
   cp_parameter_declarator *parameter;
 
@@ -1705,6 +1883,7 @@ make_parameter_declarator (cp_decl_specifier_seq *decl_specifiers,
   parameter->declarator = declarator;
   parameter->default_argument = default_argument;
   parameter->template_parameter_pack_p = template_parameter_pack_p;
+  parameter->bltnode = bltnode;
 
   return parameter;
 }
@@ -3868,6 +4047,10 @@ cp_parser_identifier (cp_parser* parser)
 {
   cp_token *token;
 
+  AUTO_BLT_NODE (parser, BLT_IDENTIFIER);
+  if (0)
+    BLT_ADD_NEXT_TOKEN (parser);
+
   /* Look for the identifier.  */
   token = cp_parser_require (parser, CPP_NAME, RT_NAME);
   /* Return the value.  */
@@ -4371,19 +4554,23 @@ cp_parser_translation_unit (cp_parser* parser)
 
   bool success;
 
-  /* Create the declarator obstack, if necessary.  */
-  if (!cp_error_declarator)
-    {
-      gcc_obstack_init (&declarator_obstack);
-      /* Create the error declarator.  */
-      cp_error_declarator = make_declarator (cdk_error);
-      /* Create the empty parameter list.  */
-      no_parameters = make_parameter_declarator (NULL, NULL, NULL_TREE);
-      /* Remember where the base of the declarator obstack lies.  */
-      declarator_obstack_base = obstack_next_free (&declarator_obstack);
-    }
+  {
+    AUTO_BLT_NODE (parser, BLT_TRANSLATION_UNIT);
 
-  cp_parser_declaration_seq_opt (parser);
+    /* Create the declarator obstack, if necessary.  */
+    if (!cp_error_declarator)
+      {
+        gcc_obstack_init (&declarator_obstack);
+        /* Create the error declarator.  */
+        cp_error_declarator = make_declarator (cdk_error);
+        /* Create the empty parameter list.  */
+        no_parameters = make_parameter_declarator (NULL, NULL, NULL_TREE);
+        /* Remember where the base of the declarator obstack lies.  */
+        declarator_obstack_base = obstack_next_free (&declarator_obstack);
+      }
+
+    cp_parser_declaration_seq_opt (parser);
+  }
 
   /* If there are no tokens left then all went well.  */
   if (cp_lexer_next_token_is (parser->lexer, CPP_EOF))
@@ -4787,6 +4974,8 @@ cp_parser_primary_expression (cp_parser *parser,
 			      bool decltype_p,
 			      cp_id_kind *idk)
 {
+  AUTO_BLT_NODE (parser, BLT_PRIMARY_EXPRESSION);
+
   cp_token *token = NULL;
 
   /* Assume the primary expression is not an id-expression.  */
@@ -5419,6 +5608,8 @@ cp_parser_id_expression (cp_parser *parser,
 			 bool declarator_p,
 			 bool optional_p)
 {
+  AUTO_BLT_NODE (parser, BLT_ID_EXPRESSION);
+
   bool global_scope_p;
   bool nested_name_specifier_p;
 
@@ -5558,6 +5749,8 @@ cp_parser_unqualified_id (cp_parser* parser,
 			  bool declarator_p,
 			  bool optional_p)
 {
+  AUTO_BLT_NODE (parser, BLT_UNQUALIFIED_ID);
+
   cp_token *token;
 
   /* Peek at the next token.  */
@@ -6419,6 +6612,8 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
   bool is_member_access = false;
   int saved_in_statement = -1;
 
+  AUTO_BLT_NODE_WITH_RETURN (parser, BLT_POSTFIX_EXPRESSION, &postfix_expression);
+
   /* Peek at the next token.  */
   token = cp_lexer_peek_token (parser->lexer);
   loc = token->location;
@@ -6438,6 +6633,8 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 	const char *saved_message;
 	bool saved_in_type_id_in_expr_p;
 
+        BLT_ADD_NEXT_TOKEN (parser);
+
 	/* All of these can be handled in the same way from the point
 	   of view of parsing.  Begin by consuming the token
 	   identifying the cast.  */
@@ -6524,6 +6721,7 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 	bool saved_in_type_id_in_expr_p;
 
 	/* Consume the `typeid' token.  */
+        BLT_ADD_NEXT_TOKEN (parser);
 	cp_lexer_consume_token (parser->lexer);
 	/* Look for the `(' token.  */
 	cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN);
@@ -7552,6 +7750,8 @@ cp_parser_parenthesized_expression_list (cp_parser* parser,
 					 bool *non_constant_p,
 					 location_t *close_paren_loc)
 {
+  AUTO_BLT_NODE (parser, BLT_EXPRESSION_LIST);
+
   vec<tree, va_gc> *expression_list;
   bool fold_expr_p = is_attribute_list != non_attr;
   tree identifier = NULL_TREE;
@@ -7830,6 +8030,8 @@ static cp_expr
 cp_parser_unary_expression (cp_parser *parser, cp_id_kind * pidk,
 			    bool address_p, bool cast_p, bool decltype_p)
 {
+  AUTO_BLT_NODE (parser, BLT_UNARY_EXPRESSION);
+
   cp_token *token;
   enum tree_code unary_operator;
 
@@ -9297,6 +9499,7 @@ static cp_expr
 cp_parser_assignment_expression (cp_parser* parser, cp_id_kind * pidk,
 				 bool cast_p, bool decltype_p)
 {
+  AUTO_BLT_NODE (parser, BLT_ASSIGNMENT_EXPRESSION);
   cp_expr expr;
 
   /* If the next token is the `throw' keyword, then we're looking at
@@ -12502,6 +12705,8 @@ cp_parser_already_scoped_statement (cp_parser* parser, bool *if_p,
 static void
 cp_parser_declaration_seq_opt (cp_parser* parser)
 {
+  AUTO_BLT_NODE (parser, BLT_DECLARATION_SEQ);
+
   while (true)
     {
       cp_token *token;
@@ -12573,6 +12778,8 @@ cp_parser_declaration_seq_opt (cp_parser* parser)
 static void
 cp_parser_declaration (cp_parser* parser)
 {
+  AUTO_BLT_NODE (parser, BLT_DECLARATION);
+
   cp_token token1;
   cp_token token2;
   int saved_pedantic;
@@ -12717,6 +12924,8 @@ cp_parser_block_declaration (cp_parser *parser,
       return;
     }
 
+  AUTO_BLT_NODE (parser, BLT_BLOCK_DECLARATION);
+
   /* Peek at the next token to figure out which kind of declaration is
      present.  */
   token1 = cp_lexer_peek_token (parser->lexer);
@@ -12801,6 +13010,8 @@ cp_parser_simple_declaration (cp_parser* parser,
 			      bool function_definition_allowed_p,
 			      tree *maybe_range_for_decl)
 {
+  AUTO_BLT_NODE (parser, BLT_SIMPLE_DECLARATION);
+
   cp_decl_specifier_seq decl_specifiers;
   int declares_class_or_enum;
   bool saw_declarator;
@@ -13291,6 +13502,8 @@ cp_parser_decl_specifier_seq (cp_parser* parser,
 			      cp_decl_specifier_seq *decl_specs,
 			      int* declares_class_or_enum)
 {
+  AUTO_BLT_NODE (parser, BLT_DECL_SPECIFIER_SEQ);
+
   bool constructor_possible_p = !parser->in_declarator_p;
   bool found_decl_spec = false;
   cp_token *start_token = NULL;
@@ -13390,6 +13603,7 @@ cp_parser_decl_specifier_seq (cp_parser* parser,
 	  else
 	    {
 	      ds = ds_friend;
+	      ADD_NODE_FOR_NEXT_TOKEN (BLT_DECL_SPECIFIER);
 	      /* Consume the token.  */
 	      cp_lexer_consume_token (parser->lexer);
 	    }
@@ -13397,6 +13611,7 @@ cp_parser_decl_specifier_seq (cp_parser* parser,
 
         case RID_CONSTEXPR:
 	  ds = ds_constexpr;
+	  ADD_NODE_FOR_NEXT_TOKEN (BLT_DECL_SPECIFIER);
           cp_lexer_consume_token (parser->lexer);
           break;
 
@@ -13419,6 +13634,7 @@ cp_parser_decl_specifier_seq (cp_parser* parser,
 	       typedef  */
 	case RID_TYPEDEF:
 	  ds = ds_typedef;
+	  ADD_NODE_FOR_NEXT_TOKEN (BLT_DECL_SPECIFIER);
 	  /* Consume the token.  */
 	  cp_lexer_consume_token (parser->lexer);
 	  /* A constructor declarator cannot appear in a typedef.  */
@@ -13443,6 +13659,7 @@ cp_parser_decl_specifier_seq (cp_parser* parser,
 	case RID_AUTO:
           if (cxx_dialect == cxx98) 
             {
+	      ADD_NODE_FOR_NEXT_TOKEN (BLT_DECL_SPECIFIER);
 	      /* Consume the token.  */
 	      cp_lexer_consume_token (parser->lexer);
 
@@ -13467,6 +13684,7 @@ cp_parser_decl_specifier_seq (cp_parser* parser,
 	case RID_STATIC:
 	case RID_EXTERN:
 	case RID_MUTABLE:
+	  ADD_NODE_FOR_NEXT_TOKEN (BLT_DECL_SPECIFIER);
 	  /* Consume the token.  */
 	  cp_lexer_consume_token (parser->lexer);
           cp_parser_set_storage_class (parser, decl_specs, token->keyword,
@@ -13475,6 +13693,7 @@ cp_parser_decl_specifier_seq (cp_parser* parser,
 	case RID_THREAD:
 	  /* Consume the token.  */
 	  ds = ds_thread;
+          ADD_NODE_FOR_NEXT_TOKEN (BLT_DECL_SPECIFIER);
 	  cp_lexer_consume_token (parser->lexer);
 	  break;
 
@@ -14852,6 +15071,8 @@ cp_parser_operator (cp_parser* parser)
 static void
 cp_parser_template_declaration (cp_parser* parser, bool member_p)
 {
+  AUTO_BLT_NODE (parser, BLT_TEMPLATE_DECLARATION);
+
   /* Check for `export'.  */
   if (cp_lexer_next_token_is_keyword (parser->lexer, RID_EXPORT))
     {
@@ -14876,6 +15097,8 @@ cp_parser_template_declaration (cp_parser* parser, bool member_p)
 static tree
 cp_parser_template_parameter_list (cp_parser* parser)
 {
+  AUTO_BLT_NODE (parser, BLT_TEMPLATE_PARAMETER_LIST);
+
   tree parameter_list = NULL_TREE;
 
   begin_template_parm_list ();
@@ -16348,6 +16571,8 @@ cp_parser_template_argument (cp_parser* parser)
 static void
 cp_parser_explicit_instantiation (cp_parser* parser)
 {
+  AUTO_BLT_NODE (parser, BLT_EXPLICIT_INSTANTIATION);
+
   int declares_class_or_enum;
   cp_decl_specifier_seq decl_specifiers;
   tree extension_specifier = NULL_TREE;
@@ -16461,6 +16686,8 @@ cp_parser_explicit_instantiation (cp_parser* parser)
 static void
 cp_parser_explicit_specialization (cp_parser* parser)
 {
+  AUTO_BLT_NODE (parser, BLT_EXPLICIT_SPECIALIZATION);
+
   bool need_lang_pop;
   cp_token *token = cp_lexer_peek_token (parser->lexer);
 
@@ -16562,6 +16789,8 @@ cp_parser_type_specifier (cp_parser* parser,
 			  int* declares_class_or_enum,
 			  bool* is_cv_qualifier)
 {
+  AUTO_BLT_NODE (parser, BLT_TYPE_SPECIFIER);
+
   tree type_spec = NULL_TREE;
   cp_token *token;
   enum rid keyword;
@@ -16652,18 +16881,21 @@ cp_parser_type_specifier (cp_parser* parser,
 
     case RID_CONST:
       ds = ds_const;
+      ADD_NODE_FOR_NEXT_TOKEN (BLT_CV_QUALIFIER);
       if (is_cv_qualifier)
 	*is_cv_qualifier = true;
       break;
 
     case RID_VOLATILE:
       ds = ds_volatile;
+      ADD_NODE_FOR_NEXT_TOKEN (BLT_CV_QUALIFIER);
       if (is_cv_qualifier)
 	*is_cv_qualifier = true;
       break;
 
     case RID_RESTRICT:
       ds = ds_restrict;
+      ADD_NODE_FOR_NEXT_TOKEN (BLT_CV_QUALIFIER);
       if (is_cv_qualifier)
 	*is_cv_qualifier = true;
       break;
@@ -19108,6 +19340,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_blt_for_function_definition (cp_parser *parser)
+{
+  if (!CURRENT_BLT_NODE)
+    return;
+
+  blt_node *block_decl
+    = CURRENT_BLT_NODE->get_ancestor_of_kind (BLT_BLOCK_DECLARATION);
+
+  if (!block_decl)
+    /* We might have a template-declaration.  */
+    return;
+
+  blt_node *simple_decl
+    = block_decl->get_first_child_of_kind (BLT_SIMPLE_DECLARATION);
+  gcc_assert (simple_decl);
+
+  blt_node *parent = block_decl->get_parent ();
+  gcc_assert (parent);
+
+  simple_decl->set_kind (BLT_FUNCTION_DEFINITION);
+  parent->replace_child (block_decl, simple_decl);
+}
+
 /* Declarators [gram.dcl.decl] */
 
 /* Parse an init-declarator.
@@ -19324,6 +19598,7 @@ cp_parser_init_declarator (cp_parser* parser,
 		      "on a function-definition");
 	  /* This is a function-definition.  */
 	  *function_definition_p = true;
+          update_blt_for_function_definition (parser);
 
 	  /* Parse the function definition.  */
 	  if (member_p)
@@ -19640,6 +19915,8 @@ cp_parser_declarator (cp_parser* parser,
 		      bool* parenthesized_p,
 		      bool member_p, bool friend_p)
 {
+  AUTO_BLT_NODE (parser, BLT_DECLARATOR);
+
   cp_declarator *declarator;
   enum tree_code code;
   cp_cv_quals cv_quals;
@@ -19741,6 +20018,8 @@ cp_parser_direct_declarator (cp_parser* parser,
 			     int* ctor_dtor_or_conv_p,
 			     bool member_p, bool friend_p)
 {
+  AUTO_BLT_NODE (parser, BLT_DIRECT_DECLARATOR);
+
   cp_token *token;
   cp_declarator *declarator = NULL;
   tree scope = NULL_TREE;
@@ -19808,6 +20087,7 @@ cp_parser_direct_declarator (cp_parser* parser,
 		cp_parser_parse_tentatively (parser);
 
 	      /* Consume the `('.  */
+	      BLT_ADD_NEXT_TOKEN (parser);
 	      cp_lexer_consume_token (parser->lexer);
 	      if (first)
 		{
@@ -20254,6 +20534,9 @@ cp_parser_direct_declarator (cp_parser* parser,
   parser->default_arg_ok_p = saved_default_arg_ok_p;
   parser->in_declarator_p = saved_in_declarator_p;
 
+  if (declarator)
+    declarator->bltnode = CURRENT_BLT_NODE;
+
   return declarator;
 }
 
@@ -20396,6 +20679,8 @@ cp_parser_ptr_operator (cp_parser* parser,
 static cp_cv_quals
 cp_parser_cv_qualifier_seq_opt (cp_parser* parser)
 {
+  AUTO_BLT_NODE (parser, BLT_CV_QUALIFIER_SEQ);
+
   cp_cv_quals cv_quals = TYPE_UNQUALIFIED;
 
   while (true)
@@ -20428,6 +20713,8 @@ cp_parser_cv_qualifier_seq_opt (cp_parser* parser)
       if (!cv_qualifier)
 	break;
 
+      ADD_NODE_FOR_NEXT_TOKEN (BLT_CV_QUALIFIER);
+
       if (cv_quals & cv_qualifier)
 	{
 	  gcc_rich_location richloc (token->location);
@@ -21039,6 +21326,8 @@ function_being_declared_is_template_p (cp_parser* parser)
 static tree
 cp_parser_parameter_declaration_clause (cp_parser* parser)
 {
+  AUTO_BLT_NODE (parser, BLT_PARAMETER_DECLARATION_CLAUSE);
+
   tree parameters;
   cp_token *token;
   bool ellipsis_p;
@@ -21068,6 +21357,7 @@ cp_parser_parameter_declaration_clause (cp_parser* parser)
   if (token->type == CPP_ELLIPSIS)
     {
       /* Consume the `...' token.  */
+      BLT_ADD_NEXT_TOKEN (parser);
       cp_lexer_consume_token (parser->lexer);
       return NULL_TREE;
     }
@@ -21089,6 +21379,7 @@ cp_parser_parameter_declaration_clause (cp_parser* parser)
 	       == CPP_CLOSE_PAREN))
     {
       /* Consume the `void' token.  */
+      BLT_ADD_NEXT_TOKEN (parser);
       cp_lexer_consume_token (parser->lexer);
       /* There are no parameters.  */
       return void_list_node;
@@ -21108,6 +21399,7 @@ cp_parser_parameter_declaration_clause (cp_parser* parser)
   if (token->type == CPP_COMMA)
     {
       /* Consume the `,'.  */
+      BLT_ADD_NEXT_TOKEN (parser);
       cp_lexer_consume_token (parser->lexer);
       /* Expect an ellipsis.  */
       ellipsis_p
@@ -21118,6 +21410,7 @@ cp_parser_parameter_declaration_clause (cp_parser* parser)
   else if (token->type == CPP_ELLIPSIS)
     {
       /* Consume the `...' token.  */
+      BLT_ADD_NEXT_TOKEN (parser);
       cp_lexer_consume_token (parser->lexer);
       /* And remember that we saw it.  */
       ellipsis_p = true;
@@ -21129,6 +21422,7 @@ cp_parser_parameter_declaration_clause (cp_parser* parser)
   if (!ellipsis_p)
     parameters = chainon (parameters, void_list_node);
 
+  BLT_SET_TREE (parameters);
   return parameters;
 }
 
@@ -21146,6 +21440,7 @@ cp_parser_parameter_declaration_clause (cp_parser* parser)
 static tree
 cp_parser_parameter_declaration_list (cp_parser* parser, bool *is_error)
 {
+  AUTO_BLT_NODE (parser, BLT_PARAMETER_DECLARATION_LIST);
   tree parameters = NULL_TREE;
   tree *tail = &parameters;
   bool saved_in_unbraced_linkage_specification_p;
@@ -21203,6 +21498,7 @@ cp_parser_parameter_declaration_list (cp_parser* parser, bool *is_error)
 				 PARM,
 				 parameter->default_argument != NULL_TREE,
 				 &parameter->decl_specifiers.attributes);
+          BLT_SET_TREE (decl);
 	}
 
       deprecated_state = DEPRECATED_NORMAL;
@@ -21253,6 +21549,7 @@ cp_parser_parameter_declaration_list (cp_parser* parser, bool *is_error)
 	    break;
 	  /* Otherwise, there must be more parameters.  Consume the
 	     `,'.  */
+          BLT_ADD_NEXT_TOKEN (parser);
 	  cp_lexer_consume_token (parser->lexer);
 	  /* When parsing something like:
 
@@ -21308,6 +21605,7 @@ cp_parser_parameter_declaration_list (cp_parser* parser, bool *is_error)
 	  }
       }
 
+  BLT_SET_TREE (parameters);
   return parameters;
 }
 
@@ -21333,6 +21631,8 @@ cp_parser_parameter_declaration (cp_parser *parser,
 				 bool template_parm_p,
 				 bool *parenthesized_p)
 {
+  AUTO_BLT_NODE (parser, BLT_PARAMETER_DECLARATION);
+
   int declares_class_or_enum;
   cp_decl_specifier_seq decl_specifiers;
   cp_declarator *declarator;
@@ -21543,7 +21843,8 @@ cp_parser_parameter_declaration (cp_parser *parser,
   return make_parameter_declarator (&decl_specifiers,
 				    declarator,
 				    default_argument,
-				    template_parameter_pack_p);
+				    template_parameter_pack_p,
+                                    CURRENT_BLT_NODE);
 }
 
 /* Parse a default argument and return it.
@@ -22207,6 +22508,8 @@ cp_parser_class_specifier_1 (cp_parser* parser)
   tree scope = NULL_TREE;
   cp_token *closing_brace;
 
+  AUTO_BLT_NODE (parser, BLT_CLASS_SPECIFIER);
+
   push_deferring_access_checks (dk_no_deferred);
 
   /* Parse the class-head.  */
@@ -22515,6 +22818,8 @@ cp_parser_class_specifier_1 (cp_parser* parser)
   parser->in_unbraced_linkage_specification_p
     = saved_in_unbraced_linkage_specification_p;
 
+  BLT_SET_TREE (type);
+
   return type;
 }
 
@@ -22563,6 +22868,8 @@ static tree
 cp_parser_class_head (cp_parser* parser,
 		      bool* nested_name_specifier_p)
 {
+  AUTO_BLT_NODE (parser, BLT_CLASS_HEAD);
+
   tree nested_name_specifier;
   enum tag_types class_key;
   tree id = NULL_TREE;
@@ -23041,6 +23348,8 @@ cp_parser_class_key (cp_parser* parser)
   cp_token *token;
   enum tag_types tag_type;
 
+  AUTO_BLT_NODE (parser, BLT_CLASS_KEY);
+
   /* Look for the class-key.  */
   token = cp_parser_require (parser, CPP_KEYWORD, RT_CLASS_KEY);
   if (!token)
@@ -23171,9 +23480,11 @@ cp_parser_member_specification_opt (cp_parser* parser)
 static void
 cp_parser_member_declaration (cp_parser* parser)
 {
+  AUTO_BLT_NODE (parser, BLT_MEMBER_DECLARATION);
+
   cp_decl_specifier_seq decl_specifiers;
   tree prefix_attributes;
-  tree decl;
+  tree decl = NULL_TREE;
   int declares_class_or_enum;
   bool friend_p;
   cp_token *token = NULL;
@@ -23677,6 +23988,7 @@ cp_parser_member_declaration (cp_parser* parser)
   cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON);
  out:
   parser->colon_corrects_to_scope_p = saved_colon_corrects_to_scope_p;
+  BLT_SET_TREE (decl);
 }
 
 /* Parse a pure-specifier.
@@ -24140,6 +24452,8 @@ cp_parser_exception_specification_opt (cp_parser* parser)
 static tree
 cp_parser_type_id_list (cp_parser* parser)
 {
+  AUTO_BLT_NODE (parser, BLT_TYPE_ID_LIST);
+
   tree types = NULL_TREE;
 
   while (true)
@@ -24189,6 +24503,8 @@ cp_parser_type_id_list (cp_parser* parser)
 static tree
 cp_parser_try_block (cp_parser* parser)
 {
+  AUTO_BLT_NODE (parser, BLT_TRY_BLOCK);
+
   tree try_block;
 
   cp_parser_require_keyword (parser, RID_TRY, RT_TRY);
@@ -24213,6 +24529,8 @@ cp_parser_try_block (cp_parser* parser)
 static bool
 cp_parser_function_try_block (cp_parser* parser)
 {
+  AUTO_BLT_NODE (parser, BLT_FUNCTION_TRY_BLOCK);
+
   tree compound_stmt;
   tree try_block;
   bool ctor_initializer_p;
@@ -24243,6 +24561,8 @@ cp_parser_function_try_block (cp_parser* parser)
 static void
 cp_parser_handler_seq (cp_parser* parser)
 {
+  AUTO_BLT_NODE (parser, BLT_HANDLER_SEQ);
+
   while (true)
     {
       cp_token *token;
@@ -24265,6 +24585,8 @@ cp_parser_handler_seq (cp_parser* parser)
 static void
 cp_parser_handler (cp_parser* parser)
 {
+  AUTO_BLT_NODE (parser, BLT_HANDLER);
+
   tree handler;
   tree declaration;
 
@@ -24292,6 +24614,8 @@ cp_parser_handler (cp_parser* parser)
 static tree
 cp_parser_exception_declaration (cp_parser* parser)
 {
+  AUTO_BLT_NODE (parser, BLT_EXCEPTION_DECLARATION);
+
   cp_decl_specifier_seq type_specifiers;
   cp_declarator *declarator;
   const char *saved_message;
@@ -24345,6 +24669,8 @@ cp_parser_throw_expression (cp_parser* parser)
   tree expression;
   cp_token* token;
 
+  AUTO_BLT_NODE (parser, BLT_THROW_EXPRESSION);
+
   cp_parser_require_keyword (parser, RID_THROW, RT_THROW);
   token = cp_lexer_peek_token (parser->lexer);
   /* Figure out whether or not there is an assignment-expression
@@ -24386,6 +24712,8 @@ cp_parser_asm_specification_opt (cp_parser* parser)
   if (!cp_parser_is_keyword (token, RID_ASM))
     return NULL_TREE;
 
+  AUTO_BLT_NODE (parser, BLT_ASM_SPECIFICATION);
+
   /* Consume the `asm' token.  */
   cp_lexer_consume_token (parser->lexer);
   /* Look for the `('.  */
@@ -28113,7 +28441,10 @@ cp_parser_require (cp_parser* parser,
 		   required_token token_desc)
 {
   if (cp_lexer_next_token_is (parser->lexer, type))
-    return cp_lexer_consume_token (parser->lexer);
+    {
+      BLT_ADD_NEXT_TOKEN (parser);
+      return cp_lexer_consume_token (parser->lexer);
+    }
   else
     {
       /* Output the MESSAGE -- unless we're parsing tentatively.  */
@@ -38647,6 +38978,12 @@ c_parse_file (void)
   push_deferring_access_checks (flag_access_control
 				? dk_no_deferred : dk_no_check);
   cp_parser_translation_unit (the_parser);
+
+  if (flag_blt && flag_dump_blt)
+    the_parser->blt_root_node->dump (stderr);
+
+  the_blt_root_node = the_parser->blt_root_node;
+
   the_parser = NULL;
 }
 
diff --git a/gcc/cp/parser.h b/gcc/cp/parser.h
index 0994e1e..49c8eb6 100644
--- a/gcc/cp/parser.h
+++ b/gcc/cp/parser.h
@@ -213,6 +213,8 @@ struct cp_oacc_routine_data : cp_omp_declare_simd_data {
   location_t loc;
 };
 
+class blt_node;
+
 /* The cp_parser structure represents the C++ parser.  */
 
 struct GTY(()) cp_parser {
@@ -412,6 +414,11 @@ struct GTY(()) cp_parser {
      context e.g., because they could never be deduced.  */
   int prevent_constrained_type_specifiers;
 
+  /* The top-level node for the translation-unit.  */
+  blt_node * GTY((skip)) blt_root_node;
+
+  /* The current node being parsed.  */
+  blt_node * GTY((skip)) blt_current_node;
 };
 
 /* In parser.c  */
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index da133bd..8db4b4b 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -41,6 +41,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "type-utils.h"
 #include "gimplify.h"
 #include "gcc-rich-location.h"
+#include "blt.h"
 
 /* The type of functions taking a tree, and some additional data, and
    returning an int.  */
@@ -4480,6 +4481,13 @@ build_template_decl (tree decl, tree parms, bool member_template_p)
   DECL_SOURCE_LOCATION (tmpl) = DECL_SOURCE_LOCATION (decl);
   DECL_MEMBER_TEMPLATE_P (tmpl) = member_template_p;
 
+  if (flag_blt)
+    {
+      blt_node *decl_bltnode = blt_get_node_for_tree (decl);
+      if (decl_bltnode)
+        blt_set_node_for_tree (tmpl, decl_bltnode);
+    }
+
   return tmpl;
 }
 
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH 16/17] Language Server Protocol: proof-of-concept GCC implementation
  2017-07-24 19:31 [PATCH 00/17] RFC: New source-location representation; Language Server Protocol David Malcolm
                   ` (15 preceding siblings ...)
  2017-07-24 19:40 ` [PATCH 05/17] C++ " David Malcolm
@ 2017-07-24 19:41 ` David Malcolm
  2017-07-26 17:10 ` [PATCH 00/17] RFC: New source-location representation; Language Server Protocol Jim Wilson
                   ` (2 subsequent siblings)
  19 siblings, 0 replies; 38+ messages in thread
From: David Malcolm @ 2017-07-24 19:41 UTC (permalink / raw)
  To: gcc-patches; +Cc: David Malcolm

This patch implements a concrete subclass of the lsp::server
class implemented in the previous patch, wiring it up to GCC's
internal representation.

The patch is very much just a proof-of-concept; the only method it
implements is "textDocument/definition" i.e. "report the definition
of the symbol at source location ($FILE, $ROW, $COL)"; this is currently
only implemented when clicking on a struct-or-union-specifier
("struct foo" or "union foo" in the C frontend).

Notable other limitations include the complete absense of
support for change-monitoring, or the idea of where the "truth"
of the source is (filesystem vs memory).  Also, this can only cope
with one source file and language at a time; a real implementation
would presumably need some kind of multiplexing to cope with a project
consisting of multiple source files (and perhaps mixing C and C++).

gcc/ChangeLog:
	* Makefile.in (OBJS): Add lsp-main.o.
	* common.opt (-flsp): New option.
	* lsp-main.c: New file.
	* lsp-main.h: New file.
	* toplev.c: Include "lsp-main.h".
	(compile_file): Call serve_lsp if -flsp was used.
---
 gcc/Makefile.in |   1 +
 gcc/common.opt  |   4 ++
 gcc/lsp-main.c  | 168 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 gcc/lsp-main.h  |  25 +++++++++
 gcc/toplev.c    |   4 ++
 5 files changed, 202 insertions(+)
 create mode 100644 gcc/lsp-main.c
 create mode 100644 gcc/lsp-main.h

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index e5120c2..69e5fec 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1383,6 +1383,7 @@ OBJS = \
 	loop-unroll.o \
 	lower-subreg.o \
 	lsp.o \
+	lsp-main.o \
 	lra.o \
 	lra-assigns.o \
 	lra-coalesce.o \
diff --git a/gcc/common.opt b/gcc/common.opt
index e81165c..7c4be65 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -1746,6 +1746,10 @@ flra-remat
 Common Report Var(flag_lra_remat) Optimization
 Do CFG-sensitive rematerialization in LRA.
 
+flsp=
+Common Joined RejectNegative UInteger Var(flag_lsp) Init(-1)
+-flsp=<port-number>	Serve the Language Server Protocol on the given port.
+
 flto
 Common
 Enable link-time optimization.
diff --git a/gcc/lsp-main.c b/gcc/lsp-main.c
new file mode 100644
index 0000000..3f8cc0f
--- /dev/null
+++ b/gcc/lsp-main.c
@@ -0,0 +1,168 @@
+/* Language Server Protocol implementation.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "http-server.h"
+#include "json.h"
+#include "json-rpc.h"
+#include "lsp.h"
+#include "blt.h"
+#include "tree.h"
+
+using namespace lsp;
+
+class is_record_definition : public blt_predicate
+{
+ public:
+  is_record_definition (tree record_type) : m_record_type (record_type) {}
+
+  bool satisfied_by_node_p (const blt_node &node) FINAL OVERRIDE
+  {
+    if (node.get_kind () == BLT_STRUCT_CONTENTS
+	&& node.get_tree () == m_record_type)
+      return true;
+    return false;
+  }
+
+ private:
+  tree m_record_type;
+};
+
+static bool
+Location_from_blt_node (const blt_node *node, Location &out)
+{
+  if (!node)
+    return false;
+
+  location_t start = node->get_start ();
+  if (start == UNKNOWN_LOCATION)
+    return false;
+  location_t finish = node->get_finish ();
+  if (finish == UNKNOWN_LOCATION)
+    return false;
+
+  expanded_location el_start = expand_location (start);
+  expanded_location el_finish = expand_location (finish);
+  if (el_start.file != el_finish.file)
+    return false;
+
+  out.uri = el_start.file;
+  /* Convert from GCC's 1-based lines and columns to LSP's
+     0-based lines and columns.
+     Offset the end column back by 1 since blt_node finish
+     locations are inclusive, whereas in LSP the endpoint of a Range
+     is exclusive.  */
+  out.range.start.line = el_start.line - 1;
+  out.range.start.character = el_start.column - 1;
+  out.range.end.line = el_finish.line - 1;
+  out.range.end.character = el_finish.column - 1 + 1;
+  return true;
+}
+
+/* Implementation of lsp::server, wired up to GCC's IR.  */
+
+class gcc_lsp_server : public lsp::noop_server
+{
+  /* Implementation of "textDocument/definition":
+     find the definition of the symbol at a given source location.  */
+
+  void
+  do_text_document_definition (const TextDocumentPositionParams &p,
+			       vec<Location> &out) OVERRIDE;
+};
+
+
+/* class gcc_lsp_server : public lsp::noop_server.  */
+
+/* Implementation of "textDocument/definition":
+   find the definition of the symbol at a given source location.  */
+
+void
+gcc_lsp_server::do_text_document_definition (const TextDocumentPositionParams &p,
+					     vec<Location> &out)
+{
+  /* TODO.  */
+  /* Convert from LSP's 0-based lines and columns to GCC's
+     1-based lines and columns.  */
+  blt_node *blt
+    = the_blt_root_node->get_descendant_at_location (p.textDocument.uri,
+						     p.position.line + 1,
+						     p.position.character + 1);
+  if (!blt)
+    /* No results.  */
+    return;
+
+  blt->dump (stderr);
+
+  switch (blt->get_kind ())
+    {
+    case BLT_STRUCT_OR_UNION_SPECIFIER:
+      {
+	/* Attempt to locate the RECORD_TYPE for the
+	   struct-or-union-specifier.  */
+	tree record_type = blt->get_tree ();
+	if (!record_type)
+	  return;
+
+	if (1)
+	  the_blt_root_node->dump (stderr);
+
+	/* Find a struct-contents with tree == record_type.  */
+	is_record_definition pred (record_type);
+	blt_node *blt_struct_contents
+	  = the_blt_root_node->find_descendant_satisfying (pred);
+	if (!blt_struct_contents)
+	  return;
+
+	if (1)
+	  blt_struct_contents->dump (stderr);
+	blt_node *blt_struct_defn = blt_struct_contents->get_parent ();
+
+	Location result;
+	if (Location_from_blt_node (blt_struct_defn, result))
+	  out.safe_push (result);
+      }
+      break;
+
+    default:
+      {
+	/* Just show the blt_node itself.  */
+	Location result;
+	if (Location_from_blt_node (blt, result))
+	  out.safe_push (result);
+      }
+      break;
+    }
+}
+
+/* Serve LSP on PORT.  */
+
+void
+serve_lsp (int port)
+{
+  // FIXME
+  gcc_lsp_server lsp_server;
+  lsp::jsonrpc_server json_lsp_server (true, lsp_server);
+  jsonrpc::http_server http_server (json_lsp_server);
+
+  fprintf (stderr, "serving on port %i\n", port);
+  http_server.serve (port);
+}
diff --git a/gcc/lsp-main.h b/gcc/lsp-main.h
new file mode 100644
index 0000000..ec57201
--- /dev/null
+++ b/gcc/lsp-main.h
@@ -0,0 +1,25 @@
+/* Entrypoint to Language Server Protocol implementation.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_LSP_MAIN_H
+#define GCC_LSP_MAIN_H
+
+extern void serve_lsp (int port);
+
+#endif  /* GCC_LSP_MAIN_H  */
diff --git a/gcc/toplev.c b/gcc/toplev.c
index e6c69a4..716f906 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -80,6 +80,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "hsa-common.h"
 #include "edit-context.h"
 #include "tree-pass.h"
+#include "lsp-main.h"
 
 #if defined(DBX_DEBUGGING_INFO) || defined(XCOFF_DEBUGGING_INFO)
 #include "dbxout.h"
@@ -470,6 +471,9 @@ compile_file (void)
   timevar_pop (TV_PARSE_GLOBAL);
   timevar_stop (TV_PHASE_PARSING);
 
+  if (flag_lsp != -1)
+    serve_lsp (flag_lsp);
+
   if (flag_dump_locations)
     dump_location_info (stderr);
 
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 01/17] Add param-type-mismatch.c/C testcases as a baseline
  2017-07-24 19:31 ` [PATCH 01/17] Add param-type-mismatch.c/C testcases as a baseline David Malcolm
@ 2017-07-24 19:56   ` Eric Gallager
  2017-09-01 17:00   ` Jeff Law
  1 sibling, 0 replies; 38+ messages in thread
From: Eric Gallager @ 2017-07-24 19:56 UTC (permalink / raw)
  To: David Malcolm; +Cc: gcc-patches

On Mon, Jul 24, 2017 at 4:04 PM, David Malcolm <dmalcolm@redhat.com> wrote:
> Amongst other things, this patch kit aims to improve our handling
> of mismatched types within function calls.
>
> For example, given:
>
>   extern int callee_1 (int one, const char *two, float three);
>
>   int test_1 (int first, int second, float third)
>   {
>     return callee_1 (first, second, third);
>   }
>
> the C++ FE currently reports:
>
>   error: invalid conversion from 'int' to 'const char*' [-fpermissive]
>    return callee_1 (first, second, third);
>                                         ^
>   note:   initializing argument 2 of 'int callee_1(int, const char*, float)'
>    extern int callee_1 (int one, const char *two, float three);
>               ^~~~~~~~
>
> when it ought to underline the pertinent parts of the code:
>
>   error: invalid conversion from 'int' to 'const char*' [-fpermissive]
>    return callee_1 (first, second, third);
>                            ^~~~~~
>   note:   initializing argument 2 of 'int callee_1(int, const char*, float)'
>    extern int callee_1 (int one, const char *two, float three);
>                                  ^~~~~~~~~~~~~~~
>
> The C FE currently does better, underlining the pertinent argument at
> the callsite (due to the "vec<location_t> arg_loc" passed around
> when the call is created); but, like the C++ frontend, it doesn't
> underline the pertinent parameter at the decl of the callee.
>
> This patch adds a pair of test cases to exercise various cases of
> this in the C and C++ frontends, to establish a baseline for how
> we currently handle these; the "TODO" comments in the test cases
> note the aspects where we could do better (some of which are fixed
> by this kit).
>
> Patches 6 and 7 of the kit fix the parameter highlighting, for
> C and C++ respectively (making use of -fblt).

Speaking of mismatched types for arguments to function calls, while
you're at it, could you
update gcc.dg/Wtraditional-conversion-2.c to use underlining, too?
Currently it just checks
the message and not any of the location information.

>
> gcc/testsuite/ChangeLog:
>         * g++.dg/diagnostic/param-type-mismatch.C: New test acse.
>         * gcc.dg/param-type-mismatch.c: New test case.
> ---
>  .../g++.dg/diagnostic/param-type-mismatch.C        | 162 +++++++++++++++++++++
>  gcc/testsuite/gcc.dg/param-type-mismatch.c         |  63 ++++++++
>  2 files changed, 225 insertions(+)
>  create mode 100644 gcc/testsuite/g++.dg/diagnostic/param-type-mismatch.C
>  create mode 100644 gcc/testsuite/gcc.dg/param-type-mismatch.c
>
> diff --git a/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch.C b/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch.C
> new file mode 100644
> index 0000000..864ead1
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch.C
> @@ -0,0 +1,162 @@
> +// { dg-options "-fdiagnostics-show-caret" }
> +
> +/* A collection of calls where argument 2 is of the wrong type.
> +
> +   TODO: we should put the caret and underline for the diagnostic
> +   at the second argument, rather than the close paren.
> +
> +   TODO: we should highlight the second parameter of the callee, rather
> +   than its name.  */
> +
> +/* decl, with argname.  */
> +
> +extern int callee_1 (int one, const char *two, float three); // { dg-line callee_1 }
> +
> +int test_1 (int first, int second, float third)
> +{
> +  return callee_1 (first, second, third); // { dg-error "invalid conversion from 'int' to 'const char\\*'" }
> +  /* { dg-begin-multiline-output "" }
> +   return callee_1 (first, second, third);
> +                                        ^
> +     { dg-end-multiline-output "" } */
> +  // { dg-message "initializing argument 2 of 'int callee_1\\(int, const char\\*, float\\)'" "" { target *-*-* } callee_1 }
> +  /* { dg-begin-multiline-output "" }
> + extern int callee_1 (int one, const char *two, float three);
> +            ^~~~~~~~
> +     { dg-end-multiline-output "" } */
> +}
> +
> +/* decl, without argname.  */
> +
> +extern int callee_2 (int, const char *, float); // { dg-line callee_2 }
> +
> +int test_2 (int first, int second, float third)
> +{
> +  return callee_2 (first, second, third); // { dg-error "invalid conversion from 'int' to 'const char\\*'" }
> +  /* { dg-begin-multiline-output "" }
> +   return callee_2 (first, second, third);
> +                                        ^
> +     { dg-end-multiline-output "" } */
> +  // { dg-message "initializing argument 2 of 'int callee_2\\(int, const char\\*, float\\)'" "" { target *-*-* } callee_2 }
> +  /* { dg-begin-multiline-output "" }
> + extern int callee_2 (int, const char *, float);
> +            ^~~~~~~~
> +     { dg-end-multiline-output "" } */
> +}
> +
> +/* defn, with argname.  */
> +
> +static int callee_3 (int one, const char *two, float three) // { dg-line callee_3 }
> +{
> +  return callee_2 (one, two, three);
> +}
> +
> +int test_3 (int first, int second, float third)
> +{
> +  return callee_3 (first, second, third); // { dg-error "invalid conversion from 'int' to 'const char\\*'" }
> +  /* { dg-begin-multiline-output "" }
> +   return callee_3 (first, second, third);
> +                                        ^
> +     { dg-end-multiline-output "" } */
> +  // { dg-message "initializing argument 2 of 'int callee_3\\(int, const char\\*, float\\)'" "" { target *-*-* } callee_3 }
> +  /* { dg-begin-multiline-output "" }
> + static int callee_3 (int one, const char *two, float three)
> +            ^~~~~~~~
> +     { dg-end-multiline-output "" } */
> +}
> +
> +/* static member, with argname.  */
> +
> +struct s4 { static int member_1 (int one, const char *two, float three); };
> +
> +int test_4 (int first, int second, float third)
> +{
> +  return s4::member_1 (first, second, third); // { dg-error "invalid conversion from 'int' to 'const char\\*'" }
> +  /* { dg-begin-multiline-output "" }
> +   return s4::member_1 (first, second, third);
> +                                            ^
> +     { dg-end-multiline-output "" } */
> +  /* { dg-begin-multiline-output "" }
> + struct s4 { static int member_1 (int one, const char *two, float three); };
> +                        ^~~~~~~~
> +     { dg-end-multiline-output "" } */
> +}
> +
> +/* non-static member, with argname.  */
> +
> +struct s5 { int member_1 (int one, const char *two, float three); };
> +
> +int test_5 (int first, int second, float third)
> +{
> +  s5 inst;
> +  return inst.member_1 (first, second, third); // { dg-error "invalid conversion from 'int' to 'const char\\*'" }
> +  /* { dg-begin-multiline-output "" }
> +   return inst.member_1 (first, second, third);
> +                                             ^
> +     { dg-end-multiline-output "" } */
> +  /* { dg-begin-multiline-output "" }
> + struct s5 { int member_1 (int one, const char *two, float three); };
> +                 ^~~~~~~~
> +     { dg-end-multiline-output "" } */
> +}
> +
> +/* Template function.  */
> +
> +template <typename T>
> +int test_6 (int one, T two, float three);
> +
> +int test_6 (int first, int second, float third)
> +{
> +  return test_6 <const char *> (first, second, third); // { dg-error "no matching function" }
> +  /* { dg-begin-multiline-output "" }
> +   return test_6 <const char *> (first, second, third);
> +                                                     ^
> +     { dg-end-multiline-output "" } */
> +  /* { dg-begin-multiline-output "" }
> +   return test_6 <const char *> (first, second, third);
> +                                                     ^
> +     { dg-end-multiline-output "" } */
> +  /* { dg-begin-multiline-output "" }
> + int test_6 (int one, T two, float three);
> +     ^~~~~~
> +     { dg-end-multiline-output "" } */
> +}
> +
> +/* Template class, static function.  */
> +
> +template <typename T>
> +struct s7 { static int member_1 (int one, T two, float three); };
> +
> +int test_7 (int first, int second, float third)
> +{
> +  return s7 <const char *>::member_1 (first, second, third); // { dg-error "invalid conversion from 'int' to 'const char\\*'" }
> +  /* { dg-begin-multiline-output "" }
> +   return s7 <const char *>::member_1 (first, second, third);
> +                                                           ^
> +     { dg-end-multiline-output "" } */
> +  /* { dg-begin-multiline-output "" }
> + struct s7 { static int member_1 (int one, T two, float three); };
> +                        ^~~~~~~~
> +     { dg-end-multiline-output "" } */
> +}
> +
> +/* Template class, non-static function.  */
> +
> +template <typename T>
> +struct s8 { int member_1 (int one, T two, float three); };
> +
> +int test_8 (int first, int second, float third)
> +{
> +  s8 <const char *> inst;
> +  return inst.member_1 (first, second, third); // { dg-error "invalid conversion from 'int' to 'const char\\*'" }
> +  /* { dg-begin-multiline-output "" }
> +   return inst.member_1 (first, second, third);
> +                                             ^
> +     { dg-end-multiline-output "" } */
> +  /* { dg-begin-multiline-output "" }
> + struct s8 { int member_1 (int one, T two, float three); };
> +                 ^~~~~~~~
> +     { dg-end-multiline-output "" } */
> +}
> +
> +// TODO: template callsite
> diff --git a/gcc/testsuite/gcc.dg/param-type-mismatch.c b/gcc/testsuite/gcc.dg/param-type-mismatch.c
> new file mode 100644
> index 0000000..70ea0bc
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/param-type-mismatch.c
> @@ -0,0 +1,63 @@
> +/* { dg-options "-fdiagnostics-show-caret" }  */
> +
> +/* A collection of calls where argument 2 is of the wrong type.
> +
> +   TODO: we should highlight the second parameter of the callee, rather
> +   than its name.  */
> +
> +/* decl, with argname.  */
> +
> +extern int callee_1 (int one, const char *two, float three); /* { dg-line callee_1 } */
> +
> +int test_1 (int first, int second, float third)
> +{
> +  return callee_1 (first, second, third); /* { dg-warning "passing argument 2 of 'callee_1' makes pointer from integer without a cast" }  */
> +  /* { dg-begin-multiline-output "" }
> +   return callee_1 (first, second, third);
> +                           ^~~~~~
> +     { dg-end-multiline-output "" } */
> +  /* { dg-message "expected 'const char \\*' but argument is of type 'int'" "" { target *-*-* } callee_1 } */
> +  /* { dg-begin-multiline-output "" }
> + extern int callee_1 (int one, const char *two, float three);
> +            ^~~~~~~~
> +     { dg-end-multiline-output "" } */
> +}
> +
> +/* decl, without argname.  */
> +
> +extern int callee_2 (int, const char *, float); /* { dg-line callee_2 } */
> +
> +int test_2 (int first, int second, float third)
> +{
> +  return callee_2 (first, second, third); /* { dg-warning "passing argument 2 of 'callee_2' makes pointer from integer without a cast" } */
> +  /* { dg-begin-multiline-output "" }
> +   return callee_2 (first, second, third);
> +                           ^~~~~~
> +     { dg-end-multiline-output "" } */
> +  /* { dg-message "expected 'const char \\*' but argument is of type 'int'" "" { target *-*-* } callee_2 } */
> +  /* { dg-begin-multiline-output "" }
> + extern int callee_2 (int, const char *, float);
> +            ^~~~~~~~
> +     { dg-end-multiline-output "" } */
> +}
> +
> +/* defn, with argname.  */
> +
> +static int callee_3 (int one, const char *two, float three) /* { dg-line callee_3 } */
> +{
> +  return callee_2 (one, two, three);
> +}
> +
> +int test_3 (int first, int second, float third)
> +{
> +  return callee_3 (first, second, third); // { dg-warning "passing argument 2 of 'callee_3' makes pointer from integer without a cast" }
> +  /* { dg-begin-multiline-output "" }
> +   return callee_3 (first, second, third);
> +                           ^~~~~~
> +     { dg-end-multiline-output "" } */
> +  /* { dg-message "expected 'const char \\*' but argument is of type 'int'" "" { target *-*-* } callee_3 } */
> +  /* { dg-begin-multiline-output "" }
> + static int callee_3 (int one, const char *two, float three)
> +            ^~~~~~~~
> +     { dg-end-multiline-output "" } */
> +}
> --
> 1.8.5.3
>

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 12/17] Add server.h and server.c
  2017-07-24 19:31 ` [PATCH 12/17] Add server.h and server.c David Malcolm
@ 2017-07-26 14:35   ` Oleg Endo
  2017-07-26 14:50     ` David Malcolm
  2017-09-01 17:09   ` Jeff Law
  1 sibling, 1 reply; 38+ messages in thread
From: Oleg Endo @ 2017-07-26 14:35 UTC (permalink / raw)
  To: David Malcolm, gcc-patches

On Mon, 2017-07-24 at 16:05 -0400, David Malcolm wrote:
> 
> +
> +You should have received a copy of the GNU General Public License
> +along with GCC; see the file COPYING3.  If not see
> +<http://www.gnu.org/licenses/>.  */
> +
> +#ifndef GCC_SERVER_H
> +#define GCC_SERVER_H
> +
> +/* Wrapper aroung "int" for file descriptors.  */
                ~~~^
              around :)

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 12/17] Add server.h and server.c
  2017-07-26 14:35   ` Oleg Endo
@ 2017-07-26 14:50     ` David Malcolm
  2017-07-26 21:00       ` Mike Stump
  0 siblings, 1 reply; 38+ messages in thread
From: David Malcolm @ 2017-07-26 14:50 UTC (permalink / raw)
  To: Oleg Endo, gcc-patches

On Wed, 2017-07-26 at 23:35 +0900, Oleg Endo wrote:
> On Mon, 2017-07-24 at 16:05 -0400, David Malcolm wrote:
> >  
> > +
> > +You should have received a copy of the GNU General Public License
> > +along with GCC; see the file COPYING3.  If not see
> > +<http://www.gnu.org/licenses/>.  */
> > +
> > +#ifndef GCC_SERVER_H
> > +#define GCC_SERVER_H
> > +
> > +/* Wrapper aroung "int" for file descriptors.  */
>                 ~~~^
>               around :)

Thanks; fixed in my working copy.

Someone pointed out to me privately that instead/as well as serving on
a port, we could be launched as a subprocess by the IDE, and serve the
RPC over stdin/stdout; this would be simpler for IDEs to cope with.  I
may have a look at supporting that for the next version.

Dave

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 00/17] RFC: New source-location representation; Language Server Protocol
  2017-07-24 19:31 [PATCH 00/17] RFC: New source-location representation; Language Server Protocol David Malcolm
                   ` (16 preceding siblings ...)
  2017-07-24 19:41 ` [PATCH 16/17] Language Server Protocol: proof-of-concept GCC implementation David Malcolm
@ 2017-07-26 17:10 ` Jim Wilson
  2017-07-28  5:12   ` Alexandre Oliva
  2017-07-27 19:51 ` Martin Sebor
  2017-10-11 19:32 ` David Malcolm
  19 siblings, 1 reply; 38+ messages in thread
From: Jim Wilson @ 2017-07-26 17:10 UTC (permalink / raw)
  To: David Malcolm, gcc-patches; +Cc: aoliva

On 07/24/2017 01:04 PM, David Malcolm wrote:
> * The LSP implementation is a just a proof-of-concept, to further
>    motivate capturing the extra data.  Turning it into a "proper" LSP
>    server implementation would be a *lot* more work, and I'm unlikely to
>    actually do that (but maybe someone on the list wants to take this on?)

Apparently Alexandre Oliva has ideas on how to implement LSP by using 
gdb.  You two may want to compare notes.

Jim

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 12/17] Add server.h and server.c
  2017-07-26 14:50     ` David Malcolm
@ 2017-07-26 21:00       ` Mike Stump
  0 siblings, 0 replies; 38+ messages in thread
From: Mike Stump @ 2017-07-26 21:00 UTC (permalink / raw)
  To: David Malcolm; +Cc: Oleg Endo, gcc-patches

[-- Attachment #1: Type: text/plain, Size: 1104 bytes --]

On Jul 26, 2017, at 7:50 AM, David Malcolm <dmalcolm@redhat.com> wrote:
> 
> On Wed, 2017-07-26 at 23:35 +0900, Oleg Endo wrote:
>> On Mon, 2017-07-24 at 16:05 -0400, David Malcolm wrote:
>>> 
>>> +
>>> +You should have received a copy of the GNU General Public License
>>> +along with GCC; see the file COPYING3.  If not see
>>> +<http://www.gnu.org/licenses/>.  */
>>> +
>>> +#ifndef GCC_SERVER_H
>>> +#define GCC_SERVER_H
>>> +
>>> +/* Wrapper aroung "int" for file descriptors.  */
>>                ~~~^
>>              around :)
> 
> Thanks; fixed in my working copy.
> 
> Someone pointed out to me privately that instead/as well as serving on
> a port, we could be launched as a subprocess by the IDE, and serve the
> RPC over stdin/stdout; this would be simpler for IDEs to cope with.  I
> may have a look at supporting that for the next version.

The security threat modeling I think is nicer if you read and write stdin/stdout.  Once you open a port, you open a security hole.  By not having holes by design, we avoid most of this space, which I see as a good thing.


[-- Attachment #2: smime.p7s --]
[-- Type: application/pkcs7-signature, Size: 1578 bytes --]

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 15/17] Language Server Protocol: add lsp::server abstract base class
  2017-07-24 19:31 ` [PATCH 15/17] Language Server Protocol: add lsp::server abstract base class David Malcolm
@ 2017-07-27  4:14   ` Trevor Saunders
  2017-07-28 14:48     ` David Malcolm
  2017-07-27  7:55   ` Richard Biener
  1 sibling, 1 reply; 38+ messages in thread
From: Trevor Saunders @ 2017-07-27  4:14 UTC (permalink / raw)
  To: David Malcolm; +Cc: gcc-patches

On Mon, Jul 24, 2017 at 04:05:12PM -0400, David Malcolm wrote:
> This patch adds an lsp::server abstract base class for implementing
> servers for the Language Server Protocol:
>   https://github.com/Microsoft/language-server-protocol
> 
> along with supporting classes mirroring those from the protocol
> description.
> 
> The public specification of the protocol uses CamelCase, and so these
> classes use CamelCase, to mirror the protocol definition.
> 
> Only a small subset of the protocol is implemented, enough for
> a proof-of-concept.
> 
> Ideally much/all of this would be autogenerated from the
> protocol definition.

I think my big question on this part of the series is why you chose to
implement this yourself instead of using something like libjsonrpccpp
for most of it.  I don't have any attachment to that particular library,
but I don't see any real advantage to be gained from implementing a json
parser ourselves.

> The patch also implements an ::lsp::jsonrpc_server subclass of
> ::jsonrpc::server, handling the marshalling from JSON-RPC to
> an lsp::server instance, expressing them as vfunc calls.

That's not my favorite approach to this space, but its certainly a
common and reasonable one.   You can probably get rid of the virtual
call overhead with final, and in networking or at least file descriptor
based communication it almost certainly doesn't matter anyway.

Trev

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 15/17] Language Server Protocol: add lsp::server abstract base class
  2017-07-24 19:31 ` [PATCH 15/17] Language Server Protocol: add lsp::server abstract base class David Malcolm
  2017-07-27  4:14   ` Trevor Saunders
@ 2017-07-27  7:55   ` Richard Biener
  2017-07-28 16:02     ` David Malcolm
  1 sibling, 1 reply; 38+ messages in thread
From: Richard Biener @ 2017-07-27  7:55 UTC (permalink / raw)
  To: David Malcolm; +Cc: GCC Patches

On Mon, Jul 24, 2017 at 10:05 PM, David Malcolm <dmalcolm@redhat.com> wrote:
> This patch adds an lsp::server abstract base class for implementing
> servers for the Language Server Protocol:
>   https://github.com/Microsoft/language-server-protocol
>
> along with supporting classes mirroring those from the protocol
> description.
>
> The public specification of the protocol uses CamelCase, and so these
> classes use CamelCase, to mirror the protocol definition.
>
> Only a small subset of the protocol is implemented, enough for
> a proof-of-concept.
>
> Ideally much/all of this would be autogenerated from the
> protocol definition.
>
> The patch also implements an ::lsp::jsonrpc_server subclass of
> ::jsonrpc::server, handling the marshalling from JSON-RPC to
> an lsp::server instance, expressing them as vfunc calls.
>
> gcc/ChangeLog:
>         * Makefile.in (OBJS): Add lsp.o.
>         * lsp.c: New file.

New files should use .cc extension (we're using C++ now).

Richard.

>         * lsp.h: New file.
>         * selftest-run-tests.c (selftest::run_tests): Call
>         selftest::lsp_c_tests.
>         * selftest.h (selftest::lsp_c_tests): New decl.
> ---
>  gcc/Makefile.in          |   1 +
>  gcc/lsp.c                | 291 +++++++++++++++++++++++++++++++++++++++++++++++
>  gcc/lsp.h                | 210 ++++++++++++++++++++++++++++++++++
>  gcc/selftest-run-tests.c |   1 +
>  gcc/selftest.h           |   1 +
>  5 files changed, 504 insertions(+)
>  create mode 100644 gcc/lsp.c
>  create mode 100644 gcc/lsp.h
>
> diff --git a/gcc/Makefile.in b/gcc/Makefile.in
> index 1f9050c..e5120c2 100644
> --- a/gcc/Makefile.in
> +++ b/gcc/Makefile.in
> @@ -1382,6 +1382,7 @@ OBJS = \
>         loop-iv.o \
>         loop-unroll.o \
>         lower-subreg.o \
> +       lsp.o \
>         lra.o \
>         lra-assigns.o \
>         lra-coalesce.o \
> diff --git a/gcc/lsp.c b/gcc/lsp.c
> new file mode 100644
> index 0000000..3b79794
> --- /dev/null
> +++ b/gcc/lsp.c
> @@ -0,0 +1,291 @@
> +/* Language Server Protocol implementation.
> +   Copyright (C) 2017 Free Software Foundation, Inc.
> +
> +This file is part of GCC.
> +
> +GCC is free software; you can redistribute it and/or modify it under
> +the terms of the GNU General Public License as published by the Free
> +Software Foundation; either version 3, or (at your option) any later
> +version.
> +
> +GCC is distributed in the hope that it will be useful, but WITHOUT ANY
> +WARRANTY; without even the implied warranty of MERCHANTABILITY or
> +FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
> +for more details.
> +
> +You should have received a copy of the GNU General Public License
> +along with GCC; see the file COPYING3.  If not see
> +<http://www.gnu.org/licenses/>.  */
> +
> +#include "config.h"
> +#include "system.h"
> +#include "coretypes.h"
> +#include "json.h"
> +#include "http-server.h"
> +#include "json-rpc.h"
> +#include "lsp.h"
> +#include "selftest.h"
> +
> +using namespace jsonrpc;
> +using namespace lsp;
> +
> +// TODO: autogenerate the interface binding/marshalling/demarshalling code
> +// from an interface description.
> +
> +#define SET_VALUE(LHS, VALUE, NAME)                            \
> +  do {                                                         \
> +    if (!(VALUE)->get_value_by_key ((NAME), (LHS), out_err))   \
> +      return result;                                           \
> +  } while (0)
> +
> +#define SET_NUMBER(LHS, VALUE, NAME)                           \
> +  do {                                                         \
> +    if (!(VALUE)->get_int_by_key ((NAME), (LHS), out_err))     \
> +      return result;                                           \
> +  } while (0)
> +
> +#define SET_STRING(LHS, VALUE, NAME)                           \
> +  do {                                                         \
> +    if (!(VALUE)->get_string_by_key ((NAME), (LHS), out_err))  \
> +      return result;                                           \
> +  } while (0)
> +
> +Position
> +Position::from_json (const json::value *params,
> +                    char *&out_err)
> +{
> +  Position result;
> +  SET_NUMBER (result.line, params, "line");
> +  SET_NUMBER (result.character, params, "character");
> +  return result;
> +}
> +
> +TextDocumentIdentifier
> +TextDocumentIdentifier::from_json (const json::value *params,
> +                                  char *&out_err)
> +{
> +  TextDocumentIdentifier result;
> +  SET_STRING (result.uri, params, "uri");
> +  return result;
> +}
> +
> +TextDocumentItem
> +TextDocumentItem::from_json (const json::value *params,
> +                            char *&out_err)
> +{
> +  TextDocumentItem result;
> +  SET_STRING (result.uri, params, "uri");
> +  SET_STRING (result.languageId, params, "languageId");
> +  SET_NUMBER (result.version, params, "version");
> +  SET_STRING (result.text, params, "text");
> +  return result;
> +}
> +
> +DidOpenTextDocumentParams
> +DidOpenTextDocumentParams::from_json (const json::value *params,
> +                                     char *&out_err)
> +{
> +  DidOpenTextDocumentParams result;
> +  // FIXME: error-handling
> +  const json::value *text_document;
> +  SET_VALUE (text_document, params, "textDocument");
> +  result.textDocument
> +    = TextDocumentItem::from_json (text_document, out_err);
> +  return result;
> +}
> +
> +DidChangeTextDocumentParams
> +DidChangeTextDocumentParams::from_json (const json::value */*params*/,
> +                                       char *&/*out_err*/)
> +{
> +  DidChangeTextDocumentParams result;
> +
> +  // FIXME
> +  return result;
> +}
> +
> +TextDocumentPositionParams
> +TextDocumentPositionParams::from_json (const json::value *params,
> +                                      char *&out_err)
> +{
> +  TextDocumentPositionParams result;
> +  const json::value *text_document;
> +  const json::value *position;
> +  SET_VALUE (text_document, params, "textDocument");
> +  SET_VALUE (position, params, "position");
> +  result.textDocument
> +    = TextDocumentIdentifier::from_json (text_document, out_err);
> +  result.position
> +    = Position::from_json (position, out_err);
> +  return result;
> +}
> +
> +/* struct Position.  */
> +
> +json::value *
> +Position::to_json () const
> +{
> +  json::object *result = new json::object ();
> +  result->set ("line", new json::number (line));
> +  result->set ("character", new json::number (character));
> +  return result;
> +}
> +
> +/* struct Range.  */
> +
> +json::value *
> +Range::to_json () const
> +{
> +  json::object *result = new json::object ();
> +  result->set ("start", start.to_json ());
> +  result->set ("end", end.to_json ());
> +  return result;
> +}
> +
> +/* struct Location.  */
> +
> +json::value *
> +Location::to_json () const
> +{
> +  json::object *result = new json::object ();
> +  result->set ("uri", new json::string (uri));
> +  result->set ("range", range.to_json ());
> +  return result;
> +}
> +
> +/* class lsp::jsonrpc_server : public ::jsonrpc::server.  */
> +
> +json::value *
> +lsp::jsonrpc_server::dispatch (const char *method, const json::value *params,
> +                              const json::value *id)
> +{
> +  if (0 == strcmp (method, "initialize"))
> +    return do_initialize (id, params);
> +  if (0 == strcmp (method, "textDocument/didOpen"))
> +    return do_text_document_did_open (params);
> +  if (0 == strcmp (method, "textDocument/didChange"))
> +    return do_text_document_did_change (params);
> +  if (0 == strcmp (method, "textDocument/definition"))
> +    return do_text_document_definition (params);
> +  return make_method_not_found (id, method);
> +}
> +
> +json::value *
> +lsp::jsonrpc_server::do_initialize (const json::value *id,
> +                                   const json::value */*params*/)
> +{
> +  // FIXME: for now, ignore params
> +
> +  json::object *server_caps = new json::object ();
> +  json::object *result = new json::object ();
> +  result->set ("capabilities", server_caps);
> +  return make_success (id, result);
> +}
> +
> +static json::value *
> +make_invalid_params_and_free_msg (const json::value *id, char *msg)
> +{
> +  json::value *err = make_invalid_params (id, msg);
> +  free (msg);
> +  return err;
> +}
> +
> +json::value *
> +lsp::jsonrpc_server::do_text_document_did_open (const json::value *params)
> +{
> +  char *err = NULL;
> +  DidOpenTextDocumentParams p
> +    = DidOpenTextDocumentParams::from_json (params, err);
> +  if (err)
> +    return make_invalid_params_and_free_msg (NULL, err); // though we ought not to return non-NULL for a notification
> +
> +  m_inner.do_text_document_did_open (p);
> +  return NULL; // notification, so no response
> +}
> +
> +json::value *
> +lsp::jsonrpc_server::do_text_document_did_change (const json::value *params)
> +{
> +  char *err = NULL;
> +  DidChangeTextDocumentParams p
> +    = DidChangeTextDocumentParams::from_json (params, err);
> +  if (err)
> +    return make_invalid_params_and_free_msg (NULL, err); // though we ought not to return non-NULL for a notification
> +
> +  m_inner.do_text_document_did_change (p);
> +
> +  return NULL; // notification, so no response
> +}
> +
> +json::value *
> +lsp::jsonrpc_server::do_text_document_definition (const json::value *params)
> +{
> +  char *err = NULL;
> +  TextDocumentPositionParams p
> +    = TextDocumentPositionParams::from_json (params, err);
> +  if (err)
> +    return make_invalid_params_and_free_msg (NULL, err);
> +
> +  auto_vec<Location> out;
> +  m_inner.do_text_document_definition (p, out);
> +
> +  json::array *result = new json::array ();
> +  unsigned i;
> +  Location *loc;
> +  FOR_EACH_VEC_ELT (out, i, loc)
> +    result->append (loc->to_json ());
> +  return result;
> +}
> +
> +#if CHECKING_P
> +
> +namespace selftest {
> +
> +/* Selftests.  */
> +
> +static void
> +test_simple ()
> +{
> +  noop_server noop;
> +  lsp::jsonrpc_server js (false, noop);
> +  const char *init_request
> +    = ("{\"jsonrpc\": \"2.0\", \"method\": \"initialize\","
> +       " \"params\": [42, 23], \"id\": 1}"); // FIXME
> +  json::value *init_response = js.handle_request_string (init_request);
> +  //const json::value *result = assert_is_success (response, 1);
> +  //ASSERT_EQ (19, result->as_number ()->get ());
> +  delete init_response;
> +
> +  const char *did_open_note
> +    = ("{\"jsonrpc\": \"2.0\", \"method\": \"textDocument/didOpen\","
> +       " \"params\": {"
> +       " \"textDocument\": "
> +       " { \"uri\": \"DocumentUri goes here\","
> +         " \"languageId\": \"c++\","
> +         " \"version\": 0,"
> +         " \"text\": \"/* Initial content.  */\"}}}");
> +  json::value *did_open_response = js.handle_request_string (did_open_note);
> +  delete did_open_response;
> +
> +  const char *did_change_note
> +    = ("{\"jsonrpc\": \"2.0\", \"method\": \"textDocument/didChange\","
> +       " \"params\": {"
> +         " \"textDocument\": {\"version\": 1},"
> +         " \"contentChanges\": [{\"text\": \"/* Hello world.  */\"}]"
> +       "}}"); // FIXME
> +  json::value *did_change_response = js.handle_request_string (did_change_note);
> +  delete did_change_response;
> +}
> +
> +
> +/* Run all of the selftests within this file.  */
> +
> +void
> +lsp_c_tests ()
> +{
> +  test_simple ();
> +}
> +
> +} // namespace selftest
> +
> +#endif /* #if CHECKING_P */
> diff --git a/gcc/lsp.h b/gcc/lsp.h
> new file mode 100644
> index 0000000..d264cbf
> --- /dev/null
> +++ b/gcc/lsp.h
> @@ -0,0 +1,210 @@
> +/* Language Server Protocol implementation.
> +   Copyright (C) 2017 Free Software Foundation, Inc.
> +
> +This file is part of GCC.
> +
> +GCC is free software; you can redistribute it and/or modify it under
> +the terms of the GNU General Public License as published by the Free
> +Software Foundation; either version 3, or (at your option) any later
> +version.
> +
> +GCC is distributed in the hope that it will be useful, but WITHOUT ANY
> +WARRANTY; without even the implied warranty of MERCHANTABILITY or
> +FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
> +for more details.
> +
> +You should have received a copy of the GNU General Public License
> +along with GCC; see the file COPYING3.  If not see
> +<http://www.gnu.org/licenses/>.  */
> +
> +#ifndef GCC_LSP_H
> +#define GCC_LSP_H
> +
> +namespace lsp {
> +
> +typedef const char *DocumentUri;
> +
> +/* Interfaces from the protocol specification (which uses camel case).  */
> +
> +/* Note that LSP uses 0-based lines and characters, whereas GCC uses
> +   1-based lines and columns.  */
> +
> +struct Position
> +{
> +  Position ()
> +  : line (0), character (0) {}
> +
> +  static Position from_json (const json::value *params,
> +                            char *&out_err);
> +  json::value *to_json () const;
> +
> +  int line;
> +  int character;
> +};
> +
> +struct Range
> +{
> +  Range ()
> +  : start (), end () {}
> +
> +  json::value *to_json () const;
> +
> +  Position start;
> +  Position end;
> +};
> +
> +struct Location
> +{
> +  Location ()
> +  : uri (NULL), range () {}
> +
> +  json::value *to_json () const;
> +
> +  DocumentUri uri;
> +  Range range;
> +};
> +
> +// Exceptions would be nicer than passing around the out_err
> +
> +// TODO: autogenerate the interface binding/marshalling/demarshalling code
> +// from an interface description.
> +
> +struct TextDocumentIdentifier
> +{
> +  TextDocumentIdentifier ()
> +  : uri (NULL) {}
> +
> +  static TextDocumentIdentifier from_json (const json::value *params,
> +                                          char *&out_err);
> +
> +  DocumentUri uri;
> +};
> +
> +struct TextDocumentItem
> +{
> +  TextDocumentItem ()
> +  : uri (NULL), languageId (NULL), version (0), text (NULL)
> +  {}
> +
> +  static TextDocumentItem from_json (const json::value *params,
> +                                    char *&out_err);
> +
> +  DocumentUri uri;
> +  const char *languageId;
> +  int version;
> +  const char *text;
> +};
> +
> +struct DidOpenTextDocumentParams
> +{
> +  DidOpenTextDocumentParams ()
> +  : textDocument () {}
> +
> +  static DidOpenTextDocumentParams from_json (const json::value *params,
> +                                             char *&out_err);
> +
> +  TextDocumentItem textDocument;
> +};
> +
> +struct DidChangeTextDocumentParams
> +{
> + public:
> +  static DidChangeTextDocumentParams from_json (const json::value *params,
> +                                               char *&out_err);
> +
> + private:
> +#if 0
> +  VersionedTextDocumentIdentifier textDocument;
> +  auto_vec<TextDocumentContentChangeEvent> contentChanges;
> +#endif
> +};
> +
> +struct TextDocumentPositionParams
> +{
> +  TextDocumentPositionParams ()
> +  : textDocument (), position () {}
> +
> +  static TextDocumentPositionParams from_json (const json::value *params,
> +                                              char *&out_err);
> +
> +  TextDocumentIdentifier textDocument;
> +  Position position;
> +};
> +
> +/* An abstract base class for implementing the LSP as vfunc calls,
> +   avoiding dealing with JSON.  */
> +
> +class server
> +{
> + public:
> +  virtual ~server () {}
> +
> +  virtual void
> +  do_text_document_did_open (const DidOpenTextDocumentParams &p) = 0;
> +
> +  virtual void
> +  do_text_document_did_change (const DidChangeTextDocumentParams &p) = 0;
> +
> +  virtual void
> +  do_text_document_definition (const TextDocumentPositionParams &p,
> +                              vec<Location> &out) = 0;
> +};
> +
> +/* A concrete subclass of lsp::server that implements everything as a no-op.  */
> +
> +class noop_server : public server
> +{
> +  void
> +  do_text_document_did_open (const DidOpenTextDocumentParams &) OVERRIDE
> +  {
> +    // no-op
> +  }
> +
> +  void
> +  do_text_document_did_change (const DidChangeTextDocumentParams &) OVERRIDE
> +  {
> +    // no-op
> +  }
> +
> +  void
> +  do_text_document_definition (const TextDocumentPositionParams &,
> +                              vec<Location> &) OVERRIDE
> +  {
> +    // no-op
> +  }
> +};
> +
> +/* A jsonrpc::server subclass that decodes incoming JSON-RPC requests
> +   and dispatches them to an lsp::server instance as vfunc calls,
> +   marshalling the inputs/outputs to/from JSON objects.  */
> +
> +class jsonrpc_server : public ::jsonrpc::server
> +{
> + public:
> +  jsonrpc_server (bool verbose, ::lsp::server &inner)
> +  : server (verbose), m_inner (inner) {}
> +
> +  json::value *
> +  dispatch (const char *method, const json::value *params,
> +           const json::value *id) FINAL OVERRIDE;
> +
> + private:
> +  json::value *
> +  do_initialize (const json::value *id, const json::value *params);
> +
> +  json::value *
> +  do_text_document_did_open (const json::value *params);
> +
> +  json::value *
> +  do_text_document_did_change (const json::value *params);
> +
> +  json::value *
> +  do_text_document_definition (const json::value *params);
> +
> + private:
> +  ::lsp::server &m_inner;
> +};
> +
> +} // namespace lsp
> +
> +#endif  /* GCC_LSP_H  */
> diff --git a/gcc/selftest-run-tests.c b/gcc/selftest-run-tests.c
> index 35ab965..5209aa8 100644
> --- a/gcc/selftest-run-tests.c
> +++ b/gcc/selftest-run-tests.c
> @@ -70,6 +70,7 @@ selftest::run_tests ()
>    json_c_tests ();
>    http_server_c_tests ();
>    json_rpc_c_tests ();
> +  lsp_c_tests ();
>
>    /* Mid-level data structures.  */
>    input_c_tests ();
> diff --git a/gcc/selftest.h b/gcc/selftest.h
> index 2312fb2..1655125 100644
> --- a/gcc/selftest.h
> +++ b/gcc/selftest.h
> @@ -187,6 +187,7 @@ extern void http_server_c_tests ();
>  extern void input_c_tests ();
>  extern void json_c_tests ();
>  extern void json_rpc_c_tests ();
> +extern void lsp_c_tests ();
>  extern void pretty_print_c_tests ();
>  extern void read_rtl_function_c_tests ();
>  extern void rtl_tests_c_tests ();
> --
> 1.8.5.3
>

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 00/17] RFC: New source-location representation; Language Server Protocol
  2017-07-24 19:31 [PATCH 00/17] RFC: New source-location representation; Language Server Protocol David Malcolm
                   ` (17 preceding siblings ...)
  2017-07-26 17:10 ` [PATCH 00/17] RFC: New source-location representation; Language Server Protocol Jim Wilson
@ 2017-07-27 19:51 ` Martin Sebor
  2017-07-28 14:28   ` David Malcolm
  2017-10-11 19:32 ` David Malcolm
  19 siblings, 1 reply; 38+ messages in thread
From: Martin Sebor @ 2017-07-27 19:51 UTC (permalink / raw)
  To: David Malcolm, gcc-patches

I think this is great.  My overall question is: is the new BLT
representation available in the middle-end?  If not, do you
have plans to make it available?  (I think it might be especially
useful there, to either accurately highlight the source of
a problem when it's far removed from the problem site, or to
track the flow of data from its source to the sink.)

> =====================
> (b) Examples of usage
> =====================
>
> Patches 6-10 in the kit update various diagnostics to use
> the improved location information where available:
>
> * C and C++: highlighting the pertinent parameter of a function
>   decl when there's a mismatched type in a call

Very nice!

>
> * C and C++: highlighting the return type in the function defn
>   when compaining about mismatch in the body (e.g. highlighting
>   the "void" when we see a "return EXPR;" within a void function).
>
> * C++: add a fix-it hint to -Wsuggest-override
>
> I have plenty of ideas for other uses of this infrastructure
> (but which aren't implemented yet), e.g.:
>
> * C++: highlight the "const" token (or suggest a fix-it hint)
>   when you have a missing "const" on the *definition* of a member
>   function that was declared as "const" (I make this mistake
>   all the time).
>
> * C++: add a fix-it hint to -Wsuggest-final-methods

To answer your call for other ideas below: There are other
suggestions that GCC could offer to help improve code, including

  *  to add the const qualifier to function pointer and reference
     arguments (including member functions)

  *  to add restrict where appropriate (especially to const pointer
     and reference arguments)

  *  to delete or default the default copy ctor or copy assignment
     operator if/when it would benefit

  *  to help highlight how to optimize data layout (with a new
     hypothetical feature that offered suggestions to reorder
     struct or class members for space efficiency or data
     locality)

>
> * highlight bogus attributes

This would be very nice.  The diagnostics in this area are weak
to put it mildly, and the highlighting is completely bogus.  It
would be great to also be able to highlight attribute operands.

>
> * add fix-it hints suggesting missing attributes
>
> ...etc, plus those "cousins of a compiler" ideas mentioned above.
>
> Any other ideas?

This may be outside the scope of your work but when a declaration
is found to conflict in some way with one seen earlier on in a file,
it would be great to be able to point to the actual source of the
conflict rather than to the immediately preceding declaration as
a whole.  As in:

   int __attribute__ ((noinline)) foo (int);

   int foo (int);

   int __attribute ((always_inline)) foo (int);

   x.c:5:35: warning: declaration of ‘int foo(int)’ with attribute 
‘always_inline’ follows declaration with attribute ‘noinline’ [-Wattributes]
    int __attribute ((always_inline)) foo (int);
                                      ^~~

Rather than printing a note like this:

   x.c:3:5: note: previous declaration of ‘int foo(int)’ was here
    int foo (int);
        ^~~

print this:

   x.c:1:5: note: previous declaration of ‘int foo(int)’ was here
   int __attribute__ ((noinline)) foo (int);
                                  ^~~

(preferably with the attribute underlined).

I'm sure there are many others.

Martin

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 04/17] C frontend: capture BLT information
  2017-07-24 19:39 ` [PATCH 04/17] C frontend: capture BLT information David Malcolm
@ 2017-07-27 19:58   ` Martin Sebor
  0 siblings, 0 replies; 38+ messages in thread
From: Martin Sebor @ 2017-07-27 19:58 UTC (permalink / raw)
  To: David Malcolm, gcc-patches

On 07/24/2017 02:05 PM, David Malcolm wrote:
> This patch extends the C frontend so that it optionally builds a BLT tree,
> by using an auto_blt_node class within the recursive descent through the
> parser, so that its ctor/dtors build the blt_node hierarchy; this is
> rapidly (I hope) rejected in the no -fblt case, so that by default, no
> blt nodes are created and it's (I hope) close to a no-op.
>
...
> @@ -4627,6 +4629,8 @@ start_decl (struct c_declarator *declarator, struct c_declspecs *declspecs,
>  			 deprecated_state);
>    if (!decl || decl == error_mark_node)
>      return NULL_TREE;
> +  if (declarator->bltnode)
> +    declarator->bltnode->set_tree (decl);

FWIW, if this checking were to become wide-spred, an alternative
to introducing conditionals like this is to set declarator->bltnode
to an object of a dummy type that doesn't do anything if -fblt isn't
specified, and to an object of the real "workhorse" type that does
the work with -fblt.  The object can either be polymorphic if the
cost of the virtual call isn't a concern, or it can be a static
wrapper around a polymorphic interface, where the (inline member
functions of the) static wrapper do the checking under the hood
so the users don't have to worry about it.  (A more C-like
alternative is to simply hide the check in an inline function.)

>
> +/* A RAII-style class for optionally building a concrete parse tree (or
> +   at least, something close to it) as the recursive descent parser runs.
> +
> +   Close to a no-op if -fblt is not selected.  */
> +
> +class auto_blt_node
> +{
> +public:
> +  auto_blt_node (c_parser* parser, enum blt_kind kind);
> +  ~auto_blt_node ();

Unless the class is safely copyable and assignable (I can't tell
from the dtor definition) I would suggest to define its copy ctor
and assignment operator private to avoid introducing subtle bugs
by inadvertently making copies.

Martin

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 00/17] RFC: New source-location representation; Language Server Protocol
  2017-07-26 17:10 ` [PATCH 00/17] RFC: New source-location representation; Language Server Protocol Jim Wilson
@ 2017-07-28  5:12   ` Alexandre Oliva
  0 siblings, 0 replies; 38+ messages in thread
From: Alexandre Oliva @ 2017-07-28  5:12 UTC (permalink / raw)
  To: Jim Wilson; +Cc: David Malcolm, gcc-patches

On Jul 26, 2017, Jim Wilson <jim.wilson@linaro.org> wrote:

> On 07/24/2017 01:04 PM, David Malcolm wrote:
>> * The LSP implementation is a just a proof-of-concept, to further
>> motivate capturing the extra data.  Turning it into a "proper" LSP
>> server implementation would be a *lot* more work, and I'm unlikely to
>> actually do that (but maybe someone on the list wants to take this on?)

> Apparently Alexandre Oliva has ideas on how to implement LSP by using
> gdb.  You two may want to compare notes.

*nod*

I thought GDB would be a better place for the server proper, because it
already knows how to deal with multiple translation units, how to
navigate the symbol tables in the presence of multiple contexts, how to
perform context-aware name completion and whatnot.

Current debug info is not enough to implement everything that's expected
of a language server, so we'd probably need a compile mode not entirely
unlike syntax-check (i.e., no actual code generation), but dumping
symbolic information about definitions (an extended subset of existing
debug info, since there's no object code to refer to, but there are
e.g. symbolic templates that don't appear at all nowadays), and either a
detailed summary of where tokens appear in sources, or something a bit
like preprocessed output, that contains even declarations brought from
header files, but perhaps without resolving preprocessor conditionals,
expanding macros or otherwise mangling actual sources.

I'm afraid this is as far as I got in the "design" that Richard Stallman
asked of me.  Lucky I knew what it was about because David had
introduced me to LSP back in March ;-)

-- 
Alexandre Oliva, freedom fighter    http://FSFLA.org/~lxoliva/
You must be the change you wish to see in the world. -- Gandhi
Be Free! -- http://FSFLA.org/   FSF Latin America board member
Free Software Evangelist|Red Hat Brasil GNU Toolchain Engineer

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 00/17] RFC: New source-location representation; Language Server Protocol
  2017-07-27 19:51 ` Martin Sebor
@ 2017-07-28 14:28   ` David Malcolm
  2017-07-28 18:32     ` Martin Sebor
  0 siblings, 1 reply; 38+ messages in thread
From: David Malcolm @ 2017-07-28 14:28 UTC (permalink / raw)
  To: Martin Sebor, gcc-patches

On Thu, 2017-07-27 at 13:51 -0600, Martin Sebor wrote:
> I think this is great.  My overall question is: is the new BLT
> representation available in the middle-end?  

Given that, err, I'm not freeing it anywhere yet (ahem), the answer is
yes.  Although it lives in the "gcc" dir, the generated tree is
naturally very language-dependent; any kind of walking of it is going
to be making language-specific assumptions.

> If not, do you
> have plans to make it available?  (I think it might be especially
> useful there, to either accurately highlight the source of
> a problem when it's far removed from the problem site, or to
> track the flow of data from its source to the sink.)

Note that at the moment in the parsers it captures lots of the stuff
outside of function bodies, which we weren't really capturing before
(params of fn decls, locations of individual tokens e.g. commas, etc),
since that's my primary motivation for the patch kit.

But currently IIRC it doesn't capture all of the stuff inside of
function bodies, as we mostly do a reasonable job there (the exception
is for various "leaf" expressions such as constants and uses of
decls/vars, which don't have a location_t; I'm working on a different
patch kit to try to address that issue).

The FE patches could be extended to capture more if desired (there may
be a memory tradeoff here).

A blt_node can be associated with a "tree" node, but it's not going to
help for things like a VAR_DECL leaf expression, since that can appear
multiple times in a so-called "tree" (actually a digraph).  It could
potentially help if you know the parent tree node, but that's messy.

> > =====================
> > (b) Examples of usage
> > =====================
> > 
> > Patches 6-10 in the kit update various diagnostics to use
> > the improved location information where available:
> > 
> > * C and C++: highlighting the pertinent parameter of a function
> >   decl when there's a mismatched type in a call
> 
> Very nice!
> 
> > 
> > * C and C++: highlighting the return type in the function defn
> >   when compaining about mismatch in the body (e.g. highlighting
> >   the "void" when we see a "return EXPR;" within a void function).
> > 
> > * C++: add a fix-it hint to -Wsuggest-override
> > 
> > I have plenty of ideas for other uses of this infrastructure
> > (but which aren't implemented yet), e.g.:
> > 
> > * C++: highlight the "const" token (or suggest a fix-it hint)
> >   when you have a missing "const" on the *definition* of a member
> >   function that was declared as "const" (I make this mistake
> >   all the time).
> > 
> > * C++: add a fix-it hint to -Wsuggest-final-methods
> 
> To answer your call for other ideas below: There are other
> suggestions that GCC could offer to help improve code, including
> 
>   *  to add the const qualifier to function pointer and reference
>      arguments (including member functions)

When there's a mismatch?  Consider e.g.

  void test (const char *dst, char *src)
  {
     memcpy (dst, src, 1024);	
  }

warning: passing argument 1 of ‘memcpy’ discards ‘const’ qualifier from
pointer target type [-Wdiscarded-qualifiers]
   memcpy (dst, src, 1024);
           ^~~
note: expected ‘void *’ but argument is of type ‘const char *’
  void test (const char *dst, char *src)
             ^~~~~
             ------
(here I've added the underlining of the "const" and the fix-it hint
suggestion deletion of "const" on param aren't).

plus we could add:

warning: argument 2 could be marked as 'const'
  void test (const char *dst, char *src)
                             
^~~~~~~~~~
                              const char * src


>   *  to add restrict where appropriate (especially to const pointer
>      and reference arguments)

When is it appropriate?  Is this something that we can infer?


>   *  to delete or default the default copy ctor or copy assignment
>      operator if/when it would benefit

Nice idea; is there a way we can tell when this is appropriate?

>   *  to help highlight how to optimize data layout (with a new
>      hypothetical feature that offered suggestions to reorder
>      struct or class members for space efficiency or data
>      locality)

Good idea.

> > 
> > * highlight bogus attributes
> 
> This would be very nice.  The diagnostics in this area are weak
> to put it mildly, and the highlighting is completely bogus.  It
> would be great to also be able to highlight attribute operands.
> 
> > 
> > * add fix-it hints suggesting missing attributes
> > 
> > ...etc, plus those "cousins of a compiler" ideas mentioned above.
> > 
> > Any other ideas?
> 
> This may be outside the scope of your work but when a declaration
> is found to conflict in some way with one seen earlier on in a file,
> it would be great to be able to point to the actual source of the
> conflict rather than to the immediately preceding declaration as
> a whole.  As in:
> 
>    int __attribute__ ((noinline)) foo (int);
> 
>    int foo (int);
> 
>    int __attribute ((always_inline)) foo (int);
> 
>    x.c:5:35: warning: declaration of ‘int foo(int)’ with attribute 
> ‘always_inline’ follows declaration with attribute ‘noinline’ [
> -Wattributes]
>     int __attribute ((always_inline)) foo (int);
>                                       ^~~
> 
> Rather than printing a note like this:
> 
>    x.c:3:5: note: previous declaration of ‘int foo(int)’ was here
>     int foo (int);
>         ^~~
> 
> print this:
> 
>    x.c:1:5: note: previous declaration of ‘int foo(int)’ was here
>    int __attribute__ ((noinline)) foo (int);
>                                   ^~~
> 
> (preferably with the attribute underlined).
> 
> I'm sure there are many others.

> Martin

Thanks
Dave

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 15/17] Language Server Protocol: add lsp::server abstract base class
  2017-07-27  4:14   ` Trevor Saunders
@ 2017-07-28 14:48     ` David Malcolm
  0 siblings, 0 replies; 38+ messages in thread
From: David Malcolm @ 2017-07-28 14:48 UTC (permalink / raw)
  To: Trevor Saunders; +Cc: gcc-patches

On Thu, 2017-07-27 at 00:14 -0400, Trevor Saunders wrote:
> On Mon, Jul 24, 2017 at 04:05:12PM -0400, David Malcolm wrote:
> > This patch adds an lsp::server abstract base class for implementing
> > servers for the Language Server Protocol:
> >   https://github.com/Microsoft/language-server-protocol
> > 
> > along with supporting classes mirroring those from the protocol
> > description.
> > 
> > The public specification of the protocol uses CamelCase, and so
> > these
> > classes use CamelCase, to mirror the protocol definition.
> > 
> > Only a small subset of the protocol is implemented, enough for
> > a proof-of-concept.
> > 
> > Ideally much/all of this would be autogenerated from the
> > protocol definition.
> 
> I think my big question on this part of the series is why you chose
> to
> implement this yourself instead of using something like libjsonrpccpp
> for most of it.  I don't have any attachment to that particular
> library,
> but I don't see any real advantage to be gained from implementing a
> json
> parser ourselves.

I think my thought was to reduce dependencies, but, yes, that looks
like a much better approach, thanks.

> > The patch also implements an ::lsp::jsonrpc_server subclass of
> > ::jsonrpc::server, handling the marshalling from JSON-RPC to
> > an lsp::server instance, expressing them as vfunc calls.
> 
> That's not my favorite approach to this space, but its certainly a
> common and reasonable one.   You can probably get rid of the virtual
> call overhead with final, and in networking or at least file
> descriptor
> based communication it almost certainly doesn't matter anyway.
> 
> Trev
> 

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 15/17] Language Server Protocol: add lsp::server abstract base class
  2017-07-27  7:55   ` Richard Biener
@ 2017-07-28 16:02     ` David Malcolm
  0 siblings, 0 replies; 38+ messages in thread
From: David Malcolm @ 2017-07-28 16:02 UTC (permalink / raw)
  To: Richard Biener; +Cc: GCC Patches

On Thu, 2017-07-27 at 09:55 +0200, Richard Biener wrote:
> On Mon, Jul 24, 2017 at 10:05 PM, David Malcolm <dmalcolm@redhat.com>
> wrote:
> > This patch adds an lsp::server abstract base class for implementing
> > servers for the Language Server Protocol:
> >   https://github.com/Microsoft/language-server-protocol
> > 
> > along with supporting classes mirroring those from the protocol
> > description.
> > 
> > The public specification of the protocol uses CamelCase, and so
> > these
> > classes use CamelCase, to mirror the protocol definition.
> > 
> > Only a small subset of the protocol is implemented, enough for
> > a proof-of-concept.
> > 
> > Ideally much/all of this would be autogenerated from the
> > protocol definition.
> > 
> > The patch also implements an ::lsp::jsonrpc_server subclass of
> > ::jsonrpc::server, handling the marshalling from JSON-RPC to
> > an lsp::server instance, expressing them as vfunc calls.
> > 
> > gcc/ChangeLog:
> >         * Makefile.in (OBJS): Add lsp.o.
> >         * lsp.c: New file.
> 
> New files should use .cc extension (we're using C++ now).
> 
> Richard.
> 

Thanks; will do for next iteration of the kit.

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 00/17] RFC: New source-location representation; Language Server Protocol
  2017-07-28 14:28   ` David Malcolm
@ 2017-07-28 18:32     ` Martin Sebor
  0 siblings, 0 replies; 38+ messages in thread
From: Martin Sebor @ 2017-07-28 18:32 UTC (permalink / raw)
  To: David Malcolm, gcc-patches

Thanks for the explanation (snipped).

>>> * C++: add a fix-it hint to -Wsuggest-final-methods
>>
>> To answer your call for other ideas below: There are other
>> suggestions that GCC could offer to help improve code, including
>>
>>   *  to add the const qualifier to function pointer and reference
>>      arguments (including member functions)
>
> When there's a mismatch?  Consider e.g.
>
>   void test (const char *dst, char *src)
>   {
>      memcpy (dst, src, 1024);	
>   }
>
> warning: passing argument 1 of ‘memcpy’ discards ‘const’ qualifier from
> pointer target type [-Wdiscarded-qualifiers]
>    memcpy (dst, src, 1024);
>            ^~~
> note: expected ‘void *’ but argument is of type ‘const char *’
>   void test (const char *dst, char *src)
>              ^~~~~
>              ------
> (here I've added the underlining of the "const" and the fix-it hint
> suggestion deletion of "const" on param aren't).

This would be useful as well but...

>
> plus we could add:
>
> warning: argument 2 could be marked as 'const'
>   void test (const char *dst, char *src)
>
> ^~~~~~~~~~
>                               const char * src

... the latter is what I meant.   It's common to forget to
declare a pointer (or reference) argument const even when it
doesn't change the object it refers to.  Suggesting the const
keyword would help drive const correctness and even expose
optimization opportunities (e.g., with the restrict suggestion
below, but also in other contexts).  This could be extended
beyond function arguments and to private class members and
perhaps even local (and static global) variables.

>
>
>>   *  to add restrict where appropriate (especially to const pointer
>>      and reference arguments)
>
> When is it appropriate?  Is this something that we can infer?

Broadly, declaring a pointer argument restrict is appropriate
whenever it's not used to access the same object as another
pointer whose value wasn't derived from the first pointer.
It shouldn't be too hard to infer the simple cases in the alias
oracle.  The main benefit I see is for out-of-line functions
that take their arguments by const pointer or const reference.
By declaring the arguments restrict GCC can assume that the
function doesn't change the pointed-to object (otherwise it
must assume that every such function casts the constness away
and changes the object).  This optimization isn't implemented
yet.

>
>
>>   *  to delete or default the default copy ctor or copy assignment
>>      operator if/when it would benefit
>
> Nice idea; is there a way we can tell when this is appropriate?

Suggesting to delete a copy ctor/assignment should be doable
in the middle-end by analyzing the ctor and dtor for paired
calls to functions that acquire and release some resource
(e.g., memory via new and delete).  A class that does that
but that doesn't define a dedicated copy ctor or assignment
can be misused to create a copy of an object and have the
same resource released more than once.  This is quite
a common mistake to make.

The second case (defaulted functions) should be detectable by
analyzing the special functions' semantics and determining
whether or not they are the same as those of the defaulted
function.  Defaulting the special function would then help
make objects of the class usable in more contexts and more
efficiently (especially if the special functions were defined
out of line).

Martin

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 01/17] Add param-type-mismatch.c/C testcases as a baseline
  2017-07-24 19:31 ` [PATCH 01/17] Add param-type-mismatch.c/C testcases as a baseline David Malcolm
  2017-07-24 19:56   ` Eric Gallager
@ 2017-09-01 17:00   ` Jeff Law
  1 sibling, 0 replies; 38+ messages in thread
From: Jeff Law @ 2017-09-01 17:00 UTC (permalink / raw)
  To: David Malcolm, gcc-patches

On 07/24/2017 02:04 PM, David Malcolm wrote:
> Amongst other things, this patch kit aims to improve our handling
> of mismatched types within function calls.
Certainly a worthy goal. It's silly that someone trying to parse the
diagnostic we currently give to have to manually walk the arguments and
figure out where the mismatch is.  Underlining the mismatched parameter
at the call site and in the declaration would be a huge step forward.

I'd support getting these tests into the testsuite independent of the
BLT work.  They may need to be xfailed for a period of time.

If you wanted to go forward on that, be my guest.

jeff

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 11/17] Add JSON implementation
  2017-07-24 19:31 ` [PATCH 11/17] Add JSON implementation David Malcolm
@ 2017-09-01 17:06   ` Jeff Law
  0 siblings, 0 replies; 38+ messages in thread
From: Jeff Law @ 2017-09-01 17:06 UTC (permalink / raw)
  To: David Malcolm, gcc-patches

On 07/24/2017 02:05 PM, David Malcolm wrote:
> This patch is the first part of the Language Server Protocol
> server proof-of-concept.
> 
> It adds support to gcc for reading and writing JSON,
> based on DOM-like trees of "json::value" instances.
> 
> gcc/ChangeLog:
> 	* Makefile.in (OBJS): Add json.o.
> 	* json.c: New file.
> 	* json.h: New file.
> 	* selftest-run-tests.c (selftest::run_tests): Call json_c_tests.
> 	* selftest.h (selftest::json_c_tests): New decl.
Didn't someone else already raise the issue of why not just use an
existing json implementation.  I realize we dislike too many external
dependencies.  BUt I also dislike recreating the wheel :-)  These days I
probably dislike the latter more than the former.

I'm not sure if I'm sold on the LSP idea in the kind of timeframes we're
looking at for gcc-8.

jeff

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 12/17] Add server.h and server.c
  2017-07-24 19:31 ` [PATCH 12/17] Add server.h and server.c David Malcolm
  2017-07-26 14:35   ` Oleg Endo
@ 2017-09-01 17:09   ` Jeff Law
  1 sibling, 0 replies; 38+ messages in thread
From: Jeff Law @ 2017-09-01 17:09 UTC (permalink / raw)
  To: David Malcolm, gcc-patches

On 07/24/2017 02:05 PM, David Malcolm wrote:
> This patch adds a "server" abstract base class for listening
> on a port, for use by the later patches to implement an LSP server.
> 
> It's largely adapted from examples in glibc's docs.  I suspect that
> I've introduced platform-specific assumptions (and that it may need some
> extra configure tests for the extra functionality), but this part of
> the kit is just a proof-of-concept.
> 
> gcc/ChangeLog:
> 	* Makefile.in (OBJS): Add server.o.
> 	* server.c: New file.
> 	* server.h: New file.
And this is where I start to get scared :-)  Once folks can start
interacting with GCC over a TCP connection we have to start thinking
much harder about the security ramifications of various hunks of code.

If we end up going down this path at some point, I'd really like to look
for ways to leverage existing code that's already being used in the wild
and hopefully has been through real security audits.

Jeff

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 02/17] diagnostics: support prefixes within diagnostic_show_locus
  2017-07-24 19:31 ` [PATCH 02/17] diagnostics: support prefixes within diagnostic_show_locus David Malcolm
@ 2017-09-01 17:11   ` Jeff Law
  0 siblings, 0 replies; 38+ messages in thread
From: Jeff Law @ 2017-09-01 17:11 UTC (permalink / raw)
  To: David Malcolm, gcc-patches

On 07/24/2017 02:04 PM, David Malcolm wrote:
> This patch reworks diagnostic_show_locus so that it respects
> the prefix of the pretty_printer.
> 
> This is used later in the patch kit for embedding source dumps
> within a hierarchical tree-like dump, by using the ASCII art for the
> tree as the prefix.
> 
> gcc/c-family/ChangeLog:
> 	* c-opts.c (c_diagnostic_finalizer): Move call to
> 	pp_destroy_prefix to before call to diagnostic_show_locus.
> 
> gcc/ChangeLog:
> 	* diagnostic-show-locus.c (layout::print_source_line):
> 	Call pp_emit_prefix.
> 	(layout::should_print_annotation_line_p): Likewise.
> 	(layout::print_leading_fixits): Likewise.
> 	(layout::print_trailing_fixits): Likewise, for first correction.
> 	(layout::print_trailing_fixits): Replace call to move_to_column
> 	with a call to print_newline, to avoid emitting a prefix after
> 	last line.
> 	(layout::move_to_column): Call pp_emit_prefix.
> 	(layout::show_ruler): Likewise.
> 	(diagnostic_show_locus): Remove save/restore of prefix.
> 	(selftest::test_diagnostic_context::test_diagnostic_context): Set
> 	prefixing rule to DIAGNOSTICS_SHOW_PREFIX_NEVER.
> 	(selftest::test_one_liner_fixit_remove): Verify the interaction of
> 	pp_set_prefix with rulers and fix-it hints.
> 	* diagnostic.c (default_diagnostic_start_span_fn): Don't modify
> 	the prefix.
> 
> gcc/testsuite/ChangeLog:
> 	* gcc.dg/plugin/diagnostic_plugin_test_show_locus.c
> 	(custom_diagnostic_finalizer): Reset pp_indentation of printer.
I don't see anything here concerning.  I think it's mostly a matter
running with this when you need it.

jeff

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 03/17] Core of BLT implementation
  2017-07-24 19:31 ` [PATCH 03/17] Core of BLT implementation David Malcolm
@ 2017-09-01 17:32   ` Jeff Law
  0 siblings, 0 replies; 38+ messages in thread
From: Jeff Law @ 2017-09-01 17:32 UTC (permalink / raw)
  To: David Malcolm, gcc-patches

On 07/24/2017 02:05 PM, David Malcolm wrote:
> This patch implements the core of the new "blt" type: an optional
> "on-the-side" hierarchical recording of source ranges, associated
> with grammar productions, with a sparse mapping to our regular
> "tree" type.
So I think one of the big questions here is whether or not BLT hits the
right compromise between the two syntax trees to meet the needs you
envision.  It's an area I'm not really versed in, so I'm probably not a
good judge of whether or not we're on the right track -- though it does
seem to my naive eyes that you need to have most of the tree to do
something like refactoring tools.

> 
> Caveats:
> * the name is a placeholder (see the comment in blt.h)
> * I'm ignoring memory management for now (e.g. how do these get freed?
>   possible a custom arena, or an obstack within a blt_context or
>   somesuch)
Yea, management is a real question.  It obviously depends on the use
case -- ie, are the use cases such that we expect to work on a function
level, then move on to the next function and so-on.  Or is it the case
that we are likely to need to build the BLT for function A, then analyze
some other code in function B, then go back and issue fixit hints for
function A?  ie, what are the expected lifetimes of a BLT.

My understanding is you can have nodes in the BLT pointing to trees and
vice-versa.  This may have implications on memory management :-)

Jeff

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 00/17] RFC: New source-location representation; Language Server Protocol
  2017-07-24 19:31 [PATCH 00/17] RFC: New source-location representation; Language Server Protocol David Malcolm
                   ` (18 preceding siblings ...)
  2017-07-27 19:51 ` Martin Sebor
@ 2017-10-11 19:32 ` David Malcolm
  19 siblings, 0 replies; 38+ messages in thread
From: David Malcolm @ 2017-10-11 19:32 UTC (permalink / raw)
  To: gcc-patches

On Mon, 2017-07-24 at 16:04 -0400, David Malcolm wrote:
> We currently capture some source location information in the
> frontends, but there are many kinds of source entity for which we
> *don't*
> retain the location information after the initial parse.
> 
> For example, in the C/C++ frontends:
> 
> * we don't capture the locations of the individual parameters
>   within e.g. an extern function declaration, so we can't underline
>   the pertinent param when there's a mismatching type in a call to
>   that function decl.
> 
> * we don't capture the locations of attributes of a function,
>   so we can't underline these if they're wrong (e.g. a "noreturn" on
> a
>   function that does in fact return).
> 
> * we don't retain the locations of things like close parens and
>   semicolons for after parsing, so we can't offer fix-it hints for
>   adding new attributes, or, say the C++11 "override" feature.
> 
> * we can't at present implement many kinds of useful "cousins" of a
>   compiler on top of the GCC codebase (e.g. code refactoring tools,
>   code reformatting tools, IDE support daemons, etc), since much of
> the
>   useful location information is discarded at parse time.
> 
> This patch kit implements:
> 
> (a) a new, optional, representation of this location information,
>     enabled by a command-line flag
> 
> (b) improvements to various diagnostics to use this location
> information
>     if it's present, falling back to the status-quo (less accurate)
>     source locations otherwise
> 
> (b) a gcc-based implementation of Microsoft's Language Server
> Protocol,
>       https://github.com/Microsoft/language-server-protocol
>     allowing IDEs to connect to a gcc-based LSP server, making RPC
>     calls to query it for things like "where is this struct
> declared?".
>     This last part is very much just a proof-of-concept.
> 
> 
> ================================
> (a) The new location information
> ================================
> 
> Our existing "tree" type represents a node within an abstract syntax
> tree,
> but this is sometimes too abstract - sometimes we want the locations
> of the clauses and tokens that were abstracted away by the frontends.
> 
> In theory we could generate the full parse tree ("concrete syntax
> tree"),
> showing every production followed to parse the input, but it is
> likely
> to be unwieldy: large and difficult to navigate.
> 
> (aside: I found myself re-reading the Dragon book to refresh my mind
> on exactly what an AST vs a CST is; I also found this blog post to be
> very useful:
>   http://eli.thegreenplace.net/2009/02/16/abstract-vs-concrete-syntax
> -trees )
> 
> So the patch kit implements a middle-ground: an additional tree of
> parse
> information, much more concrete than our "tree" type, but not quite
> the
> full parse tree.
> 
> My working title for this system is "BLT" (and hence "class
> blt_node").
> I could claim that this is a acronym for "bonus location tree" (but
> it's actually a reference to a sandwich) - it needs a name, and that
> name needs to not clash with anything else in the source tree.
> "Parse Tree" would be "PT" which clashes with "points-to", and
> "Concrete Syntax Tree" would be "CST" which clashes with our
> abbreviation
> for "constant".  ("BLT" popped into my mind somewhere between "AST"
> and "CST"; ideas for better names welcome).
> 
> blt_nodes form a tree-like structure; a blt_node has a "kind",
> identifying the terminal or nonterminal it corresponds to
> (e.g. BLT_TRANSLATION_UNIT or BLT_DECLARATION_SPECIFIERS).
> This is just an enum, but allows for language-specific traversals,
> without introducing significant language-specific features in
> the shared "gcc" dir (it's just an enum of IDs).
> 
> There is a partial mapping between "tree" and blt_node: a blt_node
> can reference a tree, and a tree can reference a blt_node, though
> typically the mapping is very sparse; most don't.  This allows us
> to go from e.g. a function_decl in the "tree" world and navigate to
> pertinent parts of the syntax that was used to declare it.
> 
> All of this is enabled by a new "-fblt" command-line option; in the
> absense of -fblt, almost all of it becomes close to no-ops, and the
> relevant diagnostics fall back to using less accurate location
> information.
> 
> So it's a kind of optional, "on-the-side" record of how we parsed
> the source, with a sparse relationship to our tree type.
> 
> The patch kit implements it for the C and C++ frontends.
> 
> An example of a BLT dump for a C file can be seen here:
>   https://dmalcolm.fedorapeople.org/gcc/2017-07-24/fdump-blt.html
> It shows the tree structure using indentation (and colorization);
> source locations are printed, and, for each node where the
> location is different from the parent, the pertinent source range
> is printed and underlined inline.
> (BTW, does the colorization of dumps look useful for other
> dump formats?  similarly for the ASCII art for printing hierarchies)
> 
> 
> =====================
> (b) Examples of usage
> =====================
> 
> Patches 6-10 in the kit update various diagnostics to use
> the improved location information where available:
> 
> * C and C++: highlighting the pertinent parameter of a function
>   decl when there's a mismatched type in a call
> 
> * C and C++: highlighting the return type in the function defn
>   when compaining about mismatch in the body (e.g. highlighting
>   the "void" when we see a "return EXPR;" within a void function).
> 
> * C++: add a fix-it hint to -Wsuggest-override
> 
> I have plenty of ideas for other uses of this infrastructure
> (but which aren't implemented yet), e.g.:
> 
> * C++: highlight the "const" token (or suggest a fix-it hint)
>   when you have a missing "const" on the *definition* of a member
>   function that was declared as "const" (I make this mistake
>   all the time).
> 
> * C++: add a fix-it hint to -Wsuggest-final-methods
> 
> * highlight bogus attributes
> 
> * add fix-it hints suggesting missing attributes
> 
> ...etc, plus those "cousins of a compiler" ideas mentioned above.
> 
> Any other ideas?
> 
> 
> ============================
> (c) Language Server Protocol
> ============================
> 
> The later parts of the patch kit implement a proof-of-concept
> LSP server, making use of the extended location information,
> exposing it to IDEs.
> 
> LSP is an RPC protocol layered on top of JSON-RPC (and hence JSON
> and HTTP):
>   https://github.com/Microsoft/language-server-protocol
> so the patch kit implements a set of classes to support
> this (including a barebones HTTP server running inside cc1), and
> a toy IDE written in PyGTK to test it.
> 
> 
> =======
> Caveats
> =======
> 
> * There are plenty of FIXMEs and TODOs in the patch kit.
> 
> * I've entirely ignored tentative parsing in the C++ frontend for
> now.
> 
> * I haven't attempted to optimize it at all yet (so no performance
>   measurements yet).
> 
> * How much of the syntax tree ought to be captured?  I've focussed on
> the
>   stuff outside of function bodies, since we don't currently capture
> that
>   well, but to do "proper" IDE support we'd want to capture things
> more
>   deeply.  (I experimented with using it to fix some of our missing
>   location information for things like uses of constants and
> variables
>   as arguments at callsites, but it quickly turned into a much more
>   invasive patch).
> 
> * The LSP implementation is a just a proof-of-concept, to further
>   motivate capturing the extra data.  Turning it into a "proper" LSP
>   server implementation would be a *lot* more work, and I'm unlikely
> to
>   actually do that (but maybe someone on the list wants to take this
> on?)
> 
> I've successfully bootstrapped&regrtested the combination of the
> patches
> on x86_64-pc-linux-gnu; takes -fself-test from 39458 passes to 41574;
> adds 30 PASS results to gcc.sum; adds 182 PASS results to g++.sum.
> 
> Thoughts?
> Dave
> 
> David Malcolm (17):
>   Add param-type-mismatch.c/C testcases as a baseline
>   diagnostics: support prefixes within diagnostic_show_locus
>   Core of BLT implementation
>   C frontend: capture BLT information
>   C++ frontend: capture BLT information
>   C: use BLT to highlight parameter of callee decl for mismatching
> types
>   C++: use BLT to highlight parameter of callee decl for mismatching
>     types
>   C: highlight return types when complaining about mismatches
>   C++: highlight return types when complaining about mismatches
>   C++: provide fix-it hints in -Wsuggest-override
>   Add JSON implementation
>   Add server.h and server.c
>   Add http-server.h and http-server.c
>   Add implementation of JSON-RPC
>   Language Server Protocol: add lsp::server abstract base class
>   Language Server Protocol: proof-of-concept GCC implementation
>   Language Server Protocol: work-in-progess on testsuite

[...snip...]

When I presented this at Cauldron, both Jason and Nathan were unhappy
with this proposal: rather than adding a new on-the-side
representation, they both wanted me to use the old representation,
fixing and extending it as necessary (I hope I'm correctly
characterizing their opinion).

Jason also pointed out a way to achieve my #1 use case for this via the
existing representation (highlighting the pertinent parameter of a
function decl when there's a mismatched type in a call); I've
implemented his approach for this in trunk (r253411 for C and r253096
for C++).

Given the above, it seems unlikely that this will go forward at this
time.

In case someone wants to tackle this, I've archived the patches into
our git mirror, as git-only branches:

dmalcolm/lsp-v1.0 has the patches I posted (and a little extra):
https://gcc.gnu.org/git/?p=gcc.git;a=shortlog;h=refs/heads/dmalcolm/lsp-v1.0


dmalcolm/lsp-v1.3 has the latest state of the extra work I did after posting:
https://gcc.gnu.org/git/?p=gcc.git;a=shortlog;h=refs/heads/dmalcolm/lsp-v1.3


Dave

^ permalink raw reply	[flat|nested] 38+ messages in thread

end of thread, other threads:[~2017-10-11 18:52 UTC | newest]

Thread overview: 38+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-07-24 19:31 [PATCH 00/17] RFC: New source-location representation; Language Server Protocol David Malcolm
2017-07-24 19:31 ` [PATCH 03/17] Core of BLT implementation David Malcolm
2017-09-01 17:32   ` Jeff Law
2017-07-24 19:31 ` [PATCH 15/17] Language Server Protocol: add lsp::server abstract base class David Malcolm
2017-07-27  4:14   ` Trevor Saunders
2017-07-28 14:48     ` David Malcolm
2017-07-27  7:55   ` Richard Biener
2017-07-28 16:02     ` David Malcolm
2017-07-24 19:31 ` [PATCH 02/17] diagnostics: support prefixes within diagnostic_show_locus David Malcolm
2017-09-01 17:11   ` Jeff Law
2017-07-24 19:31 ` [PATCH 12/17] Add server.h and server.c David Malcolm
2017-07-26 14:35   ` Oleg Endo
2017-07-26 14:50     ` David Malcolm
2017-07-26 21:00       ` Mike Stump
2017-09-01 17:09   ` Jeff Law
2017-07-24 19:31 ` [PATCH 10/17] C++: provide fix-it hints in -Wsuggest-override David Malcolm
2017-07-24 19:31 ` [PATCH 11/17] Add JSON implementation David Malcolm
2017-09-01 17:06   ` Jeff Law
2017-07-24 19:31 ` [PATCH 01/17] Add param-type-mismatch.c/C testcases as a baseline David Malcolm
2017-07-24 19:56   ` Eric Gallager
2017-09-01 17:00   ` Jeff Law
2017-07-24 19:31 ` [PATCH 17/17] Language Server Protocol: work-in-progess on testsuite David Malcolm
2017-07-24 19:31 ` [PATCH 13/17] Add http-server.h and http-server.c David Malcolm
2017-07-24 19:38 ` [PATCH 07/17] C++: use BLT to highlight parameter of callee decl for mismatching types David Malcolm
2017-07-24 19:38 ` [PATCH 14/17] Add implementation of JSON-RPC David Malcolm
2017-07-24 19:38 ` [PATCH 06/17] C: use BLT to highlight parameter of callee decl for mismatching types David Malcolm
2017-07-24 19:39 ` [PATCH 08/17] C: highlight return types when complaining about mismatches David Malcolm
2017-07-24 19:39 ` [PATCH 09/17] C++: " David Malcolm
2017-07-24 19:39 ` [PATCH 04/17] C frontend: capture BLT information David Malcolm
2017-07-27 19:58   ` Martin Sebor
2017-07-24 19:40 ` [PATCH 05/17] C++ " David Malcolm
2017-07-24 19:41 ` [PATCH 16/17] Language Server Protocol: proof-of-concept GCC implementation David Malcolm
2017-07-26 17:10 ` [PATCH 00/17] RFC: New source-location representation; Language Server Protocol Jim Wilson
2017-07-28  5:12   ` Alexandre Oliva
2017-07-27 19:51 ` Martin Sebor
2017-07-28 14:28   ` David Malcolm
2017-07-28 18:32     ` Martin Sebor
2017-10-11 19:32 ` David Malcolm

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).