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
18use POSIX;
19
20my $exitcode = 1;
21
22if(@ARGV >= 1 && $ARGV[0] eq "-0") {
23	$exitcode = 0;
24	shift;
25}
26
27if(@ARGV < 1) {
28	print STDERR "Usage: errchk COMPILER [OPTS] SOURCEFILES\n";
29	exit 1;
30}
31
32# Grab SOURCEFILES
33foreach(reverse 0 .. @ARGV-1) {
34	unless($ARGV[$_] =~ /\.(go|s)$/) {
35		@file = @ARGV[$_+1 .. @ARGV-1];
36		last;
37	}
38}
39
40foreach $file (@file) {
41	open(SRC, $file) || die "BUG: errchk: open $file: $!";
42	$src{$file} = [<SRC>];
43	close(SRC);
44}
45
46# Run command
47$cmd = join(' ', @ARGV);
48open(CMD, "exec $cmd </dev/null 2>&1 |") || die "BUG: errchk: run $cmd: $!";
49
50# 6g error messages continue onto additional lines with leading tabs.
51# Split the output at the beginning of each line that doesn't begin with a tab.
52$out = join('', <CMD>);
53@out = split(/^(?!\t)/m, $out);
54
55close CMD;
56
57if($exitcode != 0 && $? == 0) {
58	print STDERR "BUG: errchk: command succeeded unexpectedly\n";
59	print STDERR @out;
60	exit 0;
61}
62
63if($exitcode == 0 && $? != 0) {
64	print STDERR "BUG: errchk: command failed unexpectedly\n";
65	print STDERR @out;
66	exit 0;
67}
68
69if(!WIFEXITED($?)) {
70	print STDERR "BUG: errchk: compiler crashed\n";
71	print STDERR @out, "\n";
72	exit 0;
73}
74
75sub bug() {
76	if(!$bug++) {
77		print STDERR "BUG: ";
78	}
79}
80
81sub chk {
82	my $file = shift;
83	my $line = 0;
84	my $regexp;
85	my @errmsg;
86	my @match;
87	foreach my $src (@{$src{$file}}) {
88		$line++;
89		next if $src =~ m|////|;  # double comment disables ERROR
90		next unless $src =~ m|// (GC_)?ERROR (.*)|;
91		my $all = $2;
92		if($all !~ /^"([^"]*)"/) {
93			print STDERR "$file:$line: malformed regexp\n";
94			next;
95		}
96		@errmsg = grep { /$file:$line[:[]/ } @out;
97		@out = grep { !/$file:$line[:[]/ } @out;
98		if(@errmsg == 0) {
99			bug();
100			print STDERR "errchk: $file:$line: missing expected error: '$all'\n";
101			next;
102		}
103		foreach my $regexp ($all =~ /"([^"]*)"/g) {
104			# Turn relative line number in message into absolute line number.
105			if($regexp =~ /LINE(([+-])([0-9]+))?/) {
106				my $n = $line;
107				if(defined($1)) {
108					if($2 eq "+") {
109						$n += int($3);
110					} else {
111						$n -= int($3);
112					}
113				}
114				$regexp = "$`$file:$n$'";
115			}
116
117			@match = grep { /$regexp/ } @errmsg;
118			if(@match == 0) {
119				bug();
120				print STDERR "errchk: $file:$line: error messages do not match '$regexp'\n";
121				next;
122			}
123			@errmsg = grep { !/$regexp/ } @errmsg;
124		}
125		if(@errmsg != 0) {
126			bug();
127			print STDERR "errchk: $file:$line: unmatched error messages:\n";
128			foreach my $l (@errmsg) {
129				print STDERR "> $l";
130			}
131		}
132	}
133}
134
135foreach $file (@file) {
136	chk($file)
137}
138
139if(@out != 0) {
140	bug();
141	print STDERR "errchk: unmatched error messages:\n";
142	print STDERR "==================================================\n";
143	print STDERR @out;
144	print STDERR "==================================================\n";
145}
146
147exit 0;
148