用于删除 Fortran 代码中未使用的局部变量的工具

发布于 2024-10-05 05:42:25 字数 223 浏览 3 评论 0原文

有没有一些好的工具可以从自由形式的 Fortran 代码中删除未使用的局部变量?

背景是我们有一个非常大的代码库,并且在过去 20 年里一直禁用未使用的警告,因此警告数量相当多,太多而无法手动修复。

我知道 Photran 有这种重构功能,但我尝试了一下却遇到了问题。由于某种原因,它要求可以解析整个源代码(它无法对我们的代码库进行解析)来进行重构,尽管对于这种重构,在我看来应该足以仅检查单个文件。

Is there some good tool for removing unused local variables from free form Fortran code?

The background is that we have a codebase which is very large, and unused warnings has been disabled for the last 20 years, so there are quite a lot of them, too many to fix manually.

I know Photran has that kind of refactoring, but I tried it and ran into problems. For some reason it requires that the entire source code can be parsed (which it does not manage to do with our codebase) to do the refactoring, although for this refactoring it should IMO be enough to just check single files.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

趴在窗边数星星i 2024-10-12 05:42:25

我是根据编译器消息手动完成此操作的。一种可能的自动工具:SPAG / plusFORT 的文章说:

SPAG 识别并选择性地删除死代码(永远无法执行的语句)和混乱(已声明但从未使用的变量或参数)。

我使用过免费的、精简功能的版本,但不记得该版本是否删除了未使用的变量。

I've done this by hand, based on the compiler messages. One possible automatic tool: the writeup for SPAG / plusFORT says:

SPAG identifies, and optionally removes dead code (statements which could never be executed) and clutter (variables or PARAMETERs which are declared but never used).

I've used the free, reduced functionality version but don't remember whether that version removed unused variables.

2024-10-12 05:42:25

这将作为一个答案,因为它无法放入评论框中 - 但不要将其视为答案,更多的是作为评论

除此之外,抱歉,但我不知道不认为存在这样的工具。 TZXH 问你正在使用什么编译器 - 不是因为它有那么大的区别,而是(我认为)因为他可能正在考虑解析编译器警告以获取变量列表,然后浏览 Fortran 文件,然后半-手动删除这些变量。

我从来没有做过这样的事情,但这是一个可行的过程。有一些潜在的缺点,但它可以工作。

除此之外,为了分析(并编写新的,因为它确实是一个非常好的 IDE)Fortran 源代码,我找到了 SciTools了解一个非常有用的工具。不确定它是否具有满足您要求的功能,但它可能会有所帮助。

this is going as an answer, cause it can't fit in a comment box - don't think of it as an answer though, more of as a comment

Apart from that, sorry, but I don't think such a tool exists. TZXH asked what compiler you're using - not because it makes that much of a difference, but (I think) because maybe he was thinking of maybe parsing compiler warnings to get a list of variables, then going through Fortran files, and semi-manually removing those variables.

I've never done anything like that, but it's a process that could work. Has some potential drawbacks but it could work.

Apart from that, for analysis (and writing new one, for it is really a very nice IDE) of Fortran source code I found SciTools Understand a very helpful tool. Not sure whether it has a feature to do what you're asking, but it could help.

古镇旧梦 2024-10-12 05:42:25

有一个“美化”Python 脚本作为 GPL CP2K 工具链的一部分。您可以在 CP2K 项目源存储库中找到它。您将需要 prettify.py 和 normalizeFortranFile.py 脚本。由于它还尝试“美化”文件,因此如果您确实只想删除局部变量,则必须编辑脚本。

There is a "prettify" Python script as part of the GPL CP2K toolchain. You can find it in in the CP2K project source repository. You will need the prettify.py and normalizeFortranFile.py scripts. Since it also tries to "prettify" the file, you will have to edit the script if you really only want to remove local variables.

临走之时 2024-10-12 05:42:25

对于 gfortran,你可以使用这个 perl 脚本,参数“build_log”应该是 gfortran -Wunused 的输出(或者任何让你未使用的变量警告):

#!/usr/bin/perl

use strict;

my $build_log = shift;

open my $build_filehandle, "<", $build_log;

my $file;
my $line_to_edit;
my $col_to_edit;
my $word_to_remove;

my @files;
my @line_nums;
my @unused_variables;
my $line;

my $line_number = 0;
while($line = <$build_filehandle>)
{
    $line_number++;
    chomp($line);
    if($line =~ m/([^:]*):(\d+)\.(\d+):/)
    {
        $file = $1;
        $line_to_edit = $2;
        $col_to_edit = $3;
    }
    elsif($line =~ m/Warning: Unused variable '([^']*)'/)
    {
        $word_to_remove = $1;
        push(@files, $file);
        push(@line_nums, $line_to_edit);
        push(@unused_variables, $word_to_remove);
    }
}

close($build_filehandle);

# sort [file, line_num, word_to_remove] by files then line_nums then word_to_remove
my @merged_columns;
for(my $i = 0; $i < scalar(@files); $i++) # loop over all the replacements to be made
{
    push(@merged_columns, [$files[$i],$line_nums[$i],$unused_variables[$i]]);
}

# if sort is stable, sort by line_nums, then files
sub by_file_then_line
{
    $a->[0] cmp $b->[0] || # by file
    $a->[1] <=> $b->[1];
}

my @sorted_by_line_nums = sort by_file_then_line @merged_columns;

for(my $i = 0; $i < scalar(@files); $i++) # loop over all the replacements to be made
{
    $files[$i] = $sorted_by_line_nums[$i][0];
    $line_nums[$i] = $sorted_by_line_nums[$i][1];
    $unused_variables[$i] = $sorted_by_line_nums[$i][2];
}

my $print_line = 0;
my $last_file = 'null';
my $replacement_filehandle;
for(my $i = 0; $i < scalar(@files); $i++) # loop over all the replacements to be made
{
    if($files[$i] ne $last_file)
    {
        if(defined($replacement_filehandle))
        {
            # dump the line we were working on
            if($print_line == 1) # print the line that we were processing
            {
                print("$line\n");
                $print_line = 0;
            }

            # dump the rest of the file
            while($line = <$replacement_filehandle>)
            {
                chomp($line);
                print("$line\n");
            }
            # then close it
            close($replacement_filehandle);
        }
        open $replacement_filehandle, "+<", $files[$i];
        $line_number = 0;
    }
    $last_file = $files[$i];

# here we are on the right file, but might need to advance to the correct location
    while($line_number < $line_nums[$i]) # might not even enter
    {
        if($print_line == 1) # print the line that we were processing
        {
            print("$line\n");
            $print_line = 0;
        }
        $line = <$replacement_filehandle>;
        $line_number++;
        chomp($line);

        if($line_number < $line_nums[$i])
        {
            print("$line\n");
        }
    }
    $print_line = 1;

    if($line =~ m/^\s+type/i) # don't bother with types, their naming and form is too messed up to make it worth the effort, and there aren't that many
    {
        next;
    }

    $line =~ s/,/, /g; # add spaces after commas
    $line =~ s/,\s+/, /g; # collapse double commas

# case followed by stuff in parens
    $line =~ s/ ${unused_variables[$i]}\([^\)]*\),?//i;

# case followed by comma
    $line =~ s/ ${unused_variables[$i]},//i;

# case end of line
    $line =~ s/ ${unused_variables[$i]}\s*$//i;

# remove trailing commas
    $line =~ s/,\s*$//;

# collapse double commas
    $line =~ s/,\s*,/,/;

# remove empty memory declaration lines
# ie, if it does not have two sets of word characters kill it off
    if(! ($line =~ m/\w[^A-Za-z\*]+\w/))
    {
        $line = '';
    }
}


if(defined($replacement_filehandle))
{
    # dump the line we were working on
    if($print_line == 1) # print the line that we were processing
    {
        print("$line\n");
        $print_line = 0;
    }

    # dump the rest of the file
    while($line = <$replacement_filehandle>)
    {
        chomp($line);
        print("$line\n");
    }
    # then close it
    close($replacement_filehandle);
}

显然你应该查看源代码并检查它是否做了你想要它做的事情。但粗略地说,它读取编译器输出并删除编译器在其抱怨的行上抱怨的变量。

For gfortran you can use this perl script, the argument "build_log" should be the output of gfortran -Wunused (or whatever gets you unused variable warnings):

#!/usr/bin/perl

use strict;

my $build_log = shift;

open my $build_filehandle, "<", $build_log;

my $file;
my $line_to_edit;
my $col_to_edit;
my $word_to_remove;

my @files;
my @line_nums;
my @unused_variables;
my $line;

my $line_number = 0;
while($line = <$build_filehandle>)
{
    $line_number++;
    chomp($line);
    if($line =~ m/([^:]*):(\d+)\.(\d+):/)
    {
        $file = $1;
        $line_to_edit = $2;
        $col_to_edit = $3;
    }
    elsif($line =~ m/Warning: Unused variable '([^']*)'/)
    {
        $word_to_remove = $1;
        push(@files, $file);
        push(@line_nums, $line_to_edit);
        push(@unused_variables, $word_to_remove);
    }
}

close($build_filehandle);

# sort [file, line_num, word_to_remove] by files then line_nums then word_to_remove
my @merged_columns;
for(my $i = 0; $i < scalar(@files); $i++) # loop over all the replacements to be made
{
    push(@merged_columns, [$files[$i],$line_nums[$i],$unused_variables[$i]]);
}

# if sort is stable, sort by line_nums, then files
sub by_file_then_line
{
    $a->[0] cmp $b->[0] || # by file
    $a->[1] <=> $b->[1];
}

my @sorted_by_line_nums = sort by_file_then_line @merged_columns;

for(my $i = 0; $i < scalar(@files); $i++) # loop over all the replacements to be made
{
    $files[$i] = $sorted_by_line_nums[$i][0];
    $line_nums[$i] = $sorted_by_line_nums[$i][1];
    $unused_variables[$i] = $sorted_by_line_nums[$i][2];
}

my $print_line = 0;
my $last_file = 'null';
my $replacement_filehandle;
for(my $i = 0; $i < scalar(@files); $i++) # loop over all the replacements to be made
{
    if($files[$i] ne $last_file)
    {
        if(defined($replacement_filehandle))
        {
            # dump the line we were working on
            if($print_line == 1) # print the line that we were processing
            {
                print("$line\n");
                $print_line = 0;
            }

            # dump the rest of the file
            while($line = <$replacement_filehandle>)
            {
                chomp($line);
                print("$line\n");
            }
            # then close it
            close($replacement_filehandle);
        }
        open $replacement_filehandle, "+<", $files[$i];
        $line_number = 0;
    }
    $last_file = $files[$i];

# here we are on the right file, but might need to advance to the correct location
    while($line_number < $line_nums[$i]) # might not even enter
    {
        if($print_line == 1) # print the line that we were processing
        {
            print("$line\n");
            $print_line = 0;
        }
        $line = <$replacement_filehandle>;
        $line_number++;
        chomp($line);

        if($line_number < $line_nums[$i])
        {
            print("$line\n");
        }
    }
    $print_line = 1;

    if($line =~ m/^\s+type/i) # don't bother with types, their naming and form is too messed up to make it worth the effort, and there aren't that many
    {
        next;
    }

    $line =~ s/,/, /g; # add spaces after commas
    $line =~ s/,\s+/, /g; # collapse double commas

# case followed by stuff in parens
    $line =~ s/ ${unused_variables[$i]}\([^\)]*\),?//i;

# case followed by comma
    $line =~ s/ ${unused_variables[$i]},//i;

# case end of line
    $line =~ s/ ${unused_variables[$i]}\s*$//i;

# remove trailing commas
    $line =~ s/,\s*$//;

# collapse double commas
    $line =~ s/,\s*,/,/;

# remove empty memory declaration lines
# ie, if it does not have two sets of word characters kill it off
    if(! ($line =~ m/\w[^A-Za-z\*]+\w/))
    {
        $line = '';
    }
}


if(defined($replacement_filehandle))
{
    # dump the line we were working on
    if($print_line == 1) # print the line that we were processing
    {
        print("$line\n");
        $print_line = 0;
    }

    # dump the rest of the file
    while($line = <$replacement_filehandle>)
    {
        chomp($line);
        print("$line\n");
    }
    # then close it
    close($replacement_filehandle);
}

Obviously you should look at the source and check that it does what you want it to do. But roughly speaking it reads the compiler output and removes the variable that the compiler complains about on the line which it is complaining.

慈悲佛祖 2024-10-12 05:42:25

PlusFORT 是您唯一的选择,而且价格并不便宜。但它已经在数百万行旧的 Fortran (77) 代码上使用,并且相当成熟。如需意见,请尝试 comp.lang.fortran

PlusFORT is your only option and its not cheap. But it has been used on millions of lines of old Fortran (77) code and is fairly mature. For opinions try comp.lang.fortran

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文