Sinalizadores de aviso para o desenvolvimento de Objective-C

86

Como programador de C e Objective-C, sou um pouco paranóico com os sinalizadores de aviso do compilador.
Normalmente, tento encontrar uma lista completa de sinalizadores de aviso para o compilador que uso e ativar a maioria deles, a menos que eu tenha um bom motivo para não ativá-lo.

Pessoalmente, acho que isso pode realmente melhorar as habilidades de codificação, bem como a portabilidade de código em potencial, evitar alguns problemas, pois obriga você a estar ciente de todos os pequenos detalhes, possíveis problemas de implementação e arquitetura, etc.

Também é, na minha opinião, uma boa ferramenta de aprendizado todos os dias, mesmo que você seja um programador experiente.

Para a parte subjetiva desta pergunta, estou interessado em ouvir outros desenvolvedores (principalmente C, Objective-C e C ++) sobre este tópico.
Você realmente se importa com coisas como avisos pedantes, etc? E se sim ou não, por quê?

Agora sobre o Objective-C, recentemente mudei completamente para a cadeia de ferramentas LLVM (com Clang), em vez do GCC.

No meu código de produção, geralmente defino esses sinalizadores de aviso (explicitamente, mesmo que alguns deles possam ser cobertos por -Wall):

  • -Parede
  • -Wbad-função-elenco
  • -Wcast-align
  • -Wconversão
  • -Wdeclaration-after-statement
  • Implementações obsoletas
  • -Wextra
  • -Wfloat-equal
  • -Formato = 2
  • -Formato-não-literal
  • -Quatro-caracteres-constantes
  • -Wimplicit-atomic-properties
  • -Aparelhos para pendurar
  • - Declarações em falta
  • -Wmissing-field-initializers
  • -Através do atributo de formato
  • -Wmissing-noreturn
  • -Protótipos de falta
  • -Wnested-externs
  • -Wnewline-eof
  • -Definição de estilo -Wold
  • -Strings de comprimento de onda
  • -Warentheses
  • -Wpointer-arith
  • -Wredundantes-decls
  • -Tipo de retorno
  • -Ponto de sequência
  • -Wshadow
  • -Wshorten-64-to-32
  • -Wsign-compare
  • -Conversão de sinal
  • Protótipos estritos
  • -Wstrict-selector-match
  • -Wswitch
  • -Wswitch-default
  • -Wswitch-enum
  • -Seletor declarado
  • -Wuninitialized
  • -Pragmas desconhecidos
  • Código -Wunreachable
  • Função -Wunused
  • -Rótulo não utilizado
  • -Wunused-parameter
  • -Wunused-value
  • -Variável-variável
  • -Wwrite-strings

Estou interessado em ouvir o que outros desenvolvedores têm a dizer sobre isso.

Por exemplo, você acha que perdi uma bandeira específica para Clang (Objective-C) e por quê?
Ou você acha que uma bandeira em particular não é útil (ou nem é desejada) e por quê?

EDITAR

Para esclarecer a questão, observe que -Wallapenas fornece alguns avisos básicos.
Na verdade, são muito mais sinalizadores de aviso, não cobertos -Wall, daí a pergunta e a lista que forneço.

Macmade
fonte
9
Estou tentado a dizer que isso provavelmente deveria ter ficado no Stack Overflow, já que é uma pergunta sobre o uso de ferramentas, mas veremos como a comunidade muda.
Adam Lear
Obrigado pelo comentário:) De certa forma, eu concordo com você, e é por isso que eu postei isso originalmente no StackOverflow (também porque eu não sou um usuário comum aqui, que vergonha). Tenho muitas visualizações, votos positivos, etc ... Mas nem uma única resposta ... E como as pessoas sugeriram movê-la ... Bem, vamos ver
:)
Bem, espero que possamos lhe dar uma boa resposta. Boa sorte. :)
Adam Lear
Para C e gcc, o -Wwrite-stringstorna um compilador de uma linguagem muito semelhante, mas não exatamente C. Por esse motivo, não uso essa opção. Diferente dos que você especificar, eu uso -pedantic -Wstrict-overflow=5 -Winline -Wundef -Wcast-qual -Wlogical-op -Wstrict-aliasing=2e -Wno-missing-braces -Wno-missing-field-initializerspara o inicializador perfeitamente razoável struct whatever obj = {0};. Também acho que -Wconversiondá mais "spam" de advertências úteis :)
pmg

Respostas:

158

Por contexto, sou desenvolvedor de Clang trabalhando no Google. No Google, lançamos o diagnóstico de Clang para (essencialmente) todos os nossos desenvolvedores de C ++ e tratamos os avisos de Clang como erros também. Como desenvolvedor do Clang e um dos maiores usuários dos diagnósticos do Clang, tentarei lançar alguma luz sobre esses sinalizadores e como eles podem ser usados. Observe que tudo o que estou descrevendo é genericamente aplicável ao Clang e não específico ao C, C ++ ou Objective-C.

TL; DR Versão: Por favor, use -Walle -Werror, no mínimo, em qualquer novo código que você está desenvolvendo. Nós (os desenvolvedores do compilador) adicionamos avisos aqui por boas razões: eles encontram bugs. Se você encontrar um aviso que detecta bugs para você, ative-o também. Tente -Wextraum monte de bons candidatos aqui. Se um deles for muito barulhento para você usar com lucro, registre um bug . Se você escrever um código que contenha um bug "óbvio", mas o compilador não o alertou, registre um bug.

Agora para a versão longa. Primeiro, alguns antecedentes sobre grupos de sinalizadores de aviso. Existem muitos "agrupamentos" de avisos em Clang (e até certo ponto no GCC). Alguns que são relevantes para esta discussão:

  • Ativado por padrão: esses avisos estão sempre ativados, a menos que você os desative explicitamente.
  • -Wall: São avisos de que os desenvolvedores têm alta confiança em seu valor e em uma baixa taxa de falso positivo.
  • -Wextra: Esses avisos são considerados valiosos e sólidos (ou seja, não são de buggy), mas podem ter altas taxas de falso-positivos ou objeções filosóficas comuns.
  • -Weverything: Este é um grupo insano que literalmente habilita todos os avisos no Clang. Não use isso no seu código. Destina-se estritamente aos desenvolvedores do Clang ou para explorar quais avisos existem .

Existem dois critérios principais mencionados acima que orientam para onde os avisos vão em Clang, e vamos esclarecer o que eles realmente significam. O primeiro é o valor potencial de uma ocorrência específica do aviso. Esse é o benefício esperado para o usuário (desenvolvedor) quando o aviso é acionado e identifica corretamente um problema com o código.

O segundo critério é a ideia de relatórios falso-positivos . São situações em que o aviso é acionado no código, mas o problema potencial que está sendo citado não ocorre de fato devido ao contexto ou a alguma outra restrição do programa. O código avisado está realmente se comportando corretamente. Isso é especialmente ruim quando o aviso nunca foi planejado para disparar nesse padrão de código. Em vez disso, é uma deficiência na implementação do aviso que o aciona ali.

Para avisos de Clang, é necessário que o valor seja em termos de correção , não em termos de estilo, sabor ou convenções de codificação. Isso limita o conjunto de avisos disponíveis, impedindo avisos frequentemente solicitados, como aviso sempre que {}s não são usados ​​no corpo de uma ifdeclaração. Clang também é muito intolerante a falsos positivos . Diferente da maioria dos outros compiladores, ele usará uma variedade incrível de fontes de informação para remover falsos positivos, incluindo a ortografia exata da construção, presença ou ausência de '()', projeções ou mesmo macros de pré-processador!

Agora vamos dar alguns exemplos de avisos do Clang no mundo real e ver como eles são categorizados. Primeiro, um aviso padrão:

% 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.

Aqui, nenhum sinalizador foi necessário para receber esse aviso. A lógica é que esse código nunca é realmente correto, fornecendo um valor alto ao aviso , e o aviso é acionado apenas no código que Clang pode provar que cai nesse intervalo, fornecendo a ele uma taxa de falso positivo zero .

% 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 requer o -Wallsinalizador para este aviso. A razão é que existe uma quantidade não trivial de código por aí que usou (para o bem ou para o mal) o padrão de código que estamos alertando para produzir intencionalmente um valor não inicializado. Filosoficamente, não vejo sentido nisso, mas muitos outros discordam e a realidade dessa diferença de opinião é o que impulsiona o alerta -Wall. Ele ainda possui um valor muito alto e uma taxa de falso-positivo muito baixa , mas em algumas bases de código não é inicial.

% 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.

Este aviso requer a -Wextrabandeira. O motivo é que existem bases de código muito grandes em que o sinal incompatível nas comparações é extremamente comum. Embora esse aviso encontre alguns erros, a probabilidade do código ser um erro quando o usuário escreve é ​​bastante baixa, em média. O resultado é uma taxa falso-positiva extremamente alta . No entanto, quando há um erro em um programa devido a regras de promoção estranhas, geralmente é extremamente sutil emitir esse aviso quando sinaliza que um erro possui um valor relativamente alto . Como conseqüência, Clang fornece e expõe sob uma bandeira.

Normalmente, os avisos não ficam muito tempo fora da -Wextrabandeira. Clang se esforça muito para não implementar avisos que não veem uso e teste regulares. Os avisos adicionais ativados -Weverythingnormalmente são avisos em desenvolvimento ativo ou com erros ativos. Eles serão corrigidos e colocados sob sinalizadores apropriados ou deverão ser removidos.

Agora que entendemos como essas coisas funcionam com o Clang, vamos tentar voltar à pergunta original: que avisos você deve ativar para o seu desenvolvimento? A resposta é, infelizmente, que depende. Considere as seguintes perguntas para ajudar a determinar quais avisos funcionam melhor para sua situação.

  • Você tem controle sobre todo o seu código ou é parte dele externo?
  • Quais são seus objetivos? Pegando bugs ou escrevendo um código melhor?
  • Qual é a sua tolerância falso-positiva? Você está disposto a escrever um código extra para silenciar avisos regularmente?

Em primeiro lugar, se você não controlar o código, não tente ativar avisos extras. Esteja preparado para desligar alguns. Existe muito código incorreto no mundo e talvez você não consiga consertar tudo. Está bem. Trabalhe para encontrar uma maneira de concentrar seus esforços no código que você controla.

Em seguida, descubra o que você deseja com seus avisos. Isso é diferente para pessoas diferentes. O Clang tentará avisar sem nenhuma opção sobre erros flagrantes ou padrões de código para os quais temos um longo histórico histórico indicando que a taxa de erros é extremamente alta. Ao permitir que -Wallvocê receba um conjunto de avisos muito mais agressivo, destinado a detectar os erros mais comuns que os desenvolvedores do Clang observaram no código C ++. Mas com ambos, a taxa de falso positivo deve permanecer bastante baixa.

Finalmente, se você estiver perfeitamente disposto a silenciar * falso-positivo * a cada passo, vá em frente -Wextra. Arquive bugs se você observar avisos que estão capturando muitos bugs reais, mas com falsos positivos tolos ou inúteis. Estamos constantemente trabalhando para encontrar maneiras de trazer cada vez mais a lógica de detecção de erros presente -Wextrapara -Wallonde podemos evitar os falsos positivos.

Muitos acharão que nenhuma dessas opções é a ideal para eles. No Google, desativamos alguns avisos -Walldevido a muitos códigos existentes que violam o aviso. Também ativamos alguns avisos explicitamente, mesmo que não sejam ativados -Wall, porque eles têm um valor particularmente alto para nós. Sua milhagem varia, mas provavelmente varia de maneiras semelhantes. Muitas vezes, pode ser muito melhor ativar alguns avisos importantes em vez de todos -Wextra.

Eu incentivaria todos a ativar -Wallqualquer código não legado. Para o novo código, os avisos aqui são quase sempre valiosos e realmente aprimoram a experiência de desenvolvimento de código. Por outro lado, eu incentivaria todos a não habilitar bandeiras além -Wextra. Se você encontrar um aviso Clang que -Wextra não inclui mas o que prova a todos valioso para você, simplesmente envie um erro e nós provavelmente pode colocá-la debaixo -Wextra. Se você habilitar explicitamente algum subconjunto dos avisos -Wextradependerá muito do seu código, seu estilo de codificação e se a manutenção dessa lista é mais fácil do que consertar tudo o que foi descoberto -Wextra.

Na lista de avisos do OP (que incluía ambos -Walle -Wextra), apenas os seguintes avisos não são cobertos por esses dois grupos (ou ativados por padrão). O primeiro grupo enfatiza por que a dependência excessiva de sinalizadores de aviso explícitos pode ser ruim: nada disso é implementado no Clang! Eles são aceitos na linha de comando apenas para compatibilidade com o 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

O próximo intervalo de avisos desnecessários na lista original são aqueles que são redundantes com os outros nessa lista:

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

Há também uma seleção de avisos que são mais categoricamente diferentes. Eles lidam com variantes de dialetos de idiomas, e não com códigos de buggy ou não. Com exceção de -Wwrite-strings, todos esses são avisos para extensões de idioma fornecidas pelo Clang. O aviso de Clang sobre seu uso depende da prevalência da extensão. O Clang visa a compatibilidade com o GCC e, em muitos casos, facilita isso com extensões de idioma implícitas que são amplamente utilizadas. -Wwrite-strings, como comentado no OP, é um sinalizador de compatibilidade do GCC que realmente altera a semântica do programa. Lamento profundamente esta bandeira, mas temos de apoiá-la devido ao legado que ela tem agora.

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

As opções restantes que estão realmente ativando avisos potencialmente interessantes são:

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

A razão pela qual elas não estão presentes -Wallou -Wextranem sempre é clara. Para muitos deles, eles são realmente baseado em avisos do CCG ( -Wconversion, -Wshadow, etc.) e, como tal Clang tenta imitar o comportamento do GCC. Estamos lentamente dividindo alguns deles em avisos mais detalhados e úteis. Aqueles então têm uma maior probabilidade de fazer parte de um dos grupos de alerta de nível superior. Dito isto, escolher um aviso -Wconversioné tão amplo que provavelmente continuará sendo sua própria categoria de "nível superior" no futuro próximo. Alguns outros avisos que o GCC possui, mas que têm baixo valor e altas taxas de falso positivo, podem ser relegados a uma terra de ninguém semelhante.

Outras razões pelas quais esses itens não estão em um dos baldes maiores incluem erros simples, problemas falsos positivos muito significativos e avisos em desenvolvimento. Vou examinar os erros de arquivamento para os que eu puder identificar. Todos eles devem migrar para um sinalizador de balde grande e apropriado ou ser removidos do Clang.

Espero que isso esclareça a situação de aviso com Clang e forneça algumas dicas para aqueles que tentam escolher um conjunto de avisos para seu uso ou uso de sua empresa.

Chandler Carruth
fonte
8
Excelente resposta! Muito obrigado. Precisarei relê-lo algumas vezes, mas voltarei em breve com alguns comentários. Obrigado novamente!
Macmade
1
Chandler Carruth: Ótima resposta! Mas você poderia atualizá-lo, se alguma coisa mudou? Estou usando todos os seus avisos em sua última lista, mas talvez alguns já tenham sido movidos para o Wextra?
precisa saber é
Esta é uma postagem excelente. Descobrir, porém, que encontrar novos avisos é um uso extremamente bom para uma bandeira, por isso estou desapontado por você parecer tão negativo em relação ao grupo "tudo", apesar de reconhecer que é útil para esse tipo de exploração.
Kyle Strand
Chandler Carruth: Eu concordo com a lógica por trás do aviso sobre o código "nunca realmente correto". É que não ter -Wnoque desligar warn_no_constructor_for_refconst faz com que o PITA use o Boost.ConceptCheck e similares: github.com/boostorg/concept_check/blob/…
mloskot
2

Eu não uso Clang, mas espero que você não se importe com a minha opinião também. Eu também aceito o nível máximo de aviso (presumo que seja isso que -Wall significa) e trato os avisos como erros (presumo que seja o que -Werror significa) e desabilito apenas os poucos avisos que não fazem muito sentido. Na verdade, estou bastante chateado pelo fato de certos avisos muito úteis não serem emitidos pelo compilador C #, e é preciso executar ferramentas especiais de análise de código para vê-los. Gosto de avisos, porque eles me ajudam a detectar pequenos erros e bugs no meu código. Eu gosto tanto deles que, quando um aviso é emitido para um pedaço de código que eu sei que está correto, refato esse pedaço de código de qualquer maneira, para que o aviso não seja emitido, em vez de desativá-lo, ou - até pior - deixe aparecer e ignore. Eu acho que os avisos são incríveis,

Mike Nakis
fonte
1
Obrigado pela resposta. Infelizmente, -Wallfornece apenas avisos básicos. Muitos sinalizadores de aviso existentes não são cobertos -Wall, daí a lista na minha pergunta.
Macmade
0

Eu normalmente uso as configurações padrão do Xcode para um novo projeto; fornece um bom equilíbrio entre útil e irritante. Qualquer lista específica de avisos e outras opções do compilador serão baseadas amplamente nas preferências pessoais ou nos requisitos do projeto; portanto, não acho que exista muito valor em tentar examinar sua lista e argumentar a favor ou contra avisos específicos. Se isso funciona para você, ótimo. Se você achar que cometeu algum erro que o compilador possa detectar e desejar ajuda com isso no futuro, procure uma opção do compilador para avisar sobre isso e ativá-lo.

Uma coisa que muitos programadores defendem é ativar a opção "Tratar avisos como erros" (-Werror) para impedir que as compilações sejam concluídas com êxito, se houver algum aviso.

Caleb
fonte
Obrigado pela resposta! Concordo com -Werror. Na verdade, como eu estou usando Clang, eu definir todas estas advertências usando pragmas (clang #pragma de diagnóstico), e eles são definidos como erros fatais, por isso este é o mesmo que -Werror:)
Macmade
0

Eu acho que a melhor evidência de que as pessoas se preocupam com os avisos é o fato de elas existirem e serem amplamente usadas. Sou fortemente da opinião de que quanto mais erros forem detectados durante o tempo de compilação, melhor. Se essa não fosse uma crença amplamente aceita, todo mundo usaria linguagens fracas de tipo dinâmico, porque seus compiladores ou intérpretes oferecem muito mais liberdade. Pelo contrário, mesmo em linguagens de script, o uso de strictsinalizadores é popular. Em linguagens estáticas de tipo fortemente digitado, as pessoas não apenas usam com -Wall -Werrorfrequência, mas também consideram os avisos tão valiosos que desejam ainda mais , como também pagam por ferramentas de análise estática, como o Coverity, cujo único objetivo é fornecer avisos.

Isso não significa que não há detratores. Muitos desenvolvedores vêem os avisos trabalhando contra eles a curto prazo, em vez de trabalhar para eles a longo prazo, e não tentam corrigir o problema subjacente. Portanto, você vê um código adorável como esse trecho que me deparei ontem:

node = node; // Shut up compiler warning
Karl Bielefeldt
fonte
Obrigado pela sua resposta. O problema é que muitas vezes vejo pessoas trabalhando sem -Werrore realmente ignorando os avisos emitidos pelo compilador. Ou, se o fizerem, eles geralmente aderem -Wall. A maioria das bandeiras que forneço na minha pergunta não é coberta -Walle, pessoalmente, também acho que elas são essenciais. Então, eu sou apenas paranóico? ; )
Macmade
0

Geralmente, eu me inclino mais para -Wall e definitivamente acredito em incluir -Werror. Um módulo nunca deve ter avisos esperados. Você receberá outro aviso e perderá. E esse será realmente o problema.

Também depende se você está adicionando "-Wall -Werror" a um projeto novo ou antigo. Se for novo, opte por ele e exija perfeição; caso contrário, você provavelmente estará sujeito a dias ou semanas de edições e testes. Acabei de fazer isso em um projeto um tanto pequeno e antigo e passei dois ou três dias removendo os avisos. Eu acho que o código está mais limpo agora.

Em outras palavras, tente e veja.

Randy

Randy Stegbauer
fonte
1
Se você está preocupado apenas com -Walle -Werror, acho que deveria reler a pergunta. Muitos sinalizadores de aviso não são cobertos por -Wall. Obrigado pela resposta, de qualquer maneira.
Macmade