// Compile-time diagnostics tests — verify the type checker catches errors var fd = use('fd') var shop = use('internal/shop') var streamline = use('streamline') var tmpfile = "/tmp/_cell_compile_test.cm" function write_source(src) { fd.slurpwrite(tmpfile, stone(blob(src))) } function get_diagnostics(src) { write_source(src) var compiled = shop.mcode_file(tmpfile) compiled._warn = true var optimized = streamline(compiled) if (optimized._diagnostics == null) return [] return optimized._diagnostics } function has_diagnostic(diags, severity, pattern) { var i = 0 while (i < length(diags)) { if (diags[i].severity == severity && search(diags[i].message, pattern) != null) { return true } i = i + 1 } return false } function expect(diags, severity, pattern) { if (!has_diagnostic(diags, severity, pattern)) { return `expected ${severity} matching '${pattern}', got ${text(length(diags))} diagnostic(s)` } return null } function expect_clean(diags) { if (length(diags) > 0) { return `expected no diagnostics, got ${text(length(diags))}` } return null } return { // === Store errors === test_store_field_on_array: function() { var d = get_diagnostics("var a = []\na[\"x\"] = 1") return expect(d, "error", "storing named property on array") }, test_store_index_on_record: function() { var d = get_diagnostics("var a = {}\na[1] = 1") return expect(d, "error", "storing numeric index on record") }, test_store_field_on_text: function() { var d = get_diagnostics("var s = \"hello\"\ns.x = 1") return expect(d, "error", "storing property on text") }, test_store_index_on_text: function() { var d = get_diagnostics("var s = \"hello\"\ns[0] = 1") return expect(d, "error", "storing index on text") }, test_push_on_text: function() { var d = get_diagnostics("var s = \"hello\"\ns[] = 1") return expect(d, "error", "push on text") }, test_push_on_record: function() { var d = get_diagnostics("var r = {}\nr[] = 1") return expect(d, "error", "push on record") }, // === Invoke errors === test_invoke_null: function() { var d = get_diagnostics("var x = null\nx()") return expect(d, "error", "invoking null") }, test_invoke_number: function() { var d = get_diagnostics("var x = 42\nx()") return expect(d, "error", "invoking") }, test_invoke_text: function() { var d = get_diagnostics("var x = \"hello\"\nx()") return expect(d, "error", "invoking") }, // === Warnings === test_field_on_array_warns: function() { var d = get_diagnostics("var a = [1, 2]\nvar x = a.name") return expect(d, "warning", "named property access on array") }, test_field_on_text_warns: function() { var d = get_diagnostics("var s = \"hello\"\nvar x = s.name") return expect(d, "warning", "named property access on text") }, test_record_key_on_record_warns: function() { var d = get_diagnostics("var r = {a: 1}\nvar k = {}\nvar x = r[k]") return expect(d, "warning", "record key on record") }, // === Clean code produces no diagnostics === test_clean_array_ops: function() { var d = get_diagnostics("var a = [1, 2, 3]\nvar x = a[0]\na[1] = 5\na[] = 4") return expect_clean(d) }, test_clean_record_ops: function() { var d = get_diagnostics("var r = {a: 1, b: 2}\nvar x = r.a\nr.c = 3") return expect_clean(d) }, test_clean_function_call: function() { var d = get_diagnostics("function f(a, b) { return a + b }\nvar x = f(1, 2)") return expect_clean(d) } }