mirror of
https://github.com/Noratrieb/riverdelta.git
synced 2026-01-14 16:35:03 +01:00
Detect unused variables
This commit is contained in:
parent
f164aad631
commit
9270f52e6b
33 changed files with 340 additions and 63 deletions
15
src/ast.ts
15
src/ast.ts
|
|
@ -134,9 +134,8 @@ export type ItemKindFunction<P extends Phase> = {
|
|||
};
|
||||
|
||||
export type FunctionArg<P extends Phase> = {
|
||||
name: string;
|
||||
ident: Ident;
|
||||
type: Type<P>;
|
||||
span: Span;
|
||||
};
|
||||
|
||||
export type ItemKindType<P extends Phase> = {
|
||||
|
|
@ -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<From extends Phase, To extends Phase>(
|
|||
): Item<To> {
|
||||
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<From extends Phase, To extends Phase>(
|
|||
};
|
||||
}
|
||||
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,
|
||||
|
|
|
|||
|
|
@ -81,6 +81,7 @@ export type Options = {
|
|||
packageName: string;
|
||||
debug: Set<string>;
|
||||
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<string>();
|
||||
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 = "<hardcoded>";
|
||||
input = hardcodedInput;
|
||||
|
|
@ -136,5 +141,6 @@ export function parseArgs(hardcodedInput: string): Options {
|
|||
packageName,
|
||||
debug,
|
||||
noOutput,
|
||||
noStd,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
18
src/error.ts
18
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 ?? "<unknown>"}:${lineNo}`);
|
||||
|
||||
emitter(`${lineNo} | ${spanToSnippet(content, line)}`);
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -233,7 +233,7 @@ function parseItem(t: State): [State, Item<Parsed>] {
|
|||
[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<Parsed>] {
|
|||
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<Parsed>[];
|
||||
[t, params] = parseCommaSeparatedList(t, ")", (t) => {
|
||||
let name;
|
||||
[t, name] = expectNext<TokenIdent>(t, "identifier");
|
||||
[t] = expectNext(t, ":");
|
||||
let type;
|
||||
[t, type] = parseType(t);
|
||||
[t, params] = parseCommaSeparatedList(
|
||||
t,
|
||||
")",
|
||||
(t): [State, FunctionArg<Parsed>] => {
|
||||
let name;
|
||||
[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 returnType = undefined;
|
||||
|
|
@ -732,7 +736,7 @@ function parseType(t: State): [State, Type<Parsed>] {
|
|||
}
|
||||
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 extends BaseToken>(
|
|||
[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<T extends BaseToken>(
|
|||
}
|
||||
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<Built>, 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<Built>, 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,
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ function printItem(item: Item<AnyPhase>): string {
|
|||
|
||||
function printFunction(func: ItemFunction<AnyPhase>): 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<AnyPhase>): string {
|
|||
|
||||
function printImportDef(def: ItemImport<AnyPhase>): 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)}` : "";
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<Resolved>): Pkg<Typecked> {
|
||||
const cx = {
|
||||
|
|
@ -55,7 +56,7 @@ export function typeck(gcx: GlobalContext, ast: Pkg<Resolved>): Pkg<Typecked> {
|
|||
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<Resolved>): Pkg<Typecked> {
|
|||
}
|
||||
}
|
||||
|
||||
lintProgram(gcx, typecked);
|
||||
|
||||
return typecked;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -162,7 +162,7 @@ export class InferContext {
|
|||
}
|
||||
}
|
||||
|
||||
this.error.emit(
|
||||
this.error.emitError(
|
||||
new CompilerError(
|
||||
`cannot assign ${printTy(rhs)} to ${printTy(lhs)}`,
|
||||
span,
|
||||
|
|
|
|||
108
src/typeck/lint.ts
Normal file
108
src/typeck/lint.ts
Normal 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,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
//@check-pass
|
||||
|
||||
function dropping(a: I32) =
|
||||
function dropping(_a: I32) =
|
||||
___asm(
|
||||
__locals(),
|
||||
"local.get 0",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
function a(a: I32) =
|
||||
function a(_a: I32) =
|
||||
___asm(
|
||||
__locals(),
|
||||
0,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
function dropping(a: I32) =
|
||||
function dropping(_a: I32) =
|
||||
___asm(
|
||||
__locals(),
|
||||
"meow meow",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
function dropping(a: I32) =
|
||||
function dropping(_a: I32) =
|
||||
___asm(
|
||||
"local.get 0",
|
||||
"drop",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
function dropping(a: I32) = (
|
||||
function dropping(_a: I32) = (
|
||||
1;
|
||||
___asm(__locals(), "drop");
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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 = "";
|
||||
^
|
||||
|
|
|
|||
|
|
@ -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(): () =;
|
||||
|
|
|
|||
|
|
@ -2,4 +2,4 @@ function main() = (
|
|||
x();
|
||||
);
|
||||
|
||||
function x(a: Int) = ;
|
||||
function x(_a: Int) = ;
|
||||
22
ui-tests/lint/unused_vars.nil
Normal file
22
ui-tests/lint/unused_vars.nil
Normal 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) = ;
|
||||
12
ui-tests/lint/unused_vars.stderr
Normal file
12
ui-tests/lint/unused_vars.stderr
Normal 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) = ;
|
||||
^
|
||||
|
|
@ -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;
|
||||
^
|
||||
|
|
|
|||
12
ui-tests/type/generics/generics_structs_in_args.stderr
Normal file
12
ui-tests/type/generics/generics_structs_in_args.stderr
Normal 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) = ;
|
||||
^
|
||||
12
ui-tests/type/generics/structs.stderr
Normal file
12
ui-tests/type/generics/structs.stderr
Normal 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) = ;
|
||||
^
|
||||
|
|
@ -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],
|
||||
^^
|
||||
|
|
|
|||
4
ui-tests/type/type_alias.stderr
Normal file
4
ui-tests/type/type_alias.stderr
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
warning: unused variable: `a`
|
||||
--> $DIR/type_alias.nil:6
|
||||
6 | let a: A = (0, 0);
|
||||
^
|
||||
24
ui-tests/type/type_assignments.stderr
Normal file
24
ui-tests/type/type_assignments.stderr
Normal 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;
|
||||
^^
|
||||
Loading…
Add table
Add a link
Reference in a new issue