Detect unused variables

This commit is contained in:
nora 2024-05-13 20:02:08 +02:00
parent f164aad631
commit 9270f52e6b
33 changed files with 340 additions and 63 deletions

View file

@ -134,9 +134,8 @@ export type ItemKindFunction<P extends Phase> = {
}; };
export type FunctionArg<P extends Phase> = { export type FunctionArg<P extends Phase> = {
name: string; ident: Ident;
type: Type<P>; type: Type<P>;
span: Span;
}; };
export type ItemKindType<P extends Phase> = { export type ItemKindType<P extends Phase> = {
@ -449,6 +448,8 @@ export type Resolution =
* ``` * ```
* When traversing resolutions, a stack of locals has to be kept. * When traversing resolutions, a stack of locals has to be kept.
* It's similar to a De Bruijn index. * It's similar to a De Bruijn index.
*
* You generally want to index the stack as stack[stack.len - 1 - res.idx].
*/ */
index: number; index: number;
} }
@ -577,10 +578,9 @@ export function superFoldItem<From extends Phase, To extends Phase>(
): Item<To> { ): Item<To> {
switch (item.kind) { switch (item.kind) {
case "function": { case "function": {
const args = item.params.map(({ name, type, span }) => ({ const args = item.params.map(({ ident, type }) => ({
name, ident,
type: folder.type(type), type: folder.type(type),
span,
})); }));
return { return {
@ -620,10 +620,9 @@ export function superFoldItem<From extends Phase, To extends Phase>(
}; };
} }
case "import": { case "import": {
const args = item.params.map(({ name, type, span }) => ({ const args = item.params.map(({ ident, type }) => ({
name, ident,
type: folder.type(type), type: folder.type(type),
span,
})); }));
return { return {
...item, ...item,

View file

@ -81,6 +81,7 @@ export type Options = {
packageName: string; packageName: string;
debug: Set<string>; debug: Set<string>;
noOutput: boolean; noOutput: boolean;
noStd: boolean;
}; };
export function parseArgs(hardcodedInput: string): Options { export function parseArgs(hardcodedInput: string): Options {
@ -89,6 +90,7 @@ export function parseArgs(hardcodedInput: string): Options {
let packageName: string; let packageName: string;
let debug = new Set<string>(); let debug = new Set<string>();
let noOutput = false; let noOutput = false;
let noStd = false;
if (process.argv.length > 2) { if (process.argv.length > 2) {
filename = process.argv[2]; filename = process.argv[2];
@ -117,6 +119,9 @@ export function parseArgs(hardcodedInput: string): Options {
if (process.argv.some((arg) => arg === "--no-output")) { if (process.argv.some((arg) => arg === "--no-output")) {
noOutput = true; noOutput = true;
} }
if (process.argv.some((arg) => arg === "--no-std")) {
noStd = true;
}
} else { } else {
filename = "<hardcoded>"; filename = "<hardcoded>";
input = hardcodedInput; input = hardcodedInput;
@ -136,5 +141,6 @@ export function parseArgs(hardcodedInput: string): Options {
packageName, packageName,
debug, debug,
noOutput, noOutput,
noStd,
}; };
} }

View file

@ -44,12 +44,18 @@ export class ErrorHandler {
private emitter = (msg: string) => globalThis.console.error(msg), private emitter = (msg: string) => globalThis.console.error(msg),
) {} ) {}
public emit(err: CompilerError): ErrorEmitted { public emitError(err: CompilerError): ErrorEmitted {
renderError(this.emitter, err); renderDiagnostic(this.emitter, err, (msg) => chalk.red(`error: ${msg}`));
this.errors.push(err); this.errors.push(err);
return ERROR_EMITTED; return ERROR_EMITTED;
} }
public warn(err: CompilerError): void {
renderDiagnostic(this.emitter, err, (msg) =>
chalk.yellow(`warning: ${msg}`),
);
}
public hasErrors(): boolean { public hasErrors(): boolean {
return this.errors.length > 0; return this.errors.length > 0;
} }
@ -72,7 +78,11 @@ export class CompilerError {
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
const console = {}; const console = {};
function renderError(emitter: Emitter, e: CompilerError) { function renderDiagnostic(
emitter: Emitter,
e: CompilerError,
render_msg: (msg: string) => string,
) {
const { span } = e; const { span } = e;
const { content } = span.file; const { content } = span.file;
@ -88,7 +98,7 @@ function renderError(emitter: Emitter, e: CompilerError) {
} }
const lineIdx = lineSpans.indexOf(line); const lineIdx = lineSpans.indexOf(line);
const lineNo = lineIdx + 1; const lineNo = lineIdx + 1;
emitter(chalk.red(`error: ${e.msg}`)); emitter(render_msg(e.msg));
emitter(` --> ${span.file.path ?? "<unknown>"}:${lineNo}`); emitter(` --> ${span.file.path ?? "<unknown>"}:${lineNo}`);
emitter(`${lineNo} | ${spanToSnippet(content, line)}`); emitter(`${lineNo} | ${spanToSnippet(content, line)}`);

View file

@ -44,7 +44,7 @@ function main() {
const start = Date.now(); const start = Date.now();
if (packageName !== "std") { if (packageName !== "std" && !opts.noStd) {
gcx.pkgLoader(gcx, "std", Span.startOfFile(file)); gcx.pkgLoader(gcx, "std", Span.startOfFile(file));
} }

View file

@ -114,7 +114,7 @@ export function tokenize(handler: ErrorHandler, file: LoadedFile): LexerResult {
return { ok: true, tokens: tokenizeInner(file) }; return { ok: true, tokens: tokenizeInner(file) };
} catch (e) { } catch (e) {
if (e instanceof LexerError) { if (e instanceof LexerError) {
const err: ErrorEmitted = handler.emit(e.inner); const err: ErrorEmitted = handler.emitError(e.inner);
return { ok: false, err }; return { ok: false, err };
} else { } else {
throw e; throw e;

View file

@ -104,7 +104,7 @@ export const loadPkg: PkgLoader = (
return dummyErrorPkg( return dummyErrorPkg(
pkgId, pkgId,
name, name,
gcx.error.emit( gcx.error.emitError(
new CompilerError(`cycle detected loading extern module ${name}`, span), new CompilerError(`cycle detected loading extern module ${name}`, span),
), ),
); );
@ -118,7 +118,7 @@ export const loadPkg: PkgLoader = (
const file = loadModuleFile(".", name, span); const file = loadModuleFile(".", name, span);
if (!file.ok) { 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); const tokens = tokenize(gcx.error, file.value);

View file

@ -233,7 +233,7 @@ function parseItem(t: State): [State, Item<Parsed>] {
[t] = expectNext(t, ")"); [t] = expectNext(t, ")");
} else { } else {
if (name.span.file.path === undefined) { if (name.span.file.path === undefined) {
t.gcx.error.emit( t.gcx.error.emitError(
new CompilerError( new CompilerError(
`no known source file for statement, cannot load file relative to it`, `no known source file for statement, cannot load file relative to it`,
name.span, name.span,
@ -245,7 +245,7 @@ function parseItem(t: State): [State, Item<Parsed>] {
const file = loadModuleFile(name.span.file.path, name.ident, name.span); const file = loadModuleFile(name.span.file.path, name.ident, name.span);
if (!file.ok) { if (!file.ok) {
t.gcx.error.emit(file.err); t.gcx.error.emitError(file.err);
contents = []; contents = [];
} else { } else {
const tokens = tokenize(t.gcx.error, file.value); const tokens = tokenize(t.gcx.error, file.value);
@ -311,15 +311,19 @@ function parseFunctionSig(t: State): [State, FunctionSig] {
[t] = expectNext(t, "("); [t] = expectNext(t, "(");
let params: FunctionArg<Parsed>[]; let params: FunctionArg<Parsed>[];
[t, params] = parseCommaSeparatedList(t, ")", (t) => { [t, params] = parseCommaSeparatedList(
let name; t,
[t, name] = expectNext<TokenIdent>(t, "identifier"); ")",
[t] = expectNext(t, ":"); (t): [State, FunctionArg<Parsed>] => {
let type; let name;
[t, type] = parseType(t); [t, name] = expectNext<TokenIdent>(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 colon;
let returnType = undefined; let returnType = undefined;
@ -732,7 +736,7 @@ function parseType(t: State): [State, Type<Parsed>] {
} }
default: { default: {
throw new FatalParseError( throw new FatalParseError(
t.gcx.error.emit( t.gcx.error.emitError(
new CompilerError( new CompilerError(
`unexpected token: \`${tok.kind}\`, expected type`, `unexpected token: \`${tok.kind}\`, expected type`,
span, span,
@ -799,7 +803,7 @@ function expectNext<T extends BaseToken>(
[t, tok] = maybeNextT(t); [t, tok] = maybeNextT(t);
if (!tok) { if (!tok) {
throw new FatalParseError( throw new FatalParseError(
t.gcx.error.emit( t.gcx.error.emitError(
new CompilerError( new CompilerError(
`expected \`${kind}\`, found end of file`, `expected \`${kind}\`, found end of file`,
Span.eof(t.file), Span.eof(t.file),
@ -809,7 +813,7 @@ function expectNext<T extends BaseToken>(
} }
if (tok.kind !== kind) { if (tok.kind !== kind) {
throw new FatalParseError( throw new FatalParseError(
t.gcx.error.emit( t.gcx.error.emitError(
new CompilerError( new CompilerError(
`expected \`${kind}\`, found \`${tok.kind}\``, `expected \`${kind}\`, found \`${tok.kind}\``,
tok.span, tok.span,
@ -824,7 +828,7 @@ function next(t: State): [State, Token] {
const [rest, next] = maybeNextT(t); const [rest, next] = maybeNextT(t);
if (!next) { if (!next) {
throw new FatalParseError( throw new FatalParseError(
t.gcx.error.emit( t.gcx.error.emitError(
new CompilerError("unexpected end of file", Span.eof(t.file)), 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 { function unexpectedToken(t: ParseState, token: Token, expected: string): never {
throw new FatalParseError( throw new FatalParseError(
t.gcx.error.emit( t.gcx.error.emitError(
new CompilerError(`unexpected token, expected ${expected}`, token.span), new CompilerError(`unexpected token, expected ${expected}`, token.span),
), ),
); );
@ -875,7 +879,7 @@ function validateAst(ast: Pkg<Built>, gcx: GlobalContext) {
}); });
return expr; return expr;
} else if (expr.kind === "let") { } else if (expr.kind === "let") {
gcx.error.emit( gcx.error.emitError(
new CompilerError("let is only allowed in blocks", expr.span), new CompilerError("let is only allowed in blocks", expr.span),
); );
return superFoldExpr(expr, this); return superFoldExpr(expr, this);
@ -886,7 +890,7 @@ function validateAst(ast: Pkg<Built>, gcx: GlobalContext) {
const innerClass = binaryExprPrecedenceClass(inner.binaryKind); const innerClass = binaryExprPrecedenceClass(inner.binaryKind);
if (ourClass !== innerClass) { if (ourClass !== innerClass) {
gcx.error.emit( gcx.error.emitError(
new CompilerError( new CompilerError(
`mixing operators without parentheses is not allowed. ${side} is ${inner.binaryKind}, which is different from ${expr.binaryKind}`, `mixing operators without parentheses is not allowed. ${side} is ${inner.binaryKind}, which is different from ${expr.binaryKind}`,
expr.span, expr.span,

View file

@ -57,7 +57,7 @@ function printItem(item: Item<AnyPhase>): string {
function printFunction(func: ItemFunction<AnyPhase>): string { function printFunction(func: ItemFunction<AnyPhase>): string {
const args = func.params const args = func.params
.map(({ name, type }) => `${name}: ${printType(type)}`) .map(({ ident: name, type }) => `${name}: ${printType(type)}`)
.join(", "); .join(", ");
const ret = func.returnType ? `: ${printType(func.returnType)}` : ""; const ret = func.returnType ? `: ${printType(func.returnType)}` : "";
return `function ${func.name}(${args})${ret} = ${printExpr(func.body, 0)};`; return `function ${func.name}(${args})${ret} = ${printExpr(func.body, 0)};`;
@ -89,7 +89,7 @@ function printTypeDef(type: ItemType<AnyPhase>): string {
function printImportDef(def: ItemImport<AnyPhase>): string { function printImportDef(def: ItemImport<AnyPhase>): string {
const args = def.params const args = def.params
.map(({ name, type }) => `${name}: ${printType(type)}`) .map(({ ident: name, type }) => `${name}: ${printType(type)}`)
.join(", "); .join(", ");
const ret = def.returnType ? `: ${printType(def.returnType)}` : ""; const ret = def.returnType ? `: ${printType(def.returnType)}` : "";

View file

@ -92,7 +92,7 @@ function resolveModule(
contents.forEach((item) => { contents.forEach((item) => {
const existing = items.get(item.name); const existing = items.get(item.name);
if (existing !== undefined) { if (existing !== undefined) {
cx.gcx.error.emit( cx.gcx.error.emitError(
new CompilerError( new CompilerError(
`item \`${item.name}\` has already been declared`, `item \`${item.name}\` has already been declared`,
item.span, item.span,
@ -164,7 +164,7 @@ function resolveModule(
return { return {
kind: "error", kind: "error",
err: cx.gcx.error.emit( err: cx.gcx.error.emitError(
new CompilerError(`cannot find ${ident.name}`, ident.span), new CompilerError(`cannot find ${ident.name}`, ident.span),
), ),
}; };
@ -179,18 +179,17 @@ function resolveModule(
switch (item.kind) { switch (item.kind) {
case "function": { case "function": {
const params = item.params.map(({ name, span, type }) => ({ const params = item.params.map(({ ident, type }) => ({
name, ident,
span,
type: this.type(type), type: this.type(type),
})); }));
const returnType = item.returnType && this.type(item.returnType); 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 body = this.expr(item.body);
const revParams = item.params.slice(); const revParams = item.params.slice();
revParams.reverse(); revParams.reverse();
revParams.forEach(({ name }) => popScope(name)); revParams.forEach(({ ident: name }) => popScope(name.name));
return { return {
kind: "function", kind: "function",
@ -289,7 +288,7 @@ function resolveModule(
let pathRes: Resolution; let pathRes: Resolution;
if (typeof expr.field.value === "number") { if (typeof expr.field.value === "number") {
const err: ErrorEmitted = cx.gcx.error.emit( const err: ErrorEmitted = cx.gcx.error.emitError(
new CompilerError( new CompilerError(
"module contents cannot be indexed with a number", "module contents cannot be indexed with a number",
expr.field.span, expr.field.span,
@ -304,7 +303,7 @@ function resolveModule(
); );
if (pathResItem === undefined) { if (pathResItem === undefined) {
const err: ErrorEmitted = cx.gcx.error.emit( const err: ErrorEmitted = cx.gcx.error.emitError(
new CompilerError( new CompilerError(
`module ${module.name} has no item ${expr.field.value}`, `module ${module.name} has no item ${expr.field.value}`,
expr.field.span, expr.field.span,

View file

@ -32,5 +32,5 @@ export function tyErrorFrom(prev: { err: ErrorEmitted }): Ty {
} }
export function emitError(cx: TypeckCtx, err: CompilerError): ErrorEmitted { export function emitError(cx: TypeckCtx, err: CompilerError): ErrorEmitted {
return cx.gcx.error.emit(err); return cx.gcx.error.emitError(err);
} }

View file

@ -17,6 +17,7 @@ import { emitError } from "./base";
import { checkBody, exprError } from "./expr"; import { checkBody, exprError } from "./expr";
import { InferContext } from "./infer"; import { InferContext } from "./infer";
import { typeOfItem } from "./item"; import { typeOfItem } from "./item";
import { lintProgram } from "./lint";
export function typeck(gcx: GlobalContext, ast: Pkg<Resolved>): Pkg<Typecked> { export function typeck(gcx: GlobalContext, ast: Pkg<Resolved>): Pkg<Typecked> {
const cx = { const cx = {
@ -55,7 +56,7 @@ export function typeck(gcx: GlobalContext, ast: Pkg<Resolved>): Pkg<Typecked> {
cx, cx,
new CompilerError( new CompilerError(
`import parameters must be I32 or Int`, `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<Resolved>): Pkg<Typecked> {
} }
} }
lintProgram(gcx, typecked);
return typecked; return typecked;
} }

View file

@ -162,7 +162,7 @@ export class InferContext {
} }
} }
this.error.emit( this.error.emitError(
new CompilerError( new CompilerError(
`cannot assign ${printTy(rhs)} to ${printTy(lhs)}`, `cannot assign ${printTy(rhs)} to ${printTy(lhs)}`,
span, span,

108
src/typeck/lint.ts Normal file
View file

@ -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<Typecked>): void {
const cx: LintContext = {
locals: [],
};
const checker: Folder<Typecked, Typecked> = {
...mkDefaultFolder(),
itemInner(item: Item<Typecked>): Item<Typecked> {
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,
),
);
}
}
}

View file

@ -2,5 +2,6 @@ import ("wasi_snapshot_preview1" "fd_write")
fd_write(fd: I32, ciovec_ptr: I32, ciovec_len: I32, out_ptr: I32): I32; fd_write(fd: I32, ciovec_ptr: I32, ciovec_len: I32, out_ptr: I32): I32;
function print(s: String) = ( function print(s: String) = (
let s: (I32, I32) = ___transmute(s); // TODO: do it
let _s: (I32, I32) = ___transmute(s);
); );

View file

@ -29,7 +29,7 @@ function allocate(size: I32, align: I32): I32 = (
alignedPtr alignedPtr
); );
function deallocate(ptr: I32, size: I32) = ( function deallocate(_ptr: I32, _size: I32) = (
std.println("uwu deawwocate :3"); std.println("uwu deawwocate :3");
); );

View file

@ -1,6 +1,7 @@
mod alloc; 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( ___asm(
__locals(), __locals(),
"local.get 2", "local.get 2",

View file

@ -1,6 +1,6 @@
//@check-pass //@check-pass
function dropping(a: I32) = function dropping(_a: I32) =
___asm( ___asm(
__locals(), __locals(),
"local.get 0", "local.get 0",

View file

@ -1,4 +1,4 @@
function a(a: I32) = function a(_a: I32) =
___asm( ___asm(
__locals(), __locals(),
0, 0,

View file

@ -1,4 +1,4 @@
function dropping(a: I32) = function dropping(_a: I32) =
___asm( ___asm(
__locals(), __locals(),
"meow meow", "meow meow",

View file

@ -1,4 +1,4 @@
function dropping(a: I32) = function dropping(_a: I32) =
___asm( ___asm(
"local.get 0", "local.get 0",
"drop", "drop",

View file

@ -1,4 +1,4 @@
function dropping(a: I32) = ( function dropping(_a: I32) = (
1; 1;
___asm(__locals(), "drop"); ___asm(__locals(), "drop");
); );

View file

@ -1,11 +1,11 @@
function a(a: I32) = function a(_a: I32) =
___asm( ___asm(
__locals(), __locals(),
"local.get 0 0", "local.get 0 0",
"drop", "drop",
); );
function b(a: I32) = function b(_a: I32) =
___asm( ___asm(
__locals(), __locals(),
"local.get", "local.get",

View file

@ -10,3 +10,11 @@ error: cannot assign String to Int
--> $DIR/basic_recovery.nil:3 --> $DIR/basic_recovery.nil:3
3 | let b: Int = ""; 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 = "";
^

View file

@ -5,14 +5,14 @@ function main() = (
singleArg("hi!"); singleArg("hi!");
manyArgs(1,2,3,4,5,6); manyArgs(1,2,3,4,5,6);
let a: () = returnNothing(); let _a: () = returnNothing();
let b: () = returnExplicitUnit(); let _b: () = returnExplicitUnit();
let c: String = returnString(); let _c: String = returnString();
); );
function noArgs() =; function noArgs() =;
function singleArg(a: String) =; function singleArg(_a: String) =;
function manyArgs(a: Int, b: Int, c: Int, d: Int, e: Int, f: Int) =; function manyArgs(_a: Int, _b: Int, _c: Int, _d: Int, _e: Int, _f: Int) =;
function returnNothing() =; function returnNothing() =;
function returnExplicitUnit(): () =; function returnExplicitUnit(): () =;

View file

@ -2,4 +2,4 @@ function main() = (
x(); x();
); );
function x(a: Int) = ; function x(_a: Int) = ;

View file

@ -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) = ;

View file

@ -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) = ;
^

View file

@ -2,3 +2,7 @@ error: type I32 does not take any generic arguments but 1 were passed
--> $DIR/generics_on_primitive.nil:2 --> $DIR/generics_on_primitive.nil:2
2 | let a: I32[I32] = 0; 2 | let a: I32[I32] = 0;
^^^ ^^^
warning: unused variable: `a`
--> $DIR/generics_on_primitive.nil:2
2 | let a: I32[I32] = 0;
^

View file

@ -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) = ;
^

View file

@ -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) = ;
^

View file

@ -22,3 +22,51 @@ error: type () does not take any generic arguments but 1 were passed
--> $DIR/wrong_amount.nil:22 --> $DIR/wrong_amount.nil:22
22 | c3: C[I32], 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],
^^

View file

@ -0,0 +1,4 @@
warning: unused variable: `a`
--> $DIR/type_alias.nil:6
6 | let a: A = (0, 0);
^

View file

@ -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;
^^