Forráskód Böngészése

Add `Dump` functions to Check, Parse, and Lex (#4669)

- Provide `Check::Dump(context, arg)` and similar.
- gdb and lldb should do contextual lookup, and `call Dump(*this,
Lex::TokenIndex::Invalid)` has been tested with gdb.
- Since this is only for debug, keeps the functions fully separated from
code.
- Uses alwayslink to ensure objects are correctly linked, even though
there are no calls.
- `-Wno-missing-prototypes` is needed when we don't have forward
declarations.
- Code is not linked in opt builds, using `#ifndef NDEBUG`.
- This probably could be doing something in BUILD files with a
`select()`, but the `#ifndef` seemed easier.

This is based on #4620, but uses free functions instead of member
functions.

Co-authored-by: Dana Jansens <danakj@orodu.net>

---------

Co-authored-by: danakj <danakj@orodu.net>
Jon Ross-Perkins 1 éve
szülő
commit
3ce0df67bb

+ 3 - 2
common/ostream.h

@@ -5,13 +5,14 @@
 #ifndef CARBON_COMMON_OSTREAM_H_
 #define CARBON_COMMON_OSTREAM_H_
 
+// Libraries should include this header instead of raw_ostream.
+
 #include <concepts>
 #include <ostream>
 #include <type_traits>
 
-#include "llvm/Support/raw_os_ostream.h"
-// Libraries should include this header instead of raw_ostream.
 #include "llvm/Support/Compiler.h"
+#include "llvm/Support/raw_os_ostream.h"
 #include "llvm/Support/raw_ostream.h"  // IWYU pragma: export
 
 namespace Carbon {

+ 20 - 0
toolchain/check/BUILD

@@ -92,6 +92,25 @@ cc_library(
     ],
 )
 
+cc_library(
+    name = "dump",
+    srcs = ["dump.cpp"],
+    # Contains Dump methods without a forward declaration.
+    copts = ["-Wno-missing-prototypes"],
+    deps = [
+        ":context",
+        "//common:check",
+        "//common:ostream",
+        "//toolchain/lex:dump",
+        "//toolchain/lex:tokenized_buffer",
+        "//toolchain/parse:dump",
+        "//toolchain/parse:tree",
+        "//toolchain/sem_ir:file",
+    ],
+    # Always link dump methods.
+    alwayslink = 1,
+)
+
 cc_library(
     name = "check",
     srcs = [
@@ -111,6 +130,7 @@ cc_library(
     hdrs = ["check.h"],
     deps = [
         ":context",
+        ":dump",
         ":impl",
         ":interface",
         ":pointer_dereference",

+ 7 - 2
toolchain/check/context.h

@@ -495,10 +495,15 @@ class Context {
   }
 
   auto sem_ir() -> SemIR::File& { return *sem_ir_; }
+  auto sem_ir() const -> const SemIR::File& { return *sem_ir_; }
 
-  auto parse_tree() -> const Parse::Tree& { return sem_ir_->parse_tree(); }
+  auto parse_tree() const -> const Parse::Tree& {
+    return sem_ir_->parse_tree();
+  }
 
-  auto tokens() -> const Lex::TokenizedBuffer& { return parse_tree().tokens(); }
+  auto tokens() const -> const Lex::TokenizedBuffer& {
+    return parse_tree().tokens();
+  }
 
   auto node_stack() -> NodeStack& { return node_stack_; }
 

+ 78 - 0
toolchain/check/dump.cpp

@@ -0,0 +1,78 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+// This library contains functions to assist dumping objects to stderr during
+// interactive debugging. Functions named `Dump` are intended for direct use by
+// developers, and should use overload resolution to determine which will be
+// invoked. The debugger should do namespace resolution automatically. For
+// example:
+//
+// - lldb: `expr Dump(context, id)`
+// - gdb: `call Dump(context, id)`
+//
+// The `DumpNoNewline` functions are helpers that exclude a trailing newline.
+// They're intended to be composed by `Dump` function implementations.
+
+#ifndef NDEBUG
+
+#include "toolchain/lex/dump.h"
+
+#include "common/check.h"
+#include "common/ostream.h"
+#include "toolchain/check/context.h"
+#include "toolchain/lex/tokenized_buffer.h"
+#include "toolchain/parse/dump.h"
+#include "toolchain/parse/tree.h"
+#include "toolchain/sem_ir/file.h"
+
+namespace Carbon::Check {
+
+static auto DumpNoNewline(const Context& context, SemIR::LocId loc_id) -> void {
+  if (!loc_id.is_valid()) {
+    llvm::errs() << "LocId(invalid)";
+    return;
+  }
+
+  if (loc_id.is_node_id()) {
+    auto token = context.parse_tree().node_token(loc_id.node_id());
+    auto line = context.tokens().GetLineNumber(token);
+    auto col = context.tokens().GetColumnNumber(token);
+    const char* implicit = loc_id.is_implicit() ? " implicit" : "";
+    llvm::errs() << "LocId(";
+    llvm::errs().write_escaped(context.sem_ir().filename());
+    llvm::errs() << ":" << line << ":" << col << implicit << ")";
+  } else {
+    CARBON_CHECK(loc_id.is_import_ir_inst_id());
+
+    auto import_ir_id = context.sem_ir()
+                            .import_ir_insts()
+                            .Get(loc_id.import_ir_inst_id())
+                            .ir_id;
+    const auto* import_file =
+        context.sem_ir().import_irs().Get(import_ir_id).sem_ir;
+    llvm::errs() << "LocId(import from \"";
+    llvm::errs().write_escaped(import_file->filename());
+    llvm::errs() << "\")";
+  }
+}
+
+LLVM_DUMP_METHOD auto Dump(const Context& context, Lex::TokenIndex token)
+    -> void {
+  Parse::Dump(context.parse_tree(), token);
+}
+
+LLVM_DUMP_METHOD auto Dump(const Context& context, Parse::NodeId node_id)
+    -> void {
+  Parse::Dump(context.parse_tree(), node_id);
+}
+
+LLVM_DUMP_METHOD auto Dump(const Context& context, SemIR::LocId loc_id)
+    -> void {
+  DumpNoNewline(context, loc_id);
+  llvm::errs() << '\n';
+}
+
+}  // namespace Carbon::Check
+
+#endif  // NDEBUG

+ 11 - 0
toolchain/docs/adding_features.md

@@ -23,6 +23,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
         -   [Reviewing test deltas](#reviewing-test-deltas)
     -   [Verbose output](#verbose-output)
     -   [Stack traces](#stack-traces)
+    -   [Dumping objects in interactive debuggers](#dumping-objects-in-interactive-debuggers)
 
 <!-- tocstop -->
 
@@ -496,3 +497,13 @@ While the iterative processing pattern means function stack traces will have
 minimal context for how the current function is reached, we use LLVM's
 `PrettyStackTrace` to include details about the state stack. The state stack
 will be above the function stack in crash output.
+
+### Dumping objects in interactive debuggers
+
+We provide namespace-scoped `Dump` functions in several components, such as
+[check/dump.cpp](/toolchain/check/dump.cpp). These `Dump` functions will print
+contextual information about an object to stderr. The files contain details
+regarding support.
+
+Objects which inherit from `Printable` also have `Dump` member functions, but
+these will lack contextual information.

+ 13 - 0
toolchain/lex/BUILD

@@ -184,6 +184,7 @@ cc_library(
     hdrs = ["lex.h"],
     deps = [
         ":character_set",
+        ":dump",
         ":helpers",
         ":numeric_literal",
         ":string_literal",
@@ -198,6 +199,18 @@ cc_library(
     ],
 )
 
+cc_library(
+    name = "dump",
+    srcs = ["dump.cpp"],
+    hdrs = ["dump.h"],
+    deps = [
+        ":tokenized_buffer",
+        "//common:ostream",
+    ],
+    # Always link dump methods.
+    alwayslink = 1,
+)
+
 cc_library(
     name = "token_index",
     hdrs = ["token_index.h"],

+ 36 - 0
toolchain/lex/dump.cpp

@@ -0,0 +1,36 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+#ifndef NDEBUG
+
+#include "toolchain/lex/dump.h"
+
+#include "common/ostream.h"
+
+namespace Carbon::Lex {
+
+auto DumpNoNewline(const TokenizedBuffer& tokens, TokenIndex token) -> void {
+  if (!token.is_valid()) {
+    llvm::errs() << "TokenIndex(invalid)";
+    return;
+  }
+
+  auto kind = tokens.GetKind(token);
+  auto line = tokens.GetLineNumber(token);
+  auto col = tokens.GetColumnNumber(token);
+
+  llvm::errs() << "TokenIndex(kind: " << kind << ", loc: ";
+  llvm::errs().write_escaped(tokens.source().filename());
+  llvm::errs() << ":" << line << ":" << col << ")";
+}
+
+LLVM_DUMP_METHOD auto Dump(const TokenizedBuffer& tokens, TokenIndex token)
+    -> void {
+  DumpNoNewline(tokens, token);
+  llvm::errs() << '\n';
+}
+
+}  // namespace Carbon::Lex
+
+#endif  // NDEBUG

+ 34 - 0
toolchain/lex/dump.h

@@ -0,0 +1,34 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+// This library contains functions to assist dumping objects to stderr during
+// interactive debugging. Functions named `Dump` are intended for direct use by
+// developers, and should use overload resolution to determine which will be
+// invoked. The debugger should do namespace resolution automatically. For
+// example:
+//
+// - lldb: `expr Dump(tokens, id)`
+// - gdb: `call Dump(tokens, id)`
+//
+// The `DumpNoNewline` functions are helpers that exclude a trailing newline.
+// They're intended to be composed by `Dump` function implementations.
+
+#ifndef CARBON_TOOLCHAIN_LEX_DUMP_H_
+#define CARBON_TOOLCHAIN_LEX_DUMP_H_
+
+#ifndef NDEBUG
+
+#include "toolchain/lex/tokenized_buffer.h"
+
+namespace Carbon::Lex {
+
+auto DumpNoNewline(const TokenizedBuffer& tokens, TokenIndex token) -> void;
+
+auto Dump(const TokenizedBuffer& tokens, TokenIndex token) -> void;
+
+}  // namespace Carbon::Lex
+
+#endif  // NDEBUG
+
+#endif  // CARBON_TOOLCHAIN_LEX_DUMP_H_

+ 2 - 6
toolchain/lex/tokenized_buffer.cpp

@@ -25,7 +25,7 @@ auto TokenizedBuffer::GetLine(TokenIndex token) const -> LineIndex {
 }
 
 auto TokenizedBuffer::GetLineNumber(TokenIndex token) const -> int {
-  return GetLineNumber(GetLine(token));
+  return GetLine(token).index + 1;
 }
 
 auto TokenizedBuffer::GetColumnNumber(TokenIndex token) const -> int {
@@ -162,10 +162,6 @@ auto TokenizedBuffer::IsRecoveryToken(TokenIndex token) const -> bool {
   return recovery_tokens_[token.index];
 }
 
-auto TokenizedBuffer::GetLineNumber(LineIndex line) const -> int {
-  return line.index + 1;
-}
-
 auto TokenizedBuffer::GetNextLine(LineIndex line) const -> LineIndex {
   LineIndex next(line.index + 1);
   CARBON_DCHECK(static_cast<size_t>(next.index) < line_infos_.size());
@@ -262,7 +258,7 @@ auto TokenizedBuffer::PrintToken(llvm::raw_ostream& output_stream,
       llvm::right_justify(
           llvm::formatv("'{0}'", token_info.kind().name()).str(),
           widths.kind + 2),
-      llvm::format_decimal(GetLineNumber(GetLine(token)), widths.line),
+      llvm::format_decimal(GetLineNumber(token), widths.line),
       llvm::format_decimal(GetColumnNumber(token), widths.column),
       llvm::format_decimal(GetIndentColumnNumber(line_index), widths.indent),
       token_text);

+ 0 - 3
toolchain/lex/tokenized_buffer.h

@@ -156,9 +156,6 @@ class TokenizedBuffer : public Printable<TokenizedBuffer> {
   // For example, a closing paren inserted to match an unmatched paren.
   auto IsRecoveryToken(TokenIndex token) const -> bool;
 
-  // Returns the 1-based line number.
-  auto GetLineNumber(LineIndex line) const -> int;
-
   // Returns the 1-based indentation column number.
   auto GetIndentColumnNumber(LineIndex line) const -> int;
 

+ 14 - 0
toolchain/parse/BUILD

@@ -88,6 +88,7 @@ cc_library(
     ],
     deps = [
         ":context",
+        ":dump",
         ":node_kind",
         ":state",
         ":tree",
@@ -102,6 +103,19 @@ cc_library(
     ],
 )
 
+cc_library(
+    name = "dump",
+    srcs = ["dump.cpp"],
+    hdrs = ["dump.h"],
+    deps = [
+        ":tree",
+        "//common:ostream",
+        "//toolchain/lex:dump",
+    ],
+    # Always link dump methods.
+    alwayslink = 1,
+)
+
 cc_library(
     name = "state",
     srcs = ["state.cpp"],

+ 1 - 1
toolchain/parse/context.cpp

@@ -481,7 +481,7 @@ auto Context::PrintForStackDump(llvm::raw_ostream& output) const -> void {
 
 auto Context::PrintTokenForStackDump(llvm::raw_ostream& output,
                                      Lex::TokenIndex token) const -> void {
-  output << " @ " << tokens_->GetLineNumber(tokens_->GetLine(token)) << ":"
+  output << " @ " << tokens_->GetLineNumber(token) << ":"
          << tokens_->GetColumnNumber(token) << ": token " << token << " : "
          << tokens_->GetKind(token) << "\n";
 }

+ 39 - 0
toolchain/parse/dump.cpp

@@ -0,0 +1,39 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+#ifndef NDEBUG
+
+#include "toolchain/parse/dump.h"
+
+#include "common/ostream.h"
+#include "toolchain/lex/dump.h"
+
+namespace Carbon::Parse {
+
+auto DumpNoNewline(const Tree& tree, NodeId node_id) -> void {
+  if (!node_id.is_valid()) {
+    llvm::errs() << "NodeId(invalid)";
+    return;
+  }
+
+  auto kind = tree.node_kind(node_id);
+  auto token = tree.node_token(node_id);
+
+  llvm::errs() << "NodeId(kind: " << kind << ", token: ";
+  Lex::DumpNoNewline(tree.tokens(), token);
+  llvm::errs() << ")";
+}
+
+LLVM_DUMP_METHOD auto Dump(const Tree& tree, Lex::TokenIndex token) -> void {
+  Lex::Dump(tree.tokens(), token);
+}
+
+LLVM_DUMP_METHOD auto Dump(const Tree& tree, NodeId node_id) -> void {
+  DumpNoNewline(tree, node_id);
+  llvm::errs() << '\n';
+}
+
+}  // namespace Carbon::Parse
+
+#endif  // NDEBUG

+ 35 - 0
toolchain/parse/dump.h

@@ -0,0 +1,35 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+// This library contains functions to assist dumping objects to stderr during
+// interactive debugging. Functions named `Dump` are intended for direct use by
+// developers, and should use overload resolution to determine which will be
+// invoked. The debugger should do namespace resolution automatically. For
+// example:
+//
+// - lldb: `expr Dump(tree, id)`
+// - gdb: `call Dump(tree, id)`
+//
+// The `DumpNoNewline` functions are helpers that exclude a trailing newline.
+// They're intended to be composed by `Dump` function implementations.
+
+#ifndef CARBON_TOOLCHAIN_PARSE_DUMP_H_
+#define CARBON_TOOLCHAIN_PARSE_DUMP_H_
+
+#ifndef NDEBUG
+
+#include "toolchain/parse/tree.h"
+
+namespace Carbon::Parse {
+
+auto DumpNoNewline(const Tree& tree, NodeId node_id) -> void;
+
+auto Dump(const Tree& tree, Lex::TokenIndex token) -> void;
+auto Dump(const Tree& tree, NodeId node_id) -> void;
+
+}  // namespace Carbon::Parse
+
+#endif  // NDEBUG
+
+#endif  // CARBON_TOOLCHAIN_PARSE_DUMP_H_