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