开发者

How to find assignments with no effect?

开发者 https://www.devze.com 2023-01-17 21:15 出处:网络
In the process of automatically renaming many variables in a big project I may have created a lot of things like these:

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 variable Par shadowing the member variable Par - you may or may not like this.)
  • Embarcadero C++Builder does not warn that Par = Par is useless, but it can warn that Par 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.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号