Como lidar com avisos em um projeto herdado


9

Eu trabalho em um projeto C ++ que gera muitos bilhões de avisos. A maioria dos avisos apareceu depois que o código foi escrito:

  • Inicialmente, o projeto usava o Visual C ++ 8, logo alternando para 9, mas há pouca diferença nos avisos gerados. Os avisos foram corrigidos ou silenciados, portanto não havia avisos.
  • Em seguida, um destino de 64 bits foi adicionado. Ele gerou uma enorme quantidade de avisos, principalmente por causa do uso desleixado dos tipos (por exemplo, unsignedvs. size_t). Ninguém se incomodou ou teve tempo para corrigi-los quando o código funcionava para esse fim.
  • Em seguida, foi adicionado suporte para outras plataformas usando o GCC (4.5 e 4.6, alguns 4.4 inicialmente). O GCC é muito mais exigente, por isso gerou muitos outros avisos. Mais uma vez ninguém se incomodou ou teve tempo para consertá-los. Isso foi complicado pelo fato de o GCC não ter um pragma para silenciar um aviso em particular parte do código até a versão 4.5 e, de acordo com a documentação, ainda não é o que se precisa.
  • Nesse meio tempo, alguns avisos obsoletos apareceram.

Então agora temos um projeto que gera milhares de avisos. E não posso nem dizer de quantos lugares, já que até os .cpparquivos são compilados várias vezes por diferentes compiladores e os avisos nos cabeçalhos são impressos repetidamente.

Existe uma prática recomendada para limpar algo assim? Ou pelo menos alguma experiência positiva em lidar com isso?

Respostas:


4

Não toque em nenhum código herdado. Encontre uma maneira de suprimir os avisos por enquanto. Fazer alterações aparentemente inócuas no código pode introduzir erros no código que, suponho, já foi testado e é relativamente livre de erros.

Suprimindo todos os avisos que estão atualmente na base de código, você pode fingir ter uma lista limpa. Quaisquer novos avisos introduzidos no código devem ser eliminados e, quando o código antigo é reescrito, a nova implementação não deve suprimir os avisos.

Seus objetivos aqui devem ser:

  1. Reduza a relação sinal / ruído. Se os desenvolvedores virem milhares de avisos quando construírem, não perceberão que o ramo mestre possui avisos de 2004 e 2006.

  2. Não introduza mais bugs. Ao fazer as alterações inócuas mencionadas acima, você pode introduzir bugs imprevistos. Isso é especialmente pertinente quando você tem milhares de avisos. Mesmo que a sua taxa de erro seja incrivelmente baixa, você ainda tem milhares de chances de errar.

  3. Não deixe que o código herdado abaixe seus padrões para o novo código. Só porque os avisos para uma classe foram suprimidos não significa que a nova classe possa fazer o mesmo. Eventualmente, com diligência, o código legado será eliminado da base de código e substituído por um código mais novo e sem aviso.


1
+1 "Fazer alterações aparentemente inócuas no código pode introduzir bugs no código ..." - É improvável que o código legado tenha testes automatizados. Esses erros não serão encontrados.
mattnz

@mattnz: Na verdade, os pequenos testes automatizados estão espalhados pelo código legado e não legado. É um aplicativo interativo, a maioria não pode ser razoavelmente testado em unidade. Os testes de unidade que existem falham (na corrupção de memória) no servidor de integração contínua. Já faz mais de um ano, mas ninguém conseguiu corrigi-lo porque não pode ser reproduzido no depurador. E para ser útil, o servidor de IC precisaria buscar algumas amostras de dados para testar as quais também nunca foram implementadas.
Jan Hudec

1
Agora, tudo se resume a como silenciar os avisos existentes de maneira eficiente, sem silenciar nenhum aviso semelhante no novo código.
Jan Hudec

@ Jan: você está realmente dizendo que não pode se incomodar em corrigir os poucos testes que tem? Em um fórum de programação? Mesmo? (SMH)
mattnz

2
@mattnz: Sim. Porque os testes são inúteis. E sempre será. No caso mais comum de sistemas de informações de negócios, os testes automatizados são ótimos, mas, para o tipo de aplicativo interativo, eles trazem tão pouco valor que os usamos apenas ocasionalmente para depurar algumas funções de suporte. A maioria dos erros ocorre no código que só pode ser testado interativamente. Posso imaginar escrever alguma automação para isso, mas seria muito trabalhoso, não tenho certeza se seria econômico.
Jan Hudec

14

Eu acho que avisos, que não são tratados como erros, são inúteis. Você recebe toneladas deles; portanto, ninguém se incomoda em olhar para eles, e eles perdem seu objetivo.

Minha sugestão é consertar todos eles e começar a tratá-los como erros.
Consertá-los parece assustador, mas acho que pode ser feito. Um bom programador que escolherá esse trabalho descobrirá em breve a maneira de lidar com cada aviso e o tratará de maneira muito mecânica e rápida.

Fizemos um projeto semelhante na minha empresa e funcionou - agora não temos avisos na parte do código em que isso é importante para nós, e estou falando de milhares de arquivos (não sei quantos avisos) .

Isso deve ser seguido imediatamente, tratando os avisos como erros. Quando uma parte do seu código estiver livre de aviso, altere os sinalizadores de compilação dessa parte e não deixe novos avisos entrarem. Se você não conseguir fazer isso, novos avisos aparecerão como cogumelos e seu trabalho será desperdiçado.

Uma preocupação que você pode ter é a introdução de erros como resultado da correção dos avisos. Isso é especialmente provável porque o programador que corrige os avisos provavelmente não conhece a maior parte do código que está mudando. No entanto, a maioria das alterações seria muito segura - por exemplo, adicionar um protótipo de função ou um elenco.


2
+1: "Penso que avisos, que não são tratados como erros, são inúteis.": Concordo, por que gostaria de ser avisado sobre algo que não é um problema?
Giorgio

1
@Giorgio Porque os avisos não precisam necessariamente ser corrigidos ou talvez não haja tempo / dinheiro disponível para corrigi-los agora. Eles são uma forma de dívida técnica. Isso não significa que eles não devam ser tratados eventualmente, mas também significa que eles não devem necessariamente ser varridos para debaixo do tapete. Sua única opção restante é tê-los; eles estão lá para deixá-lo com coceira, de modo que talvez eles possam se consertar.
Robert Harvey

1
@ Robert Harvey: Eu concordo totalmente com você. O único risco é que alguém comece a ignorar os avisos e, depois de um tempo, eles não causem mais coceira: eu vi avisos aparecerem durante anos no mesmo projeto, e todo desenvolvedor diria que eles deveriam ser corrigidos "em algum momento o futuro". Ninguém prestava mais atenção a esses avisos: eles haviam se tornado parte da saída normal no log de criação principal. Não sei como evitar esse tipo de situação.
Giorgio

1
O problema de corrigir todas as mentalidades "agora" é que isso leva a correções incorretas e a códigos menos sustentáveis ​​- em C ++, é incrível a facilidade com que um typecast pode fazer um aviso desaparecer, mais frequentemente do que não é a coisa errada a fazer (por exemplo, assinar / não assinar) avisos). É muito improvável que o código legado tenha testes de regressão automatizados, e as alterações no código introduzirão defeitos. "Corrigir todos e b.gg.r as consequências" não é a abordagem correta.
mattnz

@mattnz, Quebrar o código enquanto corrige o aviso é realmente a principal preocupação aqui. No entanto, acho que não há outra maneira - consertá-los e tratar os futuros como erros ou ignorá-los (e ocasionalmente encontrar bugs, sobre os quais você foi avisado).
Ugoren

3

Eu estava lendo os Padrões de codificação do C ++: 101 regras, diretrizes e práticas recomendadas, e a segunda das diretrizes sugere "Compile de forma limpa em altos níveis de aviso". Ele descreve várias razões pelas quais você não gostaria que muitos avisos em um programa e os principais dentre eles fossem

  1. Existe um problema em potencial no seu código.
  2. Deve ter compilações silenciosas e bem-sucedidas e, se não, você pode estar perdendo problemas reais.
  3. Avisos benignos ignorados podem obscurecer avisos apontando para perigos reais

Programas que compilam com avisos podem parecer corretos, mas é possível que haja problemas reais com o código que possam surgir em algum momento. Sugiro categorizar os avisos, atribuir a eles um nível de priorização e, quando tiver tempo, corrigi-los.


+1: para "Compilar com limpeza em altos níveis de aviso": acho que devemos usar o máximo de ajuda possível do compilador.
Giorgio

1

Separe um sprint / ciclo para fazer manutenção preventiva no sistema, limpando o código morto, eliminando avisos, limpando comentários ruins, coisas assim, OU instituindo uma política para eliminar avisos conforme as fontes são tocadas de qualquer maneira.
Eu não faria o primeiro apenas para me livrar dos avisos, mas se esse ciclo fosse planejado, isso faria parte da carga de trabalho. A razão disso é que tocar em um arquivo para qualquer coisa apresenta risco de erros e, portanto, arrisca a estabilidade do seu produto.


2
Não podemos deixar de lado um sprint. Portanto, eliminando os avisos conforme as fontes são tocadas por outros motivos, deve ser. Mas existem algumas fontes que não foram tocadas por muito tempo e levará muito tempo até que sejam, por isso precisamos informar os avisos desses bits herdados.
Jan Hudec

O @JanHudec pode querer fazer uma análise da gravidade dos avisos nesses casos, talvez agendar algum tempo para revisitar esse código antigo e fazer uma limpeza nos bits selecionados, refatorar para mantê-lo vivo. Caso contrário, pode ficar obsoleto e as pessoas com medo de tocá-lo, mesmo se necessário, porque ninguém fica sabendo como funciona.
Jwenting

@JanHudec - Parece que você não tem tempo para resolver os avisos se não puder fazer manutenção preventiva no sistema.
Ramhound

@ Ramhound: Suponho que poderíamos fazer um pouco de zeladoria, mas não muito, e isso deve ocorrer em paralelo com outros trabalhos.
Jan Hudec

1

Sinto sua dor porque estou em uma situação semelhante. A maneira como abordei o problema é: remova os avisos de um módulo / subprojeto de cada vez e defina o sinalizador do compilador "tratar avisos como erro" (Werr) para o módulo após a limpeza. Além disso, decidi me concentrar apenas em uma plataforma, apesar de desenvolvermos vários sistemas operacionais com diferentes compiladores. Finalmente, para as bibliotecas de terceiros que compilamos, desativei os avisos.

Veja bem, livrar-se de alguns desses avisos é muito mais fácil dizer do que fazer. Por exemplo, size_t versus problemas não assinados mencionados pode causar estouros de número inteiro se você apenas silenciar os avisos com projeções - assim, em alguns casos, você substitui sem sinal por size_t; em outros casos, é necessário detectar um estouro em tempo de execução. É tudo caso a caso - muito tedioso e difícil.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.