diff --git a/CHANGELOG.md b/CHANGELOG.md index 88018ab4..89c2227c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ - `std.serialize` takes any buzz value and return a serializable version of it (objects become maps, etc.) provided the data is has no circular reference and does not contain not serializable values (functions, fibers, etc.) - UTF8 helpers: `str.utf8Len`, `str.utf8Codepoints`, `str.utf8Valid` - New integer literal for single chars: `'A' == 65` +- Compiler will warn you when a local or global is never used or when an expression value is discarded. To silence those warnings you can use the `_ = ` or named the local/global `_`. ## Changed @@ -32,6 +33,7 @@ - `Json` now returns a `Boxed` object (which can be reused in other contexts than JSON) - Identifiers can now have `_` since pattern delimiters have changed - Changed pattern delimiters (https://github.com/buzz-language/buzz/issues/165) +- `list.append` does not return the appended value anymore ## Fixed diff --git a/src/builtin/list.zig b/src/builtin/list.zig index f5f8af8e..c236066c 100644 --- a/src/builtin/list.zig +++ b/src/builtin/list.zig @@ -27,9 +27,7 @@ pub fn append(ctx: *NativeCtx) c_int { return -1; }; - ctx.vm.push(list_value); - - return 1; + return 0; } pub fn insert(ctx: *NativeCtx) c_int { diff --git a/src/chunk.zig b/src/chunk.zig index 7ba3d36c..8b4b6021 100644 --- a/src/chunk.zig +++ b/src/chunk.zig @@ -153,8 +153,8 @@ pub const Chunk = struct { } pub fn write(self: *Self, code: u32, where: Token) !void { - _ = try self.code.append(code); - _ = try self.lines.append(where); + try self.code.append(code); + try self.lines.append(where); } pub fn addConstant(self: *Self, vm: ?*VM, value: Value) !u24 { diff --git a/src/lib/serialize.buzz b/src/lib/serialize.buzz index 780427eb..0fd2e7ca 100644 --- a/src/lib/serialize.buzz +++ b/src/lib/serialize.buzz @@ -155,7 +155,7 @@ object JsonParser { const str? char = this.peek(); if (char == " " or char == "\r" or char == "\t" or char == "\n") { - this.advance(); + _ = this.advance(); } else { return; } diff --git a/src/node.zig b/src/node.zig index 6e7afb21..301ef57d 100644 --- a/src/node.zig +++ b/src/node.zig @@ -259,6 +259,16 @@ pub const ExpressionNode = struct { return GenError.NotConstant; } + fn isLoneExpression(self: *Self) bool { + // zig fmt: off + return (self.expression.node_type != .NamedVariable or NamedVariableNode.cast(self.expression).?.value == null) + and (self.expression.node_type != .Subscript or SubscriptNode.cast(self.expression).?.value == null) + and (self.expression.node_type != .Dot or DotNode.cast(self.expression).?.value == null) + and self.expression.type_def != null + and self.expression.type_def.?.def_type != .Void; + // zig fmt: on + } + fn generate(nodePtr: *anyopaque, codegenPtr: *anyopaque, breaks: ?*std.ArrayList(usize)) anyerror!?*ObjFunction { var codegen: *CodeGen = @ptrCast(@alignCast(codegenPtr)); const node: *ParseNode = @ptrCast(@alignCast(nodePtr)); @@ -273,6 +283,20 @@ pub const ExpressionNode = struct { try codegen.emitOpCode(node.location, .OP_POP); + if (self.isLoneExpression()) { + const type_def_str = self.expression.type_def.?.toStringAlloc(codegen.gc.allocator) catch unreachable; + defer type_def_str.deinit(); + + codegen.reporter.warnFmt( + .discarded_value, + node.location, + "Discarded value of type `{s}`", + .{ + type_def_str.items, + }, + ); + } + try node.patchOptJumps(codegen); try node.endScope(codegen); diff --git a/src/obj.zig b/src/obj.zig index f628ef84..69d72806 100644 --- a/src/obj.zig +++ b/src/obj.zig @@ -1641,7 +1641,7 @@ pub const ObjList = struct { .name = try parser.gc.copyString("append"), .parameters = parameters, .defaults = std.AutoArrayHashMap(*ObjString, Value).init(parser.gc.allocator), - .return_type = obj_list, + .return_type = try parser.gc.type_registry.getTypeDef(.{ .def_type = .Void }), .yield_type = try parser.gc.type_registry.getTypeDef(.{ .def_type = .Void }), .generic_types = std.AutoArrayHashMap(*ObjString, *ObjTypeDef).init(parser.gc.allocator), .function_type = .Extern, diff --git a/src/parser.zig b/src/parser.zig index 23cb6422..7f9602a5 100644 --- a/src/parser.zig +++ b/src/parser.zig @@ -164,6 +164,17 @@ pub const Local = struct { depth: i32, is_captured: bool, constant: bool, + referenced: bool = false, + + pub fn isReferenced(self: Local) bool { + // zig fmt: off + return self.referenced + or self.type_def.def_type == .Void + or self.type_def.def_type == .Placeholder + or self.name.string[0] == '$' + or (self.name.string[0] == '_' and self.name.string.len == 1); + // zig fmt: on + } }; pub const Global = struct { @@ -176,8 +187,26 @@ pub const Global = struct { export_alias: ?[]const u8 = null, hidden: bool = false, constant: bool, + referenced: bool = false, // When resolving a placeholder, the start of the resolution is the global // If `constant` is true, we can search for any `.Assignment` link and fail then. + + pub fn isReferenced(self: Global) bool { + const function_type = if (self.type_def.def_type == .Function) + self.type_def.resolved_type.?.Function.function_type + else + null; + + // zig fmt: off + return self.referenced + or self.type_def.def_type == .Void + or self.type_def.def_type == .Placeholder + or (function_type != null and (function_type == .Extern or function_type == .Abstract or function_type == .EntryPoint or function_type == .ScriptEntryPoint)) + or self.name.string[0] == '$' + or (self.name.string[0] == '_' and self.name.string.len == 1) + or self.exported; + // zig fmt: on + } }; pub const UpValue = struct { index: u8, is_local: bool }; @@ -602,6 +631,46 @@ pub const Parser = struct { } fn endFrame(self: *Self) *FunctionNode { + var i: usize = 0; + while (i < self.current.?.local_count) : (i += 1) { + const local = self.current.?.locals[i]; + + // Check discarded locals + if (!local.isReferenced()) { + const type_def_str = local.type_def.toStringAlloc(self.gc.allocator) catch unreachable; + defer type_def_str.deinit(); + + self.reporter.warnFmt( + .unused_argument, + local.location, + "Unused local of type `{s}`", + .{ + type_def_str.items, + }, + ); + } + } + + // If global scope, check unused globals + const function_type = self.current.?.function_node.node.type_def.?.resolved_type.?.Function.function_type; + if (function_type == .Script or function_type == .ScriptEntryPoint) { + for (self.globals.items) |global| { + if (!global.isReferenced()) { + const type_def_str = global.type_def.toStringAlloc(self.gc.allocator) catch unreachable; + defer type_def_str.deinit(); + + self.reporter.warnFmt( + .unused_argument, + global.location, + "Unused global of type `{s}`", + .{ + type_def_str.items, + }, + ); + } + } + } + var current_node = self.current.?.function_node; self.current = self.current.?.enclosing; @@ -618,12 +687,29 @@ pub const Parser = struct { current.scope_depth -= 1; while (current.local_count > 0 and current.locals[current.local_count - 1].depth > current.scope_depth) { - if (current.locals[current.local_count - 1].is_captured) { + const local = current.locals[current.local_count - 1]; + + if (local.is_captured) { try closing.append(.OP_CLOSE_UPVALUE); } else { try closing.append(.OP_POP); } + // Check discarded locals + if (!local.isReferenced()) { + const type_def_str = local.type_def.toStringAlloc(self.gc.allocator) catch unreachable; + defer type_def_str.deinit(); + + self.reporter.warnFmt( + .unused_argument, + local.location, + "Unused local of type `{s}`", + .{ + type_def_str.items, + }, + ); + } + current.local_count -= 1; } @@ -1210,6 +1296,13 @@ pub const Parser = struct { constant, true, ) + else if (self.parser.current_token != null and self.parser.current_token.?.token_type == .Identifier and self.parser.current_token.?.lexeme.len == 1 and self.parser.current_token.?.lexeme[0] == '_') + try self.varDeclaration( + try self.gc.type_registry.getTypeDef(.{ .def_type = .Any }), + .Semicolon, + constant, + true, + ) else if (try self.match(.Fib)) try self.varDeclaration( try self.parseFiberType(null), @@ -1320,6 +1413,13 @@ pub const Parser = struct { constant, true, ); + } else if (self.parser.current_token != null and self.parser.current_token.?.token_type == .Identifier and self.parser.current_token.?.lexeme.len == 1 and self.parser.current_token.?.lexeme[0] == '_') { + return try self.varDeclaration( + try self.gc.type_registry.getTypeDef(.{ .def_type = .Any }), + .Semicolon, + constant, + true, + ); } else if (try self.match(.Type)) { return try self.varDeclaration( try self.gc.type_registry.getTypeDef(.{ .optional = try self.match(.Question), .def_type = .Type }), @@ -1837,12 +1937,11 @@ pub const Parser = struct { } fn expressionStatement(self: *Self, hanging: bool) !*ParseNode { - const start_location = self.parser.previous_token.?; var node = try self.gc.allocator.create(ExpressionNode); node.* = ExpressionNode{ .expression = try self.expression(hanging), }; - node.node.location = start_location; + node.node.location = node.expression.location; node.node.end_location = self.parser.previous_token.?; try self.consume(.Semicolon, "Expected `;` after expression."); @@ -2549,6 +2648,7 @@ pub const Parser = struct { // Search for a global with that name if (try self.resolveGlobal(null, identifier)) |slot| { const global: *Global = &self.globals.items[slot]; + global.referenced = true; var alias: ?Token = null; global.exported = true; @@ -2575,6 +2675,8 @@ pub const Parser = struct { } else { self.exporting = true; if (try self.declarationsOrExport(false)) |decl| { + self.globals.items[self.globals.items.len - 1].referenced = true; + self.exporting = false; var node = try self.gc.allocator.create(ExportNode); node.* = ExportNode{ @@ -4930,6 +5032,10 @@ pub const Parser = struct { const function_node = try self.function(name_token, FunctionType.Test, null); + if (function_node.type_def) |type_def| { + self.globals.items[slot].type_def = type_def; + } + // Make it as a global definition var node = try self.gc.allocator.create(VarDeclarationNode); node.* = VarDeclarationNode{ @@ -5718,7 +5824,7 @@ pub const Parser = struct { break; } - if (mem.eql(u8, name.lexeme, local.name.string)) { + if (!mem.eql(u8, name.lexeme, "_") and mem.eql(u8, name.lexeme, local.name.string)) { self.reporter.reportWithOrigin( .variable_already_exists, name, @@ -5737,8 +5843,8 @@ pub const Parser = struct { } else { if (check_name) { // Check a global with the same name doesn't exists - for (self.globals.items, 0..) |global, index| { - if (mem.eql(u8, name.lexeme, global.name.string) and !global.hidden) { + for (self.globals.items, 0..) |*global, index| { + if (!mem.eql(u8, name.lexeme, "_") and mem.eql(u8, name.lexeme, global.name.string) and !global.hidden) { // If we found a placeholder with that name, try to resolve it with `variable_type` if (global.type_def.def_type == .Placeholder and global.type_def.resolved_type.?.Placeholder.name != null and mem.eql(u8, name.lexeme, global.type_def.resolved_type.?.Placeholder.name.?.string)) { // A function declares a global with an incomplete typedef so that it can handle recursion @@ -5759,6 +5865,8 @@ pub const Parser = struct { try self.resolvePlaceholder(global.type_def, variable_type, constant); } + global.referenced = true; + return index; } else if (global.prefix == null) { self.reportError(.variable_already_exists, "A global with the same name already exists."); @@ -5777,6 +5885,7 @@ pub const Parser = struct { return 0; } + const function_type = self.current.?.function_node.node.type_def.?.resolved_type.?.Function.function_type; self.current.?.locals[self.current.?.local_count] = Local{ .name = try self.gc.copyString(name.lexeme), .location = name, @@ -5784,6 +5893,8 @@ pub const Parser = struct { .is_captured = false, .type_def = local_type, .constant = constant, + // Extern and abstract function arguments are considered referenced + .referenced = function_type == .Extern or function_type == .Abstract, }; self.current.?.local_count += 1; @@ -5844,6 +5955,10 @@ pub const Parser = struct { return null; } + if (std.mem.eql(u8, name.lexeme, "_")) { + return null; + } + var i: usize = frame.local_count - 1; while (i >= 0) : (i -= 1) { var local: *Local = &frame.locals[i]; @@ -5852,6 +5967,7 @@ pub const Parser = struct { self.reportError(.local_initializer, "Can't read local variable in its own initializer."); } + local.referenced = true; return i; } @@ -5867,6 +5983,10 @@ pub const Parser = struct { return null; } + if (std.mem.eql(u8, name.lexeme, "_")) { + return null; + } + var i: usize = self.globals.items.len - 1; while (i >= 0) : (i -= 1) { var global: *Global = &self.globals.items[i]; @@ -5879,6 +5999,8 @@ pub const Parser = struct { ); } + global.referenced = true; + return i; // Is it an import prefix? } else if (global.prefix != null and mem.eql(u8, name.lexeme, global.prefix.?)) { diff --git a/src/reporter.zig b/src/reporter.zig index 4145a876..d4f89e66 100644 --- a/src/reporter.zig +++ b/src/reporter.zig @@ -97,6 +97,8 @@ pub const Error = enum(u8) { collect_signature = 85, tostring_signature = 86, gc = 87, + discarded_value = 88, + unused_argument = 89, }; // Inspired by https://github.com/zesterer/ariadne @@ -128,6 +130,14 @@ pub const ReportKind = enum { .hint => " note", }; } + + pub fn prefix(self: ReportKind) []const u8 { + return switch (self) { + .@"error" => "E", + .warning => "W", + .hint => "I", + }; + } }; pub const Note = struct { @@ -171,12 +181,13 @@ pub const Report = struct { const main_item = self.items[0]; try out.print( - "\n{s}:{}:{}: \x1b[{d}m[E{d}] {s}{s}:\x1b[0m {s}\n", + "\n{s}:{}:{}: \x1b[{d}m[{s}{d}] {s}{s}:\x1b[0m {s}\n", .{ main_item.location.script_name, main_item.location.line + 1, main_item.location.column, main_item.kind.color(), + main_item.kind.prefix(), @intFromEnum(self.error_type), if (reporter.error_prefix) |prefix| prefix @@ -381,6 +392,23 @@ panic_mode: bool = false, had_error: bool = false, error_prefix: ?[]const u8 = null, +pub fn warn(self: *Self, error_type: Error, token: Token, message: []const u8) void { + var error_report = Report{ + .message = message, + .error_type = error_type, + .items = &[_]ReportItem{ + ReportItem{ + .kind = .warning, + .location = token, + .message = message, + }, + }, + .notes = &[_]Note{}, + }; + + error_report.reportStderr(self) catch @panic("Unable to report error"); +} + pub fn report(self: *Self, error_type: Error, token: Token, message: []const u8) void { self.panic_mode = true; self.had_error = true; @@ -408,6 +436,10 @@ pub fn reportErrorAt(self: *Self, error_type: Error, token: Token, message: []co self.report(error_type, token, message); } +pub fn warnAt(self: *Self, error_type: Error, token: Token, message: []const u8) void { + self.warn(error_type, token, message); +} + pub fn reportErrorFmt(self: *Self, error_type: Error, token: Token, comptime fmt: []const u8, args: anytype) void { var message = std.ArrayList(u8).init(self.allocator); defer message.deinit(); @@ -418,6 +450,16 @@ pub fn reportErrorFmt(self: *Self, error_type: Error, token: Token, comptime fmt self.reportErrorAt(error_type, token, message.items); } +pub fn warnFmt(self: *Self, error_type: Error, token: Token, comptime fmt: []const u8, args: anytype) void { + var message = std.ArrayList(u8).init(self.allocator); + defer message.deinit(); + + var writer = message.writer(); + writer.print(fmt, args) catch @panic("Unable to report error"); + + self.warnAt(error_type, token, message.items); +} + pub fn reportWithOrigin( self: *Self, error_type: Error, diff --git a/src/scanner.zig b/src/scanner.zig index 8cde0705..f7f7152c 100644 --- a/src/scanner.zig +++ b/src/scanner.zig @@ -57,6 +57,13 @@ pub const Scanner = struct { return try switch (char) { 'b' => self.identifier(), 'a', 'c'...'z', 'A'...'Z' => self.identifier(), + // `_` identifier is used to discard expressions or locals, but we don't allow identifiers starting with `_`, so we stop there + '_' => self.makeToken( + .Identifier, + self.source[self.current.start..self.current.offset], + null, + null, + ), '0' => if (self.match('x')) self.hexa() else if (self.match('b')) diff --git a/tests/001-basic-types.buzz b/tests/001-basic-types.buzz index 0b45f4ff..b68043fa 100644 --- a/tests/001-basic-types.buzz +++ b/tests/001-basic-types.buzz @@ -1,10 +1,10 @@ import "std"; test "Basic types" { - str string = "hello world"; - float floating = 3.14; - int integer = 3; - bool boolean = true; + str _ = "hello world"; + float _ = 3.14; + int _ = 3; + bool _ = true; } test "Char literal" { diff --git a/tests/004-lists.buzz b/tests/004-lists.buzz index 28a44185..7a1a67bf 100644 --- a/tests/004-lists.buzz +++ b/tests/004-lists.buzz @@ -9,8 +9,8 @@ test "Lists" { assert(strList[0] == "hello", message: "subscript"); | A lone list expression - [, "hello", "world"]; - [<[str]>, ["hello"], ["world"]]; + _ = [, "hello", "world"]; + _ = [<[str]>, ["hello"], ["world"]]; [[str]] nestedList = [["hello"], ["world"]]; diff --git a/tests/005-maps.buzz b/tests/005-maps.buzz index 032ba59e..1875bd3f 100644 --- a/tests/005-maps.buzz +++ b/tests/005-maps.buzz @@ -6,7 +6,7 @@ test "Maps" { "bye": 2, }; - {, 1: true, 2: false}; + _ = {, 1: true, 2: false}; assert(map["bye"] is int?, message: "yeah"); diff --git a/tests/009-gc.buzz b/tests/009-gc.buzz index 7d4766e6..35b340e3 100644 --- a/tests/009-gc.buzz +++ b/tests/009-gc.buzz @@ -14,9 +14,9 @@ object Kill { test "GC, collecting unreferenced objects" { int i = 0; - Kill k = Kill{}; | Should be kept longer + _ = Kill{}; | Should be kept longer while (i < 100) { - Kill{ yo = i }; | Should be collected since not referenced anywhere + _ = Kill{ yo = i }; | Should be collected since not referenced anywhere i = i + 1; } diff --git a/tests/016-optionals.buzz b/tests/016-optionals.buzz index 0483f18e..9aac91f4 100644 --- a/tests/016-optionals.buzz +++ b/tests/016-optionals.buzz @@ -71,5 +71,5 @@ object A { test "Check ahead" { A.instance = A{ msg = "hello" }; - A.instance?.hello(); + _ = A.instance?.hello(); } \ No newline at end of file diff --git a/tests/018-foreach.buzz b/tests/018-foreach.buzz index f596f3d6..48e78fb7 100644 --- a/tests/018-foreach.buzz +++ b/tests/018-foreach.buzz @@ -4,7 +4,7 @@ test "foreach on list" { [int] list = [1, 2, 3]; int sum = 0; - foreach (int index, int item in list) { + foreach (int item in list) { sum = sum + item; } @@ -26,7 +26,7 @@ test "foreach on map" { }; int sum = 0; - foreach (str key, int value in map) { + foreach (int value in map) { sum = sum + value; } @@ -50,7 +50,7 @@ test "foreach on enum" { test "foreach on str" { str hello = ""; - foreach (int index, str char in "hello world") { + foreach (str char in "hello world") { hello = "{hello}{char}"; } diff --git a/tests/025-fs.buzz b/tests/025-fs.buzz index 139d8222..cf00ee72 100644 --- a/tests/025-fs.buzz +++ b/tests/025-fs.buzz @@ -15,8 +15,8 @@ test "mkDir/rm" { test "ls" { bool containsREADME = false; - foreach (int i, str el in fs.list(fs.currentDirectory())) { - str randomLocal = "hello there!"; + foreach (str el in fs.list(fs.currentDirectory())) { + str _ = "hello there!"; if (el == "README.md") { containsREADME = true; break; @@ -29,7 +29,7 @@ test "ls" { assert(containsREADME, message: "Could list element in current directory"); - foreach (int i, str el in fs.list("/doesnotexist") catch ["wentwrong"]) { + foreach (str el in fs.list("/doesnotexist") catch ["wentwrong"]) { assert(el == "wentwrong", message: "Trying to list a non existent directory raised an error"); } } diff --git a/tests/032-debug.buzz b/tests/032-debug.buzz index 79aa3ffd..826fbf68 100644 --- a/tests/032-debug.buzz +++ b/tests/032-debug.buzz @@ -6,7 +6,7 @@ test "Get AST" { str source = debug.ast(` import \"std\"; - fun main([str] args) > void -> print(\"hello world\"); + fun main([str] _) > void -> print(\"hello world\"); `, scriptName: "test"); Boxed jsonAST = jsonDecode(source); diff --git a/tests/033-invoke.buzz b/tests/033-invoke.buzz index 23e77bbc..de020b29 100644 --- a/tests/033-invoke.buzz +++ b/tests/033-invoke.buzz @@ -21,5 +21,5 @@ test "Chained invoke" { ] }; - a.list[0].hello().list[0].hello(); + _ = a.list[0].hello().list[0].hello(); } \ No newline at end of file diff --git a/tests/034-scope.buzz b/tests/034-scope.buzz index babeb76b..7bdb9e0d 100644 --- a/tests/034-scope.buzz +++ b/tests/034-scope.buzz @@ -3,11 +3,11 @@ import "std"; test "locals inside a foreach" { str hello = ""; - foreach (int index, str char in "hello world") { + foreach (str _ in "hello world") { str new = "yo"; str old = "lo"; - foreach (int jndex, str cchar in "goodbye world") { + foreach (str _ in "goodbye world") { str newnew = "yoyo"; str oldold = "lolo"; @@ -16,6 +16,8 @@ test "locals inside a foreach" { assert(newnew == "yoyo", message: "locals are ok"); assert(oldold == "lolo", message: "locals are ok"); } + + assert(hello == ""); } for (int i = 0; i < 3; i = i + 1) { diff --git a/tests/037-dead-branches.buzz b/tests/037-dead-branches.buzz index a6f2a32a..767e3c7b 100644 --- a/tests/037-dead-branches.buzz +++ b/tests/037-dead-branches.buzz @@ -9,7 +9,7 @@ test "if" { } test "foreach" { - foreach (str key, str value in {}) { + foreach (str _ in {}) { assert(false, message: "unreachable"); } } diff --git a/tests/038-fibers.buzz b/tests/038-fibers.buzz index 27100b75..206faf92 100644 --- a/tests/038-fibers.buzz +++ b/tests/038-fibers.buzz @@ -28,7 +28,7 @@ fun count(int n) > str > int? { for (int i = 0; i < n; i = i + 1) { | error or yield is ignored if not called with a async call? - yield i; + _ = yield i; } return "Counting is done!"; diff --git a/tests/041-iterator.buzz b/tests/041-iterator.buzz index 3340df54..31736027 100644 --- a/tests/041-iterator.buzz +++ b/tests/041-iterator.buzz @@ -6,7 +6,7 @@ fun fibonnaci(int n) > void > int? { int? next = null; for (int i = 0; i < n; i = i + 1) { - yield n1; + _ = yield n1; next = n1 + n2; n1 = n2; n2 = next!; diff --git a/tests/042-anonymous-objects.buzz b/tests/042-anonymous-objects.buzz index 9232cd70..042ce1c3 100644 --- a/tests/042-anonymous-objects.buzz +++ b/tests/042-anonymous-objects.buzz @@ -10,7 +10,7 @@ fun getInfo() > obj{ str name, int age } { test "Anonymous objects" { obj{ str name, int age } info = getInfo(); | Two anonymous type matches - obj{ str name, int age } info2 = info; + obj{ str name, int age } _ = info; assert(info.name == "Joe" and info.age == 36, message: "Could declare, instanciate and acces anonymous objects"); assert(info is obj{ str name, int age }, message: "Type safety works with anonymous object"); diff --git a/tests/044-break-continue.buzz b/tests/044-break-continue.buzz index 3dd41a8d..632b3be8 100644 --- a/tests/044-break-continue.buzz +++ b/tests/044-break-continue.buzz @@ -2,8 +2,8 @@ import "std"; test "break properly jumps and closes scope" { [int] list = [1, 2, 3, 4, 5]; - foreach (int i, int value in list) { - str randomLocal = "hello there!"; + foreach (int value in list) { + str _ = "hello there!"; if (value == 3) { if (true) { continue; @@ -18,14 +18,14 @@ test "break properly jumps and closes scope" { test "break properly jumps and closes scope" { [int] list = [1, 2, 3, 4, 5]; - foreach (int i, int value in list) { - str randomLocal = "hello there!"; + foreach (int value in list) { + str _ = "hello there!"; if (value == 3) { if (true) { break; } } - str randomLocalAfterBreak = "after break"; + str _ = "after break"; } str anotherRandomLocal = "bye there"; diff --git a/tests/046-try-catch.buzz b/tests/046-try-catch.buzz index 03777cbb..1c6e0519 100644 --- a/tests/046-try-catch.buzz +++ b/tests/046-try-catch.buzz @@ -9,7 +9,7 @@ fun partialCatch() > void !> str { willFail(); throw 12; - } catch (int error) { + } catch (int _) { assert(false, message: "unreachable"); } } @@ -19,7 +19,7 @@ fun returnFromCatch() > int { willFail(); return 12; - } catch (str error) { + } catch (str _) { return 21; } @@ -30,7 +30,7 @@ test "Try catch" { str setme = ""; try { - str local = "i'm local to this try block"; + str _ = "i'm local to this try block"; setme = "yes"; @@ -40,10 +40,10 @@ test "Try catch" { assert(false, message: "unreachable"); } catch (str error) { - str local = "a local in catch clause"; + str _ = "a local in catch clause"; assert(error == "Yolo", message: "caught error"); - } catch (int error) { + } catch (int _) { assert(false, message: "unreachable"); } catch { assert(false, message: "unreachable"); diff --git a/tests/047-if-arrow.buzz b/tests/047-if-arrow.buzz index 2177cb0e..dca572c6 100644 --- a/tests/047-if-arrow.buzz +++ b/tests/047-if-arrow.buzz @@ -4,7 +4,7 @@ test "If arrow" { int? opt = null; str? optS = "hello"; - if (opt -> unwrapped) { + if (opt -> _) { assert(false, message: "unreachable"); } diff --git a/tests/048-generics.buzz b/tests/048-generics.buzz index 2c8fae3a..a065b1cb 100644 --- a/tests/048-generics.buzz +++ b/tests/048-generics.buzz @@ -70,12 +70,12 @@ fun genericReturn(, T value) > T { } test "generic return" { - int number = genericReturn(, 12); + assert(genericReturn(, 12) == 12, message: "could use return of generic type"); } fun fiber(, [T] data) > void > T? { - foreach (int i, T element in data) { - yield element; + foreach (T element in data) { + _ = yield element; } } diff --git a/tests/051-functional.buzz b/tests/051-functional.buzz index 1bd53553..d823c362 100644 --- a/tests/051-functional.buzz +++ b/tests/051-functional.buzz @@ -4,7 +4,7 @@ test "list.forEach" { [int] data = [1, 2, 3, 4]; int sum = 0; - data.forEach(fun (int index, int element) > void { + data.forEach(fun (int _, int element) > void { sum = sum + element; }); @@ -14,7 +14,7 @@ test "list.forEach" { test "list.map" { [int] data = [1, 2, 3, 4]; - [str] mapped = data.map(, fun (int index, int element) -> "{element}"); + [str] mapped = data.map(, fun (int _, int element) -> "{element}"); assert(mapped.join(", ") == "1, 2, 3, 4", message: "could use map"); } @@ -22,7 +22,7 @@ test "list.map" { test "list.filter" { [int] data = [1, 2, 3, 4]; - [int] odd = data.filter(fun (int index, int element) -> element % 2 != 0); + [int] odd = data.filter(fun (int _, int element) -> element % 2 != 0); assert(odd.join(", ") == "1, 3", message: "could use filter"); } @@ -32,7 +32,7 @@ test "list.reduce" { int sum = data.reduce( , - fun (int index, int element, int accumulator) -> accumulator + element, + fun (int _, int element, int accumulator) -> accumulator + element, initial: 0 ); @@ -42,7 +42,7 @@ test "list.reduce" { test "list.sort" { [int] data = [10, 3, 12, 0, 1, -3]; - data.sort(fun (int left, int right) -> left < right); + _ = data.sort(fun (int left, int right) -> left < right); foreach (int i, int value in [-3, 0, 1, 3, 10, 12]) { assert(data[i] == value, message: "list is not ordered"); diff --git a/tests/058-ffi.buzz b/tests/058-ffi.buzz index 8b7f6a7e..6b83a36b 100644 --- a/tests/058-ffi.buzz +++ b/tests/058-ffi.buzz @@ -32,7 +32,7 @@ test "pointer with Buffer" { | Since we cache the result of the type parsing this roughly equivalent buffer.writeZ(, "u64", values: [1]); assert(false, message: "Using bad buzz type triggers error"); - } catch (FFITypeMismatchError error) { + } catch (FFITypeMismatchError _) { assert(true, message: "Using bad buzz type triggers error"); } diff --git a/tests/059-types-as-value.buzz b/tests/059-types-as-value.buzz index be23c84a..c791c38e 100644 --- a/tests/059-types-as-value.buzz +++ b/tests/059-types-as-value.buzz @@ -13,7 +13,7 @@ fun dumpType(type myType) > void { protocol C {} test "Types as value" { - type myType = ; + type _ = ; type another = ; type again = ; diff --git a/tests/062-discarded-value.buzz b/tests/062-discarded-value.buzz new file mode 100644 index 00000000..865a1ab1 --- /dev/null +++ b/tests/062-discarded-value.buzz @@ -0,0 +1,7 @@ +import "std"; + +_ = "unused"; + +test "discarding value" { + _ = "i'm discarded"; +} \ No newline at end of file diff --git a/tests/compile_errors/016-unused-local.buzz b/tests/compile_errors/016-unused-local.buzz new file mode 100644 index 00000000..dbc2a8d7 --- /dev/null +++ b/tests/compile_errors/016-unused-local.buzz @@ -0,0 +1,14 @@ +| Unused local of type `int` +import "std"; + +fun hey(int unused) > void { + print("hey you missed me"); +} + +test "discarded value" { + print("hello world"); + + "forgot me?"; + + int unused = 42; +} \ No newline at end of file