mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 22:02:23 +01:00
This could probably go further, but I'm just trying to get the ability to run some significant tests without the prelude. This already requires some migration of `i32` and `bool` to non-prelude-dependent types (technically not at the moment, but I'm trying to stick with the philosophical model of the prelude). Note this undoes part of #3895 which had made all lex/parse tests depend on the prelude -- I don't think that was a necessary change. Rather, it seems better to isolate individual lex/parse tests from the prelude. I'd been on the fence in that PR (it was fixing one of the issues that contributed to wanting to not include the prelude), but now it feels more consistent. The prelude could have a parse error and we shouldn't break every parse test on that.
164 lines
6.1 KiB
C++
164 lines
6.1 KiB
C++
// 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 CARBON_TOOLCHAIN_DRIVER_DRIVER_FILE_TEST_BASE_H_
|
|
#define CARBON_TOOLCHAIN_DRIVER_DRIVER_FILE_TEST_BASE_H_
|
|
|
|
#include <string>
|
|
|
|
#include "common/error.h"
|
|
#include "llvm/ADT/STLExtras.h"
|
|
#include "llvm/ADT/SmallVector.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "llvm/Support/FormatVariadic.h"
|
|
#include "llvm/Support/VirtualFileSystem.h"
|
|
#include "testing/file_test/file_test_base.h"
|
|
#include "toolchain/driver/driver.h"
|
|
|
|
namespace Carbon::Testing {
|
|
namespace {
|
|
|
|
// Provides common test support for the driver. This is used by file tests in
|
|
// phase subdirectories.
|
|
class ToolchainFileTest : public FileTestBase {
|
|
public:
|
|
explicit ToolchainFileTest(llvm::StringRef test_name)
|
|
: FileTestBase(test_name), component_(GetComponent(test_name)) {}
|
|
|
|
auto Run(const llvm::SmallVector<llvm::StringRef>& test_args,
|
|
llvm::vfs::InMemoryFileSystem& fs, llvm::raw_pwrite_stream& stdout,
|
|
llvm::raw_pwrite_stream& stderr) -> ErrorOr<RunResult> override {
|
|
const llvm::StringRef data_dir = "";
|
|
|
|
auto prelude = Driver::FindPreludeFiles(data_dir, stderr);
|
|
if (prelude.empty()) {
|
|
return Error("Could not find prelude");
|
|
}
|
|
for (const auto& file : prelude) {
|
|
CARBON_RETURN_IF_ERROR(AddFile(fs, file));
|
|
}
|
|
|
|
Driver driver(fs, data_dir, stdout, stderr);
|
|
auto driver_result = driver.RunCommand(test_args);
|
|
|
|
RunResult result{
|
|
.success = driver_result.success,
|
|
.per_file_success = std::move(driver_result.per_file_success)};
|
|
// Drop entries that don't look like a file, and entries corresponding to
|
|
// the prelude. Note this can empty out the list.
|
|
llvm::erase_if(result.per_file_success,
|
|
[&](std::pair<llvm::StringRef, bool> entry) {
|
|
return entry.first == "." || entry.first == "-" ||
|
|
entry.first.starts_with("not_file") ||
|
|
llvm::is_contained(prelude, entry.first);
|
|
});
|
|
return result;
|
|
}
|
|
|
|
auto GetDefaultArgs() -> llvm::SmallVector<std::string> override {
|
|
llvm::SmallVector<std::string> args = {"compile",
|
|
"--phase=" + component_.str()};
|
|
|
|
if (component_ == "lex") {
|
|
args.push_back("--dump-tokens");
|
|
} else if (component_ == "parse") {
|
|
args.push_back("--dump-parse-tree");
|
|
} else if (component_ == "check") {
|
|
args.push_back("--dump-sem-ir");
|
|
} else if (component_ == "lower") {
|
|
args.push_back("--dump-llvm-ir");
|
|
} else {
|
|
CARBON_FATAL() << "Unexpected test component " << component_ << ": "
|
|
<< test_name();
|
|
}
|
|
|
|
// For `lex` and `parse`, we don't need to import the prelude; exclude it to
|
|
// focus errors. In other phases we only do this for explicit "no_prelude"
|
|
// tests.
|
|
if (component_ == "lex" || component_ == "parse" ||
|
|
test_name().find("/no_prelude/") != llvm::StringRef::npos) {
|
|
args.push_back("--no-prelude-import");
|
|
}
|
|
|
|
args.insert(args.end(), {"--exclude-dump-file-prefix=core/", "%s"});
|
|
return args;
|
|
}
|
|
|
|
auto GetDefaultFileRE(llvm::ArrayRef<llvm::StringRef> filenames)
|
|
-> std::optional<RE2> override {
|
|
if (component_ == "lex") {
|
|
return std::make_optional<RE2>(
|
|
llvm::formatv(R"(^- filename: ({0})$)", llvm::join(filenames, "|")));
|
|
}
|
|
return FileTestBase::GetDefaultFileRE(filenames);
|
|
}
|
|
|
|
auto GetLineNumberReplacements(llvm::ArrayRef<llvm::StringRef> filenames)
|
|
-> llvm::SmallVector<LineNumberReplacement> override {
|
|
auto replacements = FileTestBase::GetLineNumberReplacements(filenames);
|
|
if (component_ == "lex") {
|
|
replacements.push_back({.has_file = false,
|
|
.re = std::make_shared<RE2>(R"(line: (\s*\d+))"),
|
|
// The `{{{{` becomes `{{`.
|
|
.line_formatv = "{{{{ *}}{0}"});
|
|
}
|
|
return replacements;
|
|
}
|
|
|
|
auto DoExtraCheckReplacements(std::string& check_line) -> void override {
|
|
if (component_ == "driver") {
|
|
// TODO: Disable token output, it's not interesting for these tests.
|
|
if (llvm::StringRef(check_line).starts_with("// CHECK:STDOUT: {")) {
|
|
check_line = "// CHECK:STDOUT: {{.*}}";
|
|
}
|
|
} else if (component_ == "lex") {
|
|
// Both FileStart and FileEnd regularly have locations on CHECK
|
|
// comment lines that don't work correctly. The line happens to be correct
|
|
// for the FileEnd, but we need to avoid checking the column.
|
|
// The column happens to be right for FileStart, but the line is wrong.
|
|
static RE2 file_token_re(
|
|
R"((FileEnd.*column: |FileStart.*line: )( *\d+))");
|
|
RE2::Replace(&check_line, file_token_re, R"(\1{{ *\\d+}})");
|
|
} else {
|
|
return FileTestBase::DoExtraCheckReplacements(check_line);
|
|
}
|
|
}
|
|
|
|
private:
|
|
// Adds a file to the fs.
|
|
auto AddFile(llvm::vfs::InMemoryFileSystem& fs, llvm::StringRef path)
|
|
-> ErrorOr<Success> {
|
|
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> file =
|
|
llvm::MemoryBuffer::getFile(path);
|
|
if (file.getError()) {
|
|
return ErrorBuilder() << file.getError().message();
|
|
}
|
|
if (!fs.addFile(path, /*ModificationTime=*/0, std::move(*file))) {
|
|
return ErrorBuilder() << "Duplicate file: " << path;
|
|
}
|
|
return Success();
|
|
}
|
|
|
|
// Returns the toolchain subdirectory being tested.
|
|
static auto GetComponent(llvm::StringRef test_name) -> llvm::StringRef {
|
|
// This handles cases where the toolchain directory may be copied into a
|
|
// repository that doesn't put it at the root.
|
|
auto pos = test_name.find("toolchain/");
|
|
CARBON_CHECK(pos != llvm::StringRef::npos) << test_name;
|
|
test_name = test_name.drop_front(pos + strlen("toolchain/"));
|
|
test_name = test_name.take_front(test_name.find("/"));
|
|
return test_name;
|
|
}
|
|
|
|
const llvm::StringRef component_;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
CARBON_FILE_TEST_FACTORY(ToolchainFileTest);
|
|
|
|
} // namespace Carbon::Testing
|
|
|
|
#endif // CARBON_TOOLCHAIN_DRIVER_DRIVER_FILE_TEST_BASE_H_
|