In the process of automatically renaming many variables in a big project I may have created a lot of things like these:
class Foo {
int Par;
void Bar(int Par) {
Par = Par; // Nonsense
}
};
Now I need to identify those locations to corr开发者_如何学编程ect them. E.g. into "this->Par = Par;". Unfortunately the Visual C++ Compiler des not give me any comment about it even with all warnings on. I remember there once was a warning about it. It said "Code has no effect" or something. But it seems to be gone maybe because some people used that practice to avoid "unreferenced parameter" warnings. Is there a way to re-activate that warning? Does GCC warn here? Any Idea?
A couple of compilers can generate warnings on this:
- GCC and Clang can warn on code like this if you add the
-Wshadow
option. (Specifically, while they don't warn about the meaningless assignment, they do warn about the local variablePar
shadowing the member variablePar
- you may or may not like this.) - Embarcadero C++Builder does not warn that
Par = Par
is useless, but it can warn thatPar
isn't used after it's assigned to, which should meet your needs.
I suspect a tool like PC-Lint could also identify code like this.
Another solution is to mark your parameters as const
:
class Foo {
int Par;
void Bar(const int Par) {
Par = Par; // Compiler error!
}
};
const
on pass-by-value parameters is not part of the function signature, so you only need to add it to the function definitions within your .cpp
file, not your function declarations within your .h
file. In other words, it's legal to do this:
// foo.h
class Foo {
int Par;
void Bar(int Par);
};
// foo.cpp
void Foo::Bar(const int Par) { ... }
As kaptnole pointed out, the regex I crafted could be used directly in visual studio. Your pattern is:
^[\s\t]*([a-zA-Z_0-9])[\s\t]=[\s\t]\1[\s\t];[\s\t]*$
Follow the directions listed here:
http://msdn.microsoft.com/en-us/library/2k3te2cs%28VS.80%29.aspx
...and happy finding (without ever touching Perl!).
This perl one liner will do it for you:
perl -n -e'/^[\s\t]*([a-zA-Z_0-9]*)[\s\t]*=[\s\t]*\1[\s\t]*;[\s\t]*$/&&print "$. $_"' test_me && echo
I tested it on a file containing the following and it correctly detected all matches:
hi = hi;
hi= hi ;
hi=hi ;
Output....
xxxxx@yyyy% 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 ;
xxxxx@yyyy%
My first thought was to do it in Awk, but apparently Awk doesn't store its matches! :(
But hey, this Perl script is pretty snazzy itself... it even prints the line numbers of the find!
EDIT 1
And to answer your other question, I compiled a simple test program with such an assignment inside main with the "-pedantic" and "-Wall" flags using gcc and g++ and received no warnings in either... so I guess it doesn't warn you of this kind of redundancy.
Here's my test program:
int main (int argc, char *argv[]) {
int bob=5;
bob=bob;
return 0;
}
EDIT 2
Please note my above perl script does NOT check to see if there's a local variable of an identical name inside a function. In that case the statement might be valid, but poor style (still might be good to warn about).
And as Josh points out, the flag "-Wshadow" WILL warn about this in gcc/g++ in this specialized case.
I would suggest following Josh's advice about using const for static function arguments. In fact, any variable not passed by reference should generally be const
e.g.
void hello_world_print_numbers(int number_1, int number_2, int number_3) {
...
}
is a misassignment waiting to happen, so instead use:
void hello_world_print_numbers(const int number_1, const int number_2, const int number_3) {
...
}
...likewise in general with pointers, except in the case of passed pointers to arrays (and be careful there to pass in proper array bounds!).
void hello_world_print_numbers(const int * number_1, const int * number_2, const int * number_3) {
...
}
EDIT 3
I forgot my ^
at the start of my regex. While seemingly trivial this causes it to improperly tag assignments of type my_class->name=name;
. This was wisely pointed out by RC. The regex is now fixed and should no longer have this issue. Thanks RC!
As Brian said in his comment, this is one really good argument for having a naming convention which differentiates between member variables and function arguments for classes (of which the "m_" prefix is just one example). I'd suggest that approach, lest you need to repeat the search process regularly down the road.
You can try this, though I would expect that you could find some false positives or some cases where it may miss, but it should find the straight forward ones.## Heading ##
I took your class and put it inside a file Foo.h
as:
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
}
};
Then I created the following Perl Script called 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";
}
}
Then you can run it.
%> ./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
Then on Linux (I'm sure there is a similar command on Windows) you can do:
%> 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
I am thinking about writing a script to go over the files and detecting lines containing the pattern
exp=exp;
ignoring all white spaces in the line.
精彩评论