Răsfoiți Sursa

Include parse node being checked in crash backtrace. (#3926)

When we crash, include the source location of the parse node that we
were handling, as well as the name of the check function that we were
calling. Also include the source snippet, since it's easy to do so, and
may avoid the need to look at the input file in some cases.
Richard Smith 2 ani în urmă
părinte
comite
92860c56b4

+ 17 - 4
toolchain/check/check.cpp

@@ -706,12 +706,24 @@ auto NodeIdTraversal::Next() -> std::optional<Parse::NodeId> {
 // for example if an unrecoverable state is encountered.
 // for example if an unrecoverable state is encountered.
 // NOLINTNEXTLINE(readability-function-size)
 // NOLINTNEXTLINE(readability-function-size)
 static auto ProcessNodeIds(Context& context, llvm::raw_ostream* vlog_stream,
 static auto ProcessNodeIds(Context& context, llvm::raw_ostream* vlog_stream,
-                           ErrorTrackingDiagnosticConsumer& err_tracker)
-    -> bool {
+                           ErrorTrackingDiagnosticConsumer& err_tracker,
+                           Parse::NodeLocConverter* converter) -> bool {
   NodeIdTraversal traversal(context, vlog_stream);
   NodeIdTraversal traversal(context, vlog_stream);
 
 
+  Parse::NodeId node_id = Parse::NodeId::Invalid;
+
+  // On crash, report which token we were handling.
+  PrettyStackTraceFunction node_dumper([&](llvm::raw_ostream& output) {
+    auto loc = converter->ConvertLoc(
+        node_id, [](DiagnosticLoc, const Internal::DiagnosticBase<>&) {});
+    loc.FormatLocation(output);
+    output << ": Check::Handle" << context.parse_tree().node_kind(node_id)
+           << "\n";
+    loc.FormatSnippet(output);
+  });
+
   while (auto maybe_node_id = traversal.Next()) {
   while (auto maybe_node_id = traversal.Next()) {
-    auto node_id = *maybe_node_id;
+    node_id = *maybe_node_id;
     auto parse_kind = context.parse_tree().node_kind(node_id);
     auto parse_kind = context.parse_tree().node_kind(node_id);
 
 
     switch (parse_kind) {
     switch (parse_kind) {
@@ -762,7 +774,8 @@ static auto CheckParseTree(
   // TODO: Do this selectively when we see an impl query.
   // TODO: Do this selectively when we see an impl query.
   ImportImpls(context);
   ImportImpls(context);
 
 
-  if (!ProcessNodeIds(context, vlog_stream, unit_info.err_tracker)) {
+  if (!ProcessNodeIds(context, vlog_stream, unit_info.err_tracker,
+                      &unit_info.converter)) {
     context.sem_ir().set_has_errors(true);
     context.sem_ir().set_has_errors(true);
     return;
     return;
   }
   }

+ 4 - 1
toolchain/diagnostics/BUILD

@@ -13,7 +13,10 @@ filegroup(
 
 
 cc_library(
 cc_library(
     name = "diagnostic_emitter",
     name = "diagnostic_emitter",
-    srcs = ["diagnostic_consumer.cpp"],
+    srcs = [
+        "diagnostic.cpp",
+        "diagnostic_consumer.cpp",
+    ],
     hdrs = [
     hdrs = [
         "diagnostic.h",
         "diagnostic.h",
         "diagnostic_consumer.h",
         "diagnostic_consumer.h",

+ 45 - 0
toolchain/diagnostics/diagnostic.cpp

@@ -0,0 +1,45 @@
+// 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
+
+#include "toolchain/diagnostics/diagnostic.h"
+
+#include <algorithm>
+#include <cstdint>
+
+namespace Carbon {
+
+auto DiagnosticLoc::FormatLocation(llvm::raw_ostream& out) const -> void {
+  out << filename;
+  if (line_number > 0) {
+    out << ":" << line_number;
+    if (column_number > 0) {
+      out << ":" << column_number;
+    }
+  }
+}
+
+auto DiagnosticLoc::FormatSnippet(llvm::raw_ostream& out) const -> void {
+  if (column_number <= 0) {
+    return;
+  }
+
+  // column_number is 1-based.
+  int32_t column = column_number - 1;
+
+  out << line << "\n";
+  out.indent(column);
+  out << "^";
+  // We want to ensure that we don't underline past the end of the line in
+  // case of a multiline token.
+  // TODO: revisit this once we can reference multiple ranges on multiple
+  // lines in a single diagnostic message.
+  int underline_length =
+      std::min(length, static_cast<int32_t>(line.size()) - column);
+  for (int i = 1; i < underline_length; ++i) {
+    out << '~';
+  }
+  out << '\n';
+}
+
+}  // namespace Carbon

+ 8 - 0
toolchain/diagnostics/diagnostic.h

@@ -46,6 +46,14 @@ enum class DiagnosticLevel : int8_t {
 // is required to be less than SourceBuffer that it refers to due to the
 // is required to be less than SourceBuffer that it refers to due to the
 // contained filename and line references.
 // contained filename and line references.
 struct DiagnosticLoc {
 struct DiagnosticLoc {
+  // Write the filename, line number, and column number corresponding to this
+  // location to the given stream.
+  auto FormatLocation(llvm::raw_ostream& out) const -> void;
+
+  // Write the source snippet corresponding to this location to the given
+  // stream.
+  auto FormatSnippet(llvm::raw_ostream& out) const -> void;
+
   // Name of the file or buffer that this diagnostic refers to.
   // Name of the file or buffer that this diagnostic refers to.
   llvm::StringRef filename;
   llvm::StringRef filename;
   // A reference to the line of the error.
   // A reference to the line of the error.

+ 2 - 24
toolchain/diagnostics/diagnostic_consumer.cpp

@@ -17,35 +17,13 @@ auto StreamDiagnosticConsumer::HandleDiagnostic(Diagnostic diagnostic) -> void {
   }
   }
 
 
   for (const auto& message : diagnostic.messages) {
   for (const auto& message : diagnostic.messages) {
-    *stream_ << message.loc.filename;
-    if (message.loc.line_number > 0) {
-      *stream_ << ":" << message.loc.line_number;
-      if (message.loc.column_number > 0) {
-        *stream_ << ":" << message.loc.column_number;
-      }
-    }
+    message.loc.FormatLocation(*stream_);
     *stream_ << ": ";
     *stream_ << ": ";
     if (message.level == DiagnosticLevel::Error) {
     if (message.level == DiagnosticLevel::Error) {
       *stream_ << "ERROR: ";
       *stream_ << "ERROR: ";
     }
     }
     *stream_ << message.format_fn(message) << "\n";
     *stream_ << message.format_fn(message) << "\n";
-    if (message.loc.column_number > 0) {
-      *stream_ << message.loc.line << "\n";
-      stream_->indent(message.loc.column_number - 1);
-      *stream_ << "^";
-      int underline_length = std::max(0, message.loc.length - 1);
-      // We want to ensure that we don't underline past the end of the line in
-      // case of a multiline token.
-      // TODO: revisit this once we can reference multiple ranges on multiple
-      // lines in a single diagnostic message.
-      underline_length = std::min(
-          underline_length, static_cast<int32_t>(message.loc.line.size()) -
-                                message.loc.column_number);
-      for (int i = 0; i < underline_length; ++i) {
-        *stream_ << "~";
-      }
-      *stream_ << "\n";
-    }
+    message.loc.FormatSnippet(*stream_);
   }
   }
 }
 }