Przeglądaj źródła

Add a FatalUserError macro to help print user-caused errors. (#668)

Jon Meow 4 lat temu
rodzic
commit
034f3600e3

+ 4 - 1
common/check.h

@@ -53,7 +53,10 @@ class ExitWrapper {
 
 }  // namespace CheckInternal
 
-// Checks the given condition, and if it's false, prints an error and exits.
+// Checks the given condition, and if it's false, prints a stack, streams the
+// error message, then exits. This should be used for unexpected errors, such as
+// a bug in the application.
+//
 // For example:
 //   CHECK(is_valid) << "Data is not valid!";
 #define CHECK(condition)                                             \

+ 1 - 0
executable_semantics/ast/BUILD

@@ -30,6 +30,7 @@ cc_library(
     deps = [
         "//common:indirect_value",
         "//common:ostream",
+        "//executable_semantics/common:error",
     ],
 )
 

+ 5 - 3
executable_semantics/ast/expression.cpp

@@ -4,6 +4,8 @@
 
 #include "executable_semantics/ast/expression.h"
 
+#include "executable_semantics/common/error.h"
+
 namespace Carbon {
 
 auto Expression::GetIdentifierExpression() const
@@ -166,9 +168,9 @@ auto Expression::MakeTupleLiteral(int line_num,
   for (auto& arg : args) {
     if (arg.name == "") {
       if (seen_named_member) {
-        llvm::errs() << line_num
-                     << ": positional members must come before named members\n";
-        exit(-1);
+        FatalUserError()
+            << line_num
+            << ": positional members must come before named members";
       }
       arg.name = std::to_string(i);
       ++i;

+ 18 - 0
executable_semantics/common/BUILD

@@ -4,6 +4,24 @@
 
 package(default_visibility = ["//executable_semantics:__subpackages__"])
 
+cc_library(
+    name = "error",
+    hdrs = ["error.h"],
+    deps = [
+        "@llvm-project//llvm:Support",
+    ],
+)
+
+cc_test(
+    name = "error_test",
+    srcs = ["error_test.cpp"],
+    deps = [
+        ":error",
+        "@llvm-project//llvm:gtest",
+        "@llvm-project//llvm:gtest_main",
+    ],
+)
+
 cc_library(
     name = "tracing_flag",
     srcs = ["tracing_flag.cpp"],

+ 38 - 0
executable_semantics/common/error.h

@@ -0,0 +1,38 @@
+// 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 EXECUTABLE_SEMANTICS_COMMON_ERROR_H_
+#define EXECUTABLE_SEMANTICS_COMMON_ERROR_H_
+
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/Signals.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace Carbon {
+
+// Prints an error and exits. This should be used for non-recoverable errors
+// with user input.
+//
+// For example:
+//   FatalUserError() << "Input is not valid!";
+class FatalUserError {
+ public:
+  FatalUserError() { llvm::errs() << "ERROR: "; }
+  ~FatalUserError() {
+    // Finish with a newline.
+    llvm::errs() << "\n";
+    exit(-1);
+  }
+
+  // Forward output to llvm::errs.
+  template <typename T>
+  FatalUserError& operator<<(const T& message) {
+    llvm::errs() << message;
+    return *this;
+  }
+};
+
+}  // namespace Carbon
+
+#endif  // EXECUTABLE_SEMANTICS_COMMON_ERROR_H_

+ 15 - 0
executable_semantics/common/error_test.cpp

@@ -0,0 +1,15 @@
+// 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 "executable_semantics/common/error.h"
+
+#include "gtest/gtest.h"
+
+namespace Carbon {
+
+TEST(ErrorTest, FatalUserError) {
+  ASSERT_DEATH({ FatalUserError() << "test"; }, "ERROR: test\n");
+}
+
+}  // namespace Carbon

+ 7 - 8
executable_semantics/interpreter/typecheck.cpp

@@ -11,6 +11,7 @@
 #include <vector>
 
 #include "executable_semantics/ast/function_definition.h"
+#include "executable_semantics/common/error.h"
 #include "executable_semantics/common/tracing_flag.h"
 #include "executable_semantics/interpreter/interpreter.h"
 
@@ -19,20 +20,18 @@ namespace Carbon {
 void ExpectType(int line_num, const std::string& context, const Value* expected,
                 const Value* actual) {
   if (!TypeEqual(expected, actual)) {
-    llvm::errs() << line_num << ": type error in " << context << "\n"
-                 << "expected: " << *expected << "\n"
-                 << "actual: " << *actual << "\n";
-    exit(-1);
+    FatalUserError() << line_num << ": type error in " << context << "\n"
+                     << "expected: " << *expected << "\n"
+                     << "actual: " << *actual;
   }
 }
 
 void ExpectPointerType(int line_num, const std::string& context,
                        const Value* actual) {
   if (actual->tag() != ValKind::PointerType) {
-    llvm::errs() << line_num << ": type error in " << context << "\n"
-                 << "expected a pointer type\n"
-                 << "actual: " << *actual << "\n";
-    exit(-1);
+    FatalUserError() << line_num << ": type error in " << context << "\n"
+                     << "expected a pointer type\n"
+                     << "actual: " << *actual;
   }
 }
 

+ 2 - 0
executable_semantics/syntax/BUILD

@@ -29,9 +29,11 @@ cc_library(
     ],
     deps = [
         ":paren_contents",
+        "//common:check",
         "//common:ostream",
         "//executable_semantics/ast:declaration",
         "//executable_semantics/ast:expression",
+        "//executable_semantics/common:error",
         "//executable_semantics/common:tracing_flag",
         "//executable_semantics/interpreter",
         "//executable_semantics/interpreter:typecheck",

+ 6 - 8
executable_semantics/syntax/parse.cpp

@@ -6,6 +6,8 @@
 
 #include <iostream>
 
+#include "common/check.h"
+#include "executable_semantics/common/error.h"
 #include "executable_semantics/common/tracing_flag.h"
 #include "executable_semantics/syntax/parse_and_lex_context.h"
 #include "executable_semantics/syntax/parser.h"
@@ -21,9 +23,8 @@ auto parse(const std::string& input_file_name)
     -> std::variant<AST, SyntaxErrorCode> {
   yyin = fopen(input_file_name.c_str(), "r");
   if (yyin == nullptr) {
-    std::cerr << "Error opening '" << input_file_name
-              << "': " << std::strerror(errno) << std::endl;
-    exit(1);
+    FatalUserError() << "Error opening '" << input_file_name
+                     << "': " << std::strerror(errno);
   }
 
   std::optional<AST> parsed_input = std::nullopt;
@@ -38,11 +39,8 @@ auto parse(const std::string& input_file_name)
     return syntax_error_code;
   }
 
-  if (parsed_input == std::nullopt) {
-    std::cerr << "Internal error: parser validated syntax yet didn't produce "
-                 "an AST.\n";
-    exit(1);
-  }
+  CHECK(parsed_input != std::nullopt)
+      << "parser validated syntax yet didn't produce an AST.";
   return *parsed_input;
 }
 

+ 1 - 1
executable_semantics/testdata/fun_named_params2.golden

@@ -1,2 +1,2 @@
-5: positional members must come before named members
+ERROR: 5: positional members must come before named members
 EXIT CODE: 255

+ 1 - 1
executable_semantics/testdata/global_variable3.golden

@@ -1,4 +1,4 @@
-7: type error in initializer of variable
+ERROR: 7: type error in initializer of variable
 expected: Int
 actual: Bool
 EXIT CODE: 255

+ 1 - 1
executable_semantics/testdata/global_variable5.golden

@@ -1,4 +1,4 @@
-10: type error in return
+ERROR: 10: type error in return
 expected: ()
 actual: Int
 EXIT CODE: 255

+ 1 - 1
executable_semantics/testdata/tuple4.golden

@@ -1,2 +1,2 @@
-6: positional members must come before named members
+ERROR: 6: positional members must come before named members
 EXIT CODE: 255

+ 1 - 1
executable_semantics/testdata/tuple5.golden

@@ -1,4 +1,4 @@
-8: type error in pattern variable
+ERROR: 8: type error in pattern variable
 expected: (x = Int, y = Int)
 actual: (y = Int, x = Int)
 EXIT CODE: 255

+ 1 - 1
executable_semantics/testdata/tuple_equality3.golden

@@ -1,4 +1,4 @@
-8: type error in ==
+ERROR: 8: type error in ==
 expected: (0 = Int, 1 = Int)
 actual: (0 = Int)
 EXIT CODE: 255