github.com/xushiwei/go@v0.0.0-20130601165731-2b9d83f45bc9/src/pkg/go/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 fset = token.NewFileSet()
    18  
    19  var validFiles = []string{
    20  	"parser.go",
    21  	"parser_test.go",
    22  	"error_test.go",
    23  	"short_test.go",
    24  }
    25  
    26  func TestParse(t *testing.T) {
    27  	for _, filename := range validFiles {
    28  		_, err := ParseFile(fset, filename, nil, DeclarationErrors)
    29  		if err != nil {
    30  			t.Fatalf("ParseFile(%s): %v", filename, err)
    31  		}
    32  	}
    33  }
    34  
    35  func nameFilter(filename string) bool {
    36  	switch filename {
    37  	case "parser.go":
    38  	case "interface.go":
    39  	case "parser_test.go":
    40  	default:
    41  		return false
    42  	}
    43  	return true
    44  }
    45  
    46  func dirFilter(f os.FileInfo) bool { return nameFilter(f.Name()) }
    47  
    48  func TestParseDir(t *testing.T) {
    49  	path := "."
    50  	pkgs, err := ParseDir(fset, path, dirFilter, 0)
    51  	if err != nil {
    52  		t.Fatalf("ParseDir(%s): %v", path, err)
    53  	}
    54  	if len(pkgs) != 1 {
    55  		t.Errorf("incorrect number of packages: %d", len(pkgs))
    56  	}
    57  	pkg := pkgs["parser"]
    58  	if pkg == nil {
    59  		t.Errorf(`package "parser" not found`)
    60  		return
    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.Fatalf("ParseExpr(%s): %v", src, err)
    76  	}
    77  	// sanity check
    78  	if _, ok := x.(*ast.BinaryExpr); !ok {
    79  		t.Errorf("ParseExpr(%s): got %T, expected *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.Fatalf("ParseExpr(%s): %v", src, err)
    87  	}
    88  	// sanity check
    89  	if _, ok := x.(*ast.StructType); !ok {
    90  		t.Errorf("ParseExpr(%s): got %T, expected *ast.StructType", src, x)
    91  	}
    92  
    93  	// an invalid expression
    94  	src = "a + *"
    95  	_, err = ParseExpr(src)
    96  	if err == nil {
    97  		t.Fatalf("ParseExpr(%s): %v", src, err)
    98  	}
    99  
   100  	// it must not crash
   101  	for _, src := range valids {
   102  		ParseExpr(src)
   103  	}
   104  }
   105  
   106  func TestColonEqualsScope(t *testing.T) {
   107  	f, err := ParseFile(fset, "", `package p; func f() { x, y, z := x, y, z }`, 0)
   108  	if err != nil {
   109  		t.Fatal(err)
   110  	}
   111  
   112  	// RHS refers to undefined globals; LHS does not.
   113  	as := f.Decls[0].(*ast.FuncDecl).Body.List[0].(*ast.AssignStmt)
   114  	for _, v := range as.Rhs {
   115  		id := v.(*ast.Ident)
   116  		if id.Obj != nil {
   117  			t.Errorf("rhs %s has Obj, should not", id.Name)
   118  		}
   119  	}
   120  	for _, v := range as.Lhs {
   121  		id := v.(*ast.Ident)
   122  		if id.Obj == nil {
   123  			t.Errorf("lhs %s does not have Obj, should", id.Name)
   124  		}
   125  	}
   126  }
   127  
   128  func TestVarScope(t *testing.T) {
   129  	f, err := ParseFile(fset, "", `package p; func f() { var x, y, z = x, y, z }`, 0)
   130  	if err != nil {
   131  		t.Fatal(err)
   132  	}
   133  
   134  	// RHS refers to undefined globals; LHS does not.
   135  	as := f.Decls[0].(*ast.FuncDecl).Body.List[0].(*ast.DeclStmt).Decl.(*ast.GenDecl).Specs[0].(*ast.ValueSpec)
   136  	for _, v := range as.Values {
   137  		id := v.(*ast.Ident)
   138  		if id.Obj != nil {
   139  			t.Errorf("rhs %s has Obj, should not", id.Name)
   140  		}
   141  	}
   142  	for _, id := range as.Names {
   143  		if id.Obj == nil {
   144  			t.Errorf("lhs %s does not have Obj, should", id.Name)
   145  		}
   146  	}
   147  }
   148  
   149  func TestObjects(t *testing.T) {
   150  	const src = `
   151  package p
   152  import fmt "fmt"
   153  const pi = 3.14
   154  type T struct{}
   155  var x int
   156  func f() { L: }
   157  `
   158  
   159  	f, err := ParseFile(fset, "", src, 0)
   160  	if err != nil {
   161  		t.Fatal(err)
   162  	}
   163  
   164  	objects := map[string]ast.ObjKind{
   165  		"p":   ast.Bad, // not in a scope
   166  		"fmt": ast.Bad, // not resolved yet
   167  		"pi":  ast.Con,
   168  		"T":   ast.Typ,
   169  		"x":   ast.Var,
   170  		"int": ast.Bad, // not resolved yet
   171  		"f":   ast.Fun,
   172  		"L":   ast.Lbl,
   173  	}
   174  
   175  	ast.Inspect(f, func(n ast.Node) bool {
   176  		if ident, ok := n.(*ast.Ident); ok {
   177  			obj := ident.Obj
   178  			if obj == nil {
   179  				if objects[ident.Name] != ast.Bad {
   180  					t.Errorf("no object for %s", ident.Name)
   181  				}
   182  				return true
   183  			}
   184  			if obj.Name != ident.Name {
   185  				t.Errorf("names don't match: obj.Name = %s, ident.Name = %s", obj.Name, ident.Name)
   186  			}
   187  			kind := objects[ident.Name]
   188  			if obj.Kind != kind {
   189  				t.Errorf("%s: obj.Kind = %s; want %s", ident.Name, obj.Kind, kind)
   190  			}
   191  		}
   192  		return true
   193  	})
   194  }
   195  
   196  func TestUnresolved(t *testing.T) {
   197  	f, err := ParseFile(fset, "", `
   198  package p
   199  //
   200  func f1a(int)
   201  func f2a(byte, int, float)
   202  func f3a(a, b int, c float)
   203  func f4a(...complex)
   204  func f5a(a s1a, b ...complex)
   205  //
   206  func f1b(*int)
   207  func f2b([]byte, (int), *float)
   208  func f3b(a, b *int, c []float)
   209  func f4b(...*complex)
   210  func f5b(a s1a, b ...[]complex)
   211  //
   212  type s1a struct { int }
   213  type s2a struct { byte; int; s1a }
   214  type s3a struct { a, b int; c float }
   215  //
   216  type s1b struct { *int }
   217  type s2b struct { byte; int; *float }
   218  type s3b struct { a, b *s3b; c []float }
   219  `, 0)
   220  	if err != nil {
   221  		t.Fatal(err)
   222  	}
   223  
   224  	want := "int " + // f1a
   225  		"byte int float " + // f2a
   226  		"int float " + // f3a
   227  		"complex " + // f4a
   228  		"complex " + // f5a
   229  		//
   230  		"int " + // f1b
   231  		"byte int float " + // f2b
   232  		"int float " + // f3b
   233  		"complex " + // f4b
   234  		"complex " + // f5b
   235  		//
   236  		"int " + // s1a
   237  		"byte int " + // s2a
   238  		"int float " + // s3a
   239  		//
   240  		"int " + // s1a
   241  		"byte int float " + // s2a
   242  		"float " // s3a
   243  
   244  	// collect unresolved identifiers
   245  	var buf bytes.Buffer
   246  	for _, u := range f.Unresolved {
   247  		buf.WriteString(u.Name)
   248  		buf.WriteByte(' ')
   249  	}
   250  	got := buf.String()
   251  
   252  	if got != want {
   253  		t.Errorf("\ngot:  %s\nwant: %s", got, want)
   254  	}
   255  }
   256  
   257  var imports = map[string]bool{
   258  	`"a"`:        true,
   259  	"`a`":        true,
   260  	`"a/b"`:      true,
   261  	`"a.b"`:      true,
   262  	`"m\x61th"`:  true,
   263  	`"greek/αβ"`: true,
   264  	`""`:         false,
   265  
   266  	// Each of these pairs tests both `` vs "" strings
   267  	// and also use of invalid characters spelled out as
   268  	// escape sequences and written directly.
   269  	// For example `"\x00"` tests import "\x00"
   270  	// while "`\x00`" tests import `<actual-NUL-byte>`.
   271  	`"\x00"`:     false,
   272  	"`\x00`":     false,
   273  	`"\x7f"`:     false,
   274  	"`\x7f`":     false,
   275  	`"a!"`:       false,
   276  	"`a!`":       false,
   277  	`"a b"`:      false,
   278  	"`a b`":      false,
   279  	`"a\\b"`:     false,
   280  	"`a\\b`":     false,
   281  	"\"`a`\"":    false,
   282  	"`\"a\"`":    false,
   283  	`"\x80\x80"`: false,
   284  	"`\x80\x80`": false,
   285  	`"\xFFFD"`:   false,
   286  	"`\xFFFD`":   false,
   287  }
   288  
   289  func TestImports(t *testing.T) {
   290  	for path, isValid := range imports {
   291  		src := fmt.Sprintf("package p; import %s", path)
   292  		_, err := ParseFile(fset, "", src, 0)
   293  		switch {
   294  		case err != nil && isValid:
   295  			t.Errorf("ParseFile(%s): got %v; expected no error", src, err)
   296  		case err == nil && !isValid:
   297  			t.Errorf("ParseFile(%s): got no error; expected one", src)
   298  		}
   299  	}
   300  }
   301  
   302  func TestCommentGroups(t *testing.T) {
   303  	f, err := ParseFile(fset, "", `
   304  package p /* 1a */ /* 1b */      /* 1c */ // 1d
   305  /* 2a
   306  */
   307  // 2b
   308  const pi = 3.1415
   309  /* 3a */ // 3b
   310  /* 3c */ const e = 2.7182
   311  
   312  // Example from issue 3139
   313  func ExampleCount() {
   314  	fmt.Println(strings.Count("cheese", "e"))
   315  	fmt.Println(strings.Count("five", "")) // before & after each rune
   316  	// Output:
   317  	// 3
   318  	// 5
   319  }
   320  `, ParseComments)
   321  	if err != nil {
   322  		t.Fatal(err)
   323  	}
   324  	expected := [][]string{
   325  		{"/* 1a */", "/* 1b */", "/* 1c */", "// 1d"},
   326  		{"/* 2a\n*/", "// 2b"},
   327  		{"/* 3a */", "// 3b", "/* 3c */"},
   328  		{"// Example from issue 3139"},
   329  		{"// before & after each rune"},
   330  		{"// Output:", "// 3", "// 5"},
   331  	}
   332  	if len(f.Comments) != len(expected) {
   333  		t.Fatalf("got %d comment groups; expected %d", len(f.Comments), len(expected))
   334  	}
   335  	for i, exp := range expected {
   336  		got := f.Comments[i].List
   337  		if len(got) != len(exp) {
   338  			t.Errorf("got %d comments in group %d; expected %d", len(got), i, len(exp))
   339  			continue
   340  		}
   341  		for j, exp := range exp {
   342  			got := got[j].Text
   343  			if got != exp {
   344  				t.Errorf("got %q in group %d; expected %q", got, i, exp)
   345  			}
   346  		}
   347  	}
   348  }
   349  
   350  func getField(file *ast.File, fieldname string) *ast.Field {
   351  	parts := strings.Split(fieldname, ".")
   352  	for _, d := range file.Decls {
   353  		if d, ok := d.(*ast.GenDecl); ok && d.Tok == token.TYPE {
   354  			for _, s := range d.Specs {
   355  				if s, ok := s.(*ast.TypeSpec); ok && s.Name.Name == parts[0] {
   356  					if s, ok := s.Type.(*ast.StructType); ok {
   357  						for _, f := range s.Fields.List {
   358  							for _, name := range f.Names {
   359  								if name.Name == parts[1] {
   360  									return f
   361  								}
   362  							}
   363  						}
   364  					}
   365  				}
   366  			}
   367  		}
   368  	}
   369  	return nil
   370  }
   371  
   372  // Don't use ast.CommentGroup.Text() - we want to see exact comment text.
   373  func commentText(c *ast.CommentGroup) string {
   374  	var buf bytes.Buffer
   375  	if c != nil {
   376  		for _, c := range c.List {
   377  			buf.WriteString(c.Text)
   378  		}
   379  	}
   380  	return buf.String()
   381  }
   382  
   383  func checkFieldComments(t *testing.T, file *ast.File, fieldname, lead, line string) {
   384  	f := getField(file, fieldname)
   385  	if f == nil {
   386  		t.Fatalf("field not found: %s", fieldname)
   387  	}
   388  	if got := commentText(f.Doc); got != lead {
   389  		t.Errorf("got lead comment %q; expected %q", got, lead)
   390  	}
   391  	if got := commentText(f.Comment); got != line {
   392  		t.Errorf("got line comment %q; expected %q", got, line)
   393  	}
   394  }
   395  
   396  func TestLeadAndLineComments(t *testing.T) {
   397  	f, err := ParseFile(fset, "", `
   398  package p
   399  type T struct {
   400  	/* F1 lead comment */
   401  	//
   402  	F1 int  /* F1 */ // line comment
   403  	// F2 lead
   404  	// comment
   405  	F2 int  // F2 line comment
   406  	// f3 lead comment
   407  	f3 int  // f3 line comment
   408  }
   409  `, ParseComments)
   410  	if err != nil {
   411  		t.Fatal(err)
   412  	}
   413  	checkFieldComments(t, f, "T.F1", "/* F1 lead comment *///", "/* F1 */// line comment")
   414  	checkFieldComments(t, f, "T.F2", "// F2 lead// comment", "// F2 line comment")
   415  	checkFieldComments(t, f, "T.f3", "// f3 lead comment", "// f3 line comment")
   416  	ast.FileExports(f)
   417  	checkFieldComments(t, f, "T.F1", "/* F1 lead comment *///", "/* F1 */// line comment")
   418  	checkFieldComments(t, f, "T.F2", "// F2 lead// comment", "// F2 line comment")
   419  	if getField(f, "T.f3") != nil {
   420  		t.Error("not expected to find T.f3")
   421  	}
   422  }