É terça-feira, 14h. Caixa de entrada: "Pode revisar esse fonte rapidinho? Cliente reclamando que lock fica aberto às vezes." Anexo: ALCMAIL.prw, 935 linhas, 7 User Functions, customização de 2018 que ninguém olha há anos.

Antes da v0.2.0 da claude-advpl-skill, eu faria assim:

  • Abrir no VSCode
  • Buscar todas ocorrências de RecLock e cruzar com MsUnlock uma por uma
  • Procurar todos os TCQuery e ver se tem D_E_L_E_T_ = ' '
  • Olhar declarações Local vs variáveis usadas (varias For i := 1 to escondidas)
  • Conferir se GetMV tem default — senão quebra em produção

Tempo estimado: 2 horas. Tempo real: provavelmente 3, porque sempre tem distração.

Hoje, com a v0.2.0 que acabei de lançar:

$ /advpl-find-issues ALCMAIL.prw

=== ADVPL FIND-ISSUES ===
Arquivos analisados: 1
Issues encontradas: 16

Resumo: 2 error · 14 warn · 0 info

30 segundos. 16 problemas reais detectados. Vou mostrar cada regra que disparou — e o que cada uma significa em produção.

O que a /advpl-find-issues encontrou

2 ERROS: For sem Local (variável vira PUBLIC implícita)

ALCMAIL.prw:679 ERR for-i-not-declared
Variavel For "i" sem Local declarado — vira PUBLIC implicita

ALCMAIL.prw:730 ERR for-i-not-declared
Variavel For "i" sem Local declarado — vira PUBLIC implicita

O dev original escreveu:

User Function AlcPxNiv(c_Filial, c_Solic)
    l_ProxNiv := .F.
    For i := 1 to 10
        ...
    Next i
Return l_ProxNiv

Sem Local i := 0, o i vira PUBLIC implícita. Polui o escopo do chamador, conflita entre threads, e em produção pode dar resultado errado se outra rotina também usar i. Mesmo problema com l_ProxNiv. Aconteceu 2 vezes nesse fonte.

4 WARNs: SQL sem D_E_L_E_T_ e sem xFilial

ALCMAIL.prw:823 WRN sql-no-deleted
ALCMAIL.prw:823 WRN sql-no-filial
ALCMAIL.prw:904 WRN sql-no-deleted
ALCMAIL.prw:904 WRN sql-no-filial

Olhando a linha 823:

cQry := "SELECT XCR1.XCR_FILIAL, XCR1.XCR_NUM, ... "
cQry += "FROM " + RetSqlName("SAK") + " SAK (NOLOCK)"
cQry += "INNER JOIN " + RetSqlName("XCR") + " XCR1 (NOLOCK)"
cQry += "  ON AK_USER = XCR1.XCR_USER"
cQry += " AND AK_FILIAL = XCR1.XCR_FILIAL"
cQry += " AND XCR1.D_E_L_E_T_ = ''"

Olha o detalhe: o filtro é D_E_L_E_T_ = '' (string vazia), não ' ' (espaço). Não funciona. Protheus marca registro ativo com ' ' (espaço) e deletado com '*'. '' retorna zero linhas — a query inteira tava bugada.

O linter pegou isso porque não encontrou a string exata D_E_L_E_T_ com filtro adequado.

6 WARNs: GetMV sem default

ALCMAIL.prw:71  WRN getmv-no-default
ALCMAIL.prw:157 WRN getmv-no-default
ALCMAIL.prw:236 WRN getmv-no-default
ALCMAIL.prw:324 WRN getmv-no-default
ALCMAIL.prw:491 WRN getmv-no-default
ALCMAIL.prw:639 WRN getmv-no-default

Todas as 6 linhas têm padrão tipo:

c_HTML += ''

Se MV_XLOGO não existe (ambiente novo, cópia de empresa), GetMV() retorna NIL. NIL + string quebra a concatenação. Email sai sem imagem (best case) ou rotina explode (worst case).

Fix: GetMV("MV_XLOGO", .F., "") com default string vazia.

2 WARNs: MemoWrite esquecido em produção

ALCMAIL.prw:821 WRN memowrite-debug
ALCMAIL.prw:902 WRN memowrite-debug
MemoWrite grava arquivo em disco a cada execucao — pode ser debug esquecido

Linha 821:

MemoWrite("XCRAPROV.sql", c_Qry)

Toda vez que essa função roda, grava um arquivo XCRAPROV.sql no disco do AppServer. Isso é código de debug que ficou. Provavelmente foi usado uma vez pra inspecionar a query, e nunca foi removido.

Em volume baixo: ninguém percebe. Em rotina batch processando 10k aprovações: 10k I/O writes desnecessários por execução. Diz pra alguém isso e essa rotina passou anos assim.

2 WARNs: RecLock inclusão sem xFilial

ALCMAIL.prw:827 WRN reclock-no-xfilial (alias XCR)
ALCMAIL.prw:909 WRN reclock-no-xfilial (alias SCR)

Linha 827:

RecLock("XCR", .T.)
XCR->XCR_FILIAL := QRYXCR->XCR_FILIAL
XCR->XCR_NUM    := QRYXCR->XCR_NUM

Esse caso é falso positivo parcial — o dev setando XCR_FILIAL, só que pega da query QRYXCR. A skill alertou porque não encontrou xFilial("XCR") literal. Vale revisar pra garantir que QRYXCR->XCR_FILIAL tem o valor certo — mas não é bug grave.

As 9 regras implementadas

RegraSeveridadeO que detecta
no-msunlockERRORRecLock aberto sem MsUnlock antes do Return
sql-no-deletedWARNQuery SQL sem filtro D_E_L_E_T_ = ' '
sql-no-filialWARNQuery SQL sem filtro de filial
getmv-no-defaultWARNGetMV sem 3º param
function-not-userERRORFunction nua — RPO Token moderno rejeita
memowrite-debugWARNMemoWrite esquecido em código
for-i-not-declaredERRORFor com variável sem Local
reclock-no-xfilialWARNInclusão sem setar _FILIAL := xFilial()
old-msg-yesnoINFOMsgYesNo legado (substituir por ApMsgYesNo)

Como uso isso no dia-a-dia

Três cenários reais:

  1. Revisão de PR: dev manda fonte, rodo /advpl-find-issues antes de olhar. Pego o trivial automatizado, foco na revisão semântica do código.
  2. Auditoria de cliente novo: recebo customização legada de cliente que migrou pra gente. /advpl-find-issues src/customizacoes/ em pasta inteira me dá mapa do que tem que prioritizar.
  3. Gate de CI/CD: --severity=error em pipeline GitHub Actions / Azure DevOps. Erros graves (Function nua, For sem Local) bloqueiam o merge.

Limitações honestas

  • Falsos positivos em SQL: query muito complexa em múltiplas linhas pode escapar do detector incremental. Se filtro D_E_L_E_T_ está em fragmento longe, pode não pegar.
  • Cross-arquivo: não detecta função chamada em outro fonte (chamada não declarada). Cada arquivo é analisado isolado.
  • Macros (#xcommand): se a SX3 ou um header customizado define expansão, o analyzer analisa o fonte literal — pode ter falso positivo dentro de bloco que expande.

Roadmap v0.2.1+

  • Mais regras: SaldoTit/Posicione em loop (performance), array em ponteiro de função, transação aninhada
  • Output JSON pra integração CI/CD
  • Auto-fix flag (--fix) pra correções triviais (adicionar Local i := 0, default em GetMV)
  • Whitelist de regras por .advpllintrc

Download e instalação

v0.2.0 com a skill /advpl-find-issues está liberada. Pacote MIT, 64 KB, instala em 30 segundos:

curl -O https://archtecgroup.com.br/downloads/claude-advpl-skill-v0.2.0.zip
unzip claude-advpl-skill-v0.2.0.zip
cd claude-advpl-skill
bash install.sh    # ou .\install.ps1 no Windows

Depois disso, em qualquer sessão Claude Code:

/advpl-find-issues caminho/do/fonte.prw
/advpl-find-issues caminho/pasta/                      # recursivo
/advpl-find-issues caminho/pasta/ --severity=error     # so erros graves

Pra fechar

Auditar fonte AdvPL legado deixou de ser tarefa de uma tarde inteira. Vira 30 segundos + julgamento humano sobre o que priorizar. Esse é o tipo de ganho real que IA bem aplicada traz — não substitui você, automatiza o que é mecânico e repetitivo.

Se você é dev Protheus ou líder técnico, baixa, testa, manda feedback. Open source, MIT, no post principal da skill.

Próximo lançamento: catálogo de 69 funções AdvPL/TLPP documentadas (já no ar — /docs).