From 5923a7f7516b57049ef4f62f7902990fcf31b163 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Fri, 19 May 2017 20:55:35 -0400
Subject: [PATCH 20/22] FIXME: enough to get find-defn-of-struct working

---
 gcc/blt.c        | 129 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 gcc/blt.def      |   3 ++
 gcc/blt.h        |  23 ++++++++++
 gcc/c/c-parser.c |  17 ++++++++
 gcc/cp/parser.c  |   2 +
 gcc/lsp-main.c   |  97 ++++++++++++++++++++++++++++++++++++++---
 6 files changed, 265 insertions(+), 6 deletions(-)

diff --git a/gcc/blt.c b/gcc/blt.c
index 07b7819..d92149b 100644
--- a/gcc/blt.c
+++ b/gcc/blt.c
@@ -290,6 +290,19 @@ blt_node::get_children_of_kind (auto_vec<blt_node *> &out,
       out.safe_push (child);
 }
 
+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;
+}
+
 /* FIXME.  */
 
 blt_node *
@@ -303,6 +316,120 @@ blt_node::get_ancestor_of_kind (enum blt_kind kind) const
 
 /* FIXME.  */
 
+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;
+}
+
+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;
+};
+
+/* FIXME.  */
+
+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);
+}
+
+/* FIXME.  */
+
+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);
+
+  /* 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;
+}
+
+/* FIXME.  */
+
 void
 blt_node::assert_invariants () const
 {
@@ -381,3 +508,5 @@ debug (blt_node *node)
 {
   node->dump (stderr);
 }
+
+blt_node *the_blt_root_node;
diff --git a/gcc/blt.def b/gcc/blt.def
index f1f3795..84b0c17 100644
--- a/gcc/blt.def
+++ b/gcc/blt.def
@@ -47,6 +47,7 @@ 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")
@@ -72,6 +73,8 @@ 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_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")
diff --git a/gcc/blt.h b/gcc/blt.h
index 909dd4e..b253938 100644
--- a/gcc/blt.h
+++ b/gcc/blt.h
@@ -54,6 +54,16 @@ enum blt_kind
 #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.  */
 
@@ -91,8 +101,18 @@ public:
   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;
 
@@ -119,4 +139,7 @@ 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/c-parser.c b/gcc/c/c-parser.c
index 5c19a34..0150a6a 100644
--- a/gcc/c/c-parser.c
+++ b/gcc/c/c-parser.c
@@ -2486,6 +2486,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;
 
@@ -2971,6 +2973,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;
@@ -3006,6 +3010,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;
@@ -3114,6 +3120,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)
@@ -3125,7 +3135,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;
 }
 
@@ -18309,6 +18324,8 @@ c_parse_file (void)
   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.c b/gcc/cp/parser.c
index b81c94d..1700a4e 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -38771,6 +38771,8 @@ c_parse_file (void)
   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/lsp-main.c b/gcc/lsp-main.c
index 7866307..e971fc1 100644
--- a/gcc/lsp-main.c
+++ b/gcc/lsp-main.c
@@ -24,9 +24,54 @@ along with GCC; see the file COPYING3.  If not see
 #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;
+  out.range.start.line = el_start.line;
+  out.range.start.character = el_start.column;
+  out.range.end.line = el_finish.line;
+  out.range.end.character = el_finish.column;
+  return true;
+}
+
 /* Implementation of lsp::server.
    Ideally we'd wire this up to GCC's IR.  */
 
@@ -40,12 +85,52 @@ class gcc_lsp_server : public lsp::noop_server
 			       vec<Location> &out) OVERRIDE
   {
     /* TODO.  */
-    /* FIXME: currently, all we do is echo back the input location.  */
-    Location result;
-    result.uri = p.textDocument.uri;
-    result.range.start = p.position;
-    result.range.end = p.position;
-    out.safe_push (result);
+    blt_node *blt
+      = the_blt_root_node->get_descendant_at_location (p.textDocument.uri,
+						       p.position.line,
+						       p.position.character);
+    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:
+	/* No results.  */
+	return;
+      }
+
+
   }
 };
 
-- 
1.8.5.3

