diff mbox

[16/17] Language Server Protocol: proof-of-concept GCC implementation

Message ID 1500926714-56988-17-git-send-email-dmalcolm@redhat.com
State New
Headers show

Commit Message

David Malcolm July 24, 2017, 8:05 p.m. UTC
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 mbox

Patch

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