From 9270f52e6bac1a8521b78521063383dddd0ef5c3 Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Mon, 13 May 2024 20:02:08 +0200 Subject: [PATCH] Detect unused variables --- src/ast.ts | 15 ++- src/context.ts | 6 + src/error.ts | 18 ++- src/index.ts | 2 +- src/lexer.ts | 2 +- src/loader.ts | 4 +- src/parser.ts | 38 +++--- src/printer.ts | 4 +- src/resolve.ts | 17 ++- src/typeck/base.ts | 2 +- src/typeck/index.ts | 5 +- src/typeck/infer.ts | 2 +- src/typeck/lint.ts | 108 ++++++++++++++++++ std/io.nil | 3 +- std/rt/alloc.nil | 2 +- std/rt/rt.mod.nil | 3 +- ui-tests/asm/drop.nil | 2 +- ui-tests/asm/instr_not_string.nil | 2 +- ui-tests/asm/invalid_instr.nil | 2 +- ui-tests/asm/missing_locals.nil | 2 +- ui-tests/asm/not_toplevel.nil | 2 +- ui-tests/asm/wrong_imm.nil | 4 +- ui-tests/basic_recovery.stderr | 8 ++ ui-tests/functions/function_calls_ok.nil | 10 +- ui-tests/functions/missing_args.nil | 2 +- ui-tests/lint/unused_vars.nil | 22 ++++ ui-tests/lint/unused_vars.stderr | 12 ++ .../generics/generics_on_primitive.stderr | 4 + .../generics/generics_structs_in_args.stderr | 12 ++ ui-tests/type/generics/structs.stderr | 12 ++ ui-tests/type/generics/wrong_amount.stderr | 48 ++++++++ ui-tests/type/type_alias.stderr | 4 + ui-tests/type/type_assignments.stderr | 24 ++++ 33 files changed, 340 insertions(+), 63 deletions(-) create mode 100644 src/typeck/lint.ts create mode 100644 ui-tests/lint/unused_vars.nil create mode 100644 ui-tests/lint/unused_vars.stderr create mode 100644 ui-tests/type/generics/generics_structs_in_args.stderr create mode 100644 ui-tests/type/generics/structs.stderr create mode 100644 ui-tests/type/type_alias.stderr create mode 100644 ui-tests/type/type_assignments.stderr diff --git a/src/ast.ts b/src/ast.ts index 2ba7f51..eb7777f 100644 --- a/src/ast.ts +++ b/src/ast.ts @@ -134,9 +134,8 @@ export type ItemKindFunction

= { }; export type FunctionArg

= { - name: string; + ident: Ident; type: Type

; - span: Span; }; export type ItemKindType

= { @@ -449,6 +448,8 @@ export type Resolution = * ``` * When traversing resolutions, a stack of locals has to be kept. * It's similar to a De Bruijn index. + * + * You generally want to index the stack as stack[stack.len - 1 - res.idx]. */ index: number; } @@ -577,10 +578,9 @@ export function superFoldItem( ): Item { switch (item.kind) { case "function": { - const args = item.params.map(({ name, type, span }) => ({ - name, + const args = item.params.map(({ ident, type }) => ({ + ident, type: folder.type(type), - span, })); return { @@ -620,10 +620,9 @@ export function superFoldItem( }; } case "import": { - const args = item.params.map(({ name, type, span }) => ({ - name, + const args = item.params.map(({ ident, type }) => ({ + ident, type: folder.type(type), - span, })); return { ...item, diff --git a/src/context.ts b/src/context.ts index aa61f46..d16cf3f 100644 --- a/src/context.ts +++ b/src/context.ts @@ -81,6 +81,7 @@ export type Options = { packageName: string; debug: Set; noOutput: boolean; + noStd: boolean; }; export function parseArgs(hardcodedInput: string): Options { @@ -89,6 +90,7 @@ export function parseArgs(hardcodedInput: string): Options { let packageName: string; let debug = new Set(); let noOutput = false; + let noStd = false; if (process.argv.length > 2) { filename = process.argv[2]; @@ -117,6 +119,9 @@ export function parseArgs(hardcodedInput: string): Options { if (process.argv.some((arg) => arg === "--no-output")) { noOutput = true; } + if (process.argv.some((arg) => arg === "--no-std")) { + noStd = true; + } } else { filename = ""; input = hardcodedInput; @@ -136,5 +141,6 @@ export function parseArgs(hardcodedInput: string): Options { packageName, debug, noOutput, + noStd, }; } diff --git a/src/error.ts b/src/error.ts index 35fee9b..7da909e 100644 --- a/src/error.ts +++ b/src/error.ts @@ -44,12 +44,18 @@ export class ErrorHandler { private emitter = (msg: string) => globalThis.console.error(msg), ) {} - public emit(err: CompilerError): ErrorEmitted { - renderError(this.emitter, err); + public emitError(err: CompilerError): ErrorEmitted { + renderDiagnostic(this.emitter, err, (msg) => chalk.red(`error: ${msg}`)); this.errors.push(err); return ERROR_EMITTED; } + public warn(err: CompilerError): void { + renderDiagnostic(this.emitter, err, (msg) => + chalk.yellow(`warning: ${msg}`), + ); + } + public hasErrors(): boolean { return this.errors.length > 0; } @@ -72,7 +78,11 @@ export class CompilerError { // eslint-disable-next-line @typescript-eslint/no-unused-vars const console = {}; -function renderError(emitter: Emitter, e: CompilerError) { +function renderDiagnostic( + emitter: Emitter, + e: CompilerError, + render_msg: (msg: string) => string, +) { const { span } = e; const { content } = span.file; @@ -88,7 +98,7 @@ function renderError(emitter: Emitter, e: CompilerError) { } const lineIdx = lineSpans.indexOf(line); const lineNo = lineIdx + 1; - emitter(chalk.red(`error: ${e.msg}`)); + emitter(render_msg(e.msg)); emitter(` --> ${span.file.path ?? ""}:${lineNo}`); emitter(`${lineNo} | ${spanToSnippet(content, line)}`); diff --git a/src/index.ts b/src/index.ts index f962bfa..1d96ed2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -44,7 +44,7 @@ function main() { const start = Date.now(); - if (packageName !== "std") { + if (packageName !== "std" && !opts.noStd) { gcx.pkgLoader(gcx, "std", Span.startOfFile(file)); } diff --git a/src/lexer.ts b/src/lexer.ts index 276f98c..537fa0e 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -114,7 +114,7 @@ export function tokenize(handler: ErrorHandler, file: LoadedFile): LexerResult { return { ok: true, tokens: tokenizeInner(file) }; } catch (e) { if (e instanceof LexerError) { - const err: ErrorEmitted = handler.emit(e.inner); + const err: ErrorEmitted = handler.emitError(e.inner); return { ok: false, err }; } else { throw e; diff --git a/src/loader.ts b/src/loader.ts index eae8f88..2f3fafa 100644 --- a/src/loader.ts +++ b/src/loader.ts @@ -104,7 +104,7 @@ export const loadPkg: PkgLoader = ( return dummyErrorPkg( pkgId, name, - gcx.error.emit( + gcx.error.emitError( new CompilerError(`cycle detected loading extern module ${name}`, span), ), ); @@ -118,7 +118,7 @@ export const loadPkg: PkgLoader = ( const file = loadModuleFile(".", name, span); if (!file.ok) { - return dummyErrorPkg(pkgId, name, gcx.error.emit(file.err)); + return dummyErrorPkg(pkgId, name, gcx.error.emitError(file.err)); } const tokens = tokenize(gcx.error, file.value); diff --git a/src/parser.ts b/src/parser.ts index 6786c04..c3ac6cb 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -233,7 +233,7 @@ function parseItem(t: State): [State, Item] { [t] = expectNext(t, ")"); } else { if (name.span.file.path === undefined) { - t.gcx.error.emit( + t.gcx.error.emitError( new CompilerError( `no known source file for statement, cannot load file relative to it`, name.span, @@ -245,7 +245,7 @@ function parseItem(t: State): [State, Item] { const file = loadModuleFile(name.span.file.path, name.ident, name.span); if (!file.ok) { - t.gcx.error.emit(file.err); + t.gcx.error.emitError(file.err); contents = []; } else { const tokens = tokenize(t.gcx.error, file.value); @@ -311,15 +311,19 @@ function parseFunctionSig(t: State): [State, FunctionSig] { [t] = expectNext(t, "("); let params: FunctionArg[]; - [t, params] = parseCommaSeparatedList(t, ")", (t) => { - let name; - [t, name] = expectNext(t, "identifier"); - [t] = expectNext(t, ":"); - let type; - [t, type] = parseType(t); + [t, params] = parseCommaSeparatedList( + t, + ")", + (t): [State, FunctionArg] => { + let name; + [t, name] = expectNext(t, "identifier"); + [t] = expectNext(t, ":"); + let type; + [t, type] = parseType(t); - return [t, { name: name.ident, type, span: name.span }]; - }); + return [t, { ident: { name: name.ident, span: name.span }, type }]; + }, + ); let colon; let returnType = undefined; @@ -732,7 +736,7 @@ function parseType(t: State): [State, Type] { } default: { throw new FatalParseError( - t.gcx.error.emit( + t.gcx.error.emitError( new CompilerError( `unexpected token: \`${tok.kind}\`, expected type`, span, @@ -799,7 +803,7 @@ function expectNext( [t, tok] = maybeNextT(t); if (!tok) { throw new FatalParseError( - t.gcx.error.emit( + t.gcx.error.emitError( new CompilerError( `expected \`${kind}\`, found end of file`, Span.eof(t.file), @@ -809,7 +813,7 @@ function expectNext( } if (tok.kind !== kind) { throw new FatalParseError( - t.gcx.error.emit( + t.gcx.error.emitError( new CompilerError( `expected \`${kind}\`, found \`${tok.kind}\``, tok.span, @@ -824,7 +828,7 @@ function next(t: State): [State, Token] { const [rest, next] = maybeNextT(t); if (!next) { throw new FatalParseError( - t.gcx.error.emit( + t.gcx.error.emitError( new CompilerError("unexpected end of file", Span.eof(t.file)), ), ); @@ -841,7 +845,7 @@ function maybeNextT(t: State): [State, Token | undefined] { function unexpectedToken(t: ParseState, token: Token, expected: string): never { throw new FatalParseError( - t.gcx.error.emit( + t.gcx.error.emitError( new CompilerError(`unexpected token, expected ${expected}`, token.span), ), ); @@ -875,7 +879,7 @@ function validateAst(ast: Pkg, gcx: GlobalContext) { }); return expr; } else if (expr.kind === "let") { - gcx.error.emit( + gcx.error.emitError( new CompilerError("let is only allowed in blocks", expr.span), ); return superFoldExpr(expr, this); @@ -886,7 +890,7 @@ function validateAst(ast: Pkg, gcx: GlobalContext) { const innerClass = binaryExprPrecedenceClass(inner.binaryKind); if (ourClass !== innerClass) { - gcx.error.emit( + gcx.error.emitError( new CompilerError( `mixing operators without parentheses is not allowed. ${side} is ${inner.binaryKind}, which is different from ${expr.binaryKind}`, expr.span, diff --git a/src/printer.ts b/src/printer.ts index 977d578..6e980ad 100644 --- a/src/printer.ts +++ b/src/printer.ts @@ -57,7 +57,7 @@ function printItem(item: Item): string { function printFunction(func: ItemFunction): string { const args = func.params - .map(({ name, type }) => `${name}: ${printType(type)}`) + .map(({ ident: name, type }) => `${name}: ${printType(type)}`) .join(", "); const ret = func.returnType ? `: ${printType(func.returnType)}` : ""; return `function ${func.name}(${args})${ret} = ${printExpr(func.body, 0)};`; @@ -89,7 +89,7 @@ function printTypeDef(type: ItemType): string { function printImportDef(def: ItemImport): string { const args = def.params - .map(({ name, type }) => `${name}: ${printType(type)}`) + .map(({ ident: name, type }) => `${name}: ${printType(type)}`) .join(", "); const ret = def.returnType ? `: ${printType(def.returnType)}` : ""; diff --git a/src/resolve.ts b/src/resolve.ts index 2cedad3..9d41d03 100644 --- a/src/resolve.ts +++ b/src/resolve.ts @@ -92,7 +92,7 @@ function resolveModule( contents.forEach((item) => { const existing = items.get(item.name); if (existing !== undefined) { - cx.gcx.error.emit( + cx.gcx.error.emitError( new CompilerError( `item \`${item.name}\` has already been declared`, item.span, @@ -164,7 +164,7 @@ function resolveModule( return { kind: "error", - err: cx.gcx.error.emit( + err: cx.gcx.error.emitError( new CompilerError(`cannot find ${ident.name}`, ident.span), ), }; @@ -179,18 +179,17 @@ function resolveModule( switch (item.kind) { case "function": { - const params = item.params.map(({ name, span, type }) => ({ - name, - span, + const params = item.params.map(({ ident, type }) => ({ + ident, type: this.type(type), })); const returnType = item.returnType && this.type(item.returnType); - item.params.forEach(({ name }) => scopes.push(name)); + item.params.forEach(({ ident: name }) => scopes.push(name.name)); const body = this.expr(item.body); const revParams = item.params.slice(); revParams.reverse(); - revParams.forEach(({ name }) => popScope(name)); + revParams.forEach(({ ident: name }) => popScope(name.name)); return { kind: "function", @@ -289,7 +288,7 @@ function resolveModule( let pathRes: Resolution; if (typeof expr.field.value === "number") { - const err: ErrorEmitted = cx.gcx.error.emit( + const err: ErrorEmitted = cx.gcx.error.emitError( new CompilerError( "module contents cannot be indexed with a number", expr.field.span, @@ -304,7 +303,7 @@ function resolveModule( ); if (pathResItem === undefined) { - const err: ErrorEmitted = cx.gcx.error.emit( + const err: ErrorEmitted = cx.gcx.error.emitError( new CompilerError( `module ${module.name} has no item ${expr.field.value}`, expr.field.span, diff --git a/src/typeck/base.ts b/src/typeck/base.ts index 83d0a62..0eaed1a 100644 --- a/src/typeck/base.ts +++ b/src/typeck/base.ts @@ -32,5 +32,5 @@ export function tyErrorFrom(prev: { err: ErrorEmitted }): Ty { } export function emitError(cx: TypeckCtx, err: CompilerError): ErrorEmitted { - return cx.gcx.error.emit(err); + return cx.gcx.error.emitError(err); } diff --git a/src/typeck/index.ts b/src/typeck/index.ts index b247b18..d7ea1a6 100644 --- a/src/typeck/index.ts +++ b/src/typeck/index.ts @@ -17,6 +17,7 @@ import { emitError } from "./base"; import { checkBody, exprError } from "./expr"; import { InferContext } from "./infer"; import { typeOfItem } from "./item"; +import { lintProgram } from "./lint"; export function typeck(gcx: GlobalContext, ast: Pkg): Pkg { const cx = { @@ -55,7 +56,7 @@ export function typeck(gcx: GlobalContext, ast: Pkg): Pkg { cx, new CompilerError( `import parameters must be I32 or Int`, - item.params[i].span, + item.params[i].ident.span, ), ); } @@ -214,5 +215,7 @@ export function typeck(gcx: GlobalContext, ast: Pkg): Pkg { } } + lintProgram(gcx, typecked); + return typecked; } diff --git a/src/typeck/infer.ts b/src/typeck/infer.ts index 5a4fa8e..7cdafe8 100644 --- a/src/typeck/infer.ts +++ b/src/typeck/infer.ts @@ -162,7 +162,7 @@ export class InferContext { } } - this.error.emit( + this.error.emitError( new CompilerError( `cannot assign ${printTy(rhs)} to ${printTy(lhs)}`, span, diff --git a/src/typeck/lint.ts b/src/typeck/lint.ts new file mode 100644 index 0000000..857149b --- /dev/null +++ b/src/typeck/lint.ts @@ -0,0 +1,108 @@ +import { + Folder, + Ident, + Item, + Pkg, + Typecked, + foldAst, + mkDefaultFolder, + superFoldExpr, + superFoldItem, +} from "../ast"; +import { GlobalContext } from "../context"; +import { CompilerError } from "../error"; + +type LintContext = { + locals: LocalUsed[]; +}; + +type LocalUsed = { + ident: Ident; + used: boolean; + isParam: boolean; +}; + +export function lintProgram(gcx: GlobalContext, ast: Pkg): void { + const cx: LintContext = { + locals: [], + }; + + const checker: Folder = { + ...mkDefaultFolder(), + itemInner(item: Item): Item { + switch (item.kind) { + case "function": { + const prev = cx.locals; + cx.locals = [ + ...item.params.map((param) => ({ + ident: param.ident, + used: false, + isParam: true, + })), + ]; + superFoldItem(item, this); + checkLocalsUsed(gcx, 0, cx); + cx.locals = prev; + return item; + } + } + return superFoldItem(item, this); + }, + expr(expr) { + switch (expr.kind) { + case "let": { + superFoldExpr(expr, this); + cx.locals.push({ ident: expr.name, used: false, isParam: false }); + return expr; + } + case "block": { + const prevLocalsLen = cx.locals.length; + superFoldExpr(expr, this); + checkLocalsUsed(gcx, prevLocalsLen, cx); + cx.locals.length = prevLocalsLen; + + return expr; + } + case "ident": { + const { res } = expr.value; + switch (res.kind) { + case "local": { + cx.locals[cx.locals.length - 1 - res.index].used = true; + break; + } + } + return superFoldExpr(expr, this); + } + } + + return superFoldExpr(expr, this); + }, + ident(ident) { + return ident; + }, + type(type) { + return type; + }, + }; + + foldAst(ast, checker); +} + +function checkLocalsUsed( + gcx: GlobalContext, + prevLength: number, + cx: LintContext, +) { + for (let i = prevLength; i < cx.locals.length; i++) { + const local = cx.locals[i]; + if (!local.used && !local.ident.name.startsWith("_")) { + const kind = local.isParam ? "function parameter" : "variable"; + gcx.error.warn( + new CompilerError( + `unused ${kind}: \`${local.ident.name}\``, + local.ident.span, + ), + ); + } + } +} diff --git a/std/io.nil b/std/io.nil index cb48293..d8213e2 100644 --- a/std/io.nil +++ b/std/io.nil @@ -2,5 +2,6 @@ import ("wasi_snapshot_preview1" "fd_write") fd_write(fd: I32, ciovec_ptr: I32, ciovec_len: I32, out_ptr: I32): I32; function print(s: String) = ( - let s: (I32, I32) = ___transmute(s); + // TODO: do it + let _s: (I32, I32) = ___transmute(s); ); diff --git a/std/rt/alloc.nil b/std/rt/alloc.nil index d6f2709..b742475 100644 --- a/std/rt/alloc.nil +++ b/std/rt/alloc.nil @@ -29,7 +29,7 @@ function allocate(size: I32, align: I32): I32 = ( alignedPtr ); -function deallocate(ptr: I32, size: I32) = ( +function deallocate(_ptr: I32, _size: I32) = ( std.println("uwu deawwocate :3"); ); diff --git a/std/rt/rt.mod.nil b/std/rt/rt.mod.nil index 28cd3bc..318b4cf 100644 --- a/std/rt/rt.mod.nil +++ b/std/rt/rt.mod.nil @@ -1,6 +1,7 @@ mod alloc; -function memcpy(dst: I32, src: I32, n: I32) = +// The function parameters are not actually unused. +function memcpy(_dst: I32, _src: I32, _n: I32) = ___asm( __locals(), "local.get 2", diff --git a/ui-tests/asm/drop.nil b/ui-tests/asm/drop.nil index 6025e29..94047ec 100644 --- a/ui-tests/asm/drop.nil +++ b/ui-tests/asm/drop.nil @@ -1,6 +1,6 @@ //@check-pass -function dropping(a: I32) = +function dropping(_a: I32) = ___asm( __locals(), "local.get 0", diff --git a/ui-tests/asm/instr_not_string.nil b/ui-tests/asm/instr_not_string.nil index 2ac1287..0018d3d 100644 --- a/ui-tests/asm/instr_not_string.nil +++ b/ui-tests/asm/instr_not_string.nil @@ -1,4 +1,4 @@ -function a(a: I32) = +function a(_a: I32) = ___asm( __locals(), 0, diff --git a/ui-tests/asm/invalid_instr.nil b/ui-tests/asm/invalid_instr.nil index 58a9a50..c383579 100644 --- a/ui-tests/asm/invalid_instr.nil +++ b/ui-tests/asm/invalid_instr.nil @@ -1,4 +1,4 @@ -function dropping(a: I32) = +function dropping(_a: I32) = ___asm( __locals(), "meow meow", diff --git a/ui-tests/asm/missing_locals.nil b/ui-tests/asm/missing_locals.nil index bd7d66e..95a0937 100644 --- a/ui-tests/asm/missing_locals.nil +++ b/ui-tests/asm/missing_locals.nil @@ -1,4 +1,4 @@ -function dropping(a: I32) = +function dropping(_a: I32) = ___asm( "local.get 0", "drop", diff --git a/ui-tests/asm/not_toplevel.nil b/ui-tests/asm/not_toplevel.nil index 438304e..ddff7e1 100644 --- a/ui-tests/asm/not_toplevel.nil +++ b/ui-tests/asm/not_toplevel.nil @@ -1,4 +1,4 @@ -function dropping(a: I32) = ( +function dropping(_a: I32) = ( 1; ___asm(__locals(), "drop"); ); diff --git a/ui-tests/asm/wrong_imm.nil b/ui-tests/asm/wrong_imm.nil index 3329781..1ed6d5a 100644 --- a/ui-tests/asm/wrong_imm.nil +++ b/ui-tests/asm/wrong_imm.nil @@ -1,11 +1,11 @@ -function a(a: I32) = +function a(_a: I32) = ___asm( __locals(), "local.get 0 0", "drop", ); -function b(a: I32) = +function b(_a: I32) = ___asm( __locals(), "local.get", diff --git a/ui-tests/basic_recovery.stderr b/ui-tests/basic_recovery.stderr index e933ad1..aa671f2 100644 --- a/ui-tests/basic_recovery.stderr +++ b/ui-tests/basic_recovery.stderr @@ -10,3 +10,11 @@ error: cannot assign String to Int --> $DIR/basic_recovery.nil:3 3 | let b: Int = ""; ^ +warning: unused variable: `a` + --> $DIR/basic_recovery.nil:2 +2 | let a: Int = ""; + ^ +warning: unused variable: `b` + --> $DIR/basic_recovery.nil:3 +3 | let b: Int = ""; + ^ diff --git a/ui-tests/functions/function_calls_ok.nil b/ui-tests/functions/function_calls_ok.nil index 0deb8cf..11b59ce 100644 --- a/ui-tests/functions/function_calls_ok.nil +++ b/ui-tests/functions/function_calls_ok.nil @@ -5,14 +5,14 @@ function main() = ( singleArg("hi!"); manyArgs(1,2,3,4,5,6); - let a: () = returnNothing(); - let b: () = returnExplicitUnit(); - let c: String = returnString(); + let _a: () = returnNothing(); + let _b: () = returnExplicitUnit(); + let _c: String = returnString(); ); function noArgs() =; -function singleArg(a: String) =; -function manyArgs(a: Int, b: Int, c: Int, d: Int, e: Int, f: Int) =; +function singleArg(_a: String) =; +function manyArgs(_a: Int, _b: Int, _c: Int, _d: Int, _e: Int, _f: Int) =; function returnNothing() =; function returnExplicitUnit(): () =; diff --git a/ui-tests/functions/missing_args.nil b/ui-tests/functions/missing_args.nil index d247851..857fa31 100644 --- a/ui-tests/functions/missing_args.nil +++ b/ui-tests/functions/missing_args.nil @@ -2,4 +2,4 @@ function main() = ( x(); ); -function x(a: Int) = ; \ No newline at end of file +function x(_a: Int) = ; \ No newline at end of file diff --git a/ui-tests/lint/unused_vars.nil b/ui-tests/lint/unused_vars.nil new file mode 100644 index 0000000..356b3f5 --- /dev/null +++ b/ui-tests/lint/unused_vars.nil @@ -0,0 +1,22 @@ +//@check-pass + +function main() = ( + let x = 0; + let _ok = 0; + let used = 0; + + used; +); + +function x() = ( + let x = 0; + ( + let x = 0; + call(x); + ); + let y = x; +); + +function call(_a: Int) = ; + +function param(p: Int) = ; diff --git a/ui-tests/lint/unused_vars.stderr b/ui-tests/lint/unused_vars.stderr new file mode 100644 index 0000000..ad493fa --- /dev/null +++ b/ui-tests/lint/unused_vars.stderr @@ -0,0 +1,12 @@ +warning: unused variable: `x` + --> $DIR/unused_vars.nil:4 +4 | let x = 0; + ^ +warning: unused variable: `y` + --> $DIR/unused_vars.nil:17 +17 | let y = x; + ^ +warning: unused function parameter: `p` + --> $DIR/unused_vars.nil:22 +22 | function param(p: Int) = ; + ^ diff --git a/ui-tests/type/generics/generics_on_primitive.stderr b/ui-tests/type/generics/generics_on_primitive.stderr index d4019c2..887b9c2 100644 --- a/ui-tests/type/generics/generics_on_primitive.stderr +++ b/ui-tests/type/generics/generics_on_primitive.stderr @@ -2,3 +2,7 @@ error: type I32 does not take any generic arguments but 1 were passed --> $DIR/generics_on_primitive.nil:2 2 | let a: I32[I32] = 0; ^^^ +warning: unused variable: `a` + --> $DIR/generics_on_primitive.nil:2 +2 | let a: I32[I32] = 0; + ^ diff --git a/ui-tests/type/generics/generics_structs_in_args.stderr b/ui-tests/type/generics/generics_structs_in_args.stderr new file mode 100644 index 0000000..d70b550 --- /dev/null +++ b/ui-tests/type/generics/generics_structs_in_args.stderr @@ -0,0 +1,12 @@ +warning: unused function parameter: `a` + --> $DIR/generics_structs_in_args.nil:11 +11 | function test(a: A[I32], b: B[I32, Int, I32], c: C) = ; + ^ +warning: unused function parameter: `b` + --> $DIR/generics_structs_in_args.nil:11 +11 | function test(a: A[I32], b: B[I32, Int, I32], c: C) = ; + ^ +warning: unused function parameter: `c` + --> $DIR/generics_structs_in_args.nil:11 +11 | function test(a: A[I32], b: B[I32, Int, I32], c: C) = ; + ^ diff --git a/ui-tests/type/generics/structs.stderr b/ui-tests/type/generics/structs.stderr new file mode 100644 index 0000000..3597880 --- /dev/null +++ b/ui-tests/type/generics/structs.stderr @@ -0,0 +1,12 @@ +warning: unused function parameter: `a` + --> $DIR/structs.nil:9 +9 | function test(a: A[I32], b: B[I32, Int, I32], c: C) = ; + ^ +warning: unused function parameter: `b` + --> $DIR/structs.nil:9 +9 | function test(a: A[I32], b: B[I32, Int, I32], c: C) = ; + ^ +warning: unused function parameter: `c` + --> $DIR/structs.nil:9 +9 | function test(a: A[I32], b: B[I32, Int, I32], c: C) = ; + ^ diff --git a/ui-tests/type/generics/wrong_amount.stderr b/ui-tests/type/generics/wrong_amount.stderr index 4728cd1..03675ee 100644 --- a/ui-tests/type/generics/wrong_amount.stderr +++ b/ui-tests/type/generics/wrong_amount.stderr @@ -22,3 +22,51 @@ error: type () does not take any generic arguments but 1 were passed --> $DIR/wrong_amount.nil:22 22 | c3: C[I32], ^ +warning: unused function parameter: `a1` + --> $DIR/wrong_amount.nil:9 +9 | a1: A, + ^^ +warning: unused function parameter: `a2` + --> $DIR/wrong_amount.nil:10 +10 | a2: A[], + ^^ +warning: unused function parameter: `a3` + --> $DIR/wrong_amount.nil:11 +11 | a3: A[I32], + ^^ +warning: unused function parameter: `a4` + --> $DIR/wrong_amount.nil:12 +12 | a4: A[I32, I32], + ^^ +warning: unused function parameter: `b1` + --> $DIR/wrong_amount.nil:14 +14 | b1: B, + ^^ +warning: unused function parameter: `b2` + --> $DIR/wrong_amount.nil:15 +15 | b2: B[], + ^^ +warning: unused function parameter: `b3` + --> $DIR/wrong_amount.nil:16 +16 | b3: B[Int, Int], + ^^ +warning: unused function parameter: `b4` + --> $DIR/wrong_amount.nil:17 +17 | b4: B[Int, I32, Int], + ^^ +warning: unused function parameter: `b5` + --> $DIR/wrong_amount.nil:18 +18 | b5: B[Int, Int, Int, Int], + ^^ +warning: unused function parameter: `c1` + --> $DIR/wrong_amount.nil:20 +20 | c1: C, + ^^ +warning: unused function parameter: `c2` + --> $DIR/wrong_amount.nil:21 +21 | c2: C[], + ^^ +warning: unused function parameter: `c3` + --> $DIR/wrong_amount.nil:22 +22 | c3: C[I32], + ^^ diff --git a/ui-tests/type/type_alias.stderr b/ui-tests/type/type_alias.stderr new file mode 100644 index 0000000..46551de --- /dev/null +++ b/ui-tests/type/type_alias.stderr @@ -0,0 +1,4 @@ +warning: unused variable: `a` + --> $DIR/type_alias.nil:6 +6 | let a: A = (0, 0); + ^ diff --git a/ui-tests/type/type_assignments.stderr b/ui-tests/type/type_assignments.stderr new file mode 100644 index 0000000..60ecfea --- /dev/null +++ b/ui-tests/type/type_assignments.stderr @@ -0,0 +1,24 @@ +warning: unused variable: `a1` + --> $DIR/type_assignments.nil:15 +15 | let a1: Int = a; + ^^ +warning: unused variable: `b1` + --> $DIR/type_assignments.nil:16 +16 | let b1: I32 = b; + ^^ +warning: unused variable: `c1` + --> $DIR/type_assignments.nil:17 +17 | let c1: String = c; + ^^ +warning: unused variable: `d1` + --> $DIR/type_assignments.nil:18 +18 | let d1: Bool = d; + ^^ +warning: unused variable: `e1` + --> $DIR/type_assignments.nil:19 +19 | let e1: CustomType = e; + ^^ +warning: unused variable: `f1` + --> $DIR/type_assignments.nil:20 +20 | let f1: (Int, I32) = f; + ^^