✗
Failing This test is currently failing.
Failed: backend-exec
Code
// Test: Unknown suffix should error with Levenshtein-based suggestion
//
// ~log.warning("...") should fail because "warning" is not a valid level.
// The transform should:
// 1. Match log.* pattern
// 2. Extract suffix "warning"
// 3. Check against valid levels: ["error", "warn", "info", "debug"]
// 4. Find closest match via Levenshtein: "warn"
// 5. Emit compile error: "Unknown log level 'warning', did you mean 'warn'?"
~[comptime|transform]pub event log.* {
event_name: []const u8,
item: *const Item,
program: *const Program,
}
| transformed { program: *const Program }
| compile_error { message: []const u8 }
~proc log.* {
const std = @import("std");
const allocator = std.heap.page_allocator;
const valid_levels = [_][]const u8{ "error", "warn", "info", "debug" };
// Levenshtein edit distance - computes minimum edits to transform a into b
const levenshtein = struct {
fn calc(alloc: std.mem.Allocator, a: []const u8, b: []const u8) usize {
if (a.len == 0) return b.len;
if (b.len == 0) return a.len;
// Use two rows instead of full matrix for O(min(m,n)) space
var prev_row = alloc.alloc(usize, b.len + 1) catch return std.math.maxInt(usize);
var curr_row = alloc.alloc(usize, b.len + 1) catch return std.math.maxInt(usize);
defer alloc.free(prev_row);
defer alloc.free(curr_row);
// Initialize first row
for (prev_row, 0..) |*cell, j| {
cell.* = j;
}
// Fill matrix row by row
for (a, 0..) |ca, i| {
curr_row[0] = i + 1;
for (b, 0..) |cb, j| {
const cost: usize = if (ca == cb) 0 else 1;
curr_row[j + 1] = @min(
@min(prev_row[j + 1] + 1, curr_row[j] + 1), // delete, insert
prev_row[j] + cost // substitute
);
}
// Swap rows
const tmp = prev_row;
prev_row = curr_row;
curr_row = tmp;
}
return prev_row[b.len];
}
}.calc;
// Extract the log level from event_name
const level = event_name[4..]; // Skip "log."
// Check if level is valid
var found = false;
for (valid_levels) |valid| {
if (std.mem.eql(u8, level, valid)) {
found = true;
break;
}
}
if (!found) {
// Find closest match using Levenshtein edit distance
var closest: []const u8 = valid_levels[0];
var min_dist: usize = std.math.maxInt(usize);
for (valid_levels) |valid| {
const dist = levenshtein(allocator, level, valid);
if (dist < min_dist) {
min_dist = dist;
closest = valid;
}
}
const msg = std.fmt.allocPrint(
allocator,
"Unknown log level '{s}', did you mean '{s}'?",
.{ level, closest }
) catch return .{ .compile_error = .{ .message = "allocation failed" } };
return .{ .compile_error = .{ .message = msg } };
}
// Valid level - emit logging code (just replace with comment for now)
const ast = @import("ast");
const ast_functional = @import("ast_functional");
const flow = if (item.* == .flow) &item.flow else return .{ .transformed = .{ .program = program } };
const inline_code_item = ast.Item{
.inline_code = .{
.code = "// valid log level",
.location = flow.location,
.module = allocator.dupe(u8, flow.module) catch return .{ .transformed = .{ .program = program } },
},
};
const maybe_new_program = ast_functional.replaceFlowRecursive(allocator, program, flow, inline_code_item) catch return .{ .transformed = .{ .program = program } };
if (maybe_new_program) |new_program| {
const result = allocator.create(ast.Program) catch return .{ .transformed = .{ .program = program } };
result.* = new_program;
return .{ .transformed = .{ .program = result } };
}
return .{ .transformed = .{ .program = program } };
}
// This should trigger the Levenshtein suggestion
~log.warning("This should suggest 'warn'")
Test Configuration
Expected Behavior:
BACKEND_EXEC_ERRORExpected Error:
did you mean 'warn'