use base ("Understand::Codecheck");
use strict;
use constant ERR1 => "Floating Point in Equality operation";
sub register_tr_text {
my $check = shift;
$check->add_tr_text(ERR1);
}
sub name {
return "Test equality for floating point";
}
sub description {
return "Check for equality tests (==) involving floating-point values.";
}
sub detailed_description {
return << "END_DESC"
Rationale
The inherent nature of floating-point types is such that comparisons of equality will often not
evaluate to true, even when they are expected to. Also, the behaviour of such a comparison cannot
be predicted before execution, and may well vary from one implementation to another.
Check Limitations
This check only handles simple expressions like a==b, not complex expressions like a==b+c, a==(t)b or a==b->c. In these three cases, however,
if at least one operand is simple (like \'a\' in these cases) and that operand is a floating-point type, then the check will still be valid.
END_DESC
}
sub test_language {
my $language = shift;
return $language =~ /C\+\+/;
}
sub test_entity {
return 1;
}
sub test_global {
return 0;
}
sub check {
my $check = shift;
my $file = shift;
return unless $file->kind->check(" ~unresolved ~unknown");
# create lexer once for file
my $lexer = $file->lexer();
return unless $lexer;
# loop through functions defined in the file
foreach my $ref ($file->filerefs("define","function",1)) {
my $func = $ref->ent();
my ($begin,$end) = getFunctionDefnLines($func);
next if (!$begin);
do_one_function($check,$file,$func,$lexer,$begin,$end);
}
}
# check one function.
sub do_one_function {
my $check = shift;
my $file = shift;
my $func = shift;
my $lexer = shift;
my $begin = shift;
my $end = shift;
# setup lexemes
my $lexeme_pos = 0;
my @lexemes = $lexer->lexemes($begin,$end);
my $lexeme = nextLexeme(\@lexemes, \$lexeme_pos);
my $seen_floating = 0; # if previous token was floating id or literal
my $seen_equality = 0; # if previous token was '=='
my $seen_paren = 0; # if previous token was ')'
while ($lexeme) {
if ($lexeme->text() eq "==") {
if ($seen_floating) {
$check->violation($func,$file,$lexeme->line_begin,$lexeme->column_begin,ERR1);
$seen_floating = 0;
} else {
$seen_equality = 1;
}
$seen_paren = 0;
} elsif (checkFloating($lexeme) && !$seen_paren) {
if ($seen_equality) {
$check->violation($func,$file,$lexeme->line_begin,$lexeme->column_begin,ERR1);
$seen_equality = 0;
} else {
$seen_floating = 1;
}
$seen_paren = 0;
} elsif ($lexeme->text() eq ")") {
$seen_floating = 0;
$seen_equality = 0;
$seen_paren = 1;
} else {
$seen_floating = 0;
$seen_equality = 0;
$seen_paren = 0;
}
$lexeme = nextLexeme(\@lexemes, \$lexeme_pos);
}
}
# return true if the lexeme is either a floating-point literal or
# an entity of floating-point type.
sub checkFloating {
my $lexeme = shift;
return 0 unless $lexeme;
if ($lexeme->token() eq "Literal") {
my $text = $lexeme->text();
return 0 if $text =~ m/^0[xXbB]/;
return 1 if $text =~ m/[.eE]/;
} else {
my $ent = $lexeme->ent() or return 0;
my $type_ref = $ent->ref("typed");
my %seen; #Avoid recursive typing
while ($type_ref &&
$type_ref->ent() != $ent && ! $seen{$type_ref->ent->id}) { # hack for parse problem before b273
$seen{$type_ref->ent->id}=1;
$ent = $type_ref->ent();
$type_ref = $ent->ref("typed");
}
return 1 if $ent->type() =~ m/float|double/;
}
return 0;
}
# Pass a function entity. Return an array of:
# the begin line
# the end line
# the defn file entity
# Return undef if this info cannot be provided.
sub getFunctionDefnLines {
my $func = shift;
my $begin_ref = $func->ref("definein");
my $end_ref = $func->ref("end");
return undef if (!$begin_ref || !$end_ref);
return ($begin_ref->line(), $end_ref->line(), $begin_ref->file());
}
# Return the next interesting lexeme or undef when all lexemes
# are used.
sub nextLexeme {
my $lexemes = shift;
my $lexeme_pos = shift;
while (${$lexeme_pos} < $#$lexemes) {
my $lexeme = $lexemes->[${$lexeme_pos}++];
next if ($lexeme && $lexeme->token() =~ m/Comment|Whitespace|Newline/);
return $lexeme;
};
return undef;
}