2010-09-23 18 views
5

En el proceso de renombrar automáticamente muchas variables en un gran proyecto que puede haber creado un montón de cosas como éstas:¿Cómo encontrar asignaciones sin efecto?

class Foo { 
    int Par; 
    void Bar(int Par) { 
     Par = Par;  // Nonsense 
    } 
}; 

ahora tengo que identificar a esos lugares para corregirlos. P.ej. en "this-> Par = Par;". Desafortunadamente, el compilador de Visual C++ no me ha dado ningún comentario al respecto, incluso con todas las advertencias activadas. Recuerdo que una vez hubo una advertencia al respecto. Decía "El código no tiene efecto" o algo así. Pero parece que se ha ido porque algunas personas usaron esa práctica para evitar advertencias de "parámetros sin referencia". ¿Hay alguna manera de volver a activar esa advertencia? ¿Advierte GCC aquí? ¿Alguna idea?

+0

@Georg ... sería bueno para el compilador para incluir una advertencia de que hay , ya que el código es redundante. Las declaraciones de variables redundantes son advertidas, por lo que este parece ser un caso de advertencia apropiado también. Pero basado en mi prueba g ++/gcc no advierte sobre este tipo de sintaxis. –

+0

@Jason: Sí, no es necesario que me diga eso. Acabo de decir que el cambio que Brian sugiere no cambia nada. –

+0

¿Ha cambiado tanto la base de códigos que no puede revisar los cambios que ha realizado utilizando la solución de diferencias que proporciona su SCM? Por ejemplo, utilizo TortoiseSVN y proporciona una GUI realmente agradable para todos los archivos modificados más una buena herramienta gráfica de diferencias para revisar archivos individuales. (Utiliza un SCM, ¿no?) – sbi

Respuesta

6

Un par de compiladores puede generar advertencias sobre esto:

  • CCG y Sonido metálico puede advertir en el código como este si se agrega la opción -Wshadow. (En concreto, mientras que No advierten sobre la asignación de sentido, que hacen advierten sobre la variable local Par sombreando la variable miembro Par -. Usted puede o no puede tener gusto de esto)
  • Embarcadero C++ Builder hace No advierta que Par = Par es inútil, pero puede advertir que Par no se usa después de que se haya asignado, lo que debería satisfacer sus necesidades.

Sospecho que una herramienta como PC-Lint también podría identificar código como este.

Otra solución consiste en marcar sus parámetros como const:

class Foo { 
    int Par; 
    void Bar(const int Par) { 
     Par = Par;  // Compiler error! 
    } 
}; 

const en parámetros pase-por-valor no es parte de la firma de la función, por lo que sólo tendrá que añadir a las definiciones de las funciones dentro de su .cpp archivo, no las declaraciones de función dentro de su archivo .h.En otras palabras, es legal para hacer esto:

// foo.h 
class Foo { 
    int Par; 
    void Bar(int Par); 
}; 

// foo.cpp 
void Foo::Bar(const int Par) { ... } 
+0

Si OP conocía los lugares a donde ir y agregaba el 'const', podría arreglar la siguiente línea. – Arun

+0

@ArunSaha - 'const' se puede agregar a todos los parámetros de paso por valor (por un script, por ejemplo), y es sin dudas una buena práctica de programación de todos modos (por lo que no hará daño agregarlo a todas partes) y usar ayudará a evitar que se realicen asignaciones malas como esta en el futuro. –

+0

¡Entendido ahora! Buen punto. Por cierto, yo mismo practico tu recomendación. Pero de alguna manera no pude conectarme a su sugerencia en primera lectura :(. Su sugerencia '-Wshadow' es, en mi humilde opinión, más directa. +1. – Arun

0

estoy pensando en escribir un guión para repasar los archivos y detectar líneas que contienen el patrón

exp=exp; 

haciendo caso omiso de todos los espacios en blanco en la línea.

+0

Esto se puede hacer con un perl de una sola línea. Véase más arriba. –

1

Como dijo Brian en su comentario, este es un argumento realmente bueno para tener una convención de nomenclatura que diferencia entre variables miembro y argumentos funcionales para las clases (de las cuales el prefijo "m_" es solo un ejemplo). Sugeriría ese enfoque, para no tener que repetir el proceso de búsqueda regularmente en el camino.

+0

Bueno, para ser sincero, el problema surgió porque decidí eliminar esos prefijos :-). No me gustan más. Las expresiones se ven demasiado feas y demasiado largas. Creo que después de poder volver a ejecutar el proyecto no tendré muchos problemas en el futuro. Cuando escribo un nuevo código, soy consciente de esas cosas. –

+0

Bueno, por lo que vale, esta es una de esas situaciones en las que (en mi opinión) está sacrificando el mantenimiento futuro del código para "belleza"; si no para usted, entonces para otros desarrolladores. Puede ser perfectamente aceptable en su caso (p. Ej., Proyecto pequeño, desarrollador único, tiempo de vida limitado, etc.), pero no es algo que haría. – Nick

4

Como señaló kaptnole, la expresión regular que diseñé podría usarse directamente en el estudio visual. Su patrón es:

^[\ s \ t] * ([a-zA-Z_0-9] ) [\ s \ t] = [\ s \ t] \ 1 [\ s \ t]; [\ s \ t] * $

Siga las instrucciones que figuran en esta lista:
http://msdn.microsoft.com/en-us/library/2k3te2cs%28VS.80%29.aspx
... y hallazgo feliz (sin tener que tocar Perl!).


Este Perl un trazador de líneas lo hará por usted:

perl -n -e'/^[\s\t]*([a-zA-Z_0-9]*)[\s\t]*=[\s\t]*\1[\s\t]*;[\s\t]*$/&&print "$. $_"' test_me && echo 

he comprobado en un archivo que contenga la siguiente y se detecta correctamente todos los partidos:

hi = hi; 
hi= hi ; 
    hi=hi ; 

salida .. ..

[email protected]% perl -n -e'/[\s\t]*([a-zA-Z_0-9]*)[\s\t]*=[\s\t]*\1[\s\t]*;[\s\t]*$/&&print "$. $_"' test_me && echo 
1 hi = hi; 
2 hi= hi ; 
3 hi=hi ; 
[email protected]% 

Mi primer thoug ¡Lo haría en Awk, pero aparentemente Awk no guarda sus coincidencias! :(

Pero bueno, este script Perl es muy llamativo en sí ... incluso imprime los números de línea del hallazgo!


EDIT 1

Y para responder a su otra pregunta , Compilé un programa de prueba simple con una asignación dentro de main con los indicadores "-pedantic" y "-Wall" usando gcc y g ++ y tampoco recibí advertencias ... así que supongo que no te avisa de este tipo de redundancia.

Aquí es mi programa de pruebas:

int main (int argc, char *argv[]) { 
    int bob=5; 
    bob=bob; 
    return 0; 
} 

EDIT 2

Tenga en cuenta mi anterior script de perl no comprueba para ver si hay una variable local de una denominación dentro de una función . En ese caso, la declaración podría ser válida, pero con un estilo pobre (aún podría ser bueno advertirlo).

Y como señala Josh, la bandera "-Wshadow" LE advertirá sobre esto en gcc/g ++ en este caso especializado.

Sugeriría seguir los consejos de Josh sobre el uso de const para argumentos de funciones estáticas. De hecho, cualquier variable no aprobada por referencia generalmente debe ser const

por ej.

void hello_world_print_numbers(int number_1, int number_2, int number_3) { 
    ... 
} 

es una asignación equivocada a punto de ocurrir, así que en vez de usar:

void hello_world_print_numbers(const int number_1, const int number_2, const int number_3) { 
    ... 
} 

... Del mismo modo, en general, con los punteros, excepto en el caso de punteros pasados ​​a matrices (y tener cuidado de no pasar en los límites apropiados de la matriz!).

void hello_world_print_numbers(const int * number_1, const int * number_2, const int * number_3) { 
    ... 
} 

EDITAR 3

me olvidó mi ^ al comienzo de mi expresión regular. Aunque aparentemente trivial, esto hace que etiquete incorrectamente asignaciones del tipo my_class->name=name;. Esto fue sabiamente señalado por RC. La expresión regular ahora está arreglada y ya no debería tener este problema. Gracias RC!

+0

+1: para la secuencia de comandos rápida de Perl. (Pensé en la idea, pero en realidad se te ocurrió el guión). [Por cierto, no pude entender cómo EDIT 2 está ayudando a la respuesta.] – Arun

+0

¡Gracias!Edit 2 explica cómo si cambiara a usar variables constantes (en los casos en que no necesitara modificar la variable pasada), tales asignaciones darían un error - una manera de tiempo de compilación de detectar esto, sin recurrir a un guión como el mío. A menos que declare una variable local con el mismo nombre, en cuyo caso todavía no habría error ... –

+0

Creo que esto encuentra incorrectamente esto-> x = x; –

1

Usted puede probar esto, aunque yo esperaría que usted podría encontrar algunos falsos positivos o algunos casos en los que puede pasar por alto, pero debe encontrar los recta hacia adelante. la partida ## ##

me llevó a su clase y ponerlo dentro de un archivo Foo.h como:

class Foo { 
    int Par; 
    void Bar(int Par) { 
     Par = Par;  // Nonsense 
     Par=Par;  // Nonsense 
     Par = Par;  // Nonsense 
     Par = Par ;  // Nonsense 

     this->x = x; // Do not match this 
    } 
}; 

Entonces creé el siguiente script Perl llamado match.pl

#!/usr/bin/perl 
use strict; 

my $filename = $ARGV[0]; 
open(my $F, $ARGV[0]) || die("Cannot open file: $filename"); 
print "Procesing File: $filename\n"; 

my $lineNum = 0; 
while (<$F>) 
{ 
    $lineNum++; 

    chomp; 
    my $line = $_; 
    if ($line =~ /(?:^|\s+)(\w+?)\s*=\s*\1\s*;/) 
    { 
     print "\t$filename:$lineNum: $line\n"; 
    } 
} 

Luego puede ejecutarlo.

%> ./match.pl Foo.h 
    Procesing File: Foo.h 
    Foo.h:4:   Par = Par;  // Nonsense 
    Foo.h:5:   Par=Par;  // Nonsense 
    Foo.h:6:   Par = Par;  // Nonsense 
    Foo.h:7:   Par = Par ;  // Nonsense 

Entonces en Linux (estoy seguro de que hay un comando similar en Windows) que puede hacer:

%> find *.cpp *.h -exec ./match.pl {} \; 
Procesing File: test.cpp 
Procesing File: test2.cpp 
Procesing File: test3.cpp 
Procesing File: Foo.h 
    Foo.h:4:   Par = Par;  // Nonsense 
    Foo.h:5:   Par=Par;  // Nonsense 
    Foo.h:6:   Par = Par;  // Nonsense 
    Foo.h:7:   Par = Par ;  // Nonsense 
+0

Mira mi perl one-liner a continuación - es mucho más corto ... –

+0

Gracias por su esfuerzo. Podría intentar con expresiones regulares directamente desde la búsqueda de Visual Studio. –

Cuestiones relacionadas