Home | History | Annotate | Download | only in parser
      1 // Copyright 2009 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 parser
      6 
      7 import (
      8 	"bytes"
      9 	"fmt"
     10 	"go/ast"
     11 	"go/token"
     12 	"os"
     13 	"strings"
     14 	"testing"
     15 )
     16 
     17 var validFiles = []string{
     18 	"parser.go",
     19 	"parser_test.go",
     20 	"error_test.go",
     21 	"short_test.go",
     22 }
     23 
     24 func TestParse(t *testing.T) {
     25 	for _, filename := range validFiles {
     26 		_, err := ParseFile(token.NewFileSet(), filename, nil, DeclarationErrors)
     27 		if err != nil {
     28 			t.Fatalf("ParseFile(%s): %v", filename, err)
     29 		}
     30 	}
     31 }
     32 
     33 func nameFilter(filename string) bool {
     34 	switch filename {
     35 	case "parser.go", "interface.go", "parser_test.go":
     36 		return true
     37 	case "parser.go.orig":
     38 		return true // permit but should be ignored by ParseDir
     39 	}
     40 	return false
     41 }
     42 
     43 func dirFilter(f os.FileInfo) bool { return nameFilter(f.Name()) }
     44 
     45 func TestParseDir(t *testing.T) {
     46 	path := "."
     47 	pkgs, err := ParseDir(token.NewFileSet(), path, dirFilter, 0)
     48 	if err != nil {
     49 		t.Fatalf("ParseDir(%s): %v", path, err)
     50 	}
     51 	if n := len(pkgs); n != 1 {
     52 		t.Errorf("got %d packages; want 1", n)
     53 	}
     54 	pkg := pkgs["parser"]
     55 	if pkg == nil {
     56 		t.Errorf(`package "parser" not found`)
     57 		return
     58 	}
     59 	if n := len(pkg.Files); n != 3 {
     60 		t.Errorf("got %d package files; want 3", n)
     61 	}
     62 	for filename := range pkg.Files {
     63 		if !nameFilter(filename) {
     64 			t.Errorf("unexpected package file: %s", filename)
     65 		}
     66 	}
     67 }
     68 
     69 func TestParseExpr(t *testing.T) {
     70 	// just kicking the tires:
     71 	// a valid arithmetic expression
     72 	src := "a + b"
     73 	x, err := ParseExpr(src)
     74 	if err != nil {
     75 		t.Errorf("ParseExpr(%q): %v", src, err)
     76 	}
     77 	// sanity check
     78 	if _, ok := x.(*ast.BinaryExpr); !ok {
     79 		t.Errorf("ParseExpr(%q): got %T, want *ast.BinaryExpr", src, x)
     80 	}
     81 
     82 	// a valid type expression
     83 	src = "struct{x *int}"
     84 	x, err = ParseExpr(src)
     85 	if err != nil {
     86 		t.Errorf("ParseExpr(%q): %v", src, err)
     87 	}
     88 	// sanity check
     89 	if _, ok := x.(*ast.StructType); !ok {
     90 		t.Errorf("ParseExpr(%q): got %T, want *ast.StructType", src, x)
     91 	}
     92 
     93 	// an invalid expression
     94 	src = "a + *"
     95 	if _, err := ParseExpr(src); err == nil {
     96 		t.Errorf("ParseExpr(%q): got no error", src)
     97 	}
     98 
     99 	// a valid expression followed by extra tokens is invalid
    100 	src = "a[i] := x"
    101 	if _, err := ParseExpr(src); err == nil {
    102 		t.Errorf("ParseExpr(%q): got no error", src)
    103 	}
    104 
    105 	// a semicolon is not permitted unless automatically inserted
    106 	src = "a + b\n"
    107 	if _, err := ParseExpr(src); err != nil {
    108 		t.Errorf("ParseExpr(%q): got error %s", src, err)
    109 	}
    110 	src = "a + b;"
    111 	if _, err := ParseExpr(src); err == nil {
    112 		t.Errorf("ParseExpr(%q): got no error", src)
    113 	}
    114 
    115 	// various other stuff following a valid expression
    116 	const validExpr = "a + b"
    117 	const anything = "dh3*#D)#_"
    118 	for _, c := range "!)]};," {
    119 		src := validExpr + string(c) + anything
    120 		if _, err := ParseExpr(src); err == nil {
    121 			t.Errorf("ParseExpr(%q): got no error", src)
    122 		}
    123 	}
    124 
    125 	// ParseExpr must not crash
    126 	for _, src := range valids {
    127 		ParseExpr(src)
    128 	}
    129 }
    130 
    131 func TestColonEqualsScope(t *testing.T) {
    132 	f, err := ParseFile(token.NewFileSet(), "", `package p; func f() { x, y, z := x, y, z }`, 0)
    133 	if err != nil {
    134 		t.Fatal(err)
    135 	}
    136 
    137 	// RHS refers to undefined globals; LHS does not.
    138 	as := f.Decls[0].(*ast.FuncDecl).Body.List[0].(*ast.AssignStmt)
    139 	for _, v := range as.Rhs {
    140 		id := v.(*ast.Ident)
    141 		if id.Obj != nil {
    142 			t.Errorf("rhs %s has Obj, should not", id.Name)
    143 		}
    144 	}
    145 	for _, v := range as.Lhs {
    146 		id := v.(*ast.Ident)
    147 		if id.Obj == nil {
    148 			t.Errorf("lhs %s does not have Obj, should", id.Name)
    149 		}
    150 	}
    151 }
    152 
    153 func TestVarScope(t *testing.T) {
    154 	f, err := ParseFile(token.NewFileSet(), "", `package p; func f() { var x, y, z = x, y, z }`, 0)
    155 	if err != nil {
    156 		t.Fatal(err)
    157 	}
    158 
    159 	// RHS refers to undefined globals; LHS does not.
    160 	as := f.Decls[0].(*ast.FuncDecl).Body.List[0].(*ast.DeclStmt).Decl.(*ast.GenDecl).Specs[0].(*ast.ValueSpec)
    161 	for _, v := range as.Values {
    162 		id := v.(*ast.Ident)
    163 		if id.Obj != nil {
    164 			t.Errorf("rhs %s has Obj, should not", id.Name)
    165 		}
    166 	}
    167 	for _, id := range as.Names {
    168 		if id.Obj == nil {
    169 			t.Errorf("lhs %s does not have Obj, should", id.Name)
    170 		}
    171 	}
    172 }
    173 
    174 func TestObjects(t *testing.T) {
    175 	const src = `
    176 package p
    177 import fmt "fmt"
    178 const pi = 3.14
    179 type T struct{}
    180 var x int
    181 func f() { L: }
    182 `
    183 
    184 	f, err := ParseFile(token.NewFileSet(), "", src, 0)
    185 	if err != nil {
    186 		t.Fatal(err)
    187 	}
    188 
    189 	objects := map[string]ast.ObjKind{
    190 		"p":   ast.Bad, // not in a scope
    191 		"fmt": ast.Bad, // not resolved yet
    192 		"pi":  ast.Con,
    193 		"T":   ast.Typ,
    194 		"x":   ast.Var,
    195 		"int": ast.Bad, // not resolved yet
    196 		"f":   ast.Fun,
    197 		"L":   ast.Lbl,
    198 	}
    199 
    200 	ast.Inspect(f, func(n ast.Node) bool {
    201 		if ident, ok := n.(*ast.Ident); ok {
    202 			obj := ident.Obj
    203 			if obj == nil {
    204 				if objects[ident.Name] != ast.Bad {
    205 					t.Errorf("no object for %s", ident.Name)
    206 				}
    207 				return true
    208 			}
    209 			if obj.Name != ident.Name {
    210 				t.Errorf("names don't match: obj.Name = %s, ident.Name = %s", obj.Name, ident.Name)
    211 			}
    212 			kind := objects[ident.Name]
    213 			if obj.Kind != kind {
    214 				t.Errorf("%s: obj.Kind = %s; want %s", ident.Name, obj.Kind, kind)
    215 			}
    216 		}
    217 		return true
    218 	})
    219 }
    220 
    221 func TestUnresolved(t *testing.T) {
    222 	f, err := ParseFile(token.NewFileSet(), "", `
    223 package p
    224 //
    225 func f1a(int)
    226 func f2a(byte, int, float)
    227 func f3a(a, b int, c float)
    228 func f4a(...complex)
    229 func f5a(a s1a, b ...complex)
    230 //
    231 func f1b(*int)
    232 func f2b([]byte, (int), *float)
    233 func f3b(a, b *int, c []float)
    234 func f4b(...*complex)
    235 func f5b(a s1a, b ...[]complex)
    236 //
    237 type s1a struct { int }
    238 type s2a struct { byte; int; s1a }
    239 type s3a struct { a, b int; c float }
    240 //
    241 type s1b struct { *int }
    242 type s2b struct { byte; int; *float }
    243 type s3b struct { a, b *s3b; c []float }
    244 `, 0)
    245 	if err != nil {
    246 		t.Fatal(err)
    247 	}
    248 
    249 	want := "int " + // f1a
    250 		"byte int float " + // f2a
    251 		"int float " + // f3a
    252 		"complex " + // f4a
    253 		"complex " + // f5a
    254 		//
    255 		"int " + // f1b
    256 		"byte int float " + // f2b
    257 		"int float " + // f3b
    258 		"complex " + // f4b
    259 		"complex " + // f5b
    260 		//
    261 		"int " + // s1a
    262 		"byte int " + // s2a
    263 		"int float " + // s3a
    264 		//
    265 		"int " + // s1a
    266 		"byte int float " + // s2a
    267 		"float " // s3a
    268 
    269 	// collect unresolved identifiers
    270 	var buf bytes.Buffer
    271 	for _, u := range f.Unresolved {
    272 		buf.WriteString(u.Name)
    273 		buf.WriteByte(' ')
    274 	}
    275 	got := buf.String()
    276 
    277 	if got != want {
    278 		t.Errorf("\ngot:  %s\nwant: %s", got, want)
    279 	}
    280 }
    281 
    282 var imports = map[string]bool{
    283 	`"a"`:        true,
    284 	"`a`":        true,
    285 	`"a/b"`:      true,
    286 	`"a.b"`:      true,
    287 	`"m\x61th"`:  true,
    288 	`"greek/"`: true,
    289 	`""`:         false,
    290 
    291 	// Each of these pairs tests both `` vs "" strings
    292 	// and also use of invalid characters spelled out as
    293 	// escape sequences and written directly.
    294 	// For example `"\x00"` tests import "\x00"
    295 	// while "`\x00`" tests import `<actual-NUL-byte>`.
    296 	`"\x00"`:     false,
    297 	"`\x00`":     false,
    298 	`"\x7f"`:     false,
    299 	"`\x7f`":     false,
    300 	`"a!"`:       false,
    301 	"`a!`":       false,
    302 	`"a b"`:      false,
    303 	"`a b`":      false,
    304 	`"a\\b"`:     false,
    305 	"`a\\b`":     false,
    306 	"\"`a`\"":    false,
    307 	"`\"a\"`":    false,
    308 	`"\x80\x80"`: false,
    309 	"`\x80\x80`": false,
    310 	`"\xFFFD"`:   false,
    311 	"`\xFFFD`":   false,
    312 }
    313 
    314 func TestImports(t *testing.T) {
    315 	for path, isValid := range imports {
    316 		src := fmt.Sprintf("package p; import %s", path)
    317 		_, err := ParseFile(token.NewFileSet(), "", src, 0)
    318 		switch {
    319 		case err != nil && isValid:
    320 			t.Errorf("ParseFile(%s): got %v; expected no error", src, err)
    321 		case err == nil && !isValid:
    322 			t.Errorf("ParseFile(%s): got no error; expected one", src)
    323 		}
    324 	}
    325 }
    326 
    327 func TestCommentGroups(t *testing.T) {
    328 	f, err := ParseFile(token.NewFileSet(), "", `
    329 package p /* 1a */ /* 1b */      /* 1c */ // 1d
    330 /* 2a
    331 */
    332 // 2b
    333 const pi = 3.1415
    334 /* 3a */ // 3b
    335 /* 3c */ const e = 2.7182
    336 
    337 // Example from issue 3139
    338 func ExampleCount() {
    339 	fmt.Println(strings.Count("cheese", "e"))
    340 	fmt.Println(strings.Count("five", "")) // before & after each rune
    341 	// Output:
    342 	// 3
    343 	// 5
    344 }
    345 `, ParseComments)
    346 	if err != nil {
    347 		t.Fatal(err)
    348 	}
    349 	expected := [][]string{
    350 		{"/* 1a */", "/* 1b */", "/* 1c */", "// 1d"},
    351 		{"/* 2a\n*/", "// 2b"},
    352 		{"/* 3a */", "// 3b", "/* 3c */"},
    353 		{"// Example from issue 3139"},
    354 		{"// before & after each rune"},
    355 		{"// Output:", "// 3", "// 5"},
    356 	}
    357 	if len(f.Comments) != len(expected) {
    358 		t.Fatalf("got %d comment groups; expected %d", len(f.Comments), len(expected))
    359 	}
    360 	for i, exp := range expected {
    361 		got := f.Comments[i].List
    362 		if len(got) != len(exp) {
    363 			t.Errorf("got %d comments in group %d; expected %d", len(got), i, len(exp))
    364 			continue
    365 		}
    366 		for j, exp := range exp {
    367 			got := got[j].Text
    368 			if got != exp {
    369 				t.Errorf("got %q in group %d; expected %q", got, i, exp)
    370 			}
    371 		}
    372 	}
    373 }
    374 
    375 func getField(file *ast.File, fieldname string) *ast.Field {
    376 	parts := strings.Split(fieldname, ".")
    377 	for _, d := range file.Decls {
    378 		if d, ok := d.(*ast.GenDecl); ok && d.Tok == token.TYPE {
    379 			for _, s := range d.Specs {
    380 				if s, ok := s.(*ast.TypeSpec); ok && s.Name.Name == parts[0] {
    381 					if s, ok := s.Type.(*ast.StructType); ok {
    382 						for _, f := range s.Fields.List {
    383 							for _, name := range f.Names {
    384 								if name.Name == parts[1] {
    385 									return f
    386 								}
    387 							}
    388 						}
    389 					}
    390 				}
    391 			}
    392 		}
    393 	}
    394 	return nil
    395 }
    396 
    397 // Don't use ast.CommentGroup.Text() - we want to see exact comment text.
    398 func commentText(c *ast.CommentGroup) string {
    399 	var buf bytes.Buffer
    400 	if c != nil {
    401 		for _, c := range c.List {
    402 			buf.WriteString(c.Text)
    403 		}
    404 	}
    405 	return buf.String()
    406 }
    407 
    408 func checkFieldComments(t *testing.T, file *ast.File, fieldname, lead, line string) {
    409 	f := getField(file, fieldname)
    410 	if f == nil {
    411 		t.Fatalf("field not found: %s", fieldname)
    412 	}
    413 	if got := commentText(f.Doc); got != lead {
    414 		t.Errorf("got lead comment %q; expected %q", got, lead)
    415 	}
    416 	if got := commentText(f.Comment); got != line {
    417 		t.Errorf("got line comment %q; expected %q", got, line)
    418 	}
    419 }
    420 
    421 func TestLeadAndLineComments(t *testing.T) {
    422 	f, err := ParseFile(token.NewFileSet(), "", `
    423 package p
    424 type T struct {
    425 	/* F1 lead comment */
    426 	//
    427 	F1 int  /* F1 */ // line comment
    428 	// F2 lead
    429 	// comment
    430 	F2 int  // F2 line comment
    431 	// f3 lead comment
    432 	f3 int  // f3 line comment
    433 }
    434 `, ParseComments)
    435 	if err != nil {
    436 		t.Fatal(err)
    437 	}
    438 	checkFieldComments(t, f, "T.F1", "/* F1 lead comment *///", "/* F1 */// line comment")
    439 	checkFieldComments(t, f, "T.F2", "// F2 lead// comment", "// F2 line comment")
    440 	checkFieldComments(t, f, "T.f3", "// f3 lead comment", "// f3 line comment")
    441 	ast.FileExports(f)
    442 	checkFieldComments(t, f, "T.F1", "/* F1 lead comment *///", "/* F1 */// line comment")
    443 	checkFieldComments(t, f, "T.F2", "// F2 lead// comment", "// F2 line comment")
    444 	if getField(f, "T.f3") != nil {
    445 		t.Error("not expected to find T.f3")
    446 	}
    447 }
    448 
    449 // TestIssue9979 verifies that empty statements are contained within their enclosing blocks.
    450 func TestIssue9979(t *testing.T) {
    451 	for _, src := range []string{
    452 		"package p; func f() {;}",
    453 		"package p; func f() {L:}",
    454 		"package p; func f() {L:;}",
    455 		"package p; func f() {L:\n}",
    456 		"package p; func f() {L:\n;}",
    457 		"package p; func f() { ; }",
    458 		"package p; func f() { L: }",
    459 		"package p; func f() { L: ; }",
    460 		"package p; func f() { L: \n}",
    461 		"package p; func f() { L: \n; }",
    462 	} {
    463 		fset := token.NewFileSet()
    464 		f, err := ParseFile(fset, "", src, 0)
    465 		if err != nil {
    466 			t.Fatal(err)
    467 		}
    468 
    469 		var pos, end token.Pos
    470 		ast.Inspect(f, func(x ast.Node) bool {
    471 			switch s := x.(type) {
    472 			case *ast.BlockStmt:
    473 				pos, end = s.Pos()+1, s.End()-1 // exclude "{", "}"
    474 			case *ast.LabeledStmt:
    475 				pos, end = s.Pos()+2, s.End() // exclude "L:"
    476 			case *ast.EmptyStmt:
    477 				// check containment
    478 				if s.Pos() < pos || s.End() > end {
    479 					t.Errorf("%s: %T[%d, %d] not inside [%d, %d]", src, s, s.Pos(), s.End(), pos, end)
    480 				}
    481 				// check semicolon
    482 				offs := fset.Position(s.Pos()).Offset
    483 				if ch := src[offs]; ch != ';' != s.Implicit {
    484 					want := "want ';'"
    485 					if s.Implicit {
    486 						want = "but ';' is implicit"
    487 					}
    488 					t.Errorf("%s: found %q at offset %d; %s", src, ch, offs, want)
    489 				}
    490 			}
    491 			return true
    492 		})
    493 	}
    494 }
    495 
    496 // TestIncompleteSelection ensures that an incomplete selector
    497 // expression is parsed as a (blank) *ast.SelectorExpr, not a
    498 // *ast.BadExpr.
    499 func TestIncompleteSelection(t *testing.T) {
    500 	for _, src := range []string{
    501 		"package p; var _ = fmt.",             // at EOF
    502 		"package p; var _ = fmt.\ntype X int", // not at EOF
    503 	} {
    504 		fset := token.NewFileSet()
    505 		f, err := ParseFile(fset, "", src, 0)
    506 		if err == nil {
    507 			t.Errorf("ParseFile(%s) succeeded unexpectedly", src)
    508 			continue
    509 		}
    510 
    511 		const wantErr = "expected selector or type assertion"
    512 		if !strings.Contains(err.Error(), wantErr) {
    513 			t.Errorf("ParseFile returned wrong error %q, want %q", err, wantErr)
    514 		}
    515 
    516 		var sel *ast.SelectorExpr
    517 		ast.Inspect(f, func(n ast.Node) bool {
    518 			if n, ok := n.(*ast.SelectorExpr); ok {
    519 				sel = n
    520 			}
    521 			return true
    522 		})
    523 		if sel == nil {
    524 			t.Error("found no *ast.SelectorExpr")
    525 			continue
    526 		}
    527 		const wantSel = "&{fmt _}"
    528 		if fmt.Sprint(sel) != wantSel {
    529 			t.Errorf("found selector %s, want %s", sel, wantSel)
    530 			continue
    531 		}
    532 	}
    533 }
    534