Indicadores de advertencia de Clang para el desarrollo de Objective-C

86

Como programador de C & Objective-C, estoy un poco paranoico con los indicadores de advertencia del compilador.
Por lo general, trato de encontrar una lista completa de indicadores de advertencia para el compilador que uso, y activar la mayoría de ellos, a menos que tenga una buena razón para no activarlo.

Personalmente, creo que esto puede mejorar las habilidades de codificación, así como la posible portabilidad del código, evitar algunos problemas, ya que te obliga a estar al tanto de cada pequeño detalle, implementación potencial y problemas de arquitectura, etc.

También es, en mi opinión, una buena herramienta de aprendizaje todos los días, incluso si eres un programador experimentado.

Para la parte subjetiva de esta pregunta, estoy interesado en escuchar a otros desarrolladores (principalmente C, Objective-C y C ++) sobre este tema.
¿Realmente te interesan cosas como advertencias pedantes, etc.? Y si sí o no, ¿por qué?

Ahora sobre Objective-C, recientemente cambié completamente a la cadena de herramientas LLVM (con Clang), en lugar de GCC.

En mi código de producción, generalmente establezco estos indicadores de advertencia (explícitamente, incluso si algunos de ellos pueden estar cubiertos por -Wall):

  • -Pared
  • -Wbad-function-cast
  • -Wcast-alinear
  • -Wconversion
  • -Declaración-después-declaración
  • Implementaciones obsoletas
  • -Wextra
  • -Wfloat-equal
  • -Wformat = 2
  • -Formato-no literal
  • -Cuatro-constantes-char
  • -Propiedades atómicas implícitas
  • -Artesanías
  • -Declaraciones
  • -Inicializadores de campo de Wmissing
  • -Wmissing-format-attribute
  • -Wisssing-noreturn
  • -Presentación-prototipos
  • -Wnested-externs
  • -Wnewline-eof
  • -Definición de estilo de mundo
  • -Cadenas de longitud de onda
  • Paréntesis
  • -Wpointer-arith
  • -Dulces redundantes
  • -Tipo de retorno
  • -Punto de secuencia
  • -Sombra
  • -Wshorten-64-a-32
  • -Wsign-compare
  • -Wsign-conversion
  • -Wstrict-prototypes
  • -Wstrict-selector-match
  • -Wswitch
  • -Wswitch-default
  • -Wswitch-enum
  • -Wundeclared-selector
  • -Wininicializado
  • -Wunknown-pragmas
  • -Código inalcanzable
  • -Wunused-function
  • -Wunused-label
  • -Parámetro sin usar
  • -Wunused-value
  • -Wunused-variable
  • -Escribir cadenas

Estoy interesado en escuchar lo que otros desarrolladores tienen que decir sobre esto.

Por ejemplo, ¿crees que me perdí una bandera en particular para Clang (Objective-C), y por qué?
¿O crees que una bandera en particular no es útil (o no se quiere en absoluto), y por qué?

EDITAR

Para aclarar la pregunta, tenga en cuenta que -Wallsolo proporciona algunas advertencias básicas.
En realidad, son muchos más indicadores de advertencia, no cubiertos -Wall, de ahí la pregunta y la lista que proporciono.

Macmade
fuente
99
Estoy tentado a decir que esto probablemente debería haberse quedado en Stack Overflow, ya que es una pregunta sobre el uso de herramientas, pero veremos cómo cambia la comunidad.
Adam Lear
Gracias por el comentario:) En cierto modo, estoy de acuerdo con usted, y es por eso que originalmente publiqué esto en StackOverflow (también porque no soy un usuario habitual aquí, lástima). Obtuve muchas vistas, votos a favor, etc ... Pero ni una sola respuesta ... Y como la gente sugirió moverlo ... Bueno, veamos
:)
Bueno, espero que podamos darte una buena respuesta. Buena suerte. :)
Adam Lear
Para C y gcc, lo -Wwrite-stringsconvierte en un compilador de un lenguaje muy similar pero no exactamente C. Por esa sola razón, no uso esa opción. Aparte de los que especifique, yo uso -pedantic -Wstrict-overflow=5 -Winline -Wundef -Wcast-qual -Wlogical-op -Wstrict-aliasing=2y -Wno-missing-braces -Wno-missing-field-initializerspara el inicializador perfectamente razonable struct whatever obj = {0};. También encuentro que -Wconversionda más "spam" que advertencias útiles :)
pmg

Respuestas:

158

Por contexto, soy un desarrollador de Clang que trabaja en Google. En Google, hemos implementado los diagnósticos de Clang (esencialmente) para todos nuestros desarrolladores de C ++, y también tratamos las advertencias de Clang como errores. Como desarrollador de Clang y uno de los usuarios más grandes de los diagnósticos de Clang, intentaré arrojar algo de luz sobre estos indicadores y cómo se pueden usar. Tenga en cuenta que todo lo que estoy describiendo es genéricamente aplicable a Clang, y no específico a C, C ++ u Objective-C.

TL; Versión DR: utilice -Wally -Werrorcomo mínimo en cualquier código nuevo que esté desarrollando. Nosotros (los desarrolladores del compilador) agregamos advertencias aquí por buenas razones: encuentran errores. Si encuentra una advertencia que detecta errores por usted, actívela también. Intenta -Wextracon un montón de buenos candidatos aquí. Si uno de ellos es demasiado ruidoso para que lo use de manera rentable, presente un error . Si escribe código que contiene un error "obvio" pero el compilador no lo advirtió, presente un error.

Ahora para la versión larga. Primero algunos antecedentes sobre agrupaciones de banderas de advertencia. Hay muchas "agrupaciones" de advertencias en Clang (y hasta cierto punto en GCC). Algunos que son relevantes para esta discusión:

  • Activado por defecto: estas advertencias siempre están activadas a menos que las desactive explícitamente.
  • -Wall: Estas son advertencias de que los desarrolladores tienen una gran confianza tanto en su valor como en una baja tasa de falsos positivos.
  • -Wextra: Estas son advertencias que se consideran valiosas y sólidas (es decir, no tienen errores), pero pueden tener altas tasas de falsos positivos u objeciones filosóficas comunes.
  • -Weverything: Este es un grupo de locos que literalmente habilita todas las advertencias en Clang. No uses esto en tu código. Está destinado estrictamente a los desarrolladores de Clang o para explorar qué advertencias existen .

Hay dos criterios principales mencionados anteriormente que guían dónde van las advertencias en Clang, y aclaremos lo que realmente significan. El primero es el valor potencial de una ocurrencia particular de la advertencia. Este es el beneficio esperado para el usuario (desarrollador) cuando se activa la advertencia e identifica correctamente un problema con el código.

El segundo criterio es la idea de informes falsos positivos . Estas son situaciones en las que la advertencia se activa en el código, pero el problema potencial que se cita no se produce de hecho debido al contexto o alguna otra restricción del programa. El código advertido realmente se está comportando correctamente. Estos son especialmente malos cuando la advertencia nunca tuvo la intención de disparar en ese patrón de código. En cambio, es una deficiencia en la implementación de la advertencia lo que hace que se dispare allí.

Para las advertencias de Clang, se requiere que el valor esté en términos de corrección , no en términos de estilo, gusto o convenciones de codificación. Esto limita el conjunto de advertencias disponibles, lo que excluye advertencias solicitadas con frecuencia, como advertencias cuando {}no se utilizan mensajes de correo electrónico alrededor del cuerpo de una ifdeclaración. Clang también es muy intolerante con los falsos positivos . A diferencia de la mayoría de los otros compiladores, utilizará una increíble variedad de fuentes de información para eliminar los falsos positivos, incluida la ortografía exacta de la construcción, la presencia o ausencia de '()', lanzamientos o incluso macros de preprocesador adicionales.

Ahora tomemos algunas advertencias de ejemplo del mundo real de Clang, y veamos cómo están categorizadas. Primero, una advertencia predeterminada:

% nl x.cc
     1  class C { const int x; };

% clang -fsyntax-only x.cc
x.cc:1:7: warning: class 'C' does not declare any constructor to initialize its non-modifiable members
class C { const int x; };
      ^
x.cc:1:21: note: const member 'x' will never be initialized
class C { const int x; };
                    ^
1 warning generated.

Aquí no se requería ninguna bandera para obtener esta advertencia. La razón es que este código nunca es realmente correcto, lo que le da un alto valor de advertencia , y la advertencia solo se dispara en el código que Clang puede probar que cae en este segmento, lo que le da una tasa de cero falsos positivos .

% nl x2.cc
     1  int f(int x_) {
     2    int x = x;
     3    return x;
     4  }

% clang -fsyntax-only -Wall x2.cc
x2.cc:2:11: warning: variable 'x' is uninitialized when used within its own initialization [-Wuninitialized]
  int x = x;
      ~   ^
1 warning generated.

Clang requiere la -Wallbandera para esta advertencia. La razón es que existe una cantidad de código no trivial que ha utilizado (para bien o para mal) el patrón de código del que estamos advirtiendo que produzca intencionalmente un valor no inicializado. Filosóficamente, no veo ningún punto en esto, pero muchos otros no están de acuerdo y la realidad de esta diferencia de opinión es lo que impulsa la advertencia bajo la -Wallbandera. Todavía tiene un valor muy alto y una tasa muy baja de falsos positivos , pero en algunas bases de código no es un iniciador.

% nl x3.cc
     1  void g(int x);
     2  void f(int arr[], unsigned int size) {
     3    for (int i = 0; i < size; ++i)
     4      g(arr[i]);
     5  }

% clang -fsyntax-only -Wextra x3.cc
x3.cc:3:21: warning: comparison of integers of different signs: 'int' and 'unsigned int' [-Wsign-compare]
  for (int i = 0; i < size; ++i)
                  ~ ^ ~~~~
1 warning generated.

Esta advertencia requiere la -Wextrabandera. La razón es que hay bases de código muy grandes en las que el signo que no coincide en las comparaciones es extremadamente común. Si bien esta advertencia encuentra algunos errores, la probabilidad de que el código sea un error cuando el usuario escribe es bastante baja en promedio. El resultado es una tasa extremadamente alta de falsos positivos . Sin embargo, cuando hay un error en un programa debido a las extrañas reglas de promoción, a menudo es extremadamente sutil hacer que esta advertencia cuando señala un error tenga un valor relativamente alto . Como consecuencia, Clang lo proporciona y lo expone bajo una bandera.

Por lo general, las advertencias no viven mucho tiempo fuera de la -Wextrabandera. Clang se esfuerza mucho por no implementar advertencias que no ven el uso y las pruebas regulares. Las advertencias adicionales activadas por -Weverythingusualmente son advertencias en desarrollo activo o con errores activos. O serán reparados y colocados bajo las banderas apropiadas, o deberían ser eliminados.

Ahora que entendemos cómo funcionan estas cosas con Clang, intentemos volver a la pregunta original: ¿qué advertencias debe activar para su desarrollo? La respuesta es, desafortunadamente, que depende. Considere las siguientes preguntas para ayudar a determinar qué advertencias funcionan mejor para su situación.

  • ¿Tiene control sobre todo su código o parte de él es externo?
  • ¿Cuáles son tus metas? ¿Capturando errores o escribiendo mejor código?
  • ¿Cuál es tu tolerancia falsa positiva? ¿Está dispuesto a escribir código adicional para silenciar las advertencias de forma regular?

En primer lugar, si no controlas el código, no intentes activar advertencias adicionales allí. Prepárate para apagar un poco. Hay muchos códigos incorrectos en el mundo y es posible que no pueda solucionarlos. Es correcto. Trabaja para encontrar una manera de enfocar tus esfuerzos en el código que controlas.

A continuación, descubra lo que quiere de sus advertencias. Esto es diferente para diferentes personas. Clang intentará advertir sin ninguna opción sobre errores atroces o patrones de código para los cuales tenemos un precedente histórico largo que indica que la tasa de errores es extremadamente alta. Al habilitarlo -Wall, obtendrá un conjunto de advertencias mucho más agresivo dirigido a detectar los errores más comunes que los desarrolladores de Clang han observado en el código C ++. Pero con ambos, la tasa de falsos positivos debería permanecer bastante baja.

Finalmente, si está perfectamente dispuesto a silenciar * falsos positivos * s a cada paso, vaya -Wextra. Presente errores si observa advertencias que detectan muchos errores reales, pero que tienen falsos positivos sin sentido o tontos. Trabajamos constantemente para encontrar formas de incorporar cada vez más la lógica de búsqueda de errores -Wextraen -Walldonde podamos evitar los falsos positivos.

Muchos descubrirán que ninguna de estas opciones es la adecuada para ellos. En Google, hemos desactivado algunas advertencias -Walldebido a la gran cantidad de código existente que viola la advertencia. También hemos activado algunas advertencias explícitamente, a pesar de que no están habilitadas -Wall, porque tienen un valor particularmente alto para nosotros. Su millaje variará, pero probablemente variará de manera similar. A menudo puede ser mucho mejor habilitar algunas advertencias clave en lugar de todas -Wextra.

Me gustaría animar a todo el mundo para encender -Wallpara cualquier código no legado. Para el nuevo código, las advertencias aquí son casi siempre valiosas y realmente mejoran la experiencia de desarrollar código. Por el contrario, animaría a todos a no habilitar las banderas más allá -Wextra. Si encuentra una advertencia Clang que -Wextra no incluya pero que resulta en absoluto valioso para usted, sólo tiene que informar de un problema y que probablemente podemos ponerlo bajo -Wextra. Si habilita explícitamente algún subconjunto de las advertencias -Wextradependerá en gran medida de su código, su estilo de codificación y si mantener esa lista es más fácil que arreglar todo lo descubierto por -Wextra.

De la lista de advertencias del OP (que incluía ambas -Wally -Wextra) solo las siguientes advertencias no están cubiertas por esos dos grupos (o activadas de manera predeterminada). El primer grupo enfatiza por qué la excesiva dependencia de los indicadores de advertencia explícitos puede ser mala: ¡ninguno de estos se implementa en Clang! Se aceptan en la línea de comandos solo por compatibilidad con GCC.

  • -Wbad-function-cast
  • -Wdeclaration-after-statement
  • -Wmissing-format-attribute
  • -Wmissing-noreturn
  • -Wnested-externs
  • -Wnewline-eof
  • -Wold-style-definition
  • -Wredundant-decls
  • -Wsequence-point
  • -Wstrict-prototypes
  • -Wswitch-default

El siguiente grupo de advertencias innecesarias en la lista original son las que son redundantes con otras en esa lista:

  • -Wformat-nonliteral -- Subconjunto de -Wformat=2
  • -Wshorten-64-to-32 -- Subconjunto de -Wconversion
  • -Wsign-conversion -- Subconjunto de -Wconversion

También hay una selección de advertencias que son más categóricamente diferentes. Estos tratan con variantes de dialecto de idioma en lugar de con código con errores o sin errores. Con la excepción de -Wwrite-strings, todas estas son advertencias para las extensiones de idioma proporcionadas por Clang. Si Clang advierte sobre su uso depende de la prevalencia de la extensión. Clang apunta a la compatibilidad con GCC, por lo que en muchos casos lo facilita con extensiones de lenguaje implícito que se usan ampliamente. -Wwrite-strings, como se comentó en el OP, es un indicador de compatibilidad de GCC que realmente cambia la semántica del programa. Lamento profundamente esta bandera, pero tenemos que apoyarla debido al legado que tiene ahora.

  • -Wfour-char-constants
  • -Wpointer-arith
  • -Wwrite-strings

Las opciones restantes que realmente permiten advertencias potencialmente interesantes son estas:

  • -Wcast-align
  • -Wconversion
  • -Wfloat-equal
  • -Wformat=2
  • -Wimplicit-atomic-properties
  • -Wmissing-declarations
  • -Wmissing-prototypes
  • -Woverlength-strings
  • -Wshadow
  • -Wstrict-selector-match
  • -Wundeclared-selector
  • -Wunreachable-code

La razón por la que no están -Wallo -Wextrano siempre está clara. Para muchos de ellos, en realidad están basados en los avisos del CCG ( -Wconversion, -Wshadow, etc.) y como tal Clang trata de imitar el comportamiento de GCC. Desglosamos lentamente algunos de estos en advertencias más útiles y de grano fino. Esos tienen una mayor probabilidad de convertirse en uno de los grupos de advertencia de nivel superior. Dicho esto, elegir una advertencia -Wconversiones tan amplia que probablemente seguirá siendo su propia categoría de "nivel superior" en el futuro previsible. Algunas otras advertencias que tiene el CCG pero que tienen un valor bajo y altas tasas de falsos positivos pueden relegarse a una tierra de nadie similar.

Otras razones por las cuales no están en uno de los grupos más grandes incluyen errores simples, problemas muy positivos falsos positivos y advertencias en desarrollo. Voy a buscar errores en la presentación de los que puedo identificar. Todos deberían eventualmente migrar a una bandera de cubo grande adecuada o ser eliminados de Clang.

Espero que esto aclare la situación de advertencia con Clang y proporcione alguna información para aquellos que intentan elegir un conjunto de advertencias para su uso, o el uso de su compañía.

Chandler Carruth
fuente
8
Excelente respuesta! Muchas gracias. Tendré que volver a leerlo varias veces, pero volveré pronto con algunos comentarios. ¡Gracias de nuevo!
Macmade
1
@Chandler Carruth: ¡Gran respuesta! Pero, ¿podrías actualizarlo si algo ha cambiado? Estoy usando todas sus advertencias en su última lista, pero ¿tal vez algunas ya se trasladaron a Wextra?
Gartenriese
Esta es una publicación excelente. Pero encontrar nuevas advertencias es, según he encontrado, un uso extremadamente bueno para una bandera, así que estoy decepcionado de que parezca que tiene una disposición tan negativa hacia el grupo "todo", a pesar de reconocer que es útil para este tipo de exploración.
Kyle Strand
@Chandler Carruth: Estoy de acuerdo con la razón detrás de la advertencia sobre el código "nunca realmente correcto". Es solo que no tener -Wnoque apagarlo warn_no_constructor_for_refconst hace que PITA use Boost.ConceptCheck y similares: github.com/boostorg/concept_check/blob/…
mloskot
2

No uso Clang, pero espero que no te importe tener mi opinión también. También voy con el nivel máximo de advertencia (supongo que eso es lo que significa -Wall) y con tratar las advertencias como errores (supongo que eso es lo que significa -Werror) y solo desactivo esas pocas advertencias que no tienen mucho sentido. En realidad, estoy bastante molesto por el hecho de que el compilador de C # no emite ciertas advertencias muy útiles, y uno tiene que ejecutar herramientas especiales de análisis de código para verlas. Me gustan las advertencias, porque me ayudan a detectar pequeños errores y errores en mi código. Me gustan tanto, que cuando se emite una advertencia para un fragmento de código que sé que es correcto, refactorizo ​​ese fragmento de código de todos modos, para que no se emita la advertencia, en lugar de desactivarla, o --incluso peor, permita que aparezca e ignórelo. Creo que las advertencias son asombrosas,

Mike Nakis
fuente
1
Gracias por la respuesta. Lamentablemente, -Wallsolo proporciona advertencias básicas. Muchos de los indicadores de advertencia existentes no están cubiertos -Wall, de ahí la lista en mi pregunta.
Macmade
0

Normalmente uso la configuración predeterminada de Xcode para un nuevo proyecto; Proporciona un buen equilibrio entre útil y molesto. Cualquier lista específica de advertencias y otras opciones de compilación se basará en gran medida en las preferencias personales o los requisitos del proyecto, por lo que no creo que haya mucho valor al tratar de revisar su lista y argumentar a favor o en contra de advertencias específicas. Si te funciona, genial. Si descubre que ha cometido un error que el compilador debería poder detectar y desea ayuda con eso en el futuro, busque una opción de compilador para advertir sobre eso y activarlo.

Una cosa que muchos programadores recomiendan es activar la opción "Tratar las advertencias como errores" (-Werror) para evitar que las compilaciones se completen con éxito si hay advertencias.

Caleb
fuente
¡Gracias por la respuesta! Estoy de acuerdo con -Werror. En realidad, como estoy usando Clang, puse todas estas advertencias utilizando pragmas (#pragma sonido metálico de diagnóstico), y que se establecen en errores fatales, así que esto es lo mismo que -Werror:)
Macmade
0

Creo que la mejor evidencia de que las personas se preocupan por las advertencias es el hecho de que existen y son ampliamente utilizadas. Creo firmemente que cuantos más errores se detecten durante el tiempo de compilación, mejor. Si esto no fuera una creencia generalizada, todos estarían usando lenguajes débiles escritos dinámicamente, porque sus compiladores o intérpretes le brindan mucho más margen de maniobra. Por el contrario, incluso en los lenguajes de script, el uso de strictbanderas es popular. En lenguajes estáticos fuertemente tipados, las personas no solo usan con -Wall -Werrorfrecuencia, sino que a menudo encuentran advertencias tan valiosas que quieren aún más , por lo que pagan por herramientas de análisis estático como Coverity, cuyo único propósito es proporcionar advertencias.

Eso no significa que no haya detractores. Muchos desarrolladores consideran que las advertencias funcionan en su contra a corto plazo en lugar de trabajar para ellos a largo plazo, y no intentan solucionar el problema subyacente. Por lo tanto, ves un código encantador como este fragmento que encontré ayer:

node = node; // Shut up compiler warning
Karl Bielefeldt
fuente
Gracias por tu respuesta. El problema es que a menudo veo personas que trabajan sin ellos -Werrore ignoran las advertencias emitidas por el compilador. O si lo hacen, por lo general se adhieren -Wall. La mayoría de las banderas que proporciono en mi pregunta no están cubiertas -Wall, y personalmente creo que también son esenciales. Entonces, ¿soy solo paranoico? ; )
Macmade
0

En general, me inclino más hacia -Wall, y definitivamente creo en incluir también -Werror. Un módulo nunca debería haber esperado advertencias. Eventualmente recibirás otra advertencia y la perderás. Y ese será el que realmente sea un problema.

También depende de si está agregando "-Wall -Werror" a un proyecto nuevo o antiguo. Si es nuevo, hágalo y exija la perfección, de lo contrario, probablemente tendrá días o semanas de ediciones y pruebas. Acabo de hacer esto en un proyecto antiguo algo pequeño y pasé dos o tres días eliminando las advertencias. Creo que el código está más limpio ahora.

En otras palabras, pruébalo y verás.

Cachondo

Randy Stegbauer
fuente
1
Si solo le preocupa -Wally -Werror, supongo que debería volver a leer la pregunta. Muchas banderas de advertencia no están cubiertas por -Wall. Gracias por la respuesta, de todos modos.
Macmade