From 9d3081747187958ec635d81e5b00721cea2b5d95 Mon Sep 17 00:00:00 2001 From: David Malcolm Date: Wed, 8 Apr 2026 09:20:37 -0400 Subject: [PATCH 39/48] FIXME: got "textDocument/declaration" working for simple cases --- gcc/c/c-parser.cc | 22 +++ gcc/diagnostics/source-printing.cc | 4 +- gcc/json.h | 5 + gcc/lsp/gcc-impl/worker.cc | 135 +++++++++++++++++-- gcc/lsp/json-rpc.cc | 3 + gcc/lsp/lsp-spec.cc | 207 ++++++++++++++++++++++++++++- gcc/lsp/lsp-spec.h | 28 ++++ gcc/topics/ast-events-recorder.h | 11 +- gcc/topics/ast-events.h | 6 + 9 files changed, 406 insertions(+), 15 deletions(-) diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc index ac5de663655..661474b1a73 100644 --- a/gcc/c/c-parser.cc +++ b/gcc/c/c-parser.cc @@ -7231,6 +7231,7 @@ c_parser_initval (c_parser *parser, struct c_expr *after, static tree c_parser_compound_statement (c_parser *parser, location_t *endlocp) { + auto_notify_ast_node sentinel (parser, "compound-statement"); tree stmt; location_t brace_loc; brace_loc = c_parser_peek_token (parser)->location; @@ -8158,6 +8159,7 @@ mangle_metadirective_region_label (c_parser *parser, tree name) static void c_parser_label (c_parser *parser, tree std_attrs) { + auto_notify_ast_node sentinel (parser, "label"); location_t loc1 = c_parser_peek_token (parser)->location; tree label = NULL_TREE; @@ -8988,6 +8990,7 @@ c_parser_if_statement (c_parser *parser, bool *if_p, vec *chain) static void c_parser_switch_statement (c_parser *parser, bool *if_p, tree before_labels) { + auto_notify_ast_node sentinel (parser, "switch-statement"); struct c_expr ce; tree block, expr, body; unsigned char save_in_statement; @@ -9070,6 +9073,7 @@ static void c_parser_while_statement (c_parser *parser, bool ivdep, unsigned short unroll, bool novector, bool *if_p, tree before_labels) { + auto_notify_ast_node sentinel (parser, "while-statement"); tree block, cond, body; unsigned char save_in_statement; location_t loc; @@ -9151,6 +9155,7 @@ static void c_parser_do_statement (c_parser *parser, bool ivdep, unsigned short unroll, bool novector, tree before_labels) { + auto_notify_ast_node sentinel (parser, "do-statement"); tree block, cond, body; unsigned char save_in_statement; location_t loc; @@ -9277,6 +9282,7 @@ static void c_parser_for_statement (c_parser *parser, bool ivdep, unsigned short unroll, bool novector, bool *if_p, tree before_labels) { + auto_notify_ast_node sentinel (parser, "for-statement"); tree block, cond, incr, body; unsigned char save_in_statement; tree save_objc_foreach_break_label, save_objc_foreach_continue_label; @@ -9879,6 +9885,7 @@ c_parser_asm_goto_operands (c_parser *parser) struct c_expr c_parser_string_literal (c_parser *parser, bool translate, bool wide_ok) { + auto_notify_ast_node sentinel (parser, "string-literal"); struct c_expr ret; size_t count; struct obstack str_ob; @@ -10170,6 +10177,7 @@ static struct c_expr c_parser_conditional_expression (c_parser *parser, struct c_expr *after, tree omp_atomic_lhs) { + auto_notify_ast_node sentinel (parser, "conditional-statement"); struct c_expr cond, exp1, exp2, ret; location_t start, cond_loc, colon_loc; bool save_c_omp_array_section_p = c_omp_array_section_p; @@ -10657,6 +10665,7 @@ c_parser_compound_literal_scspecs (c_parser *parser) static struct c_expr c_parser_cast_expression (c_parser *parser, struct c_expr *after) { + auto_notify_ast_node sentinel (parser, "cast-expression"); location_t cast_loc = c_parser_peek_token (parser)->location; gcc_assert (!after || c_dialect_objc ()); if (after) @@ -10760,6 +10769,7 @@ c_parser_cast_expression (c_parser *parser, struct c_expr *after) static struct c_expr c_parser_unary_expression (c_parser *parser) { + auto_notify_ast_node sentinel (parser, "unary-expression"); int ext; struct c_expr ret, op; location_t op_loc = c_parser_peek_token (parser)->location; @@ -11790,6 +11800,14 @@ get_counted_by_ref (tree ref) return NULL_TREE; } +static void +maybe_set_tree_for_ast_node (c_parser *parser, tree expr) +{ + if (parser->ast_events_channel) + parser->ast_events_channel->publish + (gcc::topics::ast_events::set_tree ({expr})); +} + /* Parse a postfix expression (C90 6.3.1-6.3.2, C99 6.5.1-6.5.2, C11 6.5.1-6.5.2). Compound literals aren't handled here; callers have to call c_parser_postfix_expression_after_paren_type on encountering them. @@ -11858,6 +11876,7 @@ get_counted_by_ref (tree ref) static struct c_expr c_parser_postfix_expression (c_parser *parser) { + auto_notify_ast_node sentinel (parser, "postfix-expression"); struct c_expr expr, e1; struct c_type_name *t1, *t2; location_t loc = c_parser_peek_token (parser)->location; @@ -11912,12 +11931,15 @@ c_parser_postfix_expression (c_parser *parser) { case C_ID_ID: { + auto_notify_ast_node sentinel (parser, "identifier"); tree id = c_parser_peek_token (parser)->value; c_parser_consume_token (parser); expr.value = build_external_ref (loc, id, (c_parser_peek_token (parser)->type == CPP_OPEN_PAREN), &expr.original_type); + + maybe_set_tree_for_ast_node (parser, expr.value); set_c_expr_source_range (&expr, tok_range); break; } diff --git a/gcc/diagnostics/source-printing.cc b/gcc/diagnostics/source-printing.cc index db9b3c1f241..66c47413bea 100644 --- a/gcc/diagnostics/source-printing.cc +++ b/gcc/diagnostics/source-printing.cc @@ -1094,7 +1094,7 @@ layout_range::layout_range (const exploc_with_display_col &start_exploc, start: (col=22, line=2) finish: (col=38, line=2) - |00000011111111112222222222333333333344444444444 + |00000001111111111222222222233333333334444444444 |34567890123456789012345678901234567890123456789 --+----------------------------------------------- 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb @@ -1105,7 +1105,7 @@ layout_range::layout_range (const exploc_with_display_col &start_exploc, start: (col=14, line=3) finish: (col=08, line=5) - |00000011111111112222222222333333333344444444444 + |00000001111111111222222222233333333334444444444 |34567890123456789012345678901234567890123456789 --+----------------------------------------------- 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb diff --git a/gcc/json.h b/gcc/json.h index 54bc00e964e..c3353e9d7b1 100644 --- a/gcc/json.h +++ b/gcc/json.h @@ -382,6 +382,11 @@ class literal : public value enum kind m_kind; }; +inline std::unique_ptr +make_null () +{ + return std::make_unique (JSON_NULL); +} template inline bool diff --git a/gcc/lsp/gcc-impl/worker.cc b/gcc/lsp/gcc-impl/worker.cc index 3d48de308e8..3569001e08c 100644 --- a/gcc/lsp/gcc-impl/worker.cc +++ b/gcc/lsp/gcc-impl/worker.cc @@ -48,6 +48,8 @@ along with GCC; see the file COPYING3. If not see // FIXME: extern struct cpp_reader* parse_in; +using ast_node = gcc::topics::ast_events::recorder::saved_node; + // class lsp::gcc_impl::worker::mediator : public lsp::log_user lsp::gcc_impl::worker::mediator::mediator (lsp::logger *logger) @@ -110,7 +112,23 @@ lsp::gcc_impl::worker::mediator::handle_events_until_didOpen () if (m_filename_from_didOpen) return m_filename_from_didOpen; - } + } +} + +static void +fixup_ast (ast_node &root_node) +{ + if (root_node.m_kind == nullptr + && root_node.m_children.size () == 1) + { + auto only_child = root_node.m_children[0].release (); + /* Make this be the root instead. */ + root_node = std::move (*only_child); + + if (root_node.m_end_loc == UNKNOWN_LOCATION + && root_node.m_children.size () > 0) + root_node.m_end_loc = root_node.m_children.back ()->m_end_loc; + } } void @@ -125,6 +143,8 @@ handle_events_after_compile (lsp::frontend_worker_interface *frontend) m_frontend = frontend; + fixup_ast (m_recorder.m_root_node); + // FIXME: dump_ast (); @@ -148,7 +168,7 @@ handle_events_after_compile (lsp::frontend_worker_interface *frontend) evp.add_endpoint (&m_server_ipc_connection.m_incoming_receiver); evp.add_endpoint (&m_server_ipc_connection.m_outgoing_sender); evp.handle_pending_events (true); - } + } } void @@ -335,13 +355,43 @@ lsp::gcc_impl::worker::mediator:: on_request_textDocument_declaration (std::unique_ptr req_id, spec::DeclarationParams &&p) { - LSP_LOG_SCOPE (get_logger ()); + auto logger = get_logger (); + LSP_LOG_SCOPE (logger); gcc_assert (m_frontend); - auto ast_node = get_ast_node_at_position (p); + if (auto ast_node = get_ast_node_at_position (p)) + { + // FIXME: + if (0) + { + fprintf (stderr, "\nnode at position: \n"); + visit_node (*ast_node); + } + if (ast_node->m_expr) + { + if (DECL_P (ast_node->m_expr)) + { + location_t loc = DECL_SOURCE_LOCATION (ast_node->m_expr); + auto result (lsp::spec::Location::from_location_t (loc)); + send_response_to_server + (json_rpc::response::success (std::move (req_id), + result.to_json ())); + return; + } + } + else + { + if (logger) + logger->log ("ast node %qs has no tree node", ast_node->m_kind); + } + } - gcc_unreachable (); // FIXME + if (logger) + logger->log ("ast node not found"); + send_response_to_server + (json_rpc::response::success (std::move (req_id), + json::make_null ())); } void @@ -422,28 +472,91 @@ lsp::gcc_impl::worker::mediator::dump_ast () /* FIXME: this will send it as a diagnostic, which is probably not want we want, as it will get intercepted. */ // might need a custom context and sink? - + auto_diagnostic_group d; inform (UNKNOWN_LOCATION, "AST nodes"); for (auto &iter : m_recorder.m_root_node.m_children) visit_node (*iter); } +// FIXME: move to header? + +template +class lsp_element : public pp_element +{ +public: + lsp_element (const T &val) : m_val (val) {} + + void + add_to_phase_2 (pp_markup::context &ctxt) final override + { + auto jv = m_val.to_json (); + jv->print (&ctxt.m_pp, false); + } + +private: + T m_val; +}; + void lsp::gcc_impl::worker::mediator:: -visit_node (const gcc::topics::ast_events::recorder::saved_node &node) +visit_node (const ast_node &node) { // FIXME: see notes for dump_ast auto_diagnostic_nesting_level sentinel; - inform (node.m_start_loc, "start of %qs", node.m_kind); + const char *kind = node.m_kind ? node.m_kind : "NULL"; + lsp_element e + (lsp::spec::Range::from_location_t_pair (node.m_start_loc, + node.m_end_loc)); + if (node.m_expr) + inform (node.m_start_loc, "start of %qs: %qE: %e", kind, node.m_expr, &e); + else + inform (node.m_start_loc, "start of %qs: %e", kind, &e); for (auto &iter : node.m_children) visit_node (*iter); - inform (node.m_end_loc, "end of %qs", node.m_kind); + if (node.m_expr) + inform (node.m_end_loc, "end of %qs: %qE: %e", kind, node.m_expr, &e); + else + inform (node.m_end_loc, "end of %qs: %e", kind, &e); } -const gcc::topics::ast_events::recorder::saved_node * +static bool +node_contains_p (const ast_node &node, + const lsp::spec::TextDocumentPositionParams &p) +{ + lsp::spec::Range range_of_node + = lsp::spec::Range::from_location_t_pair (node.m_start_loc, + node.m_end_loc); + return range_of_node.contains_p (p.position); +} + +const ast_node * lsp::gcc_impl::worker::mediator:: get_ast_node_at_position (const lsp::spec::TextDocumentPositionParams &p) { - gcc_unreachable (); // FIXME + const ast_node *iter_node = &m_recorder.m_root_node; + + // FIXME: + if (0) + visit_node (m_recorder.m_root_node); + + /* Try to find the deepest descendent of the root node that + encloses the point. */ + const ast_node *current_node = &m_recorder.m_root_node; + while (true) + { + if (!node_contains_p (*current_node, p)) + return nullptr; // Something has gone wrong + + iterate: + for (auto &child_iter : current_node->m_children) + { + if (node_contains_p (*child_iter, p)) + { + current_node = child_iter.get (); + goto iterate; + } + } + return current_node; + } } diff --git a/gcc/lsp/json-rpc.cc b/gcc/lsp/json-rpc.cc index 3f3ce881469..7765054576d 100644 --- a/gcc/lsp/json-rpc.cc +++ b/gcc/lsp/json-rpc.cc @@ -104,8 +104,11 @@ json_rpc::response json_rpc::response::success (std::unique_ptr req_id, std::unique_ptr result) { + gcc_assert (req_id); + gcc_assert (result); return response (std::move (result), std::move (req_id)); } + /* A C++ class for a JSON-RPC response. */ json_rpc::error diff --git a/gcc/lsp/lsp-spec.cc b/gcc/lsp/lsp-spec.cc index d5718bf55fe..e59d36475f7 100644 --- a/gcc/lsp/lsp-spec.cc +++ b/gcc/lsp/lsp-spec.cc @@ -1,5 +1,5 @@ /* Language Server Protocol implementation. - Copyright (C) 2017-2026 Free Software Foundation, Inc. + Copyright (C) 2015-2026 Free Software Foundation, Inc. Contributed by David Malcolm . This file is part of GCC. @@ -509,6 +509,12 @@ lsp::spec::Range::from_location_t (location_t loc) { location_t start = get_start (loc); location_t finish = get_finish (loc); + return from_location_t_pair (start, finish); +} + +lsp::spec::Range +lsp::spec::Range::from_location_t_pair (location_t start, location_t finish) +{ expanded_location exp_loc_start = expand_location (start); expanded_location exp_loc_finish = expand_location (finish); @@ -522,6 +528,100 @@ lsp::spec::Range::from_location_t (location_t loc) return result; } +/* Compare with layout_range::contains_point, except that here + ranges are half-open, and we don't have column units. + // FIXME: maybe we do? + + Example A: a single-line range: + start: (col=22, line=2) + end: (col=39, line=2) + + |00000001111111111222222222233333333334444444444 + |34567890123456789012345678901234567890123456789 +--+----------------------------------------------- +01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +02|bbbbbbbbbbbbbbbbbbbSwwwwwwwwwwwwwwwFEaaaaaaaaaa +03|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + + Example B: a multiline range with + start: (col=14, line=3) + end: (col=09, line=5) + + |00000001111111111222222222233333333334444444444 + |34567890123456789012345678901234567890123456789 +--+----------------------------------------------- +01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +02|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +03|bbbbbbbbbbbSwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww +04|wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww +05|wwwwwFEaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +06|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +--+----------------------------------------------- + + Legend: + - 'b' indicates a point *before* the range + - 'S' indicates the start of the range + - 'w' indicates a point within the range + - 'F' indicates the finish of the range (which is + within it). + - 'E' indicates the end of the range (which is immediately + after it). + - 'a' indicates a subsequent point *after* the range. */ + +// FIXME: unit tests + +bool +lsp::spec::Range::contains_p (const lsp::spec::Position &p) const +{ + gcc_assert (start.line <= end.line); + + if (p.line < 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 (p.line == start.line) + { + /* On same line as start of range (corresponding + to line 02 in example A and line 03 in example B). */ + if (p.character < start.character) + /* Points on the starting line of the range, but + before the character in which it begins. */ + return false; + if (p.line < end.line) + /* This is a multiline range; the point + is within it (corresponds to line 03 in example B + from character 14 onwards) */ + return true; + /* This is a single-line range. */ + gcc_assert (p.line == end.line); + return p.character < end.character; + } + + /* 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 (p.line > start.line); + + if (p.line > end.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 (p.line < end.line) + { + /* The point is in a line that's fully within a multiline + range (e.g. line 04 in example B). */ + gcc_assert (start.line < end.line); + return true; + } + + gcc_assert (p.line == end.line); + + return p.character < end.character; +} + /* struct Location. */ BEGIN_DEFINE_TO_JSON(Location) @@ -903,6 +1003,109 @@ test_unmarshal_DocumentUri_ok () ASSERT_STREQ (r.take_value ().m_val.c_str (), "foo.c"); } +static void +test_range_contains_p_for_single_point () +{ + lsp::spec::Range r {{7, 10}, {7, 11}}; + + using Position = lsp::spec::Position; + + /* Before the line. */ + ASSERT_FALSE (r.contains_p (Position {6, 1})); + + /* On the line, but before start. */ + ASSERT_FALSE (r.contains_p (Position {7, 9})); + + /* At the point. */ + ASSERT_TRUE (r.contains_p (Position {7, 10})); + + /* On the line, after the point. */ + ASSERT_FALSE (r.contains_p (Position {7, 11})); + + /* After the line. */ + ASSERT_FALSE (r.contains_p (Position {8, 1})); +} + +static void +test_range_contains_p_for_single_line () +{ + lsp::spec::Range example_a {{2, 22}, {2, 39}}; + + using Position = lsp::spec::Position; + + /* Before the line. */ + ASSERT_FALSE (example_a.contains_p (Position {1, 1})); + + /* On the line, but before start. */ + ASSERT_FALSE (example_a.contains_p (Position {2, 21})); + + /* On the line, at the start. */ + ASSERT_TRUE (example_a.contains_p (Position {2, 22})); + + /* On the line, within the range. */ + ASSERT_TRUE (example_a.contains_p (Position {2, 23})); + + /* On the line, at the end. */ + ASSERT_TRUE (example_a.contains_p (Position {2, 38})); + + /* On the line, after the end. */ + ASSERT_FALSE (example_a.contains_p (Position {2, 39})); + + /* After the line. */ + ASSERT_FALSE (example_a.contains_p (Position {3, 39})); +} + +static void +test_range_contains_p_for_multiple_lines () +{ + lsp::spec::Range example_b {{3, 14}, {5, 9}}; + + using Position = lsp::spec::Position; + + /* Before first line. */ + ASSERT_FALSE (example_b.contains_p (Position {1, 1})); + + /* On the first line, but before start. */ + ASSERT_FALSE (example_b.contains_p (Position {3, 13})); + + /* At the start. */ + ASSERT_TRUE (example_b.contains_p (Position {3, 14})); + + /* On the first line, within the range. */ + ASSERT_TRUE (example_b.contains_p (Position {3, 15})); + + /* On an interior line. + The column number should not matter; try various boundary + values. */ + ASSERT_TRUE (example_b.contains_p (Position {4, 1})); + ASSERT_TRUE (example_b.contains_p (Position {4, 7})); + ASSERT_TRUE (example_b.contains_p (Position {4, 8})); + ASSERT_TRUE (example_b.contains_p (Position {4, 9})); + ASSERT_TRUE (example_b.contains_p (Position {4, 13})); + ASSERT_TRUE (example_b.contains_p (Position {4, 14})); + ASSERT_TRUE (example_b.contains_p (Position {4, 15})); + + /* On the final line, before the end. */ + ASSERT_TRUE (example_b.contains_p (Position {5, 7})); + + /* On the final line, at the end. */ + ASSERT_TRUE (example_b.contains_p (Position {5, 8})); + + /* On the final line, after the end. */ + ASSERT_FALSE (example_b.contains_p (Position {5, 9})); + + /* After the line. */ + ASSERT_FALSE (example_b.contains_p (Position {6, 1})); +} + +static void +test_range_contains_p () +{ + test_range_contains_p_for_single_point (); + test_range_contains_p_for_single_line (); + test_range_contains_p_for_multiple_lines (); +} + static void test_unmarshal_Position_missing_params () { @@ -955,11 +1158,13 @@ lsp_spec_cc_tests () test_unmarshal_integer_ok (); test_unmarshal_DocumentUri_not_a_string (); test_unmarshal_DocumentUri_ok (); + test_range_contains_p (); test_unmarshal_Position_missing_params (); #if 0 test_unmarshal_TextDocumentPositionParams_error (); #endif test_unmarshal_TextDocumentPositionParams_ok (); + } } // namespace selftest diff --git a/gcc/lsp/lsp-spec.h b/gcc/lsp/lsp-spec.h index 99f41afab15..88dc6b2ec0a 100644 --- a/gcc/lsp/lsp-spec.h +++ b/gcc/lsp/lsp-spec.h @@ -229,6 +229,28 @@ struct uinteger DECLARE_JSON_METHODS(uinteger) uinteger () : m_val (0) {} + uinteger (unsigned val) : m_val (val) {} + + bool + operator< (const uinteger &other) const + { + return m_val < other.m_val; + } + bool + operator<= (const uinteger &other) const + { + return m_val <= other.m_val; + } + bool + operator== (const uinteger &other) const + { + return m_val == other.m_val; + } + bool + operator> (const uinteger &other) const + { + return m_val > other.m_val; + } unsigned m_val; }; @@ -361,6 +383,12 @@ struct Range static Range from_location_t (location_t); + static Range + from_location_t_pair (location_t start, location_t finish); + + bool + contains_p (const Position &p) const; + Position start; Position end; }; diff --git a/gcc/topics/ast-events-recorder.h b/gcc/topics/ast-events-recorder.h index e831bcae4f9..0b37e6997bf 100644 --- a/gcc/topics/ast-events-recorder.h +++ b/gcc/topics/ast-events-recorder.h @@ -36,12 +36,14 @@ public: saved_node (const char *kind, location_t start_loc) : m_kind (kind), m_start_loc (start_loc), - m_end_loc (UNKNOWN_LOCATION) + m_end_loc (UNKNOWN_LOCATION), + m_expr (nullptr) {} const char *m_kind; location_t m_start_loc; location_t m_end_loc; + tree m_expr; // FIXME: GC integration? std::vector> m_children; }; @@ -60,6 +62,13 @@ public: prev_top_of_stack->m_children.push_back (std::move (new_node)); } + void on_message (const ast_events::set_tree &m) final override + { + gcc_assert (!m_stack.empty ()); + auto top_of_stack = m_stack.back (); + top_of_stack->m_expr = m.expr; + } + void on_message (const ast_events::end_node &m) final override { gcc_assert (!m_stack.empty ()); diff --git a/gcc/topics/ast-events.h b/gcc/topics/ast-events.h index ba7ebe42ff2..0d3bf829715 100644 --- a/gcc/topics/ast-events.h +++ b/gcc/topics/ast-events.h @@ -35,6 +35,11 @@ struct begin_node location_t loc; }; +struct set_tree +{ + tree expr; +}; + struct end_node { const char *kind; @@ -45,6 +50,7 @@ struct subscriber { virtual ~subscriber () {} virtual void on_message (const begin_node &) {} + virtual void on_message (const set_tree &) {} virtual void on_message (const end_node &) {} }; -- 2.49.0