Fenner
A linguagem

deprecated, experimental, tip e todo

Marcas que o compilador transforma em avisos e dicas no ponto de uso, e os níveis de {$config}.

O que é e por que existe#

Quando uma API muda, avisar no lugar onde ela é usada é o que ajuda quem chama. Quatro marcas escrevem isso na própria declaração:

MarcaPara quêSaiPadrão
deprecatedobsoleto: não use maisno usoAviso
experimentala assinatura ainda mudano usoAviso
tipsugestão ("use X no lugar")no usoDica
todopendência de quem escrevena declaração ou no comandoDica
Fenner
type
  TBox = class
    FSize: Int32 todo 'trocar por Int64';
    FOld: Int32 deprecated 'use FSize';
    procedure OldShow; deprecated;                         // sem texto: só "obsoleta"
    procedure Beta; experimental 'a assinatura ainda muda';
    procedure Slow; tip 'use Fast quando souber o tamanho';
  end class;

const
  cOldLimit = 10 deprecated 'use cNewLimit';

A marca vem depois do tipo, valor ou cabeçalho e antes do ;, sozinha ou com um texto entre aspas. Pode marcar rotina e método, tipo (class, record, enum…), campo, property, constante e variável. No tipo, a marca vem depois do end.

rc-marks.fxp
program Marks;
// Demo: marcas que o compilador transforma em mensagens no PONTO DE USO.
//   deprecated   -> Aviso: "use outra coisa"
//   experimental -> Aviso: "a API ainda muda"
//   tip          -> Dica: sugestão para quem chama
//   todo         -> Dica: pendência de quem escreve (aparece onde a marca foi escrita)
// O programa roda normalmente: as mensagens saem na COMPILAÇÃO (veja o LEIAME).

type
  TBox = class
    FSize: Int32 todo 'trocar por Int64';
    FOld: Int32 deprecated 'use FSize';
    procedure Show;
    procedure OldShow; deprecated;                           // sem texto: só "obsoleta"
    procedure Beta; experimental 'a assinatura ainda muda';
    procedure Slow; tip 'use Fast quando souber o tamanho';
    procedure Fast;
    procedure Clean; todo 'liberar o buffer interno';
  end class;

const
  cOldLimit = 10 deprecated 'use cNewLimit';
  cNewLimit = 20;

procedure TBox.Show;
begin
  WriteLn('show ', FSize);
end procedure;

procedure TBox.OldShow;
begin
  Show;                          // dentro de algo deprecated não há aviso
end procedure;

procedure TBox.Beta;
begin
  WriteLn('beta');
end procedure;

procedure TBox.Slow;
begin
  WriteLn('lento');
end procedure;

procedure TBox.Fast;
begin
  WriteLn('rápido');
end procedure;

procedure TBox.Clean;
begin
  todo 'ainda não libera nada';  // todo também funciona como comando e não gera código
  WriteLn('limpo');
end procedure;

var
  LBox: TBox;
begin
  LBox := TBox.Create;
  LBox.FSize := 7;
  LBox.FOld := 1;                // Aviso: obsoleta
  LBox.OldShow;                  // Aviso: obsoleta
  LBox.Beta;                     // Aviso: experimental
  LBox.Slow;                     // Dica: tip
  LBox.Fast;
  LBox.Clean;
  WriteLn(cOldLimit);            // Aviso: obsoleta

  // {$config ...} muda o nível da mensagem daqui para a frente: ignore, hint, warning, error, fatal
  {$config Deprecated ignore}
  LBox.OldShow;                  // calado
  {$config Deprecated warning}
  {$config Experimental hint}
  LBox.Beta;                     // agora só uma Dica
  {$config Tip ignore}
  LBox.Slow;                     // calado
end.
Saída
show 7
beta
lento
rápido
limpo
10
show 7
beta
lento

Demo 50. O programa roda normalmente; as mensagens saem na COMPILAÇÃO (veja abaixo).

As mensagens da compilação do demo:

texto
Marks.fxp(61,8): Aviso: "FOld" e' obsoleta (deprecated): use FSize
Marks.fxp(63,8): Aviso: "Beta" e' experimental: a assinatura ainda muda
Marks.fxp(64,8): Dica: "Slow" tem uma dica (tip): use Fast quando souber o tamanho
Marks.fxp(18,22): Dica: pendência (todo): liberar o buffer interno

Trocando o nível com {$config}#

Fenner
{$config Deprecated ignore}    // ignore, hint, warning, error ou fatal
{$config Experimental hint}
{$config Tip ignore}

Vale do ponto da diretiva para a frente. Padrões: Deprecated e Experimental = aviso; Tip e Todo = dica. error lista todos os usos como erro (serve para barrar um build); fatal para na hora, sem a cascata de erros: com dois usos obsoletos, error mostra os dois e fatal só o primeiro. Isso vale também para {$message fatal '…'}.

Uma mensagem de deprecated promovida a erro mantém o código de aviso (FW0011), mas é tratada como erro.

todo em detalhe#

todo também é comando (todo 'tratar o erro de abertura';) e declaração solta (todo 'revisar este módulo';). Não gera código. fc --todo <projeto|unit|pasta> lista todas as pendências, inclusive os comentários // TODO: x:

texto
Marks.fxp(11,18): pendência [todo/declaração] em TBox / FSize: trocar por Int64
Marks.fxp(18,22): pendência [todo/declaração] em TBox / Clean: liberar o buffer interno
Marks.fxp(52,3): pendência [todo/comando] em TBox.Clean: ainda não libera nada
3 pendência(s): 3 de todo, 0 de comentário

Opções: --todo-format=text|json|md, --todo-out=arq, --no-todo-comments e --todo-fail-on-any (sai com 1 se houver alguma, para integração contínua). {$config TodoUse on} (padrão off) também avisa no uso de um símbolo marcado com todo.

Regras de silêncio e armadilhas#

  • O aviso sai no uso, não na declaração. Não há aviso em código gerado pelo compilador, em arquivo da RTL, nem quando o uso está dentro de algo igualmente marcado da mesma espécie (a rotina corrente e as que a envolvem). O silêncio é por espécie: deprecated dentro de algo experimental avisa. Recursão não conta como uso externo.
  • tip e todo são palavras contextuais: só marcam quando seguidas de ; ou de texto entre aspas. var tip: Int32; e um método Tip continuam comuns.
  • Só em minúsculas: Deprecated ou OVERRIDE não são reconhecidos e a compilação falha.
  • Fora de alcance: apelido de tipo nomeado (type A = B deprecated), constante tipada, módulo inteiro e membro individual de enum não aceitam a marca. Herança (class(TOld)), is/as e o cast TOld(x) ainda não avisam.
  • platform e o {$DEPRECATED ON/OFF} do Delphi continuam sendo apenas engolidos.

Veja também#