[torque-ls] Add "goto Definition" support for labels of goto statements

This CL adds navigation support for labels in "goto" statements.
Similar to labels listed in the "otherwise" clause of call expression,
definitions of such a label can be found in two places:
  - The signature of the current macro.
  - A label block of a "try" statement that surrounds the "goto".

R=sigurds@chromium.org

Bug: v8:8880
Change-Id: I6c5ebea0b0f80b1882e6672bbb0f45196a7201ba
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1594433
Commit-Queue: Simon Zünd <szuend@chromium.org>
Reviewed-by: Sigurd Schneider <sigurds@chromium.org>
Cr-Commit-Position: refs/heads/master@{#61229}
diff --git a/src/torque/ast.h b/src/torque/ast.h
index 7e255c6..f785adf 100644
--- a/src/torque/ast.h
+++ b/src/torque/ast.h
@@ -569,12 +569,10 @@
 
 struct GotoStatement : Statement {
   DEFINE_AST_NODE_LEAF_BOILERPLATE(GotoStatement)
-  GotoStatement(SourcePosition pos, std::string label,
+  GotoStatement(SourcePosition pos, Identifier* label,
                 const std::vector<Expression*>& arguments)
-      : Statement(kKind, pos),
-        label(std::move(label)),
-        arguments(std::move(arguments)) {}
-  std::string label;
+      : Statement(kKind, pos), label(label), arguments(std::move(arguments)) {}
+  Identifier* label;
   std::vector<Expression*> arguments;
 };
 
diff --git a/src/torque/implementation-visitor.cc b/src/torque/implementation-visitor.cc
index 10de668..5353afd 100644
--- a/src/torque/implementation-visitor.cc
+++ b/src/torque/implementation-visitor.cc
@@ -811,13 +811,18 @@
 }
 
 const Type* ImplementationVisitor::Visit(GotoStatement* stmt) {
-  LocalLabel* label = LookupLabel(stmt->label);
+  Binding<LocalLabel>* label = LookupLabel(stmt->label->value);
   size_t parameter_count = label->parameter_types.size();
   if (stmt->arguments.size() != parameter_count) {
     ReportError("goto to label has incorrect number of parameters (expected ",
                 parameter_count, " found ", stmt->arguments.size(), ")");
   }
 
+  if (GlobalContext::collect_language_server_data()) {
+    LanguageServerData::AddDefinition(stmt->label->pos,
+                                      label->declaration_position());
+  }
+
   size_t i = 0;
   StackRange arguments = assembler().TopRange(0);
   for (Expression* e : stmt->arguments) {
diff --git a/src/torque/torque-parser.cc b/src/torque/torque-parser.cc
index 9d53615..af18b16 100644
--- a/src/torque/torque-parser.cc
+++ b/src/torque/torque-parser.cc
@@ -890,10 +890,9 @@
 
 base::Optional<ParseResult> MakeGotoStatement(
     ParseResultIterator* child_results) {
-  auto label = child_results->NextAs<std::string>();
+  auto label = child_results->NextAs<Identifier*>();
   auto arguments = child_results->NextAs<std::vector<Expression*>>();
-  Statement* result =
-      MakeNode<GotoStatement>(std::move(label), std::move(arguments));
+  Statement* result = MakeNode<GotoStatement>(label, std::move(arguments));
   return ParseResult{result};
 }
 
@@ -1582,7 +1581,7 @@
       Rule({Token("tail"), &callExpression}, MakeTailCallStatement),
       Rule({Token("break")}, MakeBreakStatement),
       Rule({Token("continue")}, MakeContinueStatement),
-      Rule({Token("goto"), &identifier,
+      Rule({Token("goto"), &name,
             TryOrDefault<std::vector<Expression*>>(&argumentList)},
            MakeGotoStatement),
       Rule({OneOf({"debug", "unreachable"})}, MakeDebugStatement)};
diff --git a/test/unittests/torque/ls-server-data-unittest.cc b/test/unittests/torque/ls-server-data-unittest.cc
index 037c042..9512e49 100644
--- a/test/unittests/torque/ls-server-data-unittest.cc
+++ b/test/unittests/torque/ls-server-data-unittest.cc
@@ -139,6 +139,65 @@
   EXPECT_EQ(*maybe_position, (SourcePosition{id, {2, 5}, {2, 11}}));
 }
 
+TEST(LanguageServer, GotoLabelDefinitionInSignatureGotoStmt) {
+  const std::string source =
+      "type void;\n"
+      "type never;\n"
+      "macro Foo(): never labels Fail {\n"
+      "  goto Fail;\n"
+      "}\n";
+
+  TestCompiler compiler;
+  compiler.Compile(source);
+
+  // Find the definition for 'Fail' of the goto statement on line 3.
+  const SourceId id = SourceFileMap::GetSourceId("<torque>");
+  auto maybe_position = LanguageServerData::FindDefinition(id, {3, 7});
+  ASSERT_TRUE(maybe_position.has_value());
+  EXPECT_EQ(*maybe_position, (SourcePosition{id, {2, 26}, {2, 30}}));
+}
+
+TEST(LanguageServer, GotoLabelDefinitionInTryBlockGoto) {
+  const std::string source =
+      "type void;\n"
+      "type never;\n"
+      "macro Bar() {\n"
+      "  try { goto Bailout; }\n"
+      "  label Bailout {}\n"
+      "}\n";
+
+  TestCompiler compiler;
+  compiler.Compile(source);
+
+  // Find the definition for 'Bailout' of the goto statement on line 3.
+  const SourceId id = SourceFileMap::GetSourceId("<torque>");
+  auto maybe_position = LanguageServerData::FindDefinition(id, {3, 13});
+  ASSERT_TRUE(maybe_position.has_value());
+  EXPECT_EQ(*maybe_position, (SourcePosition{id, {4, 8}, {4, 15}}));
+}
+
+TEST(LanguageServer, GotoLabelDefinitionGotoInOtherwise) {
+  const std::string source =
+      "type void;\n"
+      "type never;\n"
+      "macro Foo(): never labels Fail {\n"
+      "  goto Fail;\n"
+      "}\n"
+      "macro Bar() {\n"
+      "  try { Foo() otherwise goto Bailout; }\n"
+      "  label Bailout {}\n"
+      "}\n";
+
+  TestCompiler compiler;
+  compiler.Compile(source);
+
+  // Find the definition for 'Bailout' of the otherwise clause on line 6.
+  const SourceId id = SourceFileMap::GetSourceId("<torque>");
+  auto maybe_position = LanguageServerData::FindDefinition(id, {6, 30});
+  ASSERT_TRUE(maybe_position.has_value());
+  EXPECT_EQ(*maybe_position, (SourcePosition{id, {7, 8}, {7, 15}}));
+}
+
 }  // namespace torque
 }  // namespace internal
 }  // namespace v8