Como aumentar aviso se o valor de retorno é ignorado?
-
19-09-2019 - |
Pergunta
Eu gostaria de ver todos os lugares no meu código (C ++) que valor de retorno desrespeito de uma função. Como posso fazê-lo - com gcc ou ferramenta de análise estática de código
?exemplo de código Bad:
int f(int z) {
return z + (z*2) + z/3 + z*z + 23;
}
int main()
{
int i = 7;
f(i); ///// <<----- here I disregard the return value
return 1;
}
Por favor note que:
- deve funcionar mesmo se a função e seu uso estão em diferentes arquivos
- livre ferramenta de verificação estática
Solução
Você quer atributo warn_unused_result
do GCC:
#define WARN_UNUSED __attribute__((warn_unused_result))
int WARN_UNUSED f(int z) {
return z + (z*2) + z/3 + z*z + 23;
}
int main()
{
int i = 7;
f(i); ///// <<----- here i disregard the return value
return 1;
}
Tentando compilar este código produz:
$ gcc test.c
test.c: In function `main':
test.c:16: warning: ignoring return value of `f', declared with
attribute warn_unused_result
Você pode ver isso em uso no Linux kernel do ; eles têm uma macro __must_check
que faz a mesma coisa; Parece que você precisa GCC 3.4 ou superior para que isso funcione. Em seguida, você vai achar que macro usado em arquivos de cabeçalho do kernel:
unsigned long __must_check copy_to_user(void __user *to,
const void *from, unsigned long n);
Outras dicas
Tanto quanto eu estou ciente não há opção GCC para dar este aviso. No entanto, se você estiver interessado em funções específicas, você pode marcá-las com um atributo:
int fn() __attribute__((warn_unused_result));
o que daria um aviso se não foi utilizado o valor de retorno de fn (). Ressalva:. Eu nunca usei esse recurso me
Você pode usar este molde acessível para fazê-lo em tempo de execução.
Em vez de retornar um código de erro (por exemplo HRESULT) que você retornar um return_code
class return_value
{
public:
explicit return_value(T value)
:value(value), checked(false)
{
}
return_value(const return_value& other)
:value(other.value), checked(other.checked)
{
other.checked = true;
}
return_value& operator=(const return_value& other)
{
if( this != &other )
{
assert(checked);
value = other.value;
checked = other.checked;
other.checked = true;
}
}
~return_value(const return_value& other)
{
assert(checked);
}
T get_value()const {
checked = true;
return value;
}
private:
mutable bool checked;
T value;
};
Para C ++ 17, a resposta a esta pergunta muda desde agora temos a [[nodiscard]] atributo. Coberto de [dcl.attr.nodiscard] :
O nodiscard atributo token pode ser aplicado ao declarator-id em uma declaração de função ou para a declaração de uma classe ou enumeração. Ele deve aparecer no máximo uma vez em cada atributo-list e nenhum atributo-argumento da cláusula deve estar presente.
e
[Exemplo:
struct [[nodiscard]] error_info { /* ... */ }; error_info enable_missile_safety_mode(); void launch_missiles(); void test_missiles() { enable_missile_safety_mode(); // warning encouraged launch_missiles(); } error_info &foo(); void f() { foo(); } // warning not encouraged: not a nodiscard call, because neither // the (reference) return type nor the function is declared nodiscard
- Exemplo final]
Assim, modificando o seu exemplo ( vê-lo ao vivo ):
[[nodiscard]] int f(int z) {
return z + (z*2) + z/3 + z*z + 23;
}
int main()
{
int i = 7;
f(i); // now we obtain a diagnostic
return 1;
}
Temos agora obter um diagnóstico tanto com gcc e clang por exemplo.
warning: ignoring return value of function declared with 'nodiscard' attribute [-Wunused-result]
f(i); // now we obtain a diagnostic
^ ~
Qualquer código de análise estática (por exemplo PC-Lint ) deve ser capaz de dizer-lhe isso. Para PC-Lint, eu sei que este é o caso.
um analisador estático vai fazer o trabalho para você, mas se a sua base de código é mais do que trivial preparar para ser oprimido; -)
Um analisador estático vai ser sua melhor aposta aqui. Usamos Coverity aqui, mas existem ferramentas gratuitos disponíveis que você pode usar também.
Se você precisa de uma solução rápida e suja e você tem um estilo Linux escudo à mão, você pode tentar algo como:
grep -rn "function_name" * | grep -v "="
Isso vai encontrar todas as linhas que as referências a função especificada, mas não contém um "=". Você pode obter uma grande quantidade de falsos positivos (e eventualmente alguns falsos negativos), mas se você não tem um analisador estático é um lugar decente para começar.
O programa 'cotão' clássico costumava ser muito volúvel sobre as funções que retornaram um valor que foi ignorado. O problema era que muitas dessas advertências eram indesejados - levando a excesso de ruído na saída fiapos (que estava pegando pedaços de cotão que você queria que ignorar). Isso é provavelmente porque GCC não tem um aviso padrão para ele.
A outra questão - o outro lado - é "como você suprimir o aviso quando você sabe que você está ignorando o resultado, mas realmente não me importo". O cenário clássico para isso é:
if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
signal(SIGHUP, sighandler);
Você se preocupa com o primeiro resultado de signal()
; você sabe que o segundo será SIG_IGN (desde que você apenas configurá-lo para que). Para fugir das advertências, eu às vezes usam alguma variante em:
if ((old = signal(SIGHUP, SIG_IGN)) != SIG_IGN)
old = signal(SIGHUP, sighandler);
Isso atribui a old
ambas as vezes. Você pode acompanhar isso com 'assert (antiga == SIG_IGN)'.