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

Change explorer's file_test to support argument passing to main. (#3032)

This shifts explorer's file_test to use ExplorerMain in order to be able
to pass arguments similar to how the command line would. It also means
that various output wrapping done by the command line is shared, no
longer copied by file_test.

In order to help make this work, I plugged vfs::FileSystem into
explorer, similar to how we're doing this on the toolchain side (might
try to share more code later). I was having trouble getting an overlay
working, I think due to how paths are specified -- but due to the issues
in fixing this, I'm just loading the prelude into the InMemoryFilesystem
for now.

Under this approach, I'm unifying more of the file versus string
handling. I think we should shift towards an approach where, like
toolchain code, anyone trying to use Explorer should use a vfs
implementation to supply code. This should reduce the number of distinct
code paths which we're maintaining/testing.

Short-term, this allows the trace testdata to be merged into file_test
with less special casing, using ARGS:. Long-term, it means that the
file_test knows the ARGS to pass to generate checks to match against,
which is the direction I'm planning for autoupdate.
Jon Ross-Perkins 2 éve
szülő
commit
9e03d5efe2

+ 3 - 1
explorer/BUILD

@@ -23,6 +23,7 @@ cc_library(
     hdrs = ["main.h"],
     hdrs = ["main.h"],
     deps = [
     deps = [
         "//common:error",
         "//common:error",
+        "//common:ostream",
         "//explorer/common:trace_stream",
         "//explorer/common:trace_stream",
         "//explorer/parse_and_execute",
         "//explorer/parse_and_execute",
         "@llvm-project//llvm:Support",
         "@llvm-project//llvm:Support",
@@ -45,7 +46,8 @@ cc_library(
     testonly = 1,
     testonly = 1,
     srcs = ["file_test.cpp"],
     srcs = ["file_test.cpp"],
     deps = [
     deps = [
-        "//explorer/parse_and_execute",
+        ":main",
+        "//common:check",
         "//testing/file_test:file_test_base",
         "//testing/file_test:file_test_base",
         "//testing/util:test_raw_ostream",
         "//testing/util:test_raw_ostream",
         "@com_google_absl//absl/flags:flag",
         "@com_google_absl//absl/flags:flag",

+ 1 - 1
explorer/common/source_location.h

@@ -26,7 +26,7 @@ class SourceLocation {
   }
   }
 
 
   // The filename should be eternal or arena-allocated to eliminate copies.
   // The filename should be eternal or arena-allocated to eliminate copies.
-  explicit constexpr SourceLocation(const char* filename, int line_num,
+  explicit constexpr SourceLocation(std::string_view filename, int line_num,
                                     FileKind file_kind)
                                     FileKind file_kind)
       : filename_(filename), line_num_(line_num), file_kind_(file_kind) {}
       : filename_(filename), line_num_(line_num), file_kind_(file_kind) {}
   explicit SourceLocation(Nonnull<const std::string*> filename, int line_num,
   explicit SourceLocation(Nonnull<const std::string*> filename, int line_num,

+ 44 - 39
explorer/file_test.cpp

@@ -3,7 +3,8 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 
 
 #include "absl/flags/flag.h"
 #include "absl/flags/flag.h"
-#include "explorer/parse_and_execute/parse_and_execute.h"
+#include "common/check.h"
+#include "explorer/main.h"
 #include "testing/file_test/file_test_base.h"
 #include "testing/file_test/file_test_base.h"
 #include "testing/util/test_raw_ostream.h"
 #include "testing/util/test_raw_ostream.h"
 
 
@@ -24,61 +25,65 @@ class ParseAndExecuteTestFile : public FileTestBase {
                     const llvm::SmallVector<TestFile>& test_files,
                     const llvm::SmallVector<TestFile>& test_files,
                     llvm::raw_pwrite_stream& stdout,
                     llvm::raw_pwrite_stream& stdout,
                     llvm::raw_pwrite_stream& stderr) -> bool override {
                     llvm::raw_pwrite_stream& stderr) -> bool override {
-    CARBON_CHECK(test_args.empty())
-        << "ARGS are not currently used in explorer's file_test.";
+    // Create the files in-memory.
+    llvm::vfs::InMemoryFileSystem fs(new llvm::vfs::InMemoryFileSystem());
+    for (const auto& test_file : test_files) {
+      if (!fs.addFile(test_file.filename, /*ModificationTime=*/0,
+                      llvm::MemoryBuffer::getMemBuffer(test_file.content))) {
+        ADD_FAILURE() << "File is repeated: " << test_file.filename;
+        return false;
+      }
+    }
 
 
-    if (test_files.size() != 1) {
-      ADD_FAILURE() << "Only 1 file is supported: " << test_files.size()
-                    << " provided";
+    // Add the prelude.
+    llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> prelude =
+        llvm::MemoryBuffer::getFile("explorer/data/prelude.carbon");
+    if (prelude.getError()) {
+      ADD_FAILURE() << prelude.getError().message();
+      return false;
+    }
+    // TODO: This path is long with a prefix / because of the path expectations
+    // in tests. Change those to allow a shorter path (e.g., `prelude.carbon`)
+    // here.
+    static constexpr llvm::StringLiteral PreludePath =
+        "/explorer/data/prelude.carbon";
+    if (!fs.addFile(PreludePath, /*ModificationTime=*/0, std::move(*prelude))) {
+      ADD_FAILURE() << "Duplicate prelude.carbon";
       return false;
       return false;
     }
     }
 
 
-    // Trace output is only checked for a few tests.
-    bool check_trace_output =
-        path().string().find("/trace_testdata/") != std::string::npos;
-
-    // Capture trace streaming, but only when in debug mode.
-    TraceStream trace_stream;
-    TestRawOstream trace_stream_ostream;
-    if (check_trace_output || absl::GetFlag(FLAGS_trace)) {
-      trace_stream.set_stream(check_trace_output ? &stdout
-                                                 : &trace_stream_ostream);
-      trace_stream.set_allowed_phases({ProgramPhase::All});
-      trace_stream.set_allowed_file_kinds({FileKind::Main});
+    llvm::SmallVector<const char*> args = {"explorer"};
+    for (auto arg : test_args) {
+      args.push_back(arg.data());
     }
     }
+    TestRawOstream trace_stream;
 
 
-    // Set the location of the prelude.
-    char* test_srcdir = getenv("TEST_SRCDIR");
-    CARBON_CHECK(test_srcdir != nullptr);
-    std::string prelude_path(test_srcdir);
-    prelude_path += "/carbon/explorer/data/prelude.carbon";
+    // Trace output is only checked for a few tests.
+    bool check_trace_output =
+        path().string().find("trace_testdata/") != std::string::npos;
 
 
-    // Run the parse. Parser debug output is always off because it's difficult
-    // to redirect.
-    auto result = ParseAndExecute(
-        prelude_path, test_files[0].filename, test_files[0].content,
-        /*parser_debug=*/false, &trace_stream, &stdout);
-    // This mirrors printing currently done by main.cpp.
-    if (result.ok()) {
-      stdout << "result: " << *result << "\n";
-    } else {
-      stderr << result.error() << "\n";
-    }
+    int exit_code = ExplorerMain(
+        args.size(), args.data(), /*install_path=*/"", PreludePath, stdout,
+        stderr, check_trace_output ? stdout : trace_stream, fs);
 
 
     // Skip trace test check as they use stdout stream instead of
     // Skip trace test check as they use stdout stream instead of
     // trace_stream_ostream
     // trace_stream_ostream
     if (absl::GetFlag(FLAGS_trace)) {
     if (absl::GetFlag(FLAGS_trace)) {
-      CARBON_CHECK(!check_trace_output)
-          << "trace tests should only be run in the default mode.";
-      EXPECT_FALSE(trace_stream_ostream.TakeStr().empty())
+      EXPECT_FALSE(trace_stream.TakeStr().empty())
           << "Tracing should always do something";
           << "Tracing should always do something";
     }
     }
 
 
-    return result.ok();
+    return exit_code == EXIT_SUCCESS;
   }
   }
 
 
   auto GetDefaultArgs() -> llvm::SmallVector<std::string> override {
   auto GetDefaultArgs() -> llvm::SmallVector<std::string> override {
-    return {};
+    llvm::SmallVector<std::string> args;
+    if (absl::GetFlag(FLAGS_trace)) {
+      args.push_back("--trace_file=-");
+      args.push_back("--trace_phase=all");
+    }
+    args.push_back("%s");
+    return args;
   }
   }
 };
 };
 
 

+ 3 - 8
explorer/fuzzing/ast_to_proto_main.cpp

@@ -31,15 +31,10 @@ auto Main(int argc, char** argv) -> ErrorOr<Success> {
     return Error("Argument must be a file.");
     return Error("Argument must be a file.");
   }
   }
 
 
-  // Read the input file.
-  std::ifstream proto_file(argv[1]);
-  std::stringstream buffer;
-  buffer << proto_file.rdbuf();
-  proto_file.close();
-
   Arena arena;
   Arena arena;
-  const ErrorOr<AST> ast = Parse(&arena, argv[1], FileKind::Main,
-                                 /*parser_debug=*/false);
+  const ErrorOr<AST> ast =
+      Parse(*llvm::vfs::getRealFileSystem(), &arena, argv[1], FileKind::Main,
+            /*parser_debug=*/false);
   if (!ast.ok()) {
   if (!ast.ok()) {
     return ErrorBuilder() << "Parsing failed: " << ast.error().message();
     return ErrorBuilder() << "Parsing failed: " << ast.error().message();
   }
   }

+ 6 - 6
explorer/fuzzing/ast_to_proto_test.cpp

@@ -99,8 +99,8 @@ TEST(AstToProtoTest, SetsAllProtoFields) {
   Fuzzing::Carbon merged_proto;
   Fuzzing::Carbon merged_proto;
   for (const llvm::StringRef f : *carbon_files) {
   for (const llvm::StringRef f : *carbon_files) {
     Arena arena;
     Arena arena;
-    const ErrorOr<AST> ast =
-        Parse(&arena, f, FileKind::Main, /*parser_debug=*/false);
+    const ErrorOr<AST> ast = Parse(*llvm::vfs::getRealFileSystem(), &arena, f,
+                                   FileKind::Main, /*parser_debug=*/false);
     if (ast.ok()) {
     if (ast.ok()) {
       merged_proto.MergeFrom(AstToProto(*ast));
       merged_proto.MergeFrom(AstToProto(*ast));
     }
     }
@@ -137,8 +137,8 @@ TEST(AstToProtoTest, Roundtrip) {
   int parsed_ok_count = 0;
   int parsed_ok_count = 0;
   for (const llvm::StringRef f : *carbon_files) {
   for (const llvm::StringRef f : *carbon_files) {
     Arena arena;
     Arena arena;
-    const ErrorOr<AST> ast =
-        Parse(&arena, f, FileKind::Main, /*parser_debug=*/false);
+    const ErrorOr<AST> ast = Parse(*llvm::vfs::getRealFileSystem(), &arena, f,
+                                   FileKind::Main, /*parser_debug=*/false);
     if (ast.ok()) {
     if (ast.ok()) {
       ++parsed_ok_count;
       ++parsed_ok_count;
       const std::string source_from_proto =
       const std::string source_from_proto =
@@ -177,8 +177,8 @@ TEST(AstToProtoTest, SameProtoAfterClone) {
   int parsed_ok_count = 0;
   int parsed_ok_count = 0;
   for (const llvm::StringRef f : *carbon_files) {
   for (const llvm::StringRef f : *carbon_files) {
     Arena arena;
     Arena arena;
-    const ErrorOr<AST> ast =
-        Parse(&arena, f, FileKind::Main, /*parser_debug=*/false);
+    const ErrorOr<AST> ast = Parse(*llvm::vfs::getRealFileSystem(), &arena, f,
+                                   FileKind::Main, /*parser_debug=*/false);
     if (ast.ok()) {
     if (ast.ok()) {
       ++parsed_ok_count;
       ++parsed_ok_count;
       const AST clone = CloneAST(arena, *ast);
       const AST clone = CloneAST(arena, *ast);

+ 11 - 1
explorer/fuzzing/fuzzer_util.cpp

@@ -33,14 +33,24 @@ auto GetRunfilesFile(const std::string& file) -> ErrorOr<std::string> {
 }
 }
 
 
 auto ParseAndExecuteProto(const Fuzzing::Carbon& carbon) -> ErrorOr<int> {
 auto ParseAndExecuteProto(const Fuzzing::Carbon& carbon) -> ErrorOr<int> {
+  llvm::vfs::InMemoryFileSystem fs;
+
   const ErrorOr<std::string> prelude_path =
   const ErrorOr<std::string> prelude_path =
       GetRunfilesFile("carbon/explorer/data/prelude.carbon");
       GetRunfilesFile("carbon/explorer/data/prelude.carbon");
   // Can't do anything without a prelude, so it's a fatal error.
   // Can't do anything without a prelude, so it's a fatal error.
   CARBON_CHECK(prelude_path.ok()) << prelude_path.error();
   CARBON_CHECK(prelude_path.ok()) << prelude_path.error();
+  llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> prelude =
+      llvm::MemoryBuffer::getFile(*prelude_path);
+  CARBON_CHECK(!prelude.getError()) << prelude.getError().message();
+  CARBON_CHECK(fs.addFile("prelude.carbon", /*ModificationTime=*/0,
+                          std::move(*prelude)));
 
 
   const std::string source = ProtoToCarbon(carbon, /*maybe_add_main=*/true);
   const std::string source = ProtoToCarbon(carbon, /*maybe_add_main=*/true);
+  CARBON_CHECK(fs.addFile("fuzzer.carbon", /*ModificationTime=*/0,
+                          llvm::MemoryBuffer::getMemBuffer(source)));
+
   TraceStream trace_stream;
   TraceStream trace_stream;
-  return ParseAndExecute(*prelude_path, "fuzzer.carbon", source,
+  return ParseAndExecute(fs, "prelude.carbon", "fuzzer.carbon",
                          /*parser_debug=*/false, &trace_stream, &llvm::nulls());
                          /*parser_debug=*/false, &trace_stream, &llvm::nulls());
 }
 }
 
 

+ 25 - 10
explorer/main.cpp

@@ -17,6 +17,7 @@
 #include "common/error.h"
 #include "common/error.h"
 #include "explorer/common/trace_stream.h"
 #include "explorer/common/trace_stream.h"
 #include "explorer/parse_and_execute/parse_and_execute.h"
 #include "explorer/parse_and_execute/parse_and_execute.h"
+#include "llvm/ADT/ScopeExit.h"
 #include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/SmallString.h"
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/FileSystem.h"
 #include "llvm/Support/FileSystem.h"
@@ -32,7 +33,6 @@ namespace path = llvm::sys::path;
 auto ExplorerMain(int argc, char** argv, void* static_for_main_addr,
 auto ExplorerMain(int argc, char** argv, void* static_for_main_addr,
                   llvm::StringRef relative_prelude_path) -> int {
                   llvm::StringRef relative_prelude_path) -> int {
   llvm::setBugReportMsg(
   llvm::setBugReportMsg(
-
       "Please report issues to "
       "Please report issues to "
       "https://github.com/carbon-language/carbon-lang/issues and include the "
       "https://github.com/carbon-language/carbon-lang/issues and include the "
       "crash backtrace.\n");
       "crash backtrace.\n");
@@ -42,6 +42,20 @@ auto ExplorerMain(int argc, char** argv, void* static_for_main_addr,
   // is piped to stdout.
   // is piped to stdout.
   llvm::errs().tie(&llvm::outs());
   llvm::errs().tie(&llvm::outs());
 
 
+  std::string exe =
+      llvm::sys::fs::getMainExecutable(argv[0], static_for_main_addr);
+  llvm::StringRef install_path = path::parent_path(exe);
+
+  return ExplorerMain(argc, const_cast<const char**>(argv), install_path,
+                      relative_prelude_path, llvm::outs(), llvm::errs(),
+                      llvm::outs(), *llvm::vfs::getRealFileSystem());
+}
+
+auto ExplorerMain(int argc, const char** argv, llvm::StringRef install_path,
+                  llvm::StringRef relative_prelude_path,
+                  llvm::raw_ostream& out_stream, llvm::raw_ostream& err_stream,
+                  llvm::raw_ostream& out_stream_for_trace,
+                  llvm::vfs::FileSystem& fs) -> int {
   cl::opt<std::string> input_file_name(cl::Positional, cl::desc("<input file>"),
   cl::opt<std::string> input_file_name(cl::Positional, cl::desc("<input file>"),
                                        cl::Required);
                                        cl::Required);
   cl::opt<bool> parser_debug("parser_debug",
   cl::opt<bool> parser_debug("parser_debug",
@@ -98,10 +112,9 @@ auto ExplorerMain(int argc, char** argv, void* static_for_main_addr,
                      "Include trace output for all files")),
                      "Include trace output for all files")),
       cl::CommaSeparated);
       cl::CommaSeparated);
 
 
+  CARBON_CHECK(argc > 0);
+
   // Use the executable path as a base for the relative prelude path.
   // Use the executable path as a base for the relative prelude path.
-  std::string exe =
-      llvm::sys::fs::getMainExecutable(argv[0], static_for_main_addr);
-  llvm::StringRef install_path = path::parent_path(exe);
   llvm::SmallString<256> default_prelude_file(install_path);
   llvm::SmallString<256> default_prelude_file(install_path);
   path::append(default_prelude_file,
   path::append(default_prelude_file,
                path::begin(relative_prelude_path, path::Style::posix),
                path::begin(relative_prelude_path, path::Style::posix),
@@ -111,6 +124,8 @@ auto ExplorerMain(int argc, char** argv, void* static_for_main_addr,
                                          cl::init(default_prelude_file_str));
                                          cl::init(default_prelude_file_str));
 
 
   cl::ParseCommandLineOptions(argc, argv);
   cl::ParseCommandLineOptions(argc, argv);
+  auto reset_parser =
+      llvm::make_scope_exit([] { cl::ResetCommandLineParser(); });
 
 
   // Set up a stream for trace output.
   // Set up a stream for trace output.
   std::unique_ptr<llvm::raw_ostream> scoped_trace_stream;
   std::unique_ptr<llvm::raw_ostream> scoped_trace_stream;
@@ -147,13 +162,13 @@ auto ExplorerMain(int argc, char** argv, void* static_for_main_addr,
     trace_stream.set_allowed_file_kinds(trace_file_kinds);
     trace_stream.set_allowed_file_kinds(trace_file_kinds);
 
 
     if (trace_file_name == "-") {
     if (trace_file_name == "-") {
-      trace_stream.set_stream(&llvm::outs());
+      trace_stream.set_stream(&out_stream_for_trace);
     } else {
     } else {
       std::error_code err;
       std::error_code err;
       scoped_trace_stream =
       scoped_trace_stream =
           std::make_unique<llvm::raw_fd_ostream>(trace_file_name, err);
           std::make_unique<llvm::raw_fd_ostream>(trace_file_name, err);
       if (err) {
       if (err) {
-        llvm::errs() << err.message() << "\n";
+        err_stream << err.message() << "\n";
         return EXIT_FAILURE;
         return EXIT_FAILURE;
       }
       }
       trace_stream.set_stream(scoped_trace_stream.get());
       trace_stream.set_stream(scoped_trace_stream.get());
@@ -161,14 +176,14 @@ auto ExplorerMain(int argc, char** argv, void* static_for_main_addr,
   }
   }
 
 
   ErrorOr<int> result =
   ErrorOr<int> result =
-      ParseAndExecuteFile(prelude_file_name, input_file_name, parser_debug,
-                          &trace_stream, &llvm::outs());
+      ParseAndExecute(fs, prelude_file_name, input_file_name, parser_debug,
+                      &trace_stream, &out_stream);
   if (result.ok()) {
   if (result.ok()) {
     // Print the return code to stdout.
     // Print the return code to stdout.
-    llvm::outs() << "result: " << *result << "\n";
+    out_stream << "result: " << *result << "\n";
     return EXIT_SUCCESS;
     return EXIT_SUCCESS;
   } else {
   } else {
-    llvm::errs() << result.error() << "\n";
+    err_stream << result.error() << "\n";
     return EXIT_FAILURE;
     return EXIT_FAILURE;
   }
   }
 }
 }

+ 15 - 0
explorer/main.h

@@ -5,7 +5,9 @@
 #ifndef CARBON_EXPLORER_MAIN_H_
 #ifndef CARBON_EXPLORER_MAIN_H_
 #define CARBON_EXPLORER_MAIN_H_
 #define CARBON_EXPLORER_MAIN_H_
 
 
+#include "common/ostream.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/Support/VirtualFileSystem.h"
 
 
 namespace Carbon {
 namespace Carbon {
 
 
@@ -14,6 +16,19 @@ namespace Carbon {
 auto ExplorerMain(int argc, char** argv, void* static_for_main_addr,
 auto ExplorerMain(int argc, char** argv, void* static_for_main_addr,
                   llvm::StringRef relative_prelude_path) -> int;
                   llvm::StringRef relative_prelude_path) -> int;
 
 
+// Wrapped by the above, but without some process init. This is used directly by
+// tests, whereas the above is used directly by main binaries. The
+// out_stream_for_trace is only used when `--trace_file=-` is specified.
+//
+// TODO: We use argc/argv for parameters because command line parsing requires
+// it. When that's replaced, we should switch to
+// llvm::SmallVector<llvm::StringRef> args because it'll work better with tests.
+auto ExplorerMain(int argc, const char** argv, llvm::StringRef install_path,
+                  llvm::StringRef relative_prelude_path,
+                  llvm::raw_ostream& out_stream, llvm::raw_ostream& err_stream,
+                  llvm::raw_ostream& out_stream_for_trace,
+                  llvm::vfs::FileSystem& fs) -> int;
+
 }  // namespace Carbon
 }  // namespace Carbon
 
 
 #endif  // CARBON_EXPLORER_MAIN_H_
 #endif  // CARBON_EXPLORER_MAIN_H_

+ 7 - 31
explorer/parse_and_execute/parse_and_execute.cpp

@@ -6,7 +6,6 @@
 
 
 #include <locale>
 #include <locale>
 
 
-#include "common/check.h"
 #include "common/error.h"
 #include "common/error.h"
 #include "explorer/common/trace_stream.h"
 #include "explorer/common/trace_stream.h"
 #include "explorer/interpreter/exec_program.h"
 #include "explorer/interpreter/exec_program.h"
@@ -37,22 +36,22 @@ static auto PrintTimingOnExit(TraceStream* trace_stream, const char* label,
   return exit_scope_function;
   return exit_scope_function;
 }
 }
 
 
-static auto ParseAndExecuteHelper(std::function<ErrorOr<AST>(Arena*)> parse,
-                                  std::string_view prelude_path,
-                                  Nonnull<TraceStream*> trace_stream,
-                                  Nonnull<llvm::raw_ostream*> print_stream)
-    -> ErrorOr<int> {
+auto ParseAndExecute(llvm::vfs::FileSystem& fs, std::string_view prelude_path,
+                     std::string_view input_file_name, bool parser_debug,
+                     Nonnull<TraceStream*> trace_stream,
+                     Nonnull<llvm::raw_ostream*> print_stream) -> ErrorOr<int> {
   return RunWithExtraStack([&]() -> ErrorOr<int> {
   return RunWithExtraStack([&]() -> ErrorOr<int> {
     Arena arena;
     Arena arena;
     auto cursor = std::chrono::steady_clock::now();
     auto cursor = std::chrono::steady_clock::now();
 
 
-    ErrorOr<AST> parse_result = parse(&arena);
+    ErrorOr<AST> parse_result =
+        Parse(fs, &arena, input_file_name, FileKind::Main, parser_debug);
     auto print_parse_time = PrintTimingOnExit(trace_stream, "Parse", &cursor);
     auto print_parse_time = PrintTimingOnExit(trace_stream, "Parse", &cursor);
     if (!parse_result.ok()) {
     if (!parse_result.ok()) {
       return ErrorBuilder() << "SYNTAX ERROR: " << parse_result.error();
       return ErrorBuilder() << "SYNTAX ERROR: " << parse_result.error();
     }
     }
 
 
-    AddPrelude(prelude_path, &arena, &parse_result->declarations,
+    AddPrelude(fs, prelude_path, &arena, &parse_result->declarations,
                &parse_result->num_prelude_declarations);
                &parse_result->num_prelude_declarations);
     auto print_prelude_time =
     auto print_prelude_time =
         PrintTimingOnExit(trace_stream, "AddPrelude", &cursor);
         PrintTimingOnExit(trace_stream, "AddPrelude", &cursor);
@@ -87,27 +86,4 @@ static auto ParseAndExecuteHelper(std::function<ErrorOr<AST>(Arena*)> parse,
   });
   });
 }
 }
 
 
-auto ParseAndExecuteFile(std::string_view prelude_path,
-                         std::string_view input_file_name, bool parser_debug,
-                         Nonnull<TraceStream*> trace_stream,
-                         Nonnull<llvm::raw_ostream*> print_stream)
-    -> ErrorOr<int> {
-  auto parse = [&](Arena* arena) {
-    return Parse(arena, input_file_name, FileKind::Main, parser_debug);
-  };
-  return ParseAndExecuteHelper(parse, prelude_path, trace_stream, print_stream);
-}
-
-auto ParseAndExecute(std::string_view prelude_path,
-                     std::string_view input_file_name,
-                     std::string_view file_contents, bool parser_debug,
-                     Nonnull<TraceStream*> trace_stream,
-                     Nonnull<llvm::raw_ostream*> print_stream) -> ErrorOr<int> {
-  auto parse = [&](Arena* arena) {
-    return ParseFromString(arena, input_file_name, FileKind::Main,
-                           file_contents, parser_debug);
-  };
-  return ParseAndExecuteHelper(parse, prelude_path, trace_stream, print_stream);
-}
-
 }  // namespace Carbon
 }  // namespace Carbon

+ 3 - 12
explorer/parse_and_execute/parse_and_execute.h

@@ -7,22 +7,13 @@
 
 
 #include "common/error.h"
 #include "common/error.h"
 #include "explorer/common/trace_stream.h"
 #include "explorer/common/trace_stream.h"
+#include "llvm/Support/VirtualFileSystem.h"
 
 
 namespace Carbon {
 namespace Carbon {
 
 
 // Parses and executes the input file, returning the program result on success.
 // Parses and executes the input file, returning the program result on success.
-// This API is intended for use by main execution.
-auto ParseAndExecuteFile(std::string_view prelude_path,
-                         std::string_view input_file_name, bool parser_debug,
-                         Nonnull<TraceStream*> trace_stream,
-                         Nonnull<llvm::raw_ostream*> print_stream)
-    -> ErrorOr<int>;
-
-// Parses and executes the source, returning the program result on success.
-// Discards output.
-auto ParseAndExecute(std::string_view prelude_path,
-                     std::string_view input_file_name,
-                     std::string_view file_contents, bool parser_debug,
+auto ParseAndExecute(llvm::vfs::FileSystem& fs, std::string_view prelude_path,
+                     std::string_view input_file_name, bool parser_debug,
                      Nonnull<TraceStream*> trace_stream,
                      Nonnull<TraceStream*> trace_stream,
                      Nonnull<llvm::raw_ostream*> print_stream) -> ErrorOr<int>;
                      Nonnull<llvm::raw_ostream*> print_stream) -> ErrorOr<int>;
 
 

+ 11 - 2
explorer/parse_and_execute/parse_and_execute_test.cpp

@@ -10,10 +10,16 @@
 namespace Carbon::Testing {
 namespace Carbon::Testing {
 namespace {
 namespace {
 
 
-using ::testing::Eq;
 using ::testing::MatchesRegex;
 using ::testing::MatchesRegex;
 
 
 TEST(ParseAndExecuteTest, Recursion) {
 TEST(ParseAndExecuteTest, Recursion) {
+  llvm::vfs::InMemoryFileSystem fs;
+  llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> prelude =
+      llvm::MemoryBuffer::getFile("explorer/data/prelude.carbon");
+  ASSERT_FALSE(prelude.getError()) << prelude.getError().message();
+  ASSERT_TRUE(fs.addFile("prelude.carbon", /*ModificationTime=*/0,
+                         std::move(*prelude)));
+
   std::string source = R"(
   std::string source = R"(
     package Test api;
     package Test api;
     fn Main() -> i32 {
     fn Main() -> i32 {
@@ -32,9 +38,12 @@ TEST(ParseAndExecuteTest, Recursion) {
         ;
         ;
     }
     }
   )";
   )";
+  ASSERT_TRUE(fs.addFile("test.carbon", /*ModificationTime=*/0,
+                         llvm::MemoryBuffer::getMemBuffer(source)));
+
   TraceStream trace_stream;
   TraceStream trace_stream;
   auto err =
   auto err =
-      ParseAndExecute("explorer/data/prelude.carbon", "test.carbon", source,
+      ParseAndExecute(fs, "prelude.carbon", "test.carbon",
                       /*parser_debug=*/false, &trace_stream, &llvm::nulls());
                       /*parser_debug=*/false, &trace_stream, &llvm::nulls());
   ASSERT_FALSE(err.ok());
   ASSERT_FALSE(err.ok());
   // Don't expect any particular source location for the error.
   // Don't expect any particular source location for the error.

+ 1 - 0
explorer/syntax/BUILD

@@ -44,6 +44,7 @@ cc_library(
         "//explorer/ast",
         "//explorer/ast",
         "//explorer/common:arena",
         "//explorer/common:arena",
         "//explorer/common:nonnull",
         "//explorer/common:nonnull",
+        "@llvm-project//llvm:Support",
     ],
     ],
 )
 )
 
 

+ 25 - 21
explorer/syntax/parse.cpp

@@ -10,7 +10,6 @@
 #include "explorer/syntax/lexer.h"
 #include "explorer/syntax/lexer.h"
 #include "explorer/syntax/parse_and_lex_context.h"
 #include "explorer/syntax/parse_and_lex_context.h"
 #include "explorer/syntax/parser.h"
 #include "explorer/syntax/parser.h"
-#include "llvm/Support/Error.h"
 
 
 namespace Carbon {
 namespace Carbon {
 
 
@@ -42,30 +41,35 @@ static auto ParseImpl(yyscan_t scanner, Nonnull<Arena*> arena,
   return *ast;
   return *ast;
 }
 }
 
 
-auto Parse(Nonnull<Arena*> arena, std::string_view input_file_name,
-           FileKind file_kind, bool parser_debug) -> ErrorOr<AST> {
-  std::string name_str(input_file_name);
-  FILE* input_file = fopen(name_str.c_str(), "r");
-  if (input_file == nullptr) {
-    return ProgramError(SourceLocation(name_str.c_str(), 0, file_kind))
-           << "Error opening file: " << std::strerror(errno);
+auto Parse(llvm::vfs::FileSystem& fs, Nonnull<Arena*> arena,
+           std::string_view input_file_name, FileKind file_kind,
+           bool parser_debug) -> ErrorOr<AST> {
+  llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>> input_file =
+      fs.openFileForRead(input_file_name);
+  if (input_file.getError()) {
+    return ProgramError(SourceLocation(input_file_name, 0, file_kind))
+           << "Error opening file: " << input_file.getError().message();
   }
   }
 
 
-  // Prepare the lexer.
-  yyscan_t scanner;
-  yylex_init(&scanner);
-  auto* buffer = yy_create_buffer(input_file, YY_BUF_SIZE, scanner);
-  yy_switch_to_buffer(buffer, scanner);
-
-  ErrorOr<AST> result =
-      ParseImpl(scanner, arena, input_file_name, file_kind, parser_debug);
+  llvm::ErrorOr<llvm::vfs::Status> status = (*input_file)->status();
+  if (status.getError()) {
+    return Error(status.getError().message());
+  }
+  auto size = status->getSize();
+  if (size >= std::numeric_limits<int32_t>::max()) {
+    return ProgramError(SourceLocation(input_file_name, 0, file_kind))
+           << "File is over the 2GiB input limit.";
+  }
 
 
-  // Clean up the lexer.
-  yy_delete_buffer(buffer, scanner);
-  yylex_destroy(scanner);
-  fclose(input_file);
+  llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> buffer =
+      (*input_file)
+          ->getBuffer(input_file_name, size, /*RequiresNullTerminator=*/false);
+  if (buffer.getError()) {
+    return Error(buffer.getError().message());
+  }
 
 
-  return result;
+  return ParseFromString(arena, input_file_name, file_kind,
+                         (*buffer)->getBuffer(), parser_debug);
 }
 }
 
 
 auto ParseFromString(Nonnull<Arena*> arena, std::string_view input_file_name,
 auto ParseFromString(Nonnull<Arena*> arena, std::string_view input_file_name,

+ 4 - 2
explorer/syntax/parse.h

@@ -11,13 +11,15 @@
 #include "explorer/ast/ast.h"
 #include "explorer/ast/ast.h"
 #include "explorer/common/arena.h"
 #include "explorer/common/arena.h"
 #include "explorer/common/source_location.h"
 #include "explorer/common/source_location.h"
+#include "llvm/Support/VirtualFileSystem.h"
 
 
 namespace Carbon {
 namespace Carbon {
 
 
 // Returns the AST representing the contents of the named file, or an error code
 // Returns the AST representing the contents of the named file, or an error code
 // if parsing fails. Allocations go into the provided arena.
 // if parsing fails. Allocations go into the provided arena.
-auto Parse(Nonnull<Arena*> arena, std::string_view input_file_name,
-           FileKind file_kind, bool parser_debug) -> ErrorOr<Carbon::AST>;
+auto Parse(llvm::vfs::FileSystem& fs, Nonnull<Arena*> arena,
+           std::string_view input_file_name, FileKind file_kind,
+           bool parser_debug) -> ErrorOr<Carbon::AST>;
 
 
 // Equivalent to `Parse`, but parses the contents of `file_contents`.
 // Equivalent to `Parse`, but parses the contents of `file_contents`.
 // `input_file_name` is used only for reporting source locations, and does
 // `input_file_name` is used only for reporting source locations, and does

+ 4 - 3
explorer/syntax/prelude.cpp

@@ -9,15 +9,16 @@
 namespace Carbon {
 namespace Carbon {
 
 
 // Adds the Carbon prelude to `declarations`.
 // Adds the Carbon prelude to `declarations`.
-void AddPrelude(std::string_view prelude_file_name, Nonnull<Arena*> arena,
+void AddPrelude(llvm::vfs::FileSystem& fs, std::string_view prelude_file_name,
+                Nonnull<Arena*> arena,
                 std::vector<Nonnull<Declaration*>>* declarations,
                 std::vector<Nonnull<Declaration*>>* declarations,
                 int* num_prelude_declarations) {
                 int* num_prelude_declarations) {
   ErrorOr<AST> parse_result =
   ErrorOr<AST> parse_result =
-      Parse(arena, prelude_file_name, FileKind::Prelude, false);
+      Parse(fs, arena, prelude_file_name, FileKind::Prelude, false);
   if (!parse_result.ok()) {
   if (!parse_result.ok()) {
     // Try again with tracing, to help diagnose the problem.
     // Try again with tracing, to help diagnose the problem.
     ErrorOr<AST> trace_parse_result =
     ErrorOr<AST> trace_parse_result =
-        Parse(arena, prelude_file_name, FileKind::Prelude, true);
+        Parse(fs, arena, prelude_file_name, FileKind::Prelude, true);
     CARBON_FATAL() << "Failed to parse prelude:\n"
     CARBON_FATAL() << "Failed to parse prelude:\n"
                    << trace_parse_result.error();
                    << trace_parse_result.error();
   }
   }

+ 3 - 1
explorer/syntax/prelude.h

@@ -10,11 +10,13 @@
 #include "explorer/ast/declaration.h"
 #include "explorer/ast/declaration.h"
 #include "explorer/common/arena.h"
 #include "explorer/common/arena.h"
 #include "explorer/common/nonnull.h"
 #include "explorer/common/nonnull.h"
+#include "llvm/Support/VirtualFileSystem.h"
 
 
 namespace Carbon {
 namespace Carbon {
 
 
 // Adds the Carbon prelude to `declarations`.
 // Adds the Carbon prelude to `declarations`.
-void AddPrelude(std::string_view prelude_file_name, Nonnull<Arena*> arena,
+void AddPrelude(llvm::vfs::FileSystem& fs, std::string_view prelude_file_name,
+                Nonnull<Arena*> arena,
                 std::vector<Nonnull<Declaration*>>* declarations,
                 std::vector<Nonnull<Declaration*>>* declarations,
                 int* num_prelude_declarations);
                 int* num_prelude_declarations);
 
 

+ 1 - 0
explorer/trace_testdata/full_trace.carbon

@@ -18,6 +18,7 @@ fn Main() -> i32 {
 }
 }
 
 
 // Place checks after code so that line numbers are stable, reducing merge conflicts.
 // Place checks after code so that line numbers are stable, reducing merge conflicts.
+// ARGS: --trace_file=- --trace_phase=all %s
 // AUTOUPDATE
 // AUTOUPDATE
 // CHECK:STDOUT: ********** source program **********
 // CHECK:STDOUT: ********** source program **********
 // CHECK:STDOUT: interface TestInterface {
 // CHECK:STDOUT: interface TestInterface {

+ 0 - 1
toolchain/driver/driver_file_test_base.h

@@ -8,7 +8,6 @@
 #include <cstdio>
 #include <cstdio>
 #include <fstream>
 #include <fstream>
 
 
-#include "llvm/ADT/ScopeExit.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/MemoryBuffer.h"
 #include "llvm/Support/MemoryBuffer.h"