Pular para o conteúdo

Erros

O Kriol distingue duas espécies de erros de execução:

  • Bugs do programa, como um índice fora dos limites, um resto por zero ou uma condição de konfirma falsa. Terminam o programa de imediato com um erro de execução.
  • Falhas esperadas, como uma entrada inválida ou um ficheiro que não existe. A função que falha declara que pode falhar, e quem a chama decide o que fazer.

A diferença é de intenção. Um bug é uma coisa que tu fizeste mal, e não há nada de útil a fazer em execução a não ser parar e mostrar onde. Uma falha esperada vem de fora do programa, e um programa bem feito tem uma resposta para ela.

SituaçãoEspécieO que o Kriol faz
Índice de array fora dos limitesBugTermina o programa
Divisão ou resto por zeroBugTermina o programa
konfirma falso, ou panikuBugTermina o programa
Texto que não é um número em int::konvertiFalha esperadaDevolve um Erru a quem chamou
Entrada que acabou, em tomaFalha esperadaDevolve um Erru a quem chamou
lansa numa função tua com : ErruFalha esperadaDevolve um Erru a quem chamou
konfirma(idadi >= 0, "a idade não pode ser negativa");
si estadu == 0 {
// ...
} sinon {
paniku(f"estado inválido: {estadu}");
}

Um erro de execução escreve programa.kriol:N: erro de execução: mensagem, com o ficheiro e a linha, e termina o programa com o código 1. Vê Controlo de fluxo.

Erru é um molde que já existe em todos os programas:

molda Erru {
textu mensage;
}

Uma função que pode falhar declara-o a seguir ao tipo de retorno, depois de ::

fn metadi(int a, int b) num : Erru {
si b == 0 {
lansa Erru{mensage = "divizon pa zero"};
}
divolvi (num) a / b;
}
  • fn nome() int : Erru devolve um int ou falha com um Erru.
  • fn nome() : Erru não devolve valor, mas pode falhar.
  • lansa <erru>; falha a função de imediato. Só pode ser usado numa função que declare : Erru.
  • Por agora Erru é o único tipo de erro aceite depois de :.
  • Erru é um nome reservado: um programa não pode declarar outro molda Erru.

A mensagem é texto livre, e pode ser uma f-string, o que é a forma de incluir o valor que causou a falha:

lansa Erru{mensage = f"mês inválido: {m}"};

Quem chama uma função que pode falhar pode tratar o erro de duas formas, descritas a seguir. Se não o tratar, o erro não desaparece: quando a função falhar, o programa termina nessa chamada com programa.kriol:N: erro não tratado: mensagem. O compilador avisa disso, mas continua a compilar.

fn inisiu() {
num x = metadi(1, 0); // aviso: o erro de 'metadi' não é tratado
} // em execução: programa.kriol:2: erro não tratado: divizon pa zero

Para o compilador tratar os avisos como erros, compila com --strict.

tenta chamada devolve o valor da chamada, ou, se ela falhar, falha também a função atual com o mesmo erro. Por isso só pode ser usado dentro de uma função que declare : Erru.

fn valida(int n) : Erru {
si n < 0 {
lansa Erru{mensage = f"negativu: {n}"};
}
}
fn kaminhu(int n) num : Erru {
tenta valida(n);
num m = tenta metadi(n, 4);
divolvi m + 1;
}

chamada sinon valor dá o valor da chamada, ou valor se ela falhar. O valor depois de sinon só é calculado quando a chamada falha, e tem de ser compatível com o tipo devolvido.

num m = metadi(7, 0) sinon -1.0; // -1.0

sinon tem a precedência mais baixa de todos os operadores (acima só da atribuição), por isso usa parênteses para o combinar com outros operadores: (metadi(7, 2) sinon 0.0) == 3.5.

QueresUsa
Continuar com um valor razoávelchamada sinon valor
Desistir e deixar quem chamou decidirtenta chamada
Parar o programa aqui, porque a falha não devia acontecernão trates, ou converte num paniku

inisiu também pode declarar : Erru. Se um erro chegar até ela sem ser tratado, o programa escreve programa.kriol:N: erro não tratado: mensagem, com a linha onde o erro saiu de inisiu, e termina com o código 1:

fn inisiu() : Erru {
num x = tenta metadi(1, 0);
}

É o atalho habitual para programas pequenos: usa tenta em todas as chamadas que podem falhar, e deixa a mensagem aparecer se algo correr mal.

Ignorar o erro de uma função declarada por ti com : Erru gera um aviso:

programa.kriol:9: aviso: o erro de 'metadi' não é tratado, por isso o programa
termina se a função falhar; usa 'tenta' ou 'sinon'

É provável que seja um esquecimento, porque quem escreveu a função previu esse caminho.

As funções embutidas que podem falhar, toma e tipo::konverti, não geram aviso, nem com --strict. Parar quando a entrada acaba, ou quando o texto não é um número, é um comportamento aceitável, como num índice fora dos limites, e mantém simples os primeiros programas.

Terminal window
kriol programa.kriol # os avisos são mostrados, mas o programa compila
kriol --strict programa.kriol # qualquer aviso passa a ser um erro de compilação

Usa --strict em projetos e na integração contínua para garantir que nenhum erro é ignorado.

  • Um erro não tratado termina o programa na chamada que falhou; só tenta e sinon o tratam.
  • tenta e sinon só se aplicam a chamadas de funções que podem falhar.
  • tenta só pode ser usado dentro de uma função que declare : Erru.
  • sinon não se aplica a funções que não devolvem valor.
  • Erru não pode ser redeclarado.
  • O tipo de uma chamada que pode falhar não se pode escrever numa declaração: o resultado só existe para tenta e sinon o consumirem.
  • Não há forma de ler a mensagem de um erro que apanhaste: sinon dá-te um valor alternativo, não o Erru. Se precisares da mensagem, escreve-a no lansa ou deixa o erro subir.