2010-01-11 9 views
44

Me gustaría ver todos los lugares en mi código (C++) que ignoran el valor de retorno de una función. ¿Cómo puedo hacerlo? ¿Con la herramienta de análisis de código estático o gcc?¿Cómo se activa la advertencia si el valor de retorno no se tiene en cuenta?

código Mal ejemplo:

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:

  • que debería funcionar incluso si la función y su utilización aparecen en diferentes archivos
  • libre herramienta de comprobación estática
+3

Esto imprimirá muchas advertencias si usa 'printf' por ejemplo. –

+0

Se disparará en su 'cout' s también. – Bill

+1

La razón "probable" por la que no se puede aplicar a través de la línea de comandos es que si tiene una razón válida para ignorar un resultado, terminaría con la necesidad de asignar una "variable no utilizada" que generaría una advertencia. Por ejemplo, ciertamente no desea que 'T & operator = (T rhs);' lo obligue a captar el resultado;) –

Respuesta

48

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

Tratando de 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 

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

unsigned long __must_check copy_to_user(void __user *to, 
             const void *from, unsigned long n); 
+0

elimino la aceptación de la respuesta, desafortunadamente funciona solo en un archivo. – Drakosha

+0

¿Has intentado usarlo en el archivo de encabezado? –

+2

Funciona bien en un archivo de encabezado para mí. Puse un prototipo de función con WARN_UNUSED en un nuevo archivo lib.h y luego lo incluí en test.c y recibí la misma advertencia. También tenga en cuenta que así es como lo hace el kernel de Linux. –

4

Cualquier código de análisis estático (p.ej. PC-Lint) debería poder decírselo. Para PC-Lint, sé que este es el caso.

4

un analizador estático va a hacer el trabajo por usted, pero si su base de código es más que trivial se preparan para ser abrumado ;-)

10

Por lo que yo soy consciente de que no hay opción de GCC para dar esta advertencia . Sin embargo, si usted está interesado en funciones específicas, se puede etiquetar con un atributo:

int fn() __attribute__((warn_unused_result)); 

lo que daría una advertencia si el valor de retorno de fn no se utilizó(). Advertencia: nunca he usado esta función yo mismo.

4

Un analizador estático será su mejor apuesta aquí. Utilizamos Coverity aquí, pero hay free tools disponibles que puede usar también.

Si necesita una solución rápida y sucia, y tiene una concha de estilo Linux a mano, puede intentar algo como:

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

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

10

Se puede utilizar esta plantilla práctica de hacerlo en tiempo de ejecución.

En lugar de devolver un código de error (por ejemplo, HRESULT), devuelve un código de retorno <HRESULT>, que afirma si se sale del alcance sin que se lea el valor. No es una herramienta de análisis estático, pero de todos modos es útil.

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

+1 - esa es una buena idea – Bill

+2

Es una buena idea. Desafortunadamente, viola el principio de "los destructores no deben tirar": http://www.parashift.com/c++faq-lite/exceptions.html#faq-17.3, pero tal vez eso sería aceptable para un "control de todas las devoluciones". "modo de prueba (probablemente no sea necesario el control en una versión de lanzamiento final). – timday

+1

Buen punto - Lo he cambiado para afirmar en su lugar. –

2

El clásico programa "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 provocaba un ruido excesivo en la salida de pelusa (recogía trozos de pelusa que querías que ignorara). Esa es probablemente la razón por la cual GCC no tiene una advertencia estándar para eso.

El otro problema, la otra cara de la moneda, es "¿cómo suprimir la advertencia cuando sabes que estás ignorando el resultado pero realmente no te importa?". El escenario clásico para eso es:

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

Le importan los primeros resultados de signal(); sabes que el segundo será SIG_IGN (ya que solo lo configuraste para eso). Para alejarse de las advertencias, a veces utilizo alguna variante en:

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

Esto asigna a old en ambas ocasiones. Puedes seguir eso con 'assert (old == SIG_IGN)'.

+7

En realidad, la forma típica de decir "no, realmente no me importa el valor de retorno" es con un reparto a 'nulo ', p. '(void) printf (" ¡Hola, mundo! \ n ");' Incluso en el nivel de advertencia más alto, no se emitirá ninguna advertencia para esto con el lanzamiento explícito a nulo. –

+0

Sí, el reparto '' (void) ''funciona ... pero parece feo en el código. Y tengo que cuidar el código que reemplaza ese feo-gramo con '' VOID' 'en su lugar; asigna a '' (void) ''emitir con compiladores de C estándar (todos los compiladores de C en estos días), pero se originaron en los días previos a que fueran estándar y luego hizo cosas al asignar el resultado a una variable estática de archivo (' '#define VOID _void_ = ''o más o menos). Aaah; los recuerdos y los aros que tuvieron que saltar. –

Cuestiones relacionadas