Sistema e ferramentas
A biblioteca padrão do Glide expõe alguns módulos que ficam próximos ao runtime e ao próprio compilador: tratamento tipado de sinais do SO que se integra ao select!, captura de backtrace em tempo de execução, a superfície AST de meta para bibliotecas de macros, e os códigos de lint que o compilador emite em tempo de build. Cada um é pequeno e focado; esta página documenta todos os itens públicos.
Import
import stdlib::signal::*; // enum Signal + signal_chan / raise / default / ignore
import stdlib::backtrace::*; // stack_trace / stack_trace_skip
import stdlib::meta::*; // nós AST Expr/Stmt/Type/... + construtores (para @proc_macro)
import stdlib::lint::*; // códigos LINT_* + lint_code()
Cada módulo é independente — importe apenas os que usar.
| Módulo | Superfície pública | Resumo |
|---|---|---|
stdlib::signal |
1 enum (3 métodos) + 4 funções livres + 1 helper interno | Sinais do SO entregues como valores chan<Signal>. |
stdlib::backtrace |
2 funções | Percorre a pilha de chamadas ativa em um *Vector<string>. |
stdlib::meta |
re-export de bootstrap::ast (10+ structs, 100+ constantes, 50+ construtores) |
A superfície AST em tempo de compilação para autores de @proc_macro. |
stdlib::lint |
8 constantes + 1 função | Nomes para os códigos de aviso embutidos do compilador + um helper para lint customizado. |
Sinais
stdlib::signal entrega sinais do SO como valores em um chan<Signal>, de forma que o tratamento de sinais se compõe com o restante da sua concorrência via select!/recv(), em vez de rodar dentro de um handler async-signal-unsafe. No POSIX, a implementação usa o clássico truque do self-pipe: um handler sigaction escreve o signum num pipe, uma thread leitora o lê e empurra um Signal para o canal. No Windows, conecta-se ao SetConsoleCtrlHandler (Ctrl+C → SIGINT, Ctrl+Break → SIGTERM, fechar/logoff/shutdown → SIGHUP).
Superfície pública
| Item | Assinatura | Descrição |
|---|---|---|
enum Signal |
8 variantes nomeadas + Other(i32) |
O valor do sinal carregado no canal. |
Signal::to_int |
fn to_int(self: Signal) -> i32 |
Signum POSIX. |
Signal::from_int |
fn from_int(n: i32) -> Signal |
Constrói a partir de um signum bruto (total). |
Signal::name |
fn name(self: Signal) -> string |
Nome no estilo POSIX. |
signal_chan |
fn signal_chan(s: Signal) -> chan<Signal> |
Inscreve-se; retorna o canal de entrega. |
signal_default |
fn signal_default(s: Signal) -> ! |
Restaura o handler padrão do SO. |
signal_ignore |
fn signal_ignore(s: Signal) -> ! |
Descarta o sinal inteiramente. |
signal_raise |
fn signal_raise(s: Signal) -> ! |
Lança o sinal contra este processo. |
_signal_push |
fn _signal_push(c: chan<Signal>, n: i32) |
Interno — empurra from_int(n) para um canal. |
enum Signal
Os oito sinais POSIX com nomes consagrados, mais uma válvula de escape Other(i32) para qualquer signum fora do conjunto.
pub enum Signal {
Hup, // SIGHUP = 1
Int, // SIGINT = 2
Quit, // SIGQUIT = 3
Kill, // SIGKILL = 9
Usr1, // SIGUSR1 = 10
Usr2, // SIGUSR2 = 12
Pipe, // SIGPIPE = 13
Term, // SIGTERM = 15
Other(i32), // qualquer signum não listado acima
}
| Método | Assinatura | Descrição |
|---|---|---|
to_int |
fn to_int(self: Signal) -> i32 |
Signum POSIX; Other(n) retorna n literalmente. |
from_int |
fn from_int(n: i32) -> Signal |
Constrói a partir de um signum bruto; valores desconhecidos tornam-se Other(n). Sempre tem sucesso (total). |
name |
fn name(self: Signal) -> string |
Nome no estilo POSIX ("SIGINT"); Other(n) retorna "SIG#<n>". |
import stdlib::signal::*;
fn main() -> i32 {
let s: Signal = Signal::Int;
println!(s.name(), s.to_int()); // SIGINT 2
let t: Signal = Signal::from_int(15);
println!(t.name()); // SIGTERM
let o: Signal = Signal::Other(42);
println!(o.name(), o.to_int()); // SIG#42 42
match s {
Int => { println!("interrupt"); }
Term => { println!("terminate"); }
_ => { println!("other"); }
}
return 0;
}
import stdlib::signal::*;
// match não consegue comparar inteiros — converta para signum e ramifique com if/else.
fn classify(s: Signal) -> string {
let n: i32 = s.to_int();
if n == 2 { return "interrupt"; }
if n == 15 { return "terminate"; }
if n == 1 { return "hangup"; }
return "other";
}
fn main() -> i32 {
println!(classify(Signal::Int)); // interrupt
println!(classify(Signal::Term)); // terminate
println!(classify(Signal::from_int(99))); // other
return 0;
}
Inscrevendo-se — signal_chan
pub fn signal_chan(s: Signal) -> chan<Signal>
Inscreve-se em s. Cada ocorrência empurra o mesmo Signal no canal retornado. Idempotente — chamadas repetidas para o mesmo sinal retornam o mesmo canal (o slot é indexado pelo signum, limitado a 0..63). O buffer comporta 64; sinais entregues mais rápido do que são consumidos além desse limite são descartados (aceitável para sinais de desligamento, que chegam uma única vez).
import stdlib::signal::*;
fn main() -> !i32 {
let usr1: chan<Signal> = signal_chan(Signal::Usr1);
signal_raise(Signal::Usr1)?;
let s: Signal = usr1.recv();
println!("got", s.name()); // got SIGUSR1
return ok(0);
}
Um servidor de verdade aciona o canal a partir de um loop, tipicamente junto com outros braços em um select!. recv() retorna Signal (não ?Signal), portanto um recv() simples bloqueia até que um sinal chegue:
import stdlib::signal::*;
fn run() -> !i32 {
let intr: chan<Signal> = signal_chan(Signal::Int);
let term: chan<Signal> = signal_chan(Signal::Term);
signal_raise(Signal::Term)?;
while true {
let sig: Signal = term.recv();
match sig {
Int => { println!("shutting down (int)"); break; }
Term => { println!("shutting down (term)"); break; }
_ => { }
}
}
intr.close(); // silencia aviso de canal aberto; permite que receptores parem
term.close();
return ok(0);
}
fn main() -> !i32 {
return run();
}
Alterando a disposição — signal_default, signal_ignore
pub fn signal_default(s: Signal) -> !
pub fn signal_ignore(s: Signal) -> !
| Função | Efeito |
|---|---|
signal_default(s) |
Restaura o handler padrão do SO (SIG_DFL). Um canal de um signal_chan(s) anterior para de receber. |
signal_ignore(s) |
Descarta s inteiramente (SIG_IGN) — sem canal, sem ação padrão. Comum para SIGPIPE em servidores que detectam desconexões no nível de leitura/escrita. |
Ambas retornam ! (um Result sem valor); propague com ? ou use o padrão com ??. Elas produzem err("signal_default failed") / err("signal_ignore failed") se a chamada subjacente a sigaction (POSIX) falhar — na prática apenas para um signum fora do intervalo (< 0 ou >= 64).
import stdlib::signal::*;
fn main() -> !i32 {
signal_ignore(Signal::Pipe)?; // escritas em sockets fechados não nos matam
signal_default(Signal::Int)?; // Ctrl+C encerra novamente
return ok(0);
}
Lançando — signal_raise
pub fn signal_raise(s: Signal) -> !
Lança s contra o processo atual. Se uma inscrição estiver ativa, o sinal é empurrado diretamente para o canal (confiável entre plataformas, sem round-trip pelo SO); caso contrário, cai de volta para raise(sig) no POSIX ou GenerateConsoleCtrlEvent no Windows. Graças ao caminho direto, um lançamento dentro do processo funciona para qualquer signum uma vez inscrito — inclusive SIGUSR1/SIGUSR2 no Windows, que não tem análogo no nível do SO. Isso o torna a forma idiomática de acionar um braço de select! em testes. Retorna err("signal_raise failed") em caso de falha.
_signal_push (helper interno)
pub fn _signal_push(c: chan<Signal>, n: i32)
Empurra Signal::from_int(n) em c. É pub apenas porque a thread leitora em C o chama do lado Glide (para que o runtime nunca precise do layout privado de Signal). Normalmente você não tem motivo para chamá-lo; prefira signal_raise para injetar um sinal em testes.
import stdlib::signal::*;
fn main() -> !i32 {
let intr: chan<Signal> = signal_chan(Signal::Int);
_signal_push(intr, 2);
let s: Signal = intr.recv();
println!(s.name()); // SIGINT
return ok(0);
}
Backtraces
stdlib::backtrace percorre a pilha de chamadas ativa via o unwinder da plataforma (backtrace()/backtrace_symbols() no glibc/macOS/FreeBSD, CaptureStackBackTrace + SymFromAddr/SymGetLineFromAddr64 do DbgHelp no Windows). As linhas são aproximadas: com -O2 e omissão do frame pointer você pode obter menos frames ou menos detalhes, e em libcs sem execinfo.h (p. ex., musl) o resultado degrada para vazio.
| Função | Assinatura | Descrição |
|---|---|---|
stack_trace |
fn stack_trace() -> *Vector<string> |
Pilha de chamadas atual, um frame por elemento. A primeira entrada é o chamador de stack_trace; o próprio unwinder é filtrado. |
stack_trace_skip |
fn stack_trace_skip(n: i32) -> *Vector<string> |
O mesmo, mas pula os n frames mais próximos do chamador — útil para ocultar um shim fino (p. ex., a expansão de uma macro log_error!). No Windows, n é limitado a 0..32. |
Ambas retornam um vetor vazio quando a plataforma não consegue percorrer a pilha — nunca um erro ou ?T, portanto sempre verifique .len() == 0 em vez de presumir que existem frames. As strings de frame são o que o unwinder produz — tipicamente binary(function+0xoffset) [0xaddr] no POSIX e function (file:line) (quando há informação de linha) ou function+0xoffset no Windows.
import stdlib::backtrace::*;
fn buggy() {
let frames: *Vector<string> = stack_trace();
for f in frames {
println!(f);
}
let skipped: *Vector<string> = stack_trace_skip(1);
println!("frames", skipped.len());
}
fn main() -> i32 {
buggy();
return 0;
}
Uso defensivo — trate explicitamente o caso degradado (vazio) e acesse o frame do topo com .get(0):
import stdlib::backtrace::*;
fn here() {
let frames: *Vector<string> = stack_trace();
if frames.len() == 0 {
println!("(no frames available)"); // p. ex. musl / build com FPO
return;
}
println!("top frame:", frames.get(0));
println!("frame count:", frames.len());
}
fn caller() { here(); }
fn main() -> i32 {
caller();
return 0;
}
Macros declarativas (macro name!)
Uma macro declarativa é um template no estilo macro_rules! que expande em tempo de compilação, entre o parse e a checagem de tipos. Os matchers vinculam os argumentos da chamada ($x:expr), e a forma variádica $($x:expr),* repete um fragmento do corpo com $( … )*:
macro bail!($cond:expr, $msg:expr) {
if $cond { return err($msg); }
}
macro list_each!($($v:expr),*) { // variádica
$( println!($v); )*
}
// Atrelada a tipo: forma de instância (usa `self`) e a forma `Type::name!`.
impl<T> Vector<T> {
macro push_all!($($x:expr),*) { $( self.push($x); )* }
}
Macros que retornam um valor
Quando o corpo de uma macro termina com return <expr>;, uma chamada em posição de expressão expande para uma expressão-bloco que produz esse valor — a mesma regra de um bloco literal { …; return v }. A mesma macro faz splice de statements em posição de statement e produz um valor quando usada como tal:
macro doubled!($x:expr) {
let d: i32 = $x * 2;
return d;
}
macro ints!($($x:expr),*) {
let v: *Vector<i32> = Vector::new();
$( v.push($x); )*
return v;
}
let a = doubled!(21); // 42
let b = doubled!(5) + doubled!(10); // 30 — dentro de uma expressão maior
let v = ints!(1, 2, 3); // um *Vector<i32> de verdade
Isso funciona em todas as formas de chamada — name! simples, receptor recv.name! e Type::name! — e em qualquer posição de expressão: inicializador de let, argumento de função, operando ou return.
Higiene
As variáveis locais que o corpo da macro introduz são renomeadas por expansão, então a macro pode nomear um temporário como quiser sem colidir com uma variável do chamador de mesmo nome passada como argumento:
macro inc!($x:expr) {
let tmp: i32 = $x + 1;
return tmp;
}
let tmp: i32 = 5;
let y = inc!(tmp); // y == 6 — o `tmp` de inc não captura o `tmp` do chamador
Uma macro aninhada nos argumentos de outra macro expande corretamente, e uma macro definida em termos de si mesma para num limite de recursão com um diagnóstico, em vez de travar o compilador.
Meta (superfície AST para macros)
stdlib::meta é o ponto de entrada curado e suportado para bibliotecas que criam macros @proc_macro / @proc_attr / @proc_derive. O corpo do módulo é um único re-export:
pub import bootstrap::ast::*;
Ele espelha deliberadamente os nomes internos de bootstrap::ast do compilador, mas protege as bibliotecas de macros de refatorações internas — importar stdlib::meta aparece como uma dependência normal, em vez de mexer nas entranhas do compilador. Uma proc macro é executada no interpretador embutido do compilador em tempo de compilação (antes da geração de código) e é removida do binário do consumidor, não adicionando nenhum peso em tempo de execução.
Uma macro no estilo de função recebe o *Vector<Expr> da chamada (os argumentos); uma macro de atributo/derive recebe o *Stmt anotado. Em ambos os casos, a saída é o *Vector<Stmt> intercalado no consumidor.
Structs de nós
Os tipos de nós AST que sua macro constrói e inspeciona. Todos os campos são pub; você lê p. ex. expr.kind, expr.int_val, expr.line, stmt.kind.
| Struct | Papel |
|---|---|
Expr |
Nó de expressão. Campos-chave: kind: i32 (um EX_*), line/column, int_val, str_val, bool_val, op_code, lhs/rhs/operand: *Expr, args: *Vector<Expr>, field: string, cast_to: *Type. |
Stmt |
Nó de instrução. kind: i32 (um ST_*) mais campos de payload por tipo. |
Type |
Nó de tipo. kind: i32 (um TY_*). |
Param |
Um parâmetro de função (nome + *Type). |
Field |
Um campo de struct (nome + *Type + is_pub). |
EnumVariant |
Uma variante de enum. |
MatchArm |
Um braço de match. |
SelectArm |
Um braço de select!. |
Attr |
Um atributo (@nome(args)). |
MacroParam |
Um parâmetro matcher em uma definição de macro. |
StructLitField |
Um campo de um literal de struct. |
Constantes de kind (discriminantes)
O campo kind de cada nó contém um desses discriminantes i32. O catálogo é grande; a tabela lista os prefixos e uma fatia representativa. O conjunto completo vive em bootstrap::ast e é re-exportado literalmente.
| Prefixo | Quantidade | Exemplos |
|---|---|---|
TY_* (tipos de tipo) |
13 | TY_NAMED=0, TY_POINTER=1, TY_BORROW=2, TY_SLICE=4, TY_GENERIC=5, TY_FNPTR=6, TY_RESULT=7 (!T), TY_OPTION=9 (?T), TY_TUPLE=12 |
EX_* (tipos de expr) |
27 | EX_INT=0, EX_FLOAT=1, EX_STRING=2, EX_BOOL=3, EX_IDENT=5, EX_BINARY=6, EX_UNARY=7, EX_CALL=8, EX_MEMBER=10, EX_MACRO=14, EX_IF=23, EX_MATCH=25 |
ST_* (tipos de stmt) |
27 | ST_LET=0, ST_RETURN=1, ST_EXPR=2, ST_IF=3, ST_WHILE=4, ST_FN=6, ST_STRUCT=9, ST_FOR=12, ST_ENUM=15, ST_MATCH=16, ST_TRAIT=24, ST_SELECT=26 |
UN_* (operadores unários) |
7 | UN_NEG=1, UN_NOT=2, UN_DEREF=3, UN_ADDR=4, UN_ADDR_MUT=5, UN_BIT_NOT=6, UN_TRY=7 |
OP_* (operadores binários/atribuição) |
24 | OP_ADD=1, OP_SUB=2, OP_MUL=3, OP_EQ=6, OP_NE=7, OP_LT=8, OP_AND=12, OP_OR=13, OP_ASSIGN=14, OP_COALESCE=24 (??) |
Construtores
Construa AST de substituição. Há mais de 50; agrupados aqui, com assinaturas literais para os mais usados.
| Grupo | Funções |
|---|---|
Tipos (ty_*) |
ty_named, ty_pointer, ty_generic, ty_fnptr, ty_result, ty_option, ty_optres, ty_assoc, ty_tuple |
Expressões (expr_*) |
expr_int, expr_float, expr_string, expr_bool, expr_ident, expr_char, expr_null, expr_binary, expr_unary, expr_assign, expr_call, expr_member, expr_index, expr_cast, expr_path, expr_macro, expr_macro_var, expr_if, expr_match, expr_block, expr_tuple, expr_struct_lit, expr_fnexpr, expr_postinc, expr_postdec |
Instruções (stmt_*) |
stmt_let, stmt_return, stmt_expr, stmt_fn, stmt_struct, stmt_impl, stmt_impl_trait, stmt_if, stmt_while, stmt_for, stmt_break, stmt_continue, stmt_const, stmt_import, stmt_enum, stmt_spawn, stmt_match, stmt_craw, stmt_asm |
| Helpers | make_param, make_field, strip_ptr, ast_fill_defaults, pass_diag |
pub fn expr_int(n: i64, line: i32, col: i32) -> *Expr // nó de literal inteiro
pub fn expr_string(s: string, line: i32, col: i32) -> *Expr // literal de string
pub fn expr_ident(name: string, line: i32, col: i32) -> *Expr // identificador nu
pub fn expr_binary(op: i32, lhs: *Expr, rhs: *Expr) -> *Expr // op é um código OP_*
pub fn expr_call(callee: *Expr, args: *Vector<Expr>) -> *Expr // f(args)
pub fn stmt_let(name: string, ty: *Type, value: *Expr,
is_mut: bool, line: i32, col: i32) -> *Stmt // let name: ty = value;
pub fn stmt_expr(e: *Expr) -> *Stmt // envolve um Expr como Stmt
pub fn ty_named(name: string) -> *Type // um nome de tipo simples
pub fn pass_diag(line: i32, col: i32, severity: i32,
code: string, msg: string) // emite um diagnóstico
A proc macro mínima: answer!() expande para o literal 42 em cada ponto de chamada.
import stdlib::meta::*;
@proc_macro(answer)
fn impl_answer(args: *Vector<Expr>) -> *Vector<Stmt> {
let out: *Vector<Stmt> = Vector::new();
out.push(*stmt_expr(expr_int(42, 0, 0)));
return out;
}
fn main() -> i32 {
return 0;
}
Construir uma instrução composta — let x: i32 = 1 + 2; — encadeia ty_named, expr_int, expr_binary (com o código de operação OP_ADD) e stmt_let:
import stdlib::meta::*;
@proc_macro(one_plus_two)
fn impl_one_plus_two(args: *Vector<Expr>) -> *Vector<Stmt> {
let out: *Vector<Stmt> = Vector::new();
let sum: *Expr = expr_binary(OP_ADD, expr_int(1, 0, 0), expr_int(2, 0, 0));
let s: *Stmt = stmt_let("x", ty_named("i32"), sum, false, 0, 0);
out.push(*s);
return out;
}
fn main() -> i32 {
return 0;
}
Macros também podem inspecionar suas entradas lendo o campo kind em relação aos discriminantes EX_*:
import stdlib::meta::*;
// Emite uma string nomeando o kind da primeira expressão argumento.
@proc_macro(describe_first)
fn impl_describe_first(args: *Vector<Expr>) -> *Vector<Stmt> {
let out: *Vector<Stmt> = Vector::new();
let label: string = "unknown";
if args.len() > 0 {
let first: Expr = args.get(0);
if first.kind == EX_INT { label = "int"; }
if first.kind == EX_STRING { label = "string"; }
if first.kind == EX_IDENT { label = "ident"; }
}
out.push(*stmt_expr(expr_string(label, 0, 0)));
return out;
}
fn main() -> i32 {
return 0;
}
pass_diag — diagnósticos customizados em tempo de compilação
pub fn pass_diag(line: i32, col: i32, severity: i32, code: string, msg: string)
Emite um diagnóstico a partir de uma macro (ou passe do compilador). severity 1 = erro (falha o build), 2 = aviso. code é um id curto em kebab-case que o usuário pode silenciar com @allow("<code>"). Passe o .line / .column do nó AST que você está sinalizando para que o erro aponte para a fonte correta.
import stdlib::meta::*;
// Avisa (severity 2) se a macro for chamada sem argumentos.
@proc_macro(no_empty)
fn impl_no_empty(args: *Vector<Expr>) -> *Vector<Stmt> {
let out: *Vector<Stmt> = Vector::new();
if args.len() == 0 {
pass_diag(0, 0, 2, "no-empty", "no_empty! called with no arguments");
}
out.push(*stmt_expr(expr_int(0, 0, 0)));
return out;
}
fn main() -> i32 {
return 0;
}
Lint
stdlib::lint documenta os códigos de aviso embutidos do compilador e oferece um helper de tempo de execução. Não há executores de lint aqui — o linting acontece dentro do compilador; este módulo é a superfície estável para os nomes dos códigos mais um helper para construir códigos de lint customizados. (Para emitir um diagnóstico customizado programaticamente a partir de uma macro/passe, use pass_diag em stdlib::meta, acima.)
Códigos de aviso embutidos
Cada código abaixo pode ser silenciado com @allow("<code>") no item circundante (a função, um let interno, um bloco, um if/while, ou a instrução em questão). Múltiplos códigos: @allow("unfreed-alloc", "deprecated-fn"). A supressão segue a AST e não se propaga para cima — um @allow mais estreito não silencia avisos emitidos antes no mesmo escopo externo.
| Constante | Valor | Disparado quando |
|---|---|---|
LINT_DEPRECATED_FN |
"deprecated-fn" |
Chamada a uma função marcada com @deprecated. |
LINT_UNSTABLE_FN |
"unstable-fn" |
Chamada a uma função marcada com @unstable. |
LINT_UNFREED_ALLOC |
"unfreed-alloc" |
malloc(...) / __glide_palloc_make() sem um free correspondente. |
LINT_ARENA_NOT_FREED |
"arena-not-freed" |
Arena::new(...) sem .free() / defer. |
LINT_UNUSED_VAR |
"unused-var" |
Um let local nunca é lido. |
LINT_UNUSED_PARAM |
"unused-param" |
Um parâmetro de função nunca é lido. |
LINT_UNUSED_FN |
"unused-fn" |
Uma função nunca é chamada ou referenciada. |
LINT_UNNECESSARY_MUT |
"unnecessary-mut" |
Um let mut que nunca é reatribuído. |
Códigos embutidos adicionais que o compilador emite mas que não têm constante nomeada aqui: addr-of-temporary (retornar &Foo { … } / refs pendentes), missing-return (um caminho de função não--> void cai fora do fim), large-return (uma struct >64 bytes retornada por valor), chan-leak (um chan deixado aberto em um retorno), e lint:<category> (qualquer função marcada com @lint(...)).
Uma segunda família de códigos de código morto também é emitida sem constante nomeada. Cada um é silenciado por @allow("unused"), e cada um pula declarações pub, genéricas e com impl (qualquer coisa que possa ser alcançada de fora do arquivo ou através de uma trait):
unused-struct— umastructnunca é construída ou referenciada.unused-enum— umenumnunca é usado.unused-const— umconstde nível superior nunca é lido.unused-variant— uma variante deenumnunca é construída ou usada em match.redundant-import— o mesmo módulo é importado duas vezes em um arquivo.
import stdlib::lint::*;
fn main() -> i32 {
println!(LINT_DEPRECATED_FN); // deprecated-fn
println!(LINT_UNSTABLE_FN); // unstable-fn
println!(LINT_UNFREED_ALLOC); // unfreed-alloc
println!(LINT_ARENA_NOT_FREED); // arena-not-freed
println!(LINT_UNUSED_VAR); // unused-var
println!(LINT_UNUSED_PARAM); // unused-param
println!(LINT_UNUSED_FN); // unused-fn
println!(LINT_UNNECESSARY_MUT); // unnecessary-mut
return 0;
}
Lints customizados — lint_code
pub fn lint_code(category: string) -> string
Constrói o código canônico lint:<category> a partir de um nome de categoria (literalmente "lint:".concat(category)). Útil quando um editor ou ferramenta de build filtra/formata diagnósticos de lint customizado programaticamente.
import stdlib::lint::*;
fn main() -> i32 {
let code: string = lint_code("blocking_io");
println!(code); // lint:blocking_io
return 0;
}
Anexando e suprimindo lints
Uma função anotada com @lint("blocking_io", "reason") aparece em cada ponto de chamada:
! lint:blocking_io
function `read_sync` is flagged by `lint:blocking_io`:
blocks the coro scheduler — use spawn_thread
Suprima-o em um ponto de chamada com @allow("lint:blocking_io"). Tanto @lint quanto as formas nomeadas @deprecated se anexam à definição da função; o @allow vai na chamada (ou no item que a envolve):
import stdlib::lint::*;
@deprecated("use parse_v2 instead")
fn parse_v1(s: string) -> i32 { return 0; }
@lint("blocking_io", "blocks the coro scheduler — use spawn_thread")
fn read_sync(path: string) -> i32 { return 0; }
fn main() -> i32 {
@allow("deprecated-fn")
let a: i32 = parse_v1("x");
@allow("lint:blocking_io")
let b: i32 = read_sync("/etc/hosts");
println!(a + b);
return 0;
}