#!/usr/bin/perl # # Usage: # while_for_check.pl < $file.sm # # Description: # searches for following construct: # while|for (something); # and does some sanity checks like: # - 'while|for (something); {' # - 'while|for (something);' is an infinite loop # # Copyright 2003-2007 Michael Stefaniuc use strict; use smatch; use ifcond; sub error_msg($$$) { my $lineno = shift; my $funcpos = shift; my $msg = shift; print get_filename(), " $lineno $funcpos $msg\n"; } sub is_invariant($) { # return's 1 if the expr evaluates to the same value on every call my $expr = shift; # having a '++', '--' or a function call should be safe if ($expr =~ m/\b(?:p(?:ost|re)(?:in|de)crement|call)_expr\b/) { return 0; } # check for +=, -=, *=, etc. and also "foo = foo->bar" if ($expr =~ m/modify_expr\(var_decl\(([^)]+)\)= (?:\w+_expr|component_ref)\(\((?:[^)]+ )?var_decl\(\1\)/) { return 0; } # default return 1; } # no need for the external state functions my $state = "__none"; my $payload = ""; while (my $data = get_data()){ if ($data =~ /^(while_cond|for_stmt)\s*(.+)$/) { if (ord($1) == 0x66) { $state = "for"; } else { $state = "while"; } $payload = $2; next; } if ($data =~ /^end_$state/) { # found a while|for (...); # checking what's in the (...) is expensive so first peek if we get # next a '{'. But first cache some data which we'll loose my $lineno = get_lineno(); my $func_pos = get_func_pos(); my $state_sav = $state; $state = "__none"; $data = get_data(); if ($data =~ /^cmpstmt_start/) { # bummer ... found while|for (...); { error_msg($lineno, $func_pos, "found '$state_sav (...); {'"); next; } # now check if the (...) is invariant if (is_invariant($payload)) { # seems to be an infinite loop error_msg($lineno, $func_pos, "possible infinite $state_sav loop"); } # we peeked a line in advance redo; } # not found what we are looking for. reset the state. $state = "__none"; }