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
Foi útil?

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 , que afirma se ele sai do escopo, sem o valor que está sendo lido. Não é uma ferramenta de análise estática, mas é nenhum útil a menos.

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)'.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top