github.com/corona10/go@v0.0.0-20180224231303-7a218942be57/src/cmd/compile/internal/syntax/parser_test.go (about) 1 // Copyright 2016 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. 4 5 package syntax 6 7 import ( 8 "bytes" 9 "cmd/internal/src" 10 "flag" 11 "fmt" 12 "io/ioutil" 13 "path/filepath" 14 "runtime" 15 "strings" 16 "sync" 17 "testing" 18 "time" 19 ) 20 21 var fast = flag.Bool("fast", false, "parse package files in parallel") 22 var src_ = flag.String("src", "parser.go", "source file to parse") 23 var verify = flag.Bool("verify", false, "verify idempotent printing") 24 25 func TestParse(t *testing.T) { 26 ParseFile(*src_, func(err error) { t.Error(err) }, nil, 0) 27 } 28 29 func TestStdLib(t *testing.T) { 30 if testing.Short() { 31 t.Skip("skipping test in short mode") 32 } 33 34 var m1 runtime.MemStats 35 runtime.ReadMemStats(&m1) 36 start := time.Now() 37 38 type parseResult struct { 39 filename string 40 lines uint 41 } 42 43 results := make(chan parseResult) 44 go func() { 45 defer close(results) 46 for _, dir := range []string{ 47 runtime.GOROOT(), 48 } { 49 walkDirs(t, dir, func(filename string) { 50 if debug { 51 fmt.Printf("parsing %s\n", filename) 52 } 53 ast, err := ParseFile(filename, nil, nil, 0) 54 if err != nil { 55 t.Error(err) 56 return 57 } 58 if *verify { 59 verifyPrint(filename, ast) 60 } 61 results <- parseResult{filename, ast.Lines} 62 }) 63 } 64 }() 65 66 var count, lines uint 67 for res := range results { 68 count++ 69 lines += res.lines 70 if testing.Verbose() { 71 fmt.Printf("%5d %s (%d lines)\n", count, res.filename, res.lines) 72 } 73 } 74 75 dt := time.Since(start) 76 var m2 runtime.MemStats 77 runtime.ReadMemStats(&m2) 78 dm := float64(m2.TotalAlloc-m1.TotalAlloc) / 1e6 79 80 fmt.Printf("parsed %d lines (%d files) in %v (%d lines/s)\n", lines, count, dt, int64(float64(lines)/dt.Seconds())) 81 fmt.Printf("allocated %.3fMb (%.3fMb/s)\n", dm, dm/dt.Seconds()) 82 } 83 84 func walkDirs(t *testing.T, dir string, action func(string)) { 85 fis, err := ioutil.ReadDir(dir) 86 if err != nil { 87 t.Error(err) 88 return 89 } 90 91 var files, dirs []string 92 for _, fi := range fis { 93 if fi.Mode().IsRegular() { 94 if strings.HasSuffix(fi.Name(), ".go") { 95 path := filepath.Join(dir, fi.Name()) 96 files = append(files, path) 97 } 98 } else if fi.IsDir() && fi.Name() != "testdata" { 99 path := filepath.Join(dir, fi.Name()) 100 if !strings.HasSuffix(path, "/test") { 101 dirs = append(dirs, path) 102 } 103 } 104 } 105 106 if *fast { 107 var wg sync.WaitGroup 108 wg.Add(len(files)) 109 for _, filename := range files { 110 go func(filename string) { 111 defer wg.Done() 112 action(filename) 113 }(filename) 114 } 115 wg.Wait() 116 } else { 117 for _, filename := range files { 118 action(filename) 119 } 120 } 121 122 for _, dir := range dirs { 123 walkDirs(t, dir, action) 124 } 125 } 126 127 func verifyPrint(filename string, ast1 *File) { 128 var buf1 bytes.Buffer 129 _, err := Fprint(&buf1, ast1, true) 130 if err != nil { 131 panic(err) 132 } 133 134 ast2, err := Parse(src.NewFileBase(filename, filename), &buf1, nil, nil, nil, 0) 135 if err != nil { 136 panic(err) 137 } 138 139 var buf2 bytes.Buffer 140 _, err = Fprint(&buf2, ast2, true) 141 if err != nil { 142 panic(err) 143 } 144 145 if bytes.Compare(buf1.Bytes(), buf2.Bytes()) != 0 { 146 fmt.Printf("--- %s ---\n", filename) 147 fmt.Printf("%s\n", buf1.Bytes()) 148 fmt.Println() 149 150 fmt.Printf("--- %s ---\n", filename) 151 fmt.Printf("%s\n", buf2.Bytes()) 152 fmt.Println() 153 panic("not equal") 154 } 155 } 156 157 func TestIssue17697(t *testing.T) { 158 _, err := Parse(nil, bytes.NewReader(nil), nil, nil, nil, 0) // return with parser error, don't panic 159 if err == nil { 160 t.Errorf("no error reported") 161 } 162 } 163 164 func TestParseFile(t *testing.T) { 165 _, err := ParseFile("", nil, nil, 0) 166 if err == nil { 167 t.Error("missing io error") 168 } 169 170 var first error 171 _, err = ParseFile("", func(err error) { 172 if first == nil { 173 first = err 174 } 175 }, nil, 0) 176 if err == nil || first == nil { 177 t.Error("missing io error") 178 } 179 if err != first { 180 t.Errorf("got %v; want first error %v", err, first) 181 } 182 } 183 184 func TestLineDirectives(t *testing.T) { 185 // valid line directives lead to a syntax error after them 186 const valid = "syntax error: package statement must be first" 187 188 for _, test := range []struct { 189 src, msg string 190 filename string 191 line, col uint // 0-based 192 }{ 193 // ignored //line directives 194 {"//\n", valid, "", 2 - linebase, 0}, // no directive 195 {"//line\n", valid, "", 2 - linebase, 0}, // missing colon 196 {"//line foo\n", valid, "", 2 - linebase, 0}, // missing colon 197 {" //line foo:\n", valid, "", 2 - linebase, 0}, // not a line start 198 {"// line foo:\n", valid, "", 2 - linebase, 0}, // space between // and line 199 200 // invalid //line directives with one colon 201 {"//line :\n", "invalid line number: ", "", 0, 8}, 202 {"//line :x\n", "invalid line number: x", "", 0, 8}, 203 {"//line foo :\n", "invalid line number: ", "", 0, 12}, 204 {"//line foo:x\n", "invalid line number: x", "", 0, 11}, 205 {"//line foo:0\n", "invalid line number: 0", "", 0, 11}, 206 {"//line foo:1 \n", "invalid line number: 1 ", "", 0, 11}, 207 {"//line foo:-12\n", "invalid line number: -12", "", 0, 11}, 208 {"//line C:foo:0\n", "invalid line number: 0", "", 0, 13}, 209 {fmt.Sprintf("//line foo:%d\n", lineMax+1), fmt.Sprintf("invalid line number: %d", lineMax+1), "", 0, 11}, 210 211 // invalid //line directives with two colons 212 {"//line ::\n", "invalid line number: ", "", 0, 9}, 213 {"//line ::x\n", "invalid line number: x", "", 0, 9}, 214 {"//line foo::123abc\n", "invalid line number: 123abc", "", 0, 12}, 215 {"//line foo::0\n", "invalid line number: 0", "", 0, 12}, 216 {"//line foo:0:1\n", "invalid line number: 0", "", 0, 11}, 217 218 {"//line :123:0\n", "invalid column number: 0", "", 0, 12}, 219 {"//line foo:123:0\n", "invalid column number: 0", "", 0, 15}, 220 221 // effect of valid //line directives on positions 222 {"//line foo:123\n foo", valid, "foo", 123 - linebase, 3}, 223 {"//line foo:123\n foo", valid, " foo", 123 - linebase, 3}, 224 {"//line foo:123\n//line bar:345\nfoo", valid, "bar", 345 - linebase, 0}, 225 {"//line C:foo:123\n", valid, "C:foo", 123 - linebase, 0}, 226 {"//line " + runtime.GOROOT() + "/src/a/a.go:123\n foo", valid, "$GOROOT/src/a/a.go", 123 - linebase, 3}, 227 {"//line :x:1\n", valid, ":x", 0, 0}, 228 {"//line foo ::1\n", valid, "foo :", 0, 0}, 229 {"//line foo:123abc:1\n", valid, "foo:123abc", 0, 0}, 230 {"//line foo :123:1\n", valid, "foo ", 123 - linebase, 0}, 231 {"//line ::123\n", valid, ":", 123 - linebase, 0}, 232 233 // TODO(gri) add tests to verify correct column changes, once implemented 234 235 // ignored /*line directives 236 {"/**/", valid, "", 1 - linebase, 4}, // no directive 237 {"/*line*/", valid, "", 1 - linebase, 8}, // missing colon 238 {"/*line foo*/", valid, "", 1 - linebase, 12}, // missing colon 239 {" //line foo:*/", valid, "", 1 - linebase, 15}, // not a line start 240 {"/* line foo:*/", valid, "", 1 - linebase, 15}, // space between // and line 241 242 // invalid /*line directives with one colon 243 {"/*line :*/", "invalid line number: ", "", 0, 8}, 244 {"/*line :x*/", "invalid line number: x", "", 0, 8}, 245 {"/*line foo :*/", "invalid line number: ", "", 0, 12}, 246 {"/*line foo:x*/", "invalid line number: x", "", 0, 11}, 247 {"/*line foo:0*/", "invalid line number: 0", "", 0, 11}, 248 {"/*line foo:1 */", "invalid line number: 1 ", "", 0, 11}, 249 {"/*line C:foo:0*/", "invalid line number: 0", "", 0, 13}, 250 {fmt.Sprintf("/*line foo:%d*/", lineMax+1), fmt.Sprintf("invalid line number: %d", lineMax+1), "", 0, 11}, 251 252 // invalid /*line directives with two colons 253 {"/*line ::*/", "invalid line number: ", "", 0, 9}, 254 {"/*line ::x*/", "invalid line number: x", "", 0, 9}, 255 {"/*line foo::123abc*/", "invalid line number: 123abc", "", 0, 12}, 256 {"/*line foo::0*/", "invalid line number: 0", "", 0, 12}, 257 {"/*line foo:0:1*/", "invalid line number: 0", "", 0, 11}, 258 259 {"/*line :123:0*/", "invalid column number: 0", "", 0, 12}, 260 {"/*line foo:123:0*/", "invalid column number: 0", "", 0, 15}, 261 262 // effect of valid /*line directives on positions 263 // TODO(gri) remove \n after directives once line number is computed correctly 264 {"/*line foo:123*/\n foo", valid, "foo", 123 - linebase, 3}, 265 {"/*line foo:123*/\n//line bar:345\nfoo", valid, "bar", 345 - linebase, 0}, 266 {"/*line C:foo:123*/\n", valid, "C:foo", 123 - linebase, 0}, 267 {"/*line " + runtime.GOROOT() + "/src/a/a.go:123*/\n foo", valid, "$GOROOT/src/a/a.go", 123 - linebase, 3}, 268 {"/*line :x:1*/\n", valid, ":x", 1 - linebase, 0}, 269 {"/*line foo ::1*/\n", valid, "foo :", 1 - linebase, 0}, 270 {"/*line foo:123abc:1*/\n", valid, "foo:123abc", 1 - linebase, 0}, 271 {"/*line foo :123:1*/\n", valid, "foo ", 123 - linebase, 0}, 272 {"/*line ::123*/\n", valid, ":", 123 - linebase, 0}, 273 274 // test effect of /*line directive on (relative) position information for this line 275 // TODO(gri) add these tests 276 277 // TODO(gri) add tests to verify correct column changes, once implemented 278 } { 279 fileh := func(name string) string { 280 if strings.HasPrefix(name, runtime.GOROOT()) { 281 return "$GOROOT" + name[len(runtime.GOROOT()):] 282 } 283 return name 284 } 285 _, err := Parse(nil, strings.NewReader(test.src), nil, nil, fileh, 0) 286 if err == nil { 287 t.Errorf("%s: no error reported", test.src) 288 continue 289 } 290 perr, ok := err.(Error) 291 if !ok { 292 t.Errorf("%s: got %v; want parser error", test.src, err) 293 continue 294 } 295 if msg := perr.Msg; msg != test.msg { 296 t.Errorf("%s: got msg = %q; want %q", test.src, msg, test.msg) 297 } 298 if filename := perr.Pos.AbsFilename(); filename != test.filename { 299 t.Errorf("%s: got filename = %q; want %q", test.src, filename, test.filename) 300 } 301 if line := perr.Pos.RelLine(); line-linebase != test.line { 302 t.Errorf("%s: got line = %d; want %d", test.src, line-linebase, test.line) 303 } 304 if col := perr.Pos.Col(); col-colbase != test.col { 305 t.Errorf("%s: got col = %d; want %d", test.src, col-colbase, test.col) 306 } 307 } 308 }