summaryrefslogtreecommitdiff
path: root/tests/wrapper-compiler-errors
blob: 213382cd1d446f214021a8dcbccf5b13d031d514 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
#!/bin/sh

# Take an input file which contains a header of the form:
# /* Template: [template name] */
# followed by a blank line, then one or more sections of the form:
# /*
# [Error message|‘No error’]
# */
# {
#     [Code]
# }
#
# Sections are separated by ‘/*’ on a line of its own. The code must not contain
# C-style comments (‘/* … */’), but can contain C++-style ones (‘// …’).
#
# The wrapper script takes each section and wraps the code in a main() function
# with some standard variables and reference count handling provided by the
# named template. It then compiles
# the code using Clang with Tartan, and checks the compiler output against
# the expected error message. If the expected error message is ‘No error’ it
# asserts there’s no error.

input_filename=$1
temp_dir=`mktemp -d`
tests_dir=`dirname $0`
tartan=${tests_dir}/../scripts/tartan
tartan_plugin=${tests_dir}/../clang-plugin/.libs/libtartan.so

echo "Reading input from ${input_filename}."
echo "Using temporary directory ${temp_dir}."
echo "Using Tartan from ${tartan}."
echo "Using plugin from ${tartan_plugin}."

test_status=0

# Before starting, work out the compiler’s system include paths.
# Thanks to: http://stackoverflow.com/a/17940271/2931197
system_includes=`echo | cpp -Wp,-v 2>&1 | grep '^[[:space:]]' | \
	sed -e 's/^[[:space:]]*/-isystem/' | tr "\n" ' '`

# Extract the template name.
template_name=`head -n 1 "${input_filename}" | \
	sed -n 's/\/\*[[:space:]]*Template:\(.*\)\*\//\1/p' | \
	tr -d ' '`

echo "Using template ${template_name}."

# Split the input file up into sections, delimiting on ‘/*’ on a line by itself.
tail -n +3 "${input_filename}" > "${temp_dir}/${input_filename}.tail"
csplit --keep-files --elide-empty-files --silent \
	--prefix="${temp_dir}/${input_filename}_" \
	--suffix-format='%02d.c' \
	"${temp_dir}/${input_filename}.tail" '/^\/\*/' '{*}'

echo ""

num=0
while [[ -f `printf "${temp_dir}/${input_filename}_%02d.c" ${num}` ]]; do
	section_filename=`printf ${temp_dir}/${input_filename}_%02d.c ${num}`
	expected_error_filename=`printf ${temp_dir}/${input_filename}_%02d.expected ${num}`
	actual_error_filename=`printf ${temp_dir}/${input_filename}_%02d.actual ${num}`

	echo "${section_filename}:"
	echo "-------"
	echo ""
	echo " - Building section file ${section_filename}."
	echo " - Outputting to error files ${expected_error_filename} and ${actual_error_filename}."

	# Wrap the section’s code with a prefix and suffix.
	(cat "${template_name}.head.c"
	 cat "${section_filename}"
	 cat "${template_name}.tail.c"
	) > $section_filename.tmp
	mv -f $section_filename.tmp $section_filename

	num=$((num + 1))

	# Extract the expected comment.
	sed -n '/^\/\*/n; s/^ \* \(.*\)/\1/p' < $section_filename > $expected_error_filename

	if [[ $(<"${expected_error_filename}") == "No error" ]]; then
		echo " - Expecting no error"
		expect_error=false
	else
		echo " - Expecting an error"
		expect_error=true
	fi

	# Run the compiler.
	# e.g. Set
	# TARTAN_TEST_OPTIONS="-analyzer-checker=debug.ViewExplodedGraph" to
	# debug the ExplodedGraph
	TARTAN_PLUGIN=$tartan_plugin \
	TARTAN_OPTIONS=--quiet \
	$tartan \
		-cc1 -analyze -std=c89 -Wno-visibility \
		`pkg-config --cflags glib-2.0` \
		$system_includes \
		$section_filename > $actual_error_filename 2>&1

	# Compare the errors.
	if $expect_error; then
		# Expecting an error. Check that the expected errors are a
		# subset of the actual errors, to allow for spurious Clang
		# warnings because generated code is hard.
		grep_failed=0

		while read line
		do
			grep -F "${line}" "${actual_error_filename}" >/dev/null
			grep_status=$?

			if [ $grep_status -ne 0 ]; then
				echo " * Non-matching line:" 1>&2
				echo "${line}" 1>&2
				grep_failed=1
			fi
		done < "${expected_error_filename}"

		if [ $grep_failed = 1 ]; then
			echo " * Error: Expected compiler error was not seen." 1>&2

			echo " - Expected:" 1>&2
			cat "${expected_error_filename}" 1>&2
			echo "" 1>&2
			echo " - Actual:" 1>&2
			cat "${actual_error_filename}" 1>&2

			test_status=1
		fi
	else
		# Expecting no error.
		if [[ -s "${actual_error_filename}" ]]; then
			echo " * Error: Compiler error when none was expected." 1>&2

			echo " - Actual:" 1>&2
			cat "${actual_error_filename}" 1>&2

			test_status=1
		fi
	fi

	echo ""
	echo ""
done

# Exit status. Leave the temporary directory alone on failure.
if [[ $test_status -eq 0 ]]; then
	rm -rf ${temp_dir}
fi

exit $test_status