¿Cómo generar una advertencia si no se tiene en cuenta el valor de retorno?


Me gustaría ver todos los lugares en mi código (C++) que no tienen en cuenta el valor de retorno de una función. ¿Cómo puedo hacerlo-con gcc o herramienta de análisis de código estático?

Ejemplo de código defectuoso:

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;
}

Tenga en cuenta que:

  • debería funcionar incluso si la función y su uso están en archivos diferentes
  • libre herramienta de comprobación estática
Author: Matthieu M., 2010-01-11

8 answers

Quieres el atributo warn_unused_result de 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;
}

Intentar compilar este código produce:

$ gcc test.c
test.c: In function `main':
test.c:16: warning: ignoring return value of `f', declared with
attribute warn_unused_result

Puede ver esto en uso en el kernel de Linux ; tienen una macro __must_check que hace lo mismo; parece que necesita GCC 3.4 o superior para que esto funcione. A continuación, encontrará la macro utilizada en los archivos de cabecera del kernel:

unsigned long __must_check copy_to_user(void __user *to,
                                        const void *from, unsigned long n);
 51
Author: Eric Seppanen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-01-11 16:50:28

Por lo que sé no hay ninguna opción de GCC para dar esta advertencia. Sin embargo, si está interesado en funciones específicas, puede etiquetarlas con un atributo:

int fn() __attribute__((warn_unused_result));

Que daría una advertencia si el valor devuelto de fn() no se utiliza. Advertencia: Yo nunca he usado esta característica.

 10
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-01-11 15:47:36

Puede usar esta útil plantilla para hacerlo en tiempo de ejecución.

En lugar de devolver un código de error (por ejemplo, HRESULT) devuelve un return_code, que afirma si sale de ámbito sin que el valor se lea. No es una herramienta de análisis estático, pero es útil sin embargo.

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;
};
 10
Author: Joe Gauterin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-01-12 09:44:45

Para C++17 la respuesta a esta pregunta cambia ya que ahora tenemos el atributo [[nodiscard]]. Cubierto en [dcl.attr.nodiscard]:

El atributo-token nodiscard se puede aplicar al declarador-id en una declaración de función o a la declaración de una clase o enumeración. Aparecerá como máximo una vez en cada lista de atributos y no estará presente ninguna cláusula de argumento de atributo.

Y

[ Ejemplo:

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

- fin ejemplo ]

Así que modificando su ejemplo (verlo en 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;
}

Ahora obtenemos un diagnóstico con gcc y clang, por ejemplo

warning: ignoring return value of function declared with 'nodiscard' attribute [-Wunused-result]
  f(i); // now we obtain a diagnostic
  ^ ~
 6
Author: Shafik Yaghmour,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-07-23 15:56:34

Cualquier código de análisis estático (por ejemplo, PC-Lint) debería poder decirle eso. Para PC-Lint, sé que este es el caso.

 4
Author: Xavier Nodet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-01-11 15:33:57

Un analizador estático hará el trabajo por usted, pero si su base de código es más que trivial prepárese para ser abrumado; -)

 4
Author: Alon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-01-11 16:22:15

Un analizador estático será su mejor apuesta aquí. Aquí usamos Coverity, pero hay herramientas gratuitas disponibles que también puedes usar.

Si necesitas una solución rápida y sucia y tienes a mano un shell al estilo Linux, puedes probar algo como:

grep -rn "function_name" * | grep -v "="

Que encontrará cada línea que hace referencia a la función especificada pero no contiene un "=". Puede obtener muchos falsos positivos (y potencialmente algunos falsos negativos), pero si no tiene un analizador estático, es un lugar decente para empezar.

 4
Author: bta,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-01-11 16:31:46

El programa clásico 'lint' solía ser muy voluble sobre las funciones que devolvían un valor que se ignoraba. El problema era que muchas de esas advertencias no eran deseadas, lo que llevaba a un ruido excesivo en la salida de pelusa (estaba recogiendo trozos de pelusa que querías que ignorara). Esa es probablemente la razón por la que GCC no tiene una advertencia estándar para ello.

El otro problema - la otra cara - es "cómo se suprime la advertencia cuando se sabe que se está ignorando el resultado, pero realmente no le importa". El escenario clásico para eso es:

if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
    signal(SIGHUP, sighandler);

Te preocupas por el primer resultado de signal(); sabes que el segundo será SIG_IGN (ya que acabas de configurarlo a eso). Para alejarme de las advertencias, a veces uso alguna variante en:

if ((old = signal(SIGHUP, SIG_IGN)) != SIG_IGN)
    old = signal(SIGHUP, sighandler);

Esto asigna a old ambas veces. Puedes seguir esto con 'assert (old = = SIG_IGN)'.

 2
Author: Jonathan Leffler,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-01-11 16:48:09