#This script is designed to run with Understand - CodeCheck use base qw(Understand::Codecheck); use strict; use constant ERR1 => 'Violation: function "%1" is directly recursive, which is unsafe.'; use constant ERR2 => 'Violation: function "%1" is indirectly recursive through "%2", which is unsafe.'; sub register_tr_text { my $check = shift; $check->add_tr_text(ERR1); $check->add_tr_text(ERR2); } sub name { '7-5-4 No direct or indirect recursion allowed.' } sub description { '7-5-4 (Advisory) Functions shall not call themselves, either directly or indirectly.' } sub detailed_description { <<'END_DESC'

Rationale
Unbounded recursion is likely to lead to a stack over-flow and may impact system timings. This is also the case for an iterative algorithm.

Example
int32_t fn ( int32_t x ) 
{ 
   if ( x > 0 ) 
   { 
      x = x * fn ( x – 1 );     // Non-compliant 
   } 
   return ( x ); 
}
// File1.cpp 
int32_t fn_2 ( int32_t x ) 
{ 
   if ( x > 0 ) 
   { 
      x = x * fn_3 ( x – 1 );   // Non-compliant 
   } 
   return ( x ); 
}
// File2.cpp 
int32_t fn_3 ( int32_t x ) 
{ 
   if ( x == 0 ) 
   { 
      x = x * fn_2 ( x – 1 );   // Non-compliant 
   } 
   return ( x ); 
}
END_DESC } sub test_language { my $language = shift; return $language eq 'C++'; } sub test_entity { 1 } sub test_global { 0 } sub define_options { } sub discoverRecursion { my $check = shift; my $ent = shift; my $firstTime = do { @_ ? shift : 1 }; # to detect "directly recursive" my $seen = do { @_ ? shift : {} }; my $originalEnt = do { @_ ? shift : $ent }; return unless $ent->kind->check('function'); foreach my $callRef ($ent->refs('call', 'function', 1)) { my $callEnt = $callRef->ent; ++$seen->{$callEnt->uniquename}; next if $seen->{$callEnt->uniquename} > 1; if ($callEnt->uniquename eq $originalEnt->uniquename) { my $ref = $callRef->ent->ref; if ($firstTime){ $check->violation($originalEnt, $ref->file, $ref->line, $ref->column, ERR1, $originalEnt->longname); }else{ $check->violation($originalEnt, $ref->file, $ref->line, $ref->column, ERR2, $originalEnt->longname, $ent->longname); } return; } discoverRecursion($check, $callEnt, 0, $seen, $originalEnt); } return; } sub check { my $check = shift; my $file = shift; return unless $file->kind->check('file'); foreach my $ref ($file->filerefs('define', 'function', 1)) { my $ent = $ref->ent; discoverRecursion($check, $ent); } return; }