github.com/yunabe/lgo@v0.0.0-20190709125917-42c42d410fdf/parser/parser_test.go (about)

     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  
   535  func TestLastLineComment(t *testing.T) {
   536  	const src = `package main
   537  type x int // comment
   538  `
   539  	fset := token.NewFileSet()
   540  	f, err := ParseFile(fset, "", src, ParseComments)
   541  	if err != nil {
   542  		t.Fatal(err)
   543  	}
   544  	comment := f.Decls[0].(*ast.GenDecl).Specs[0].(*ast.TypeSpec).Comment.List[0].Text
   545  	if comment != "// comment" {
   546  		t.Errorf("got %q, want %q", comment, "// comment")
   547  	}
   548  }