use base ("Understand::Codecheck");
use strict;
use constant ERR1 => 'Fixed Value(%1) used incorrectly';
sub register_tr_text {
my $check = shift;
$check->add_tr_text(ERR1);
}
sub name {
return "Magic Numbers";
}
sub description {
return "All fixed values will be defined constants.";
}
sub detailed_description { return <<"END_DESC"
Rationale
Using fixed values(Magic Number) in code makes it difficult to know why the engineer chose that number in choosing that number and makes it hard to adapt and extend the program.
Magic numbers should be replaced with name constants to make them more usable.
Exceptions
Select if bitfield definitions should allow magic numbers.
Any exceptions should be in a comma delimited list of literals that will not be tested. For example: 0,1,1024
END_DESC
}
sub test_language {
my $language = shift;
return $language =~ /C\+\+/;
}
sub test_entity {
return 1;
}
sub test_global {
return 0;
}
sub define_options{
my $check = shift;
$check->option->checkbox('bitfield','Allow Bitfield definitions',0);
$check->option->checkbox('pureVirtual','Allow Pure Virtual Functions',0);
$check->option->checkbox('multiple','Show multiple violations per statement',0);
$check->option->text('exceptions','Exception List','');
}
sub check {
my $check = shift;
my $file = shift;
return unless $file->kind->check("file ~unknown ~unresolved");
my $lexer = $file->lexer;
return unless $lexer;
my $lexeme = $lexer->first;
my @exceptions = split ',', $check->option->lookup('exceptions');
while ($lexeme){
if ($lexeme->inactive){
#noop
}elsif ($lexeme && ($lexeme->token() eq "Keyword") && ($lexeme->text() eq "asm")){
while ($lexeme->next && ($lexeme->next()->token() ne "Keyword") && ($lexeme->text ne "endasm")){
$lexeme = $lexeme->next;
}
}elsif (isInitialization($lexeme) || ($check->option->lookup('bitfield') && isBitField($lexeme)) || ($check->option->lookup('pureVirtual') && isPureVirtual($lexeme))){
while($lexeme->next() && !(($lexeme->next()->token() eq "Punctuation") && ($lexeme->next()->text() eq ";"))){
$lexeme = $lexeme->next;
}
}elsif(isMacroDeclaration($lexeme)){
while($lexeme->next() && !(($lexeme->next()->token() eq "Newline"))) {
$lexeme = $lexeme->next;
}
}elsif(isEnumeratorDeclaration($lexeme)){
while($lexeme->next() &&
!(($lexeme->next()->token() eq "Punctuation") && ($lexeme->next()->text() eq "}")) &&
!(($lexeme->next()->token() eq "Operator") && ($lexeme->next()->text() eq ","))) {
$lexeme = $lexeme->next();
}
}elsif ($lexeme->token() eq "Literal" && ( ! @exceptions || ! ($lexeme->text ~~ @exceptions))){
#Get entity associated with literal
my $searchLex = $lexeme;
while ($searchLex && ! $searchLex->ent){
$searchLex = $searchLex->previous;
}
my $ent=0;
$ent = $searchLex->ent if ($searchLex);
$check->violation($ent,$file,$lexeme->line_begin,$lexeme->column_begin,ERR1,$lexeme->text);
#Only register one violation per statement when multiple is not selected
while(!($check->option->lookup('multiple')) && $lexeme->next() && !(($lexeme->next()->token() eq "Punctuation") && ($lexeme->next()->text() eq ";"))){
$lexeme = $lexeme->next;
}
}
$lexeme = $lexeme->next;
}
}
sub isInitialization($)
{
my ($lexeme) = @_;
my $t = $lexeme->token();
my $r = $lexeme->ref();
my $e = $lexeme->ent();
if($r && $r->kindname() eq "Init" && $e->kind()->check("Object"))
{
return 1;
}
}
sub isPureVirtual($)
{
my ($lexeme) = @_;
my $t = $lexeme->token();
my $r = $lexeme->ref();
my $e = $lexeme->ent();
if($r && $r->kind->check("declarein") && $e->kind()->check("Pure Virtual"))
{
return 1;
}
}
sub isBitField($)
{
my ($lexeme) = @_;
my $t = $lexeme->token();
my $r = $lexeme->ref();
my $e = $lexeme->ent();
if($r && $r->kind->check("definein") && $e->kind()->check("Object") && $e->freetext("Bitfield"))
{
return 1;
}
}
sub isMacroDeclaration($)
{
my ($lexeme) = @_;
my $r_val = 0;
my $t = $lexeme->token();
my $r = $lexeme->ref();
my $e = $lexeme->ent();
if($t && $r && $e &&
$t eq "Identifier" &&
$r->kindname() eq "Define" &&
$e->kind()->check("Macro")
)
{
$r_val = 1;
}
return $r_val;
}
sub isEnumeratorDeclaration($)
{
my ($lexeme) = @_;
my $r_val = 0;
my $t = $lexeme->token();
my $r = $lexeme->ref();
my $e = $lexeme->ent();
if($t && $r && $e &&
$t eq "Identifier" &&
$r->kindname() eq "Define" &&
$e->kind()->check("Enumerator")
)
{
$r_val = 1;
}
return $r_val;
}