1 #!/usr/bin/env perl 2 # Copyright 2009 The Go Authors. All rights reserved. 3 # Use of this source code is governed by a BSD-style 4 # license that can be found in the LICENSE file. 5 6 # This script checks that the compilers emit the errors which we expect. 7 # Usage: errchk COMPILER [OPTS] SOURCEFILES. This will run the command 8 # COMPILER [OPTS] SOURCEFILES. The compilation is expected to fail; if 9 # it succeeds, this script will report an error. The stderr output of 10 # the compiler will be matched against comments in SOURCEFILES. For each 11 # line of the source files which should generate an error, there should 12 # be a comment of the form // ERROR "regexp". If the compiler generates 13 # an error for a line which has no such comment, this script will report 14 # an error. Likewise if the compiler does not generate an error for a 15 # line which has a comment, or if the error message does not match the 16 # <regexp>. The <regexp> syntax is Perl but its best to stick to egrep. 17 18 use POSIX; 19 20 my $exitcode = 1; 21 22 if(@ARGV >= 1 && $ARGV[0] eq "-0") { 23 $exitcode = 0; 24 shift; 25 } 26 27 if(@ARGV < 1) { 28 print STDERR "Usage: errchk COMPILER [OPTS] SOURCEFILES\n"; 29 exit 1; 30 } 31 32 # Grab SOURCEFILES 33 foreach(reverse 0 .. @ARGV-1) { 34 unless($ARGV[$_] =~ /\.(go|s)$/) { 35 @file = @ARGV[$_+1 .. @ARGV-1]; 36 last; 37 } 38 } 39 40 # If no files have been specified try to grab SOURCEFILES from the last 41 # argument that is an existing directory if any 42 unless(@file) { 43 foreach(reverse 0 .. @ARGV-1) { 44 if(-d $ARGV[$_]) { 45 @file = glob($ARGV[$_] . "/*.go"); 46 last; 47 } 48 } 49 } 50 51 foreach $file (@file) { 52 open(SRC, $file) || die "BUG: errchk: open $file: $!"; 53 $src{$file} = [<SRC>]; 54 close(SRC); 55 } 56 57 # Run command 58 $cmd = join(' ', @ARGV); 59 open(CMD, "exec $cmd </dev/null 2>&1 |") || die "BUG: errchk: run $cmd: $!"; 60 61 # gc error messages continue onto additional lines with leading tabs. 62 # Split the output at the beginning of each line that doesn't begin with a tab. 63 $out = join('', <CMD>); 64 @out = split(/^(?!\t)/m, $out); 65 66 close CMD; 67 68 if($exitcode != 0 && $? == 0) { 69 print STDERR "BUG: errchk: command succeeded unexpectedly\n"; 70 print STDERR @out; 71 exit 0; 72 } 73 74 if($exitcode == 0 && $? != 0) { 75 print STDERR "BUG: errchk: command failed unexpectedly\n"; 76 print STDERR @out; 77 exit 0; 78 } 79 80 if(!WIFEXITED($?)) { 81 print STDERR "BUG: errchk: compiler crashed\n"; 82 print STDERR @out, "\n"; 83 exit 0; 84 } 85 86 sub bug() { 87 if(!$bug++) { 88 print STDERR "BUG: "; 89 } 90 } 91 92 sub chk { 93 my $file = shift; 94 my $line = 0; 95 my $regexp; 96 my @errmsg; 97 my @match; 98 foreach my $src (@{$src{$file}}) { 99 $line++; 100 next if $src =~ m|////|; # double comment disables ERROR 101 next unless $src =~ m|// (GC_)?ERROR (.*)|; 102 my $all = $2; 103 if($all !~ /^"([^"]*)"/) { 104 print STDERR "$file:$line: malformed regexp\n"; 105 next; 106 } 107 @errmsg = grep { /$file:$line[:[]/ } @out; 108 @out = grep { !/$file:$line[:[]/ } @out; 109 if(@errmsg == 0) { 110 bug(); 111 print STDERR "errchk: $file:$line: missing expected error: '$all'\n"; 112 next; 113 } 114 foreach my $regexp ($all =~ /"([^"]*)"/g) { 115 # Turn relative line number in message into absolute line number. 116 if($regexp =~ /LINE(([+-])([0-9]+))?/) { 117 my $n = $line; 118 if(defined($1)) { 119 if($2 eq "+") { 120 $n += int($3); 121 } else { 122 $n -= int($3); 123 } 124 } 125 $regexp = "$`$file:$n$'"; 126 } 127 128 @match = grep { /$regexp/ } @errmsg; 129 if(@match == 0) { 130 bug(); 131 print STDERR "errchk: $file:$line: error messages do not match '$regexp'\n"; 132 next; 133 } 134 @errmsg = grep { !/$regexp/ } @errmsg; 135 } 136 if(@errmsg != 0) { 137 bug(); 138 print STDERR "errchk: $file:$line: unmatched error messages:\n"; 139 foreach my $l (@errmsg) { 140 print STDERR "> $l"; 141 } 142 } 143 } 144 } 145 146 foreach $file (@file) { 147 chk($file) 148 } 149 150 if(@out != 0) { 151 bug(); 152 print STDERR "errchk: unmatched error messages:\n"; 153 print STDERR "==================================================\n"; 154 print STDERR @out; 155 print STDERR "==================================================\n"; 156 } 157 158 exit 0; 159