mirror of
https://github.com/roc-lang/roc.git
synced 2025-12-23 08:48:03 +00:00
Merge pull request #8429 from roc-lang/fix-numeral-bug
Fix `expect` bug
This commit is contained in:
commit
6a0ef40287
12 changed files with 330 additions and 24 deletions
|
|
@ -1022,6 +1022,43 @@ pub fn checkFile(self: *Self) std.mem.Allocator.Error!void {
|
|||
try self.checkDef(def_idx, &env);
|
||||
}
|
||||
|
||||
// Finally, type-check top-level statements (like expect)
|
||||
// These are separate from defs and need to be checked after all defs are processed
|
||||
// so that lookups can find their definitions
|
||||
for (stmts_slice) |stmt_idx| {
|
||||
const stmt = self.cir.store.getStatement(stmt_idx);
|
||||
const stmt_var = ModuleEnv.varFrom(stmt_idx);
|
||||
const stmt_region = self.cir.store.getNodeRegion(ModuleEnv.nodeIdxFrom(stmt_idx));
|
||||
|
||||
switch (stmt) {
|
||||
.s_expect => |expr_stmt| {
|
||||
env.reset();
|
||||
|
||||
// Enter a new rank for this expect
|
||||
try env.var_pool.pushRank();
|
||||
defer env.var_pool.popRank();
|
||||
|
||||
// Check the body expression
|
||||
_ = try self.checkExpr(expr_stmt.body, &env, .no_expectation);
|
||||
const body_var: Var = ModuleEnv.varFrom(expr_stmt.body);
|
||||
|
||||
// Unify with Bool (expects must be bool expressions)
|
||||
const bool_var = try self.freshBool(&env, stmt_region);
|
||||
_ = try self.unify(bool_var, body_var, &env);
|
||||
|
||||
// Unify statement var with body var
|
||||
_ = try self.unify(stmt_var, body_var, &env);
|
||||
|
||||
// Generalize and check deferred constraints
|
||||
try self.generalizer.generalize(self.gpa, &env.var_pool, env.rank());
|
||||
try self.checkDeferredStaticDispatchConstraints(&env);
|
||||
},
|
||||
else => {
|
||||
// Other statement types are handled elsewhere (type decls, defs, etc.)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Note that we can't use SCCs to determine the order to resolve defs
|
||||
// because anonymous static dispatch makes function order not knowable
|
||||
// before type inference
|
||||
|
|
|
|||
|
|
@ -133,3 +133,70 @@ test "infers type for octal literals" {
|
|||
try test_env.assertLastDefTypeContains("from_numeral");
|
||||
}
|
||||
}
|
||||
|
||||
test "numeric literal in comparison unifies with typed operand" {
|
||||
// When comparing a typed variable with a numeric literal,
|
||||
// the literal should unify to match the variable's type.
|
||||
// `answer == 42` desugars to `answer.is_eq(42)`, which dispatches to I64.is_eq(answer, 42),
|
||||
// which should unify 42's flex var with I64.
|
||||
const source =
|
||||
\\answer : I64
|
||||
\\answer = 42
|
||||
\\
|
||||
\\result = answer == 42
|
||||
;
|
||||
|
||||
var test_env = try TestEnv.init("Test", source);
|
||||
defer test_env.deinit();
|
||||
|
||||
// First verify no type errors
|
||||
try test_env.assertNoErrors();
|
||||
|
||||
// Verify that `answer` has type I64
|
||||
try test_env.assertDefType("answer", "I64");
|
||||
|
||||
// Verify that `result` has type Bool (the result of ==)
|
||||
try test_env.assertDefType("result", "Bool");
|
||||
|
||||
// Now verify that the binop expression's operands both have I64 type
|
||||
// Find the `result` definition and check the binop's operand types
|
||||
const ModuleEnv = @import("can").ModuleEnv;
|
||||
const defs_slice = test_env.module_env.store.sliceDefs(test_env.module_env.all_defs);
|
||||
var found_result = false;
|
||||
for (defs_slice) |def_idx| {
|
||||
const def = test_env.module_env.store.getDef(def_idx);
|
||||
const ptrn = test_env.module_env.store.getPattern(def.pattern);
|
||||
if (ptrn == .assign) {
|
||||
const def_name = test_env.module_env.getIdentStoreConst().getText(ptrn.assign.ident);
|
||||
if (std.mem.eql(u8, def_name, "result")) {
|
||||
found_result = true;
|
||||
// Get the expression - should be a binop
|
||||
const expr = test_env.module_env.store.getExpr(def.expr);
|
||||
try testing.expect(expr == .e_binop);
|
||||
const binop = expr.e_binop;
|
||||
|
||||
// Check LHS type (should be I64)
|
||||
const lhs_var = ModuleEnv.varFrom(binop.lhs);
|
||||
try test_env.type_writer.write(lhs_var);
|
||||
const lhs_type = test_env.type_writer.get();
|
||||
try testing.expectEqualStrings("I64", lhs_type);
|
||||
|
||||
// Check RHS type (the literal 42 - should also be I64 after unification)
|
||||
const rhs_var = ModuleEnv.varFrom(binop.rhs);
|
||||
try test_env.type_writer.write(rhs_var);
|
||||
const rhs_type = test_env.type_writer.get();
|
||||
try testing.expectEqualStrings("I64", rhs_type);
|
||||
|
||||
// Verify that the RHS type var is actually resolved to a nominal type, not flex
|
||||
// This is what the interpreter's translateTypeVar should see
|
||||
const rhs_resolved = test_env.module_env.types.resolveVar(rhs_var);
|
||||
// After type checking, the RHS (numeric literal) should be unified to I64,
|
||||
// which is a nominal type (structure.nominal_type), NOT a flex var
|
||||
try testing.expect(rhs_resolved.desc.content == .structure);
|
||||
try testing.expect(rhs_resolved.desc.content.structure == .nominal_type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
try testing.expect(found_result);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2744,15 +2744,31 @@ fn rocTest(allocs: *Allocators, args: cli_args.TestArgs) !void {
|
|||
env.module_name = module_name;
|
||||
try env.common.calcLineStarts(allocs.gpa);
|
||||
|
||||
// Load builtin modules required by the type checker and interpreter
|
||||
const builtin_indices = builtin_loading.deserializeBuiltinIndices(allocs.gpa, compiled_builtins.builtin_indices_bin) catch |err| {
|
||||
try stderr.print("Failed to deserialize builtin indices: {}\n", .{err});
|
||||
return err;
|
||||
};
|
||||
const builtin_source = compiled_builtins.builtin_source;
|
||||
var builtin_module = builtin_loading.loadCompiledModule(allocs.gpa, compiled_builtins.builtin_bin, "Builtin", builtin_source) catch |err| {
|
||||
try stderr.print("Failed to load Builtin module: {}\n", .{err});
|
||||
return err;
|
||||
};
|
||||
defer builtin_module.deinit();
|
||||
|
||||
// Populate module_envs with Bool, Try, Dict, Set from builtin module
|
||||
var module_envs = std.AutoHashMap(base.Ident.Idx, Can.AutoImportedType).init(allocs.gpa);
|
||||
defer module_envs.deinit();
|
||||
|
||||
const module_common_idents: Check.CommonIdents = .{
|
||||
.module_name = try env.insertIdent(base.Ident.for_text(module_name)),
|
||||
.list = try env.insertIdent(base.Ident.for_text("List")),
|
||||
.box = try env.insertIdent(base.Ident.for_text("Box")),
|
||||
.@"try" = try env.insertIdent(base.Ident.for_text("Try")),
|
||||
.bool_stmt = @enumFromInt(0), // TODO: load from builtin modules
|
||||
.try_stmt = @enumFromInt(0), // TODO: load from builtin modules
|
||||
.str_stmt = @enumFromInt(0), // TODO: load from builtin modules
|
||||
.builtin_module = null,
|
||||
.bool_stmt = builtin_indices.bool_type,
|
||||
.try_stmt = builtin_indices.try_type,
|
||||
.str_stmt = builtin_indices.str_type,
|
||||
.builtin_module = builtin_module.env,
|
||||
};
|
||||
|
||||
// Parse the source code as a full module
|
||||
|
|
@ -2768,8 +2784,16 @@ fn rocTest(allocs: *Allocators, args: cli_args.TestArgs) !void {
|
|||
// Initialize CIR fields in ModuleEnv
|
||||
try env.initCIRFields(allocs.gpa, module_name);
|
||||
|
||||
// Populate module_envs with Bool, Try, Dict, Set using shared function
|
||||
try Can.populateModuleEnvs(
|
||||
&module_envs,
|
||||
&env,
|
||||
builtin_module.env,
|
||||
builtin_indices,
|
||||
);
|
||||
|
||||
// Create canonicalizer
|
||||
var canonicalizer = Can.init(&env, &parse_ast, null) catch |err| {
|
||||
var canonicalizer = Can.init(&env, &parse_ast, &module_envs) catch |err| {
|
||||
try stderr.print("Failed to initialize canonicalizer: {}\n", .{err});
|
||||
return err;
|
||||
};
|
||||
|
|
@ -2787,8 +2811,11 @@ fn rocTest(allocs: *Allocators, args: cli_args.TestArgs) !void {
|
|||
return err;
|
||||
};
|
||||
|
||||
// Build imported_envs array with builtin module
|
||||
const imported_envs: []const *const ModuleEnv = &.{builtin_module.env};
|
||||
|
||||
// Type check the module
|
||||
var checker = Check.init(allocs.gpa, &env.types, &env, &.{}, null, &env.store.regions, module_common_idents) catch |err| {
|
||||
var checker = Check.init(allocs.gpa, &env.types, &env, imported_envs, &module_envs, &env.store.regions, module_common_idents) catch |err| {
|
||||
try stderr.print("Failed to initialize type checker: {}\n", .{err});
|
||||
return err;
|
||||
};
|
||||
|
|
@ -2800,20 +2827,8 @@ fn rocTest(allocs: *Allocators, args: cli_args.TestArgs) !void {
|
|||
};
|
||||
|
||||
// Evaluate all top-level declarations at compile time
|
||||
// Load builtin modules required by the interpreter
|
||||
const builtin_indices = builtin_loading.deserializeBuiltinIndices(allocs.gpa, compiled_builtins.builtin_indices_bin) catch |err| {
|
||||
try stderr.print("Failed to deserialize builtin indices: {}\n", .{err});
|
||||
return err;
|
||||
};
|
||||
const builtin_source = compiled_builtins.builtin_source;
|
||||
var builtin_module = builtin_loading.loadCompiledModule(allocs.gpa, compiled_builtins.builtin_bin, "Builtin", builtin_source) catch |err| {
|
||||
try stderr.print("Failed to load Builtin module: {}\n", .{err});
|
||||
return err;
|
||||
};
|
||||
defer builtin_module.deinit();
|
||||
|
||||
const builtin_types_for_eval = BuiltinTypes.init(builtin_indices, builtin_module.env, builtin_module.env, builtin_module.env);
|
||||
var comptime_evaluator = eval.ComptimeEvaluator.init(allocs.gpa, &env, &.{}, &checker.problems, builtin_types_for_eval, builtin_module.env) catch |err| {
|
||||
var comptime_evaluator = eval.ComptimeEvaluator.init(allocs.gpa, &env, imported_envs, &checker.problems, builtin_types_for_eval, builtin_module.env) catch |err| {
|
||||
try stderr.print("Failed to create compile-time evaluator: {}\n", .{err});
|
||||
return err;
|
||||
};
|
||||
|
|
@ -2826,7 +2841,7 @@ fn rocTest(allocs: *Allocators, args: cli_args.TestArgs) !void {
|
|||
};
|
||||
|
||||
// Create test runner infrastructure for test evaluation (reuse builtin_types_for_eval from above)
|
||||
var test_runner = TestRunner.init(allocs.gpa, &env, builtin_types_for_eval) catch |err| {
|
||||
var test_runner = TestRunner.init(allocs.gpa, &env, builtin_types_for_eval, imported_envs, builtin_module.env) catch |err| {
|
||||
try stderr.print("Failed to create test runner: {}\n", .{err});
|
||||
return err;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -184,3 +184,83 @@ test "fx platform stdin simple" {
|
|||
// stdin_simple reads from stdin and prints to stderr
|
||||
try testing.expect(std.mem.indexOf(u8, result.stderr, "simple test") != null);
|
||||
}
|
||||
|
||||
test "fx platform expect with main" {
|
||||
const allocator = testing.allocator;
|
||||
|
||||
try ensureRocBinary(allocator);
|
||||
|
||||
// Run `roc test` on the app that has both main! and an expect
|
||||
// Note: `roc test` only evaluates expect statements, it does not run main!
|
||||
const run_result = try std.process.Child.run(.{
|
||||
.allocator = allocator,
|
||||
.argv = &[_][]const u8{
|
||||
"./zig-out/bin/roc",
|
||||
"test",
|
||||
"test/fx/expect_with_main.roc",
|
||||
},
|
||||
});
|
||||
defer allocator.free(run_result.stdout);
|
||||
defer allocator.free(run_result.stderr);
|
||||
|
||||
switch (run_result.term) {
|
||||
.Exited => |code| {
|
||||
if (code != 0) {
|
||||
std.debug.print("Run failed with exit code {}\n", .{code});
|
||||
std.debug.print("STDOUT: {s}\n", .{run_result.stdout});
|
||||
std.debug.print("STDERR: {s}\n", .{run_result.stderr});
|
||||
return error.RunFailed;
|
||||
}
|
||||
},
|
||||
else => {
|
||||
std.debug.print("Run terminated abnormally: {}\n", .{run_result.term});
|
||||
std.debug.print("STDOUT: {s}\n", .{run_result.stdout});
|
||||
std.debug.print("STDERR: {s}\n", .{run_result.stderr});
|
||||
return error.RunFailed;
|
||||
},
|
||||
}
|
||||
|
||||
// When all tests pass without --verbose, roc test produces no output
|
||||
try testing.expectEqualStrings("", run_result.stdout);
|
||||
try testing.expectEqualStrings("", run_result.stderr);
|
||||
}
|
||||
|
||||
test "fx platform expect with numeric literal" {
|
||||
const allocator = testing.allocator;
|
||||
|
||||
try ensureRocBinary(allocator);
|
||||
|
||||
// Run `roc test` on an app that compares a typed variable with a numeric literal
|
||||
// This tests that numeric literals in top-level expects are properly typed
|
||||
const run_result = try std.process.Child.run(.{
|
||||
.allocator = allocator,
|
||||
.argv = &[_][]const u8{
|
||||
"./zig-out/bin/roc",
|
||||
"test",
|
||||
"test/fx/expect_with_literal.roc",
|
||||
},
|
||||
});
|
||||
defer allocator.free(run_result.stdout);
|
||||
defer allocator.free(run_result.stderr);
|
||||
|
||||
switch (run_result.term) {
|
||||
.Exited => |code| {
|
||||
if (code != 0) {
|
||||
std.debug.print("Run failed with exit code {}\n", .{code});
|
||||
std.debug.print("STDOUT: {s}\n", .{run_result.stdout});
|
||||
std.debug.print("STDERR: {s}\n", .{run_result.stderr});
|
||||
return error.RunFailed;
|
||||
}
|
||||
},
|
||||
else => {
|
||||
std.debug.print("Run terminated abnormally: {}\n", .{run_result.term});
|
||||
std.debug.print("STDOUT: {s}\n", .{run_result.stdout});
|
||||
std.debug.print("STDERR: {s}\n", .{run_result.stderr});
|
||||
return error.RunFailed;
|
||||
},
|
||||
}
|
||||
|
||||
// When all tests pass without --verbose, roc test produces no output
|
||||
try testing.expectEqualStrings("", run_result.stdout);
|
||||
try testing.expectEqualStrings("", run_result.stderr);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5603,6 +5603,7 @@ pub const Interpreter = struct {
|
|||
) Error!StackValue {
|
||||
const lhs_ct_var = can.ModuleEnv.varFrom(lhs_expr);
|
||||
const lhs_rt_var = try self.translateTypeVar(self.env, lhs_ct_var);
|
||||
|
||||
const rhs_ct_var = can.ModuleEnv.varFrom(rhs_expr);
|
||||
const rhs_rt_var = try self.translateTypeVar(self.env, rhs_ct_var);
|
||||
|
||||
|
|
@ -5621,6 +5622,7 @@ pub const Interpreter = struct {
|
|||
// Evaluate both operands
|
||||
var lhs = try self.evalExprMinimal(lhs_expr, roc_ops, lhs_rt_var);
|
||||
defer lhs.decref(&self.runtime_layout_store, roc_ops);
|
||||
|
||||
var rhs = try self.evalExprMinimal(rhs_expr, roc_ops, rhs_rt_var);
|
||||
defer rhs.decref(&self.runtime_layout_store, roc_ops);
|
||||
|
||||
|
|
|
|||
|
|
@ -141,11 +141,13 @@ pub const TestRunner = struct {
|
|||
allocator: std.mem.Allocator,
|
||||
cir: *ModuleEnv,
|
||||
builtin_types_param: BuiltinTypes,
|
||||
other_modules: []const *const can.ModuleEnv,
|
||||
builtin_module_env: ?*const can.ModuleEnv,
|
||||
) !TestRunner {
|
||||
return TestRunner{
|
||||
.allocator = allocator,
|
||||
.env = cir,
|
||||
.interpreter = try Interpreter.init(allocator, cir, builtin_types_param, null, &[_]*const can.ModuleEnv{}),
|
||||
.interpreter = try Interpreter.init(allocator, cir, builtin_types_param, builtin_module_env, other_modules),
|
||||
.crash = CrashContext.init(allocator),
|
||||
.roc_ops = null,
|
||||
.test_results = std.array_list.Managed(TestResult).init(allocator),
|
||||
|
|
|
|||
|
|
@ -133,6 +133,7 @@ const CompilerStageData = struct {
|
|||
solver: ?Check = null,
|
||||
bool_stmt: ?can.CIR.Statement.Idx = null,
|
||||
builtin_types: ?eval.BuiltinTypes = null,
|
||||
builtin_module: ?BuiltinModule = null,
|
||||
|
||||
// Pre-canonicalization HTML representations
|
||||
tokens_html: ?[]const u8 = null,
|
||||
|
|
@ -145,6 +146,18 @@ const CompilerStageData = struct {
|
|||
can_reports: std.array_list.Managed(reporting.Report),
|
||||
type_reports: std.array_list.Managed(reporting.Report),
|
||||
|
||||
const BuiltinModule = struct {
|
||||
env: *ModuleEnv,
|
||||
buffer: []align(collections.CompactWriter.SERIALIZATION_ALIGNMENT.toByteUnits()) u8,
|
||||
gpa: Allocator,
|
||||
|
||||
fn deinit(self: *@This()) void {
|
||||
self.env.imports.map.deinit(self.gpa);
|
||||
self.gpa.free(self.buffer);
|
||||
self.gpa.destroy(self.env);
|
||||
}
|
||||
};
|
||||
|
||||
pub fn init(alloc: Allocator, module_env: *ModuleEnv) CompilerStageData {
|
||||
return CompilerStageData{
|
||||
.module_env = module_env,
|
||||
|
|
@ -195,6 +208,11 @@ const CompilerStageData = struct {
|
|||
// Finally, deinit the ModuleEnv and free its memory
|
||||
self.module_env.deinit();
|
||||
allocator.destroy(self.module_env);
|
||||
|
||||
// Deinit the builtin module if it was loaded
|
||||
if (self.builtin_module) |*bm| {
|
||||
bm.deinit();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -1001,8 +1019,13 @@ fn compileSource(source: []const u8) !CompilerStageData {
|
|||
|
||||
logDebug("compileSource: Loading Builtin module\n", .{});
|
||||
const builtin_source = compiled_builtins.builtin_source;
|
||||
var builtin_module = try LoadedModule.loadCompiledModule(allocator, compiled_builtins.builtin_bin, "Builtin", builtin_source);
|
||||
defer builtin_module.deinit();
|
||||
const builtin_module = try LoadedModule.loadCompiledModule(allocator, compiled_builtins.builtin_bin, "Builtin", builtin_source);
|
||||
// Store in result instead of deferring deinit - we need it for test evaluation
|
||||
result.builtin_module = .{
|
||||
.env = builtin_module.env,
|
||||
.buffer = builtin_module.buffer,
|
||||
.gpa = builtin_module.gpa,
|
||||
};
|
||||
logDebug("compileSource: Builtin module loaded\n", .{});
|
||||
|
||||
// Get builtin statement indices from the builtin module
|
||||
|
|
@ -1575,7 +1598,9 @@ fn writeEvaluateTestsResponse(response_buffer: []u8, data: CompilerStageData) Re
|
|||
};
|
||||
|
||||
// Create interpreter infrastructure for test evaluation
|
||||
var test_runner = TestRunner.init(local_arena.allocator(), env, builtin_types_for_tests) catch {
|
||||
const empty_modules: []const *const ModuleEnv = &.{};
|
||||
const builtin_module_env: ?*const ModuleEnv = if (data.builtin_module) |bm| bm.env else null;
|
||||
var test_runner = TestRunner.init(local_arena.allocator(), env, builtin_types_for_tests, empty_modules, builtin_module_env) catch {
|
||||
try writeErrorResponse(response_buffer, .ERROR, "Failed to initialize test runner.");
|
||||
return;
|
||||
};
|
||||
|
|
|
|||
14
test/fx/expect_with_literal.roc
Normal file
14
test/fx/expect_with_literal.roc
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
app [main!] { pf: platform "./platform/main.roc" }
|
||||
|
||||
import pf.Stdout
|
||||
import pf.Stderr
|
||||
|
||||
main! = || {
|
||||
Stdout.line!("done")
|
||||
}
|
||||
|
||||
answer : I64
|
||||
answer = 42
|
||||
|
||||
# This tests that numeric literals in expect are properly typed
|
||||
expect answer == 42
|
||||
16
test/fx/expect_with_main.roc
Normal file
16
test/fx/expect_with_main.roc
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
app [main!] { pf: platform "./platform/main.roc" }
|
||||
|
||||
import pf.Stdout
|
||||
import pf.Stderr
|
||||
|
||||
main! = || {
|
||||
Stdout.line!("done")
|
||||
}
|
||||
|
||||
answer : I64
|
||||
answer = 42
|
||||
|
||||
fortytwo : I64
|
||||
fortytwo = 42
|
||||
|
||||
expect answer == fortytwo
|
||||
|
|
@ -197,6 +197,7 @@ UNUSED VALUE - fuzz_crash_019.md:86:11:86:17
|
|||
UNUSED VALUE - fuzz_crash_019.md:98:4:104:3
|
||||
UNUSED VALUE - fuzz_crash_019.md:105:2:105:54
|
||||
UNUSED VALUE - fuzz_crash_019.md:105:55:105:85
|
||||
UNUSED VALUE - fuzz_crash_019.md:119:2:119:10
|
||||
# PROBLEMS
|
||||
**PARSE ERROR**
|
||||
A parsing error occurred: `match_branch_missing_arrow`
|
||||
|
|
@ -998,6 +999,17 @@ This expression produces a value, but it's not being used:
|
|||
It has the type:
|
||||
__f_
|
||||
|
||||
**UNUSED VALUE**
|
||||
This expression produces a value, but it's not being used:
|
||||
**fuzz_crash_019.md:119:2:119:10:**
|
||||
```roc
|
||||
foo == 1
|
||||
```
|
||||
^^^^^^^^
|
||||
|
||||
It has the type:
|
||||
_Bool_
|
||||
|
||||
# TOKENS
|
||||
~~~zig
|
||||
KwApp,OpenSquare,LowerIdent,CloseSquare,OpenCurly,LowerIdent,OpColon,KwPlatform,StringStart,StringPart,StringEnd,CloseCurly,
|
||||
|
|
|
|||
|
|
@ -196,6 +196,7 @@ UNUSED VALUE - fuzz_crash_020.md:86:11:86:17
|
|||
UNUSED VALUE - fuzz_crash_020.md:98:4:104:3
|
||||
UNUSED VALUE - fuzz_crash_020.md:105:2:105:54
|
||||
UNUSED VALUE - fuzz_crash_020.md:105:55:105:85
|
||||
UNUSED VALUE - fuzz_crash_020.md:119:2:119:10
|
||||
# PROBLEMS
|
||||
**PARSE ERROR**
|
||||
A parsing error occurred: `match_branch_missing_arrow`
|
||||
|
|
@ -981,6 +982,17 @@ This expression produces a value, but it's not being used:
|
|||
It has the type:
|
||||
__f_
|
||||
|
||||
**UNUSED VALUE**
|
||||
This expression produces a value, but it's not being used:
|
||||
**fuzz_crash_020.md:119:2:119:10:**
|
||||
```roc
|
||||
foo == 1
|
||||
```
|
||||
^^^^^^^^
|
||||
|
||||
It has the type:
|
||||
_Bool_
|
||||
|
||||
# TOKENS
|
||||
~~~zig
|
||||
KwApp,OpenSquare,LowerIdent,CloseSquare,OpenCurly,LowerIdent,OpColon,KwPlatform,StringStart,StringPart,StringEnd,CloseCurly,
|
||||
|
|
|
|||
|
|
@ -92,6 +92,8 @@ MISSING METHOD - Color.md:22:15:22:26
|
|||
MISSING METHOD - Color.md:29:13:29:26
|
||||
TYPE MISMATCH - Color.md:32:5:45:6
|
||||
MISSING METHOD - Color.md:62:8:62:28
|
||||
MISSING METHOD - Color.md:56:8:56:34
|
||||
MISSING METHOD - Color.md:57:8:57:40
|
||||
# PROBLEMS
|
||||
**MODULE HEADER DEPRECATED**
|
||||
The `module` header is deprecated.
|
||||
|
|
@ -266,6 +268,28 @@ This **is_named_color** method is being called on the type **Str**, which has no
|
|||
|
||||
**Hint: **For this to work, the type would need to have a method named **is_named_color** associated with it in the type's declaration.
|
||||
|
||||
**MISSING METHOD**
|
||||
This **to_str** method is being called on the type **Color**, which has no method with that name:
|
||||
**Color.md:56:8:56:34:**
|
||||
```roc
|
||||
expect rgb(124, 56, 245).to_str() == "rgb(124, 56, 245)"
|
||||
```
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
||||
**Hint: **For this to work, the type would need to have a method named **to_str** associated with it in the type's declaration.
|
||||
|
||||
**MISSING METHOD**
|
||||
This **to_str** method is being called on the type **Color**, which has no method with that name:
|
||||
**Color.md:57:8:57:40:**
|
||||
```roc
|
||||
expect rgba(124, 56, 245, 255).to_str() == "rgba(124, 56, 245, 1.0)"
|
||||
```
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
||||
**Hint: **For this to work, the type would need to have a method named **to_str** associated with it in the type's declaration.
|
||||
|
||||
# TOKENS
|
||||
~~~zig
|
||||
KwModule,OpenSquare,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue