mirror of
https://github.com/ziglang/zig.git
synced 2026-02-13 04:48:20 +00:00
Step.Compile: differentiate between fuzzy and exact matches for compile errors
This commit is contained in:
parent
55c7a6d99d
commit
bc081901dc
@ -415,7 +415,7 @@ pub fn evalZigProcess(
|
||||
.Exited => {
|
||||
// Note that the exit code may be 0 in this case due to the
|
||||
// compiler server protocol.
|
||||
if (compile.expect_errors.len != 0 and s.result_error_bundle.errorMessageCount() > 0) {
|
||||
if (compile.expect_errors != null and s.result_error_bundle.errorMessageCount() > 0) {
|
||||
return error.NeedCompileErrorCheck;
|
||||
}
|
||||
},
|
||||
|
||||
@ -204,10 +204,10 @@ use_llvm: ?bool,
|
||||
use_lld: ?bool,
|
||||
|
||||
/// This is an advanced setting that can change the intent of this Compile step.
|
||||
/// If this slice has nonzero length, it means that this Compile step exists to
|
||||
/// If this value is non-null, it means that this Compile step exists to
|
||||
/// check for compile errors and return *success* if they match, and failure
|
||||
/// otherwise.
|
||||
expect_errors: []const []const u8 = &.{},
|
||||
expect_errors: ?ExpectedCompileErrors = null,
|
||||
|
||||
emit_directory: ?*GeneratedFile,
|
||||
|
||||
@ -220,6 +220,11 @@ generated_llvm_bc: ?*GeneratedFile,
|
||||
generated_llvm_ir: ?*GeneratedFile,
|
||||
generated_h: ?*GeneratedFile,
|
||||
|
||||
pub const ExpectedCompileErrors = union(enum) {
|
||||
contains: []const u8,
|
||||
exact: []const []const u8,
|
||||
};
|
||||
|
||||
pub const CSourceFiles = struct {
|
||||
dependency: ?*std.Build.Dependency,
|
||||
/// If `dependency` is not null relative to it,
|
||||
@ -2131,7 +2136,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
|
||||
|
||||
const maybe_output_bin_path = step.evalZigProcess(zig_args.items, prog_node) catch |err| switch (err) {
|
||||
error.NeedCompileErrorCheck => {
|
||||
assert(self.expect_errors.len != 0);
|
||||
assert(self.expect_errors != null);
|
||||
try checkCompileErrors(self);
|
||||
return;
|
||||
},
|
||||
@ -2390,52 +2395,70 @@ fn checkCompileErrors(self: *Compile) !void {
|
||||
|
||||
// Render the expected lines into a string that we can compare verbatim.
|
||||
var expected_generated = std.ArrayList(u8).init(arena);
|
||||
const expect_errors = self.expect_errors.?;
|
||||
|
||||
var actual_line_it = mem.splitScalar(u8, actual_stderr, '\n');
|
||||
for (self.expect_errors) |expect_line| {
|
||||
const actual_line = actual_line_it.next() orelse {
|
||||
try expected_generated.appendSlice(expect_line);
|
||||
try expected_generated.append('\n');
|
||||
continue;
|
||||
};
|
||||
if (mem.endsWith(u8, actual_line, expect_line)) {
|
||||
try expected_generated.appendSlice(actual_line);
|
||||
try expected_generated.append('\n');
|
||||
continue;
|
||||
}
|
||||
if (mem.startsWith(u8, expect_line, ":?:?: ")) {
|
||||
if (mem.endsWith(u8, actual_line, expect_line[":?:?: ".len..])) {
|
||||
try expected_generated.appendSlice(actual_line);
|
||||
try expected_generated.append('\n');
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// We scan for /?/ in expected line and if there is a match, we match everything
|
||||
// up to and after /?/.
|
||||
const expect_line_trim = mem.trim(u8, expect_line, " ");
|
||||
if (mem.indexOf(u8, expect_line_trim, "/?/")) |exp_index| {
|
||||
const actual_line_trim = mem.trim(u8, actual_line, " ");
|
||||
const exp_lhs = expect_line_trim[0..exp_index];
|
||||
const exp_rhs = expect_line_trim[exp_index + "/?/".len ..];
|
||||
if (mem.startsWith(u8, actual_line_trim, exp_lhs) and mem.endsWith(u8, actual_line_trim, exp_rhs)) {
|
||||
try expected_generated.appendSlice(actual_line);
|
||||
try expected_generated.append('\n');
|
||||
continue;
|
||||
}
|
||||
}
|
||||
try expected_generated.appendSlice(expect_line);
|
||||
try expected_generated.append('\n');
|
||||
}
|
||||
|
||||
if (mem.eql(u8, expected_generated.items, actual_stderr)) return;
|
||||
|
||||
// TODO merge this with the testing.expectEqualStrings logic, and also CheckFile
|
||||
return self.step.fail(
|
||||
\\
|
||||
\\========= expected: =====================
|
||||
\\{s}
|
||||
\\========= but found: ====================
|
||||
\\{s}
|
||||
\\=========================================
|
||||
, .{ expected_generated.items, actual_stderr });
|
||||
switch (expect_errors) {
|
||||
.contains => |expect_line| {
|
||||
while (actual_line_it.next()) |actual_line| {
|
||||
if (!matchCompileError(actual_line, expect_line)) continue;
|
||||
return;
|
||||
}
|
||||
|
||||
return self.step.fail(
|
||||
\\
|
||||
\\========= should contain: ===============
|
||||
\\{s}
|
||||
\\========= but not found: ================
|
||||
\\{s}
|
||||
\\=========================================
|
||||
, .{ expect_line, actual_stderr });
|
||||
},
|
||||
.exact => |expect_lines| {
|
||||
for (expect_lines) |expect_line| {
|
||||
const actual_line = actual_line_it.next() orelse {
|
||||
try expected_generated.appendSlice(expect_line);
|
||||
try expected_generated.append('\n');
|
||||
continue;
|
||||
};
|
||||
if (matchCompileError(actual_line, expect_line)) {
|
||||
try expected_generated.appendSlice(actual_line);
|
||||
try expected_generated.append('\n');
|
||||
continue;
|
||||
}
|
||||
try expected_generated.appendSlice(expect_line);
|
||||
try expected_generated.append('\n');
|
||||
}
|
||||
|
||||
if (mem.eql(u8, expected_generated.items, actual_stderr)) return;
|
||||
|
||||
return self.step.fail(
|
||||
\\
|
||||
\\========= expected: =====================
|
||||
\\{s}
|
||||
\\========= but found: ====================
|
||||
\\{s}
|
||||
\\=========================================
|
||||
, .{ expected_generated.items, actual_stderr });
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn matchCompileError(actual: []const u8, expected: []const u8) bool {
|
||||
if (mem.endsWith(u8, actual, expected)) return true;
|
||||
if (mem.startsWith(u8, expected, ":?:?: ")) {
|
||||
if (mem.endsWith(u8, actual, expected[":?:?: ".len..])) return true;
|
||||
}
|
||||
// We scan for /?/ in expected line and if there is a match, we match everything
|
||||
// up to and after /?/.
|
||||
const expected_trim = mem.trim(u8, expected, " ");
|
||||
if (mem.indexOf(u8, expected_trim, "/?/")) |index| {
|
||||
const actual_trim = mem.trim(u8, actual, " ");
|
||||
const lhs = expected_trim[0..index];
|
||||
const rhs = expected_trim[index + "/?/".len ..];
|
||||
if (mem.startsWith(u8, actual_trim, lhs) and mem.endsWith(u8, actual_trim, rhs)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1617,11 +1617,13 @@ fn testLdScriptPathError(b: *Build, opts: Options) *Step {
|
||||
exe.addLibraryPath(scripts.getDirectory());
|
||||
exe.linkLibC();
|
||||
|
||||
expectLinkErrors(exe, test_step, &.{
|
||||
"error: missing library dependency: GNU ld script '/?/liba.so' requires 'libfoo.so', but file not found",
|
||||
"note: tried libfoo.so",
|
||||
"note: tried /?/libfoo.so",
|
||||
});
|
||||
expectLinkErrors(
|
||||
exe,
|
||||
test_step,
|
||||
.{
|
||||
.contains = "error: missing library dependency: GNU ld script '/?/liba.so' requires 'libfoo.so', but file not found",
|
||||
},
|
||||
);
|
||||
|
||||
return test_step;
|
||||
}
|
||||
@ -1645,10 +1647,10 @@ fn testMismatchedCpuArchitectureError(b: *Build, opts: Options) *Step {
|
||||
exe.addObject(obj);
|
||||
exe.linkLibC();
|
||||
|
||||
expectLinkErrors(exe, test_step, &.{
|
||||
expectLinkErrors(exe, test_step, .{ .exact = &.{
|
||||
"invalid cpu architecture: expected 'x86_64', but found 'aarch64'",
|
||||
"note: while parsing /?/a.o",
|
||||
});
|
||||
} });
|
||||
|
||||
return test_step;
|
||||
}
|
||||
@ -2853,12 +2855,12 @@ fn testUnknownFileTypeError(b: *Build, opts: Options) *Step {
|
||||
exe.linkLibrary(dylib);
|
||||
exe.linkLibC();
|
||||
|
||||
expectLinkErrors(exe, test_step, &.{
|
||||
expectLinkErrors(exe, test_step, .{ .exact = &.{
|
||||
"unknown file type",
|
||||
"note: while parsing /?/liba.dylib",
|
||||
"undefined symbol: foo",
|
||||
"note: referenced by /?/a.o:.text",
|
||||
});
|
||||
} });
|
||||
|
||||
return test_step;
|
||||
}
|
||||
@ -2892,11 +2894,11 @@ fn testUnresolvedError(b: *Build, opts: Options) *Step {
|
||||
exe.addObject(obj2);
|
||||
exe.linkLibC();
|
||||
|
||||
expectLinkErrors(exe, test_step, &.{
|
||||
expectLinkErrors(exe, test_step, .{ .exact = &.{
|
||||
"error: undefined symbol: foo",
|
||||
"note: referenced by /?/a.o:.text.bar",
|
||||
"note: referenced by /?/b.o:.text.main",
|
||||
});
|
||||
} });
|
||||
|
||||
return test_step;
|
||||
}
|
||||
@ -3199,7 +3201,7 @@ fn addAsmSourceBytes(comp: *Compile, bytes: []const u8) void {
|
||||
comp.addAssemblyFile(file);
|
||||
}
|
||||
|
||||
fn expectLinkErrors(comp: *Compile, test_step: *Step, expected_errors: []const []const u8) void {
|
||||
fn expectLinkErrors(comp: *Compile, test_step: *Step, expected_errors: Compile.ExpectedCompileErrors) void {
|
||||
comp.expect_errors = expected_errors;
|
||||
const bin_file = comp.getEmittedBin();
|
||||
bin_file.addStepDependencies(test_step);
|
||||
|
||||
@ -640,7 +640,7 @@ pub fn lowerToBuildSteps(
|
||||
},
|
||||
.Error => |expected_msgs| {
|
||||
assert(expected_msgs.len != 0);
|
||||
artifact.expect_errors = expected_msgs;
|
||||
artifact.expect_errors = .{ .exact = expected_msgs };
|
||||
parent_step.dependOn(&artifact.step);
|
||||
},
|
||||
.Execution => |expected_stdout| no_exec: {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user