]> Cypherpunks.ru repositories - gostls13.git/blob - src/cmd/compile/internal/types2/check_test.go
[dev.typeparams] all: merge dev.regabi (d1d1099) into dev.typeparams
[gostls13.git] / src / cmd / compile / internal / types2 / check_test.go
1 // +build TODO_RSC_REMOVE_THIS
2
3 // UNREVIEWED
4 // Copyright 2011 The Go Authors. All rights reserved.
5 // Use of this source code is governed by a BSD-style
6 // license that can be found in the LICENSE file.
7
8 // This file implements a typechecker test harness. The packages specified
9 // in tests are typechecked. Error messages reported by the typechecker are
10 // compared against the error messages expected in the test files.
11 //
12 // Expected errors are indicated in the test files by putting a comment
13 // of the form /* ERROR "rx" */ immediately following an offending token.
14 // The harness will verify that an error matching the regular expression
15 // rx is reported at that source position. Consecutive comments may be
16 // used to indicate multiple errors for the same token position.
17 //
18 // For instance, the following test file indicates that a "not declared"
19 // error should be reported for the undeclared variable x:
20 //
21 //      package p
22 //      func f() {
23 //              _ = x /* ERROR "not declared" */ + 1
24 //      }
25
26 // TODO(gri) Also collect strict mode errors of the form /* STRICT ... */
27 //           and test against strict mode.
28
29 package types2_test
30
31 import (
32         "cmd/compile/internal/syntax"
33         "flag"
34         "fmt"
35         "internal/testenv"
36         "io/ioutil"
37         "os"
38         "path/filepath"
39         "regexp"
40         "strings"
41         "testing"
42
43         . "cmd/compile/internal/types2"
44 )
45
46 var (
47         haltOnError = flag.Bool("halt", false, "halt on error")
48         listErrors  = flag.Bool("errlist", false, "list errors")
49         testFiles   = flag.String("files", "", "space-separated list of test files")
50 )
51
52 func parseFiles(t *testing.T, filenames []string) ([]*syntax.File, []error) {
53         var files []*syntax.File
54         var errlist []error
55         errh := func(err error) { errlist = append(errlist, err) }
56         for _, filename := range filenames {
57                 file, err := syntax.ParseFile(filename, errh, nil, syntax.AllowGenerics)
58                 if file == nil {
59                         t.Fatalf("%s: %s", filename, err)
60                 }
61                 files = append(files, file)
62         }
63         return files, errlist
64 }
65
66 func unpackError(err error) syntax.Error {
67         switch err := err.(type) {
68         case syntax.Error:
69                 return err
70         case Error:
71                 return syntax.Error{Pos: err.Pos, Msg: err.Msg}
72         default:
73                 return syntax.Error{Msg: err.Error()}
74         }
75 }
76
77 func delta(x, y uint) uint {
78         switch {
79         case x < y:
80                 return y - x
81         case x > y:
82                 return x - y
83         default:
84                 return 0
85         }
86 }
87
88 func checkFiles(t *testing.T, sources []string, colDelta uint, trace bool) {
89         // parse files and collect parser errors
90         files, errlist := parseFiles(t, sources)
91
92         pkgName := "<no package>"
93         if len(files) > 0 {
94                 pkgName = files[0].PkgName.Value
95         }
96
97         if *listErrors && len(errlist) > 0 {
98                 t.Errorf("--- %s:", pkgName)
99                 for _, err := range errlist {
100                         t.Error(err)
101                 }
102         }
103
104         // typecheck and collect typechecker errors
105         var conf Config
106         conf.AcceptMethodTypeParams = true
107         conf.InferFromConstraints = true
108         // special case for importC.src
109         if len(sources) == 1 && strings.HasSuffix(sources[0], "importC.src") {
110                 conf.FakeImportC = true
111         }
112         conf.Trace = trace
113         conf.Importer = defaultImporter()
114         conf.Error = func(err error) {
115                 if *haltOnError {
116                         defer panic(err)
117                 }
118                 if *listErrors {
119                         t.Error(err)
120                         return
121                 }
122                 // Ignore secondary error messages starting with "\t";
123                 // they are clarifying messages for a primary error.
124                 if !strings.Contains(err.Error(), ": \t") {
125                         errlist = append(errlist, err)
126                 }
127         }
128         conf.Check(pkgName, files, nil)
129
130         if *listErrors {
131                 return
132         }
133
134         // collect expected errors
135         errmap := make(map[string]map[uint][]syntax.Error)
136         for _, filename := range sources {
137                 f, err := os.Open(filename)
138                 if err != nil {
139                         t.Error(err)
140                         continue
141                 }
142                 if m := syntax.ErrorMap(f); len(m) > 0 {
143                         errmap[filename] = m
144                 }
145                 f.Close()
146         }
147
148         // match against found errors
149         for _, err := range errlist {
150                 got := unpackError(err)
151
152                 // find list of errors for the respective error line
153                 filename := got.Pos.Base().Filename()
154                 filemap := errmap[filename]
155                 var line uint
156                 var list []syntax.Error
157                 if filemap != nil {
158                         line = got.Pos.Line()
159                         list = filemap[line]
160                 }
161                 // list may be nil
162
163                 // one of errors in list should match the current error
164                 index := -1 // list index of matching message, if any
165                 for i, want := range list {
166                         rx, err := regexp.Compile(want.Msg)
167                         if err != nil {
168                                 t.Errorf("%s:%d:%d: %v", filename, line, want.Pos.Col(), err)
169                                 continue
170                         }
171                         if rx.MatchString(got.Msg) {
172                                 index = i
173                                 break
174                         }
175                 }
176                 if index < 0 {
177                         t.Errorf("%s: no error expected: %q", got.Pos, got.Msg)
178                         continue
179                 }
180
181                 // column position must be within expected colDelta
182                 want := list[index]
183                 if delta(got.Pos.Col(), want.Pos.Col()) > colDelta {
184                         t.Errorf("%s: got col = %d; want %d", got.Pos, got.Pos.Col(), want.Pos.Col())
185                 }
186
187                 // eliminate from list
188                 if n := len(list) - 1; n > 0 {
189                         // not the last entry - swap in last element and shorten list by 1
190                         list[index] = list[n]
191                         filemap[line] = list[:n]
192                 } else {
193                         // last entry - remove list from filemap
194                         delete(filemap, line)
195                 }
196
197                 // if filemap is empty, eliminate from errmap
198                 if len(filemap) == 0 {
199                         delete(errmap, filename)
200                 }
201         }
202
203         // there should be no expected errors left
204         if len(errmap) > 0 {
205                 t.Errorf("--- %s: unreported errors:", pkgName)
206                 for filename, filemap := range errmap {
207                         for line, list := range filemap {
208                                 for _, err := range list {
209                                         t.Errorf("%s:%d:%d: %s", filename, line, err.Pos.Col(), err.Msg)
210                                 }
211                         }
212                 }
213         }
214 }
215
216 // TestCheck is for manual testing of selected input files, provided with -files.
217 func TestCheck(t *testing.T) {
218         if *testFiles == "" {
219                 return
220         }
221         testenv.MustHaveGoBuild(t)
222         DefPredeclaredTestFuncs()
223         checkFiles(t, strings.Split(*testFiles, " "), 0, testing.Verbose())
224 }
225
226 func TestTestdata(t *testing.T)  { DefPredeclaredTestFuncs(); testDir(t, 75, "testdata") } // TODO(gri) narrow column tolerance
227 func TestExamples(t *testing.T)  { testDir(t, 0, "examples") }
228 func TestFixedbugs(t *testing.T) { testDir(t, 0, "fixedbugs") }
229
230 func testDir(t *testing.T, colDelta uint, dir string) {
231         testenv.MustHaveGoBuild(t)
232
233         fis, err := ioutil.ReadDir(dir)
234         if err != nil {
235                 t.Error(err)
236                 return
237         }
238
239         for count, fi := range fis {
240                 path := filepath.Join(dir, fi.Name())
241
242                 // if fi is a directory, its files make up a single package
243                 if fi.IsDir() {
244                         if testing.Verbose() {
245                                 fmt.Printf("%3d %s\n", count, path)
246                         }
247                         fis, err := ioutil.ReadDir(path)
248                         if err != nil {
249                                 t.Error(err)
250                                 continue
251                         }
252                         files := make([]string, len(fis))
253                         for i, fi := range fis {
254                                 // if fi is a directory, checkFiles below will complain
255                                 files[i] = filepath.Join(path, fi.Name())
256                                 if testing.Verbose() {
257                                         fmt.Printf("\t%s\n", files[i])
258                                 }
259                         }
260                         checkFiles(t, files, colDelta, false)
261                         continue
262                 }
263
264                 // otherwise, fi is a stand-alone file
265                 if testing.Verbose() {
266                         fmt.Printf("%3d %s\n", count, path)
267                 }
268                 checkFiles(t, []string{path}, colDelta, false)
269         }
270 }