
Experimentar um analisador estático de código é fácil. Mas implementá-lo, especialmente em um projeto grande e legado, exige habilidade. Se abordado incorretamente, pode adicionar trabalho, atrasar o desenvolvimento e desmotivar a equipe. Vamos discutir brevemente como integrar corretamente a análise estática ao processo de desenvolvimento e começar a usá-la como parte de CI/CD.
Introdução
Recentemente, minha atenção foi atraída pela publicação "Por um lado, este é um bom artigo que vale a pena ler. Por outro lado, não acho que ele forneça uma resposta completa sobre como implementar análise estática sem dificuldades em um projeto com muito código legado. O artigo sugere que você pode aceitar a dívida técnica e trabalhar exclusivamente com código novo, mas não aborda o que fazer com essa dívida técnica posteriormente.
Nossa equipe do PVS-Studio oferece sua perspectiva sobre este tópico. Vamos explorar como surge o problema de implementar um analisador estático de código, como superar esse desafio e como eliminar a dívida técnica de forma gradual e sem complicações.
Problemas
Executar e observar como um analisador estático funciona geralmente não é difícil [Você pode encontrar erros interessantes ou até mesmo vulnerabilidades potencialmente assustadoras no código. Você pode até conseguir corrigir algo, mas é aí que muitos programadores desistem.
Todos os analisadores estáticos produzem falsos positivos. Esta é uma característica da metodologia de análise estática de código e nada pode ser feito a respeito. Em geral, este é um problema insolúvel, como confirmado pelo teorema de Rice [Os algoritmos de aprendizado de máquina também não ajudarão [Se nem mesmo uma pessoa consegue sempre dizer se um determinado código está incorreto, então você não deveria esperar isso de um programa :).
Os falsos positivos não são um problema se o analisador estático já estiver configurado:
- Conjuntos de regras desatualizados desativados;
- Alguns diagnósticos irrelevantes foram desativados;
- Se estivermos falando de C ou C++, as macros que contêm construções específicas são marcadas, o que faz com que avisos inúteis apareçam em todos os lugares onde essas macros são usadas;
- As funções próprias são marcadas como aquelas que executam ações semelhantes às funções do sistema (análogo próprio). memcpy ou printf) [];
- Os falsos positivos foram especificamente desativados por meio de comentários;
- E assim por diante.
Neste caso, pode-se esperar um baixo nível de falsos positivos, em torno de 10-15% [Em outras palavras, 9 em cada 10 avisos do analisador apontarão para um problema real no código, ou pelo menos um "cheiro de código ruim". Você há de concordar que esse cenário é extremamente agradável, e o analisador é um verdadeiro aliado do programador.

Na realidade, em um projeto grande, o cenário inicial será completamente diferente. O analisador emite centenas ou milhares de avisos sobre o código legado. É impossível determinar rapidamente quais desses avisos são relevantes e quais não são. Sentar e começar a analisar todos esses avisos é irracional, pois isso interromperá o trabalho principal por dias ou semanas. Normalmente, a equipe não pode se dar ao luxo de um cenário como esse. Isso também gerará um número enorme de diffs, que corrompem o histórico de alterações. E editar rapidamente tantos fragmentos de código em massa inevitavelmente resultará em novos erros de digitação e outros erros.
E, mais importante, tal feito na luta contra avisos faz pouco sentido. Você concordará que, se um projeto está em execução com sucesso há muitos anos, a maioria dos erros críticos já foi corrigida. Sim, essas correções foram muito caras, exigindo depuração, recebendo feedback negativo dos usuários sobre os bugs e assim por diante. Um analisador estático teria ajudado a corrigir muitos desses erros na fase de codificação, de forma rápida e barata. Mas, no momento, esses erros já foram corrigidos, de uma forma ou de outra, e o analisador detecta principalmente erros não críticos em código antigo. Esse código pode não ser usado, ou ser usado muito raramente, e um erro nele pode não ter consequências perceptíveis. Talvez a sombra de um botão esteja com a cor errada em algum lugar, mas isso não interfere no uso do produto por ninguém.
É claro que mesmo pequenos erros ainda são erros. E às vezes um erro pode esconder uma vulnerabilidade real. No entanto, descartar tudo e passar dias ou semanas lidando com erros que mal aparecem parece uma ideia duvidosa.
Os programadores ficam olhando fixamente para todos esses avisos em seu código antigo e funcional... E pensam: vamos dispensar a análise estática. Vamos escrever alguma funcionalidade nova e útil.
À sua maneira, eles têm razão. Acreditam que primeiro precisam se livrar de todos esses avisos. Só assim poderão se beneficiar de análises de código regulares. Caso contrário, os novos avisos serão simplesmente abafados pelos antigos, e ninguém lhes dará atenção.
A analogia aqui é a mesma que com os avisos do compilador. Não é sem motivo que se recomenda manter o número de avisos do compilador em 0. Se houver 1000 avisos, quando houver 1001, ninguém prestará atenção e não ficará claro onde procurar por esse aviso mais recente.

O pior cenário possível é quando alguém em um nível hierárquico superior impõe o uso da análise estática de código. Isso só desmotiva a equipe, pois, da perspectiva deles, adiciona complexidade burocrática que apenas atrapalha o progresso. Ninguém vai olhar os relatórios do analisador, e todo o uso será "no papel". Em outras palavras, a análise é formalmente integrada ao processo DevOps, mas, na prática, não traz nenhum benefício para ninguém. Ouvimos relatos detalhados de participantes de conferências enquanto conversávamos em estandes. Essas experiências podem desencorajar programadores a usar ferramentas de análise estática por um longo tempo, senão para sempre.
Implementação e eliminação da dívida técnica
Na verdade, não há nada de complicado ou assustador em implementar análise estática, mesmo em um projeto antigo de grande porte.
CI / CD
Além disso, o analisador pode ser integrado imediatamente ao seu processo de desenvolvimento contínuo. Por exemplo, a distribuição do PVS-Studio inclui utilitários para visualizar o relatório no formato desejado, bem como notificações para desenvolvedores que escreveram trechos de código problemáticos. Para quem tiver interesse em informações mais detalhadas sobre como executar o PVS-Studio em sistemas de CI/CD, recomendo a leitura da documentação pertinente. Documentação e uma série de artigos:
Mas voltemos à questão do grande número de falsos positivos nos estágios iniciais de implementação de ferramentas de análise de código.
Corrigir dívidas técnicas existentes e lidar com novos avisos.
Os analisadores estáticos comerciais modernos analisam apenas os novos avisos que aparecem em código novo ou modificado. As implementações desse mecanismo variam, mas a essência é a mesma. No analisador estático PVS-Studio, essa funcionalidade é implementada da seguinte forma.
Para começar rapidamente a usar a análise estática, oferecemos aos usuários do PVS-Studio o mecanismo de supressão em massa de avisos [A ideia geral é a seguinte: o usuário executou o analisador e recebeu diversos avisos. Como um projeto está em desenvolvimento há muitos anos, está ativo, crescendo e gerando receita, é provável que o relatório não contenha muitos avisos indicando defeitos críticos. Em outras palavras, os bugs críticos já foram corrigidos, de uma forma ou de outra, por meios mais dispendiosos ou graças ao feedback dos clientes. Consequentemente, tudo o que o analisador encontra atualmente pode ser considerado dívida técnica, cuja correção imediata é impraticável.
Você pode configurar o PVS-Studio para considerar esses avisos irrelevantes por enquanto (adiando a dívida técnica para mais tarde), e ele deixará de exibi-los. O analisador cria um arquivo especial onde armazena informações sobre erros atualmente irrelevantes. Agora, o PVS-Studio só emitirá avisos para código novo ou modificado. Além disso, tudo isso é implementado de forma inteligente. Se, por exemplo, uma linha em branco for adicionada ao início de um arquivo de código-fonte, o analisador entende que essencialmente nada mudou e permanecerá em silêncio. Este arquivo de marcação pode ser adicionado a um sistema de controle de versão. O arquivo é grande, mas isso não é um problema, já que não há necessidade de adicioná-lo com frequência.
Agora, todos os programadores verão avisos relacionados apenas a códigos novos ou modificados. Isso significa que eles podem começar a usar o analisador no dia seguinte, por assim dizer. A dívida técnica pode ser resolvida posteriormente, e os erros podem ser corrigidos gradualmente, enquanto o analisador é aprimorado.
Portanto, o primeiro problema com a implementação do analisador em um projeto legado de grande porte foi resolvido. Agora, vamos descobrir o que fazer com a dívida técnica.
Correção de bugs e refatoração
A solução mais simples e natural é dedicar algum tempo a revisar os avisos suprimidos pelo analisador e corrigi-los gradualmente. Alguns erros de código precisam ser corrigidos, enquanto outros exigem refatoração para que o analisador entenda que o código não apresenta problemas. Um exemplo simples:
if (a = b)A maioria dos compiladores e analisadores de C++ reclama desse tipo de código, já que há uma grande probabilidade de que eles realmente quisessem escrevê-lo. (a == b)Mas existe um acordo tácito, geralmente mencionado na documentação, de que se houver parênteses extras, presume-se que o programador escreveu o código dessa forma intencionalmente, e não há necessidade de reclamar. Por exemplo, na documentação do PVS-Studio para o diagnóstico Está claramente escrito que a seguinte linha será considerada correta e segura:
if ((a = b))Outro exemplo. Há algo esquecido neste código C++? quebrar ou não?
case A:
foo();
case B:
bar();
break;O analisador PVS-Studio emitirá um aviso aqui. Isso pode não ser um erro, e nesse caso você deve dar uma dica ao analisador sintático adicionando o atributo. ou por exemplo __attribute__((fallthrough)):
case A:
foo();
[[fallthrough]];
case B:
bar();
break;Poderíamos dizer que essas alterações de código não corrigem bugs. Sim, isso é verdade, mas elas trazem dois benefícios. Primeiro, o relatório do analisador elimina falsos positivos. Segundo, o código se torna mais compreensível para quem o mantém. E isso é crucial! Só isso já justifica pequenas refatorações, tornando o código mais claro e fácil de manter. Se o analisador não entender se uma "quebra" é necessária, seus colegas programadores também não entenderão.
Além de correções de bugs e refatoração, você pode suprimir seletivamente avisos obviamente falsos do analisador. Alguns diagnósticos irrelevantes podem ser desativados. Por exemplo, algumas pessoas consideram os avisos sem sentido. sobre a comparação de valores float/double. E alguns os consideram importantes e dignos de estudo [Cabe à equipe de desenvolvimento decidir quais avisos são considerados relevantes e quais não são.
Existem outras maneiras de suprimir falsos positivos. Por exemplo, a marcação de macros foi mencionada anteriormente. Tudo isso é descrito com mais detalhes na documentação. O mais importante é entender que, se você abordar os falsos positivos de forma gradual e sistemática, eles não serão um grande problema. A grande maioria dos avisos irrelevantes desaparece após a configuração, restando apenas as áreas que realmente exigem uma revisão cuidadosa e algumas alterações no código.
Também auxiliamos sempre nossos clientes na configuração do PVS-Studio caso surjam dificuldades. Além disso, já houve casos em que nós mesmos eliminamos falsos positivos e corrigimos erros.Só para garantir, decidi mencionar que essa opção de cooperação prolongada também é possível :).
Método da catraca
Existe outra abordagem interessante para melhorar gradualmente a qualidade do código, eliminando os avisos do analisador estático. A ideia é que o número de avisos só pode diminuir.

O número de avisos gerados pelo analisador estático é registrado. O controle de qualidade é configurado para que apenas o código que não aumente o número de avisos possa ser inserido. Isso inicia um processo de redução gradual do número de avisos por meio da configuração do analisador e da correção de erros.
Mesmo que alguém decida trapacear um pouco e passar pelo controle de qualidade não eliminando avisos em seu novo código, mas melhorando o código antigo de terceiros, não é um grande problema. A engrenagem continua girando em uma única direção e o número de defeitos diminuirá gradualmente. Mesmo que alguém não queira corrigir seus próprios defeitos, ainda terá que melhorar algo no código adjacente. Em algum momento, as maneiras fáceis de reduzir avisos se esgotam e chega a hora de corrigir bugs reais.
Essa metodologia é descrita com mais detalhes em um artigo muito interessante de Ivan Ponomarev.", que recomendo a qualquer pessoa interessada em melhorar a qualidade do código."
O autor do artigo também possui um relatório sobre este tema: "".
Conclusão
Espero que este artigo torne os leitores mais receptivos às ferramentas de análise estática e os motive a integrá-las em seus processos de desenvolvimento. Caso tenham alguma dúvida, teremos prazer em respondê-las. Usuários do nosso analisador estático PVS-Studio e auxílio na sua implementação.
Existem outras dúvidas comuns sobre se a análise estática pode realmente ser conveniente e útil. Tentei dissipar a maioria dessas dúvidas na publicação "Motivos para Implementar o Analisador de Código Estático PVS-Studio em Seu Processo de Desenvolvimento" [].
Obrigado pela sua atenção e venha! e experimente o analisador PVS-Studio.
Links adicionais
- André Karpov.
- Wikipedia. .
- Andrey Karpov, Victoria Khanieva. .
- PVS-Studio. Documentação. .
- André Karpov. .
- PVS-Studio. Documentação. .
- Ivan Andryashin. .
- Pavel Eremeev, Svyatoslav Razmyslov. .
- André Karpov. .
Se você quiser compartilhar este artigo com um público que fala inglês, use o link de tradução: Andrey Karpov. .
Fonte: habr.com
