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.
The steps
| compile error | kavach check, kavach run and the editor refuse the program and say why |
| warning | the program runs, and the editor and kavach check show the warning while it is typed |
| …, auto-fix | an 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 runs | nothing 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)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)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)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`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)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)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)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)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)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 amountsA 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)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)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)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)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`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`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`?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`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)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)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)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 toA 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)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`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`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)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.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)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`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.