Skip to content
Kavachकवच

Mistakes Caught Before a Run

Also known as: bugs common mistakes typo off by one null index out of range divide by zero static analysis linter compile time safety catch errors early

● Beginner6 minBefore this: Classic Mistakes, Fixed

Everyday mistakes, each one a small program, and the step that catches it before the program runs, with the message it gives.

A mistake found while you type costs seconds. The same mistake found by a customer costs much more. These are the mistakes people make under deadline pressure: a misspelled name, a wrong type, a missing case, an index one past the end. Each was checked with kavach check when this site was built.

29 of 29 caught before the program runs, and 7 of them with an automatic fix.

compile error, auto-fix 3compile error 18warning, auto-fix 4warning 4only when it runs 0

The steps

compile errorkavach check, kavach run and the editor refuse the program and say why
warningthe program runs, and the editor and kavach check show the warning while it is typed
…, auto-fixan error or a warning with one right fix: the editor's quick fix, or kavach fix for whole folders (spec 12.7)
only when it runsnothing finds it before a run: a panic, or a wrong answer

Every mistake

Open one to see the program and what kavach check says about it.

= written where == was meantcompile error, auto-fix
fn main() {
    let x = 3
    if x = 4 {
        print("four")
    }
}

kavach check assignment-as-condition.kav printed:

kavach check assignment-as-condition.kav

error: `=` sets a variable: to compare, write `==` (spec 4.1)
assignment-as-condition.kav: Kavach code, then what kavach check assignment-as-condition.kav prints
A call that breaks the function's own contractcompile error
fn half(x: Int) -> Int
    require x > 0
{
    x / 2
}

fn main() {
    print(half(-4))
}

kavach check broken-contract.kav printed:

kavach check broken-contract.kav

error: this call always breaks `require x > 0` of `half`: `x` is -4 (spec 7.7)
broken-contract.kav: Kavach code, then what kavach check broken-contract.kav prints
A return that breaks the function's own promisecompile error
fn clamp_low(x: Int) -> Int
    ensure result >= 0
{
    if x < 0 {
        return -1
    }
    x
}

fn main() {
    print(clamp_low(5))
}

kavach check broken-ensure.kav printed:

kavach check broken-ensure.kav

error: this always breaks `ensure result >= 0` of `clamp_low`: `result` is -1 (spec 7.7)
broken-ensure.kav: Kavach code, then what kavach check broken-ensure.kav prints
A list declared constant, then changedcompile error, auto-fix
fn main() {
    const xs = [1, 2]
    xs.push(3)
    print(xs)
}

kavach check changing-a-constant.kav printed:

kavach check changing-a-constant.kav

error: `push` changes `xs`, but it is a `const`: declare it with `let`
changing-a-constant.kav: Kavach code, then what kavach check changing-a-constant.kav prints
A count that overflows after it was changedcompile error
fn main() {
    let count: Int8 = 120
    count += 7
    count += 1
    print(count)
}

kavach check changing-let-overflows.kav printed:

kavach check changing-let-overflows.kav

error: this always panics: integer overflow: 127 + 1 doesn't fit in Int8 (spec 1.8)
changing-let-overflows.kav: Kavach code, then what kavach check changing-let-overflows.kav prints
A division by zerocompile error
fn main() {
    const parts = 0
    print(100 / parts)
}

kavach check divide-by-zero.kav printed:

kavach check divide-by-zero.kav

error: this always panics: division by zero. Divide by something that can't be 0 (spec 1.8)
divide-by-zero.kav: Kavach code, then what kavach check divide-by-zero.kav prints
An else if an earlier condition already coverswarning, auto-fix
fn grade(score: Int) -> String {
    if score > 10 {
        "good"
    } else if score > 50 {
        "great"
    } else {
        "low"
    }
}

fn main() {
    print(grade(60))
}

kavach check else-if-never-true.kav printed:

kavach check else-if-never-true.kav

warning: this condition is never true here: `score > 50` means `score > 10`, and that branch is taken above. Check the order of the conditions (spec 4.7)
else-if-never-true.kav: Kavach code, then what kavach check else-if-never-true.kav prints
The largest item of a list that may be emptycompile error
fn main() {
    const xs: [Int] = []
    print(xs.max() + 1)
}

kavach check empty-list-max.kav printed:

kavach check empty-list-max.kav

error: expected a number, found `Int?`: it may be `none`, so give a default with `?? 0`, or check it with `if let` (spec 2.7)
empty-list-max.kav: Kavach code, then what kavach check empty-list-max.kav prints
A while whose variable never changeswarning
fn count(limit: Int) {
    let n = 0
    while n < limit {
        print(n)
    }
}

fn main() {
    if 1 > 2 {
        count(3)
    }
}

kavach check endless-while.kav printed:

kavach check endless-while.kav

warning: this loop never ends: `n` and `limit` don't change in it, and nothing in it leaves it. Change them in the loop, add a `break`, or write `while true` if it should run forever (spec 4.7)
endless-while.kav: Kavach code, then what kavach check endless-while.kav prints
Floats compared with == after arithmeticwarning
fn main() {
    let a = 0.1
    a = a + 0.2
    print(a == 0.3)
}

kavach check float-equality.kav printed:

kavach check float-equality.kav

warning: `==` on floats compares every bit, and rounding makes most computed values differ (`0.1 + 0.2` worked out while running isn't `0.3`): use `a.near(b)`, or `Decimal` for exact amounts
float-equality.kav: Kavach code, then what kavach check float-equality.kav prints
A public function that reads a file without saying socompile error, auto-fix
use std.fs

pub fn price() -> String ! Error {
    try fs.read_text("price.txt")
}

fn main() ! Error uses Fs {
    print(try price())
}

kavach check hidden-file-access.kav printed:

kavach check hidden-file-access.kav

error: `price` is `pub`, so it must say what it uses: add `uses Fs.read` (spec 8.1)
hidden-file-access.kav: Kavach code, then what kavach check hidden-file-access.kav prints
A literal index past the end of a literal listcompile error
fn main() {
    const xs = [1, 2, 3]
    print(xs[5])
}

kavach check index-past-end-known.kav printed:

kavach check index-past-end-known.kav

error: this always panics: index 5 is out of range: the list has 3 items (spec 1.8)
index-past-end-known.kav: Kavach code, then what kavach check index-past-end-known.kav prints
A loop whose range runs past the end of a listcompile error
fn main() {
    const sizes = [1, 2, 3]
    for i in 0..4 {
        print(sizes[i])
    }
}

kavach check index-past-the-end-in-a-loop.kav printed:

kavach check index-past-the-end-in-a-loop.kav

error: this always panics: index 3 is out of range: the list has 3 items (spec 1.8)
index-past-the-end-in-a-loop.kav: Kavach code, then what kavach check index-past-the-end-in-a-loop.kav prints
Whole-number division where a fraction was meantwarning
fn main() {
    const half = 1 / 2
    print(half * 10)
}

kavach check integer-division-surprise.kav printed:

kavach check integer-division-surprise.kav

warning: `1 / 2` is `0`: whole numbers divide to a whole number. Write `1.0 / 2.0` for a fraction, or `1.div_floor(2)` when the whole part is meant (spec 1.8)
integer-division-surprise.kav: Kavach code, then what kavach check integer-division-surprise.kav prints
A lambda that calls a function that can fail, with no trycompile error
fn positive(n: Int) -> Bool ! String {
    if n == 0 {
        fail "zero"
    }
    n > 0
}

fn main() {
    const kept = [3, -1, 2].filter(n => positive(n))
    print(kept)
}

kavach check lambda-error-without-try.kav printed:

kavach check lambda-error-without-try.kav

error: `positive` can fail: write `try` before it to pass the error on from this lambda, which the call that takes the lambda then passes on (spec 3.3), or handle it here with `catch` or `match`
lambda-error-without-try.kav: Kavach code, then what kavach check lambda-error-without-try.kav prints
A match that forgets a casecompile error
type Status = Paid | Pending | Refunded

fn label(s: Status) -> String {
    match s {
        Status.Paid => "paid",
        Status.Pending => "pending",
    }
}

fn main() {
    print(label(Status.Paid))
}

kavach check missing-case.kav printed:

kavach check missing-case.kav

error: this `match` does not cover `Status.Refunded`
missing-case.kav: Kavach code, then what kavach check missing-case.kav prints
A name misspelledcompile error
fn main() {
    const total = 1
    print(totl)
}

kavach check misspelled-name.kav printed:

kavach check misspelled-name.kav

error: `totl` is not defined; did you mean `total`?
misspelled-name.kav: Kavach code, then what kavach check misspelled-name.kav prints
x % 2 == 1 used as the odd testwarning, auto-fix
fn odd(x: Int) -> Bool {
    x % 2 == 1
}

fn main() {
    print(odd(-3))
}

kavach check odd-test-on-negatives.kav printed:

kavach check odd-test-on-negatives.kav

warning: `x % 2 == 1` is wrong for a negative odd `x`, since `-3 % 2` is `-1`: write `x % 2 != 0`
odd-test-on-negatives.kav: Kavach code, then what kavach check odd-test-on-negatives.kav prints
A loop one past the end of a listwarning, auto-fix
fn main() {
    const xs = [1, 2, 3]
    for i in 0..=xs.len() {
        print(xs[i])
    }
}

kavach check off-by-one.kav printed:

kavach check off-by-one.kav

warning: `i` reaches `xs.len()`, one past the last index of `xs`, so `xs[i]` panics on the last turn: write `..` instead of `..=`, or loop over `xs` itself (spec 4.4)
off-by-one.kav: Kavach code, then what kavach check off-by-one.kav prints
A value that may be missing, used as if it were therecompile error
fn main() {
    const ages = ["asha": 31]
    print(ages["ravi"] + 1)
}

kavach check optional-used-as-value.kav printed:

kavach check optional-used-as-value.kav

error: expected a number, found `Int?`: it may be `none`, so give a default with `?? 0`, or check it with `if let` (spec 2.7)
optional-used-as-value.kav: Kavach code, then what kavach check optional-used-as-value.kav prints
An overflow the values already showcompile error
fn main() {
    const limit: Int32 = 2147483647
    print(limit + 1)
}

kavach check overflow-known.kav printed:

kavach check overflow-known.kav

error: this always panics: integer overflow: 2147483647 + 1 doesn't fit in Int32 (spec 1.8)
overflow-known.kav: Kavach code, then what kavach check overflow-known.kav prints
A secret printed to the logcompile error
type Card {
    secret number: String
}

fn main() {
    const c = Card(number: "4242")
    print(c.number)
}

kavach check secret-printed.kav printed:

kavach check secret-printed.kav

error: `number` is secret, so it can't go into `print`; use `reveal(…)` if you really mean to
secret-printed.kav: Kavach code, then what kavach check secret-printed.kav prints
A running total that shadows itself in a loopwarning
fn main() {
    let total = 0
    for i in 0..10 {
        let total = total + i
    }
    print(total)
}

kavach check shadowed-total.kav printed:

kavach check shadowed-total.kav

warning: `total` is never used: this makes a new `total`, hiding the one declared above. To change that one, write `total = …` without `let` (spec 1.4)
shadowed-total.kav: Kavach code, then what kavach check shadowed-total.kav prints
Text added to a numbercompile error
fn main() {
    const count = 3
    print("items: " + count)
}

kavach check text-plus-number.kav printed:

kavach check text-plus-number.kav

error: expected `String`, found `Int`
text-plus-number.kav: Kavach code, then what kavach check text-plus-number.kav prints
An error left unhandledcompile error
use std.fs

fn main() uses Fs {
    const text = fs.read_text("config.toml")
    print(text)
}

kavach check unhandled-error.kav printed:

kavach check unhandled-error.kav

error: `read_text` can fail: handle the error with `try`, `catch` or `match`
unhandled-error.kav: Kavach code, then what kavach check unhandled-error.kav prints
A match arm that can never matchwarning, auto-fix
fn name(n: Int) -> String {
    match n {
        _ => "other",
        1 => "one",
    }
}

fn main() {
    print(name(1))
}

kavach check unreachable-arm.kav printed:

kavach check unreachable-arm.kav

warning: this arm never matches: the arm above it matches every value first. Remove it, or move it up (spec 4.7)
unreachable-arm.kav: Kavach code, then what kavach check unreachable-arm.kav prints
A result computed and thrown awaycompile error
fn total(xs: [Int]) -> Int {
    xs.sum(x => x)
}

fn main() {
    total([1, 2])
    print("done")
}

kavach check unused-result.kav printed:

kavach check unused-result.kav

error: the result of `total` is unused: use it, or write `const _ = …` to throw it away
unused-result.kav: Kavach code, then what kavach check unused-result.kav prints
.unwrap() on a value still none, set before a step that was meant to fill itcompile error
fn main() {
    let winner: String? = none
    print("counting votes")
    print("the winner is {winner.unwrap()}")
}

kavach check unwrap-of-none.kav printed:

kavach check unwrap-of-none.kav

error: this always panics: `.unwrap()` on `none`. Handle `none` with `if let`, `match` or `?? default` (spec 1.8)
unwrap-of-none.kav: Kavach code, then what kavach check unwrap-of-none.kav prints
Text passed where a number is wantedcompile error
fn double(n: Int) -> Int {
    n * 2
}

fn main() {
    print(double("21"))
}

kavach check wrong-argument-type.kav printed:

kavach check wrong-argument-type.kav

error: expected `Int`, found `String`
wrong-argument-type.kav: Kavach code, then what kavach check wrong-argument-type.kav prints

Those marked auto-fix have one right fix: the editor offers it as a quick fix, and kavach fix applies it to whole folders. For the traps other languages are known for, such as 0.1 + 0.2, see Classic Mistakes, Fixed.