Cómo lidiar con las advertencias en un proyecto heredado

9

Trabajo en un proyecto C ++ que genera miles de millones de advertencias. La mayoría de las advertencias aparecieron después de escribir el código:

  • Inicialmente, el proyecto usó Visual C ++ 8, luego cambió a 9, pero hay poca diferencia en las advertencias generadas. Las advertencias fueron fijadas o silenciadas, por lo que no hubo advertencias entonces.
  • Luego se agregó un objetivo de 64 bits. Generó una gran cantidad de advertencias, principalmente debido al uso descuidado de los tipos (por ejemplo, unsignedvs. size_t). Nadie se molestó, o tuvo tiempo, para arreglarlos una vez que el código funcionó para ese propósito.
  • Luego se agregó soporte para otras plataformas que usan GCC (4.5 y 4.6, algunas 4.4 inicialmente). GCC es mucho más exigente, por lo que generó muchas más advertencias. De nuevo, nadie se molestó ni tuvo tiempo para arreglarlos. Esto se complicó por el hecho de que GCC no tenía un pragma para silenciar una advertencia en un bit de código particular hasta 4.5, y de acuerdo con la documentación todavía no es lo que uno necesitaría.
  • Mientras tanto, aparecieron algunas advertencias obsoletas.

Así que ahora tenemos un proyecto que genera miles de advertencias. Y ni siquiera puedo decir desde cuántos lugares, ya que incluso los .cpparchivos son compilados varias veces por diferentes compiladores y las advertencias en los encabezados se imprimen una y otra vez.

¿Existe alguna práctica recomendada para limpiar algo así? ¿O al menos alguna experiencia positiva al tratarlo?

Jan Hudec
fuente

Respuestas:

4

No toques ninguno de los códigos heredados. Encuentre una manera de suprimir las advertencias por ahora. Hacer cambios aparentemente inocuos en el código puede introducir errores en el código que, supongo, ya ha sido probado y está relativamente libre de errores.

Al suprimir todas las advertencias que están actualmente en la base de código, puede pretender tener una pizarra limpia. Deben eliminarse todas las advertencias nuevas introducidas en el código, y cuando se reescribe el código antiguo, la nueva implementación no debe suprimir las advertencias.

Sus objetivos aquí deberían ser:

  1. Reduzca la relación señal / ruido. Si los desarrolladores ven miles de avisos cuando compilan, no notarán que la rama maestra tiene avisos de 2004 y su rama tiene 2006.

  2. No introduzca más errores. Al realizar los cambios inocuos mencionados anteriormente, podría estar introduciendo errores imprevistos. Esto es especialmente pertinente cuando tienes miles de advertencias. Incluso si su tasa de error es increíblemente baja, aún tiene miles de posibilidades de equivocarse.

  3. No permita que el código heredado baje sus estándares para el nuevo código. El hecho de que se suprimieron las advertencias para una clase no significa que la nueva clase pueda hacer lo mismo. Finalmente, con diligencia, el código heredado se eliminará de la base de código y se reemplazará con un código más nuevo y sin advertencia.

Jonathan Rich
fuente
1
+1 "Realizar cambios aparentemente inocuos en el código puede introducir errores en el código ..." - Es poco probable que el código heredado tenga pruebas automatizadas. Estos errores no se encontrarán.
mattnz
@mattnz: En realidad, las pequeñas pruebas automatizadas se extienden tanto en código heredado como no heredado. Es una aplicación interactiva, la mayor parte no puede ser razonablemente probada en unidades. Las pruebas unitarias que existen fallan (en caso de corrupción de memoria) en el servidor de integración continua. Desde hace más de un año, pero nadie lo solucionó porque no se puede reproducir con depurador. Y para ser útil, el servidor CI necesitaría obtener algunas muestras de datos para probar en las que tampoco se implementó nunca.
Jan Hudec
1
Ahora eso se reduce a cómo silenciar las advertencias existentes de manera eficiente sin silenciar ninguna advertencia similar en el nuevo código.
Jan Hudec
@ Jan: ¿realmente estás diciendo que no puedes molestarte en arreglar las pocas pruebas que tienes? En un foro de programación? De Verdad? (SMH)
mattnz
2
@mattnz: sí. Porque las pruebas son inútiles. Y siempre lo será. En el caso más común de los sistemas de información empresarial, las pruebas automatizadas son excelentes, pero para el tipo de aplicación interactiva que tenemos, aportan tan poco valor que solo las usamos ocasionalmente para depurar algunas funciones de soporte. La mayoría de los errores ocurren en el código que solo se puede probar de forma interactiva. Me imagino escribiendo algo de automatización para ello, pero sería un montón de trabajo que no estoy seguro sería rentable.
Jan Hudec
14

Creo que las advertencias, que no se tratan como errores, son inútiles. Obtienes toneladas de ellos, por lo tanto, nadie se molesta en mirarlos y pierden su propósito.

Mi sugerencia es solucionarlos todos y comenzar a tratarlos como errores.
Arreglarlos parece aterrador, pero creo que se puede hacer. Un buen programador que se encargaría de este trabajo pronto descubrirá la manera de lidiar con cada advertencia y lo manejará de manera muy mecánica y rápida.

Hicimos un proyecto similar en mi empresa, y funcionó: ahora no tenemos advertencias en la parte del código donde es importante para nosotros, y estoy hablando de muchos miles de archivos (no sé cuántas advertencias) .

Esto debe seguirse inmediatamente tratando las advertencias como errores. Una vez que una parte de su código esté libre de advertencias, cambie los indicadores de compilación para esta parte y no permita que entren nuevas advertencias. Si no lo hace, encontrará nuevas advertencias emergentes como hongos, y su trabajo se desperdiciará.

Una preocupación que puede tener es la introducción de errores como resultado de la reparación de las advertencias. Esto es especialmente probable porque el programador que repara las advertencias probablemente no conoce la mayor parte del código que está cambiando. Sin embargo, la mayoría de los cambios serían muy seguros, por ejemplo, agregar un prototipo de función o un elenco.

Ugoren
fuente
2
+1: "Creo que las advertencias, que no se tratan como errores, son inútiles": Estoy de acuerdo, ¿por qué querría que me advirtieran sobre algo que no es un problema?
Giorgio el
1
@Giorgio Debido a que las advertencias no necesariamente tienen que repararse, o puede que no haya tiempo / dinero disponible para solucionarlas en este momento. Son una forma de deuda técnica. Eso no significa que no deberían ser manejados eventualmente, pero también significa que no necesariamente deberían ser barridos debajo de la alfombra. Su única opción restante es tenerlos; están ahí para hacerte sentir picazón, de modo que quizás eventualmente se arreglen.
Robert Harvey
1
@Robert Harvey: Estoy totalmente de acuerdo contigo. El único riesgo es que uno comienza a ignorar las advertencias y después de un tiempo ya no le provocan más picazón: he visto aparecer advertencias durante años en el mismo proyecto, y cada desarrollador diría que se suponía que debían solucionarse "en algún momento el futuro". Ya nadie estaba prestando atención a estas advertencias: se habían convertido en parte de la salida normal en el registro de compilación maestro. No estoy seguro de cómo evitar este tipo de situaciones.
Giorgio
1
El problema con la solución de la mentalidad de "ahora" es que lleva a correcciones incorrectas y a un código menos mantenible: en C ++, es sorprendente la facilidad con que un tipo de letra puede hacer que una advertencia desaparezca, la mayoría de las veces es algo incorrecto (por ejemplo, firmar / no firmar) advertencias). Es un código heredado tan poco probable que tenga pruebas de regresión automatizadas, y los cambios en el código introducirán defectos. "Arreglarlos a todos y b.gg.r las consecuencias" no es el enfoque correcto.
mattnz
@mattnz, romper el código mientras se arregla la advertencia es, de hecho, la principal preocupación aquí. Sin embargo, creo que no hay otra manera: corregirlos y tratar los futuros como errores, o ignorarlos (y ocasionalmente encontrar errores, sobre los cuales se les advirtió).
ugoren
3

Estaba leyendo Estándares de codificación C ++: 101 reglas, pautas y mejores prácticas y la segunda de las pautas sugiere "Compilar la limpieza a niveles de advertencia altos". Esboza una serie de razones por las cuales no querría tantas advertencias en un programa y las principales entre ellas son

  1. Hay un problema potencial en su código.
  2. Debe tener compilaciones silenciosas y exitosas y, de lo contrario, es posible que le falten problemas reales.
  3. Las advertencias benignas ignoradas podrían oscurecer las advertencias que apuntan a peligros reales

Los programas que se compilan con advertencias pueden parecer correctos, pero es posible que haya problemas reales con el código que pueden surgir en algún momento. Sugeriría categorizar las advertencias, darles un nivel de priorización y, cuando tenga tiempo, corregirlas.

Pulposo
fuente
+1: para "Compilar limpieza en niveles de advertencia altos": creo que deberíamos usar tanta ayuda del compilador como podamos.
Giorgio el
1

Deje de lado un ciclo / sprint para realizar un mantenimiento preventivo en el sistema, eliminar el código muerto, eliminar advertencias, eliminar comentarios negativos, cosas por el estilo, O instituir una política para eliminar las advertencias ya que las fuentes se tocan de todas formas para otra cosa.
Yo no haría lo primero solo para deshacerme de las advertencias, pero si se planificara ese ciclo, eso sería parte de la carga de trabajo. La razón de esto es que tocar un archivo para cualquier cosa introduce riesgo de errores y, por lo tanto, arriesga la estabilidad de su producto.

jwenting
fuente
2
No podemos permitirnos dejar de lado un sprint. Por lo tanto, eliminar las advertencias a medida que se tocan las fuentes por otras razones será. Pero hay algunas fuentes que no se tocaron durante mucho tiempo y tomará mucho tiempo hasta que lo hagan, por lo que debemos distinguir las advertencias de esos fragmentos heredados.
Jan Hudec
@JanHudec podría querer hacer un análisis de la gravedad de las advertencias en esos casos, tal vez programar un tiempo para volver a visitar ese código antiguo y hacer una limpieza en los bits seleccionados, refactorizar para mantenerlo vivo. De lo contrario, puede ponerse rancio y las personas tienen miedo de tocarlo, incluso si es necesario, porque no queda nadie sabe cómo funciona.
Jwenting
@ JanHudec: Parece que no tiene tiempo para resolver las advertencias si no puede permitirse el mantenimiento preventivo del sistema.
Ramhound
@Ramhound: Supongo que podríamos hacer un poco de limpieza, pero no mucho y tiene que ejecutarse en paralelo con otros trabajos.
Jan Hudec
1

Siento tu dolor porque estoy en una situación similar. La forma en que abordé el problema es: elimine las advertencias de un módulo / subproyecto en ese momento y configure el indicador del compilador "tratar advertencias como error" (Werr) para el módulo después de que se haya limpiado. Además, decidí centrarme solo en una plataforma, aunque construimos sobre varios sistemas operativos con diferentes compiladores. Finalmente, para las bibliotecas de terceros que compilamos, solo apago las advertencias.

Eso sí, deshacerse de algunas de estas advertencias es mucho más fácil decirlo que hacerlo. Por ejemplo, los problemas de size_t vs unsigned que mencionas pueden causar desbordamientos de enteros si solo silencias las advertencias con conversiones, por lo que en algunos casos reemplazas unsigned con size_t, en otros casos necesitas detectar un desbordamiento en tiempo de ejecución. Todo es caso por caso, muy tedioso y difícil.

Nemanja Trifunovic
fuente