Black Lives Matter. Support the Equal Justice Initiative.

Source file src/go/parser/parser_test.go

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

View as plain text