* tree-ssa-reassoc.c (reassociate_bb): Clarify code slighly.
[official-gcc.git] / libgo / go / go / types / check_test.go
blob24b3365717b631af8fe0ae5733a516d32c2aa78e
1 // Copyright 2011 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
5 // This file implements a typechecker test harness. The packages specified
6 // in tests are typechecked. Error messages reported by the typechecker are
7 // compared against the error messages expected in the test files.
8 //
9 // Expected errors are indicated in the test files by putting a comment
10 // of the form /* ERROR "rx" */ immediately following an offending token.
11 // The harness will verify that an error matching the regular expression
12 // rx is reported at that source position. Consecutive comments may be
13 // used to indicate multiple errors for the same token position.
15 // For instance, the following test file indicates that a "not declared"
16 // error should be reported for the undeclared variable x:
18 // package p
19 // func f() {
20 // _ = x /* ERROR "not declared" */ + 1
21 // }
23 // TODO(gri) Also collect strict mode errors of the form /* STRICT ... */
24 // and test against strict mode.
26 package types_test
28 import (
29 "flag"
30 "go/ast"
31 "go/importer"
32 "go/parser"
33 "go/scanner"
34 "go/token"
35 "internal/testenv"
36 "io/ioutil"
37 "regexp"
38 "strings"
39 "testing"
41 . "go/types"
44 var (
45 listErrors = flag.Bool("list", false, "list errors")
46 testFiles = flag.String("files", "", "space-separated list of test files")
49 // The test filenames do not end in .go so that they are invisible
50 // to gofmt since they contain comments that must not change their
51 // positions relative to surrounding tokens.
53 // Each tests entry is list of files belonging to the same package.
54 var tests = [][]string{
55 {"testdata/errors.src"},
56 {"testdata/importdecl0a.src", "testdata/importdecl0b.src"},
57 {"testdata/importdecl1a.src", "testdata/importdecl1b.src"},
58 {"testdata/importC.src"}, // special handling in checkFiles
59 {"testdata/cycles.src"},
60 {"testdata/cycles1.src"},
61 {"testdata/cycles2.src"},
62 {"testdata/cycles3.src"},
63 {"testdata/cycles4.src"},
64 {"testdata/init0.src"},
65 {"testdata/init1.src"},
66 {"testdata/init2.src"},
67 {"testdata/decls0.src"},
68 {"testdata/decls1.src"},
69 {"testdata/decls2a.src", "testdata/decls2b.src"},
70 {"testdata/decls3.src"},
71 {"testdata/decls4.src"},
72 {"testdata/const0.src"},
73 {"testdata/const1.src"},
74 {"testdata/constdecl.src"},
75 {"testdata/vardecl.src"},
76 {"testdata/expr0.src"},
77 {"testdata/expr1.src"},
78 {"testdata/expr2.src"},
79 {"testdata/expr3.src"},
80 {"testdata/methodsets.src"},
81 {"testdata/shifts.src"},
82 {"testdata/builtins.src"},
83 {"testdata/conversions.src"},
84 {"testdata/conversions2.src"},
85 {"testdata/stmt0.src"},
86 {"testdata/stmt1.src"},
87 {"testdata/gotos.src"},
88 {"testdata/labels.src"},
89 {"testdata/issues.src"},
90 {"testdata/blank.src"},
93 var fset = token.NewFileSet()
95 // Positioned errors are of the form filename:line:column: message .
96 var posMsgRx = regexp.MustCompile(`^(.*:[0-9]+:[0-9]+): *(.*)`)
98 // splitError splits an error's error message into a position string
99 // and the actual error message. If there's no position information,
100 // pos is the empty string, and msg is the entire error message.
102 func splitError(err error) (pos, msg string) {
103 msg = err.Error()
104 if m := posMsgRx.FindStringSubmatch(msg); len(m) == 3 {
105 pos = m[1]
106 msg = m[2]
108 return
111 func parseFiles(t *testing.T, filenames []string) ([]*ast.File, []error) {
112 var files []*ast.File
113 var errlist []error
114 for _, filename := range filenames {
115 file, err := parser.ParseFile(fset, filename, nil, parser.AllErrors)
116 if file == nil {
117 t.Fatalf("%s: %s", filename, err)
119 files = append(files, file)
120 if err != nil {
121 if list, _ := err.(scanner.ErrorList); len(list) > 0 {
122 for _, err := range list {
123 errlist = append(errlist, err)
125 } else {
126 errlist = append(errlist, err)
130 return files, errlist
133 // ERROR comments must start with text `ERROR "rx"` or `ERROR rx` where
134 // rx is a regular expression that matches the expected error message.
135 // Space around "rx" or rx is ignored. Use the form `ERROR HERE "rx"`
136 // for error messages that are located immediately after rather than
137 // at a token's position.
139 var errRx = regexp.MustCompile(`^ *ERROR *(HERE)? *"?([^"]*)"?`)
141 // errMap collects the regular expressions of ERROR comments found
142 // in files and returns them as a map of error positions to error messages.
144 func errMap(t *testing.T, testname string, files []*ast.File) map[string][]string {
145 // map of position strings to lists of error message patterns
146 errmap := make(map[string][]string)
148 for _, file := range files {
149 filename := fset.Position(file.Package).Filename
150 src, err := ioutil.ReadFile(filename)
151 if err != nil {
152 t.Fatalf("%s: could not read %s", testname, filename)
155 var s scanner.Scanner
156 s.Init(fset.AddFile(filename, -1, len(src)), src, nil, scanner.ScanComments)
157 var prev token.Pos // position of last non-comment, non-semicolon token
158 var here token.Pos // position immediately after the token at position prev
160 scanFile:
161 for {
162 pos, tok, lit := s.Scan()
163 switch tok {
164 case token.EOF:
165 break scanFile
166 case token.COMMENT:
167 if lit[1] == '*' {
168 lit = lit[:len(lit)-2] // strip trailing */
170 if s := errRx.FindStringSubmatch(lit[2:]); len(s) == 3 {
171 pos := prev
172 if s[1] == "HERE" {
173 pos = here
175 p := fset.Position(pos).String()
176 errmap[p] = append(errmap[p], strings.TrimSpace(s[2]))
178 case token.SEMICOLON:
179 // ignore automatically inserted semicolon
180 if lit == "\n" {
181 continue scanFile
183 fallthrough
184 default:
185 prev = pos
186 var l int // token length
187 if tok.IsLiteral() {
188 l = len(lit)
189 } else {
190 l = len(tok.String())
192 here = prev + token.Pos(l)
197 return errmap
200 func eliminate(t *testing.T, errmap map[string][]string, errlist []error) {
201 for _, err := range errlist {
202 pos, gotMsg := splitError(err)
203 list := errmap[pos]
204 index := -1 // list index of matching message, if any
205 // we expect one of the messages in list to match the error at pos
206 for i, wantRx := range list {
207 rx, err := regexp.Compile(wantRx)
208 if err != nil {
209 t.Errorf("%s: %v", pos, err)
210 continue
212 if rx.MatchString(gotMsg) {
213 index = i
214 break
217 if index >= 0 {
218 // eliminate from list
219 if n := len(list) - 1; n > 0 {
220 // not the last entry - swap in last element and shorten list by 1
221 list[index] = list[n]
222 errmap[pos] = list[:n]
223 } else {
224 // last entry - remove list from map
225 delete(errmap, pos)
227 } else {
228 t.Errorf("%s: no error expected: %q", pos, gotMsg)
233 func checkFiles(t *testing.T, testfiles []string) {
234 // parse files and collect parser errors
235 files, errlist := parseFiles(t, testfiles)
237 pkgName := "<no package>"
238 if len(files) > 0 {
239 pkgName = files[0].Name.Name
242 if *listErrors && len(errlist) > 0 {
243 t.Errorf("--- %s:", pkgName)
244 for _, err := range errlist {
245 t.Error(err)
249 // typecheck and collect typechecker errors
250 var conf Config
251 // special case for importC.src
252 if len(testfiles) == 1 && testfiles[0] == "testdata/importC.src" {
253 conf.FakeImportC = true
255 conf.Importer = importer.Default()
256 conf.Error = func(err error) {
257 if *listErrors {
258 t.Error(err)
259 return
261 // Ignore secondary error messages starting with "\t";
262 // they are clarifying messages for a primary error.
263 if !strings.Contains(err.Error(), ": \t") {
264 errlist = append(errlist, err)
267 conf.Check(pkgName, fset, files, nil)
269 if *listErrors {
270 return
273 // match and eliminate errors;
274 // we are expecting the following errors
275 errmap := errMap(t, pkgName, files)
276 eliminate(t, errmap, errlist)
278 // there should be no expected errors left
279 if len(errmap) > 0 {
280 t.Errorf("--- %s: %d source positions with expected (but not reported) errors:", pkgName, len(errmap))
281 for pos, list := range errmap {
282 for _, rx := range list {
283 t.Errorf("%s: %q", pos, rx)
289 func TestCheck(t *testing.T) {
290 testenv.MustHaveGoBuild(t)
292 // Declare builtins for testing.
293 DefPredeclaredTestFuncs()
295 // If explicit test files are specified, only check those.
296 if files := *testFiles; files != "" {
297 checkFiles(t, strings.Split(files, " "))
298 return
301 // Otherwise, run all the tests.
302 for _, files := range tests {
303 checkFiles(t, files)