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

     1  package converter
     2  
     3  import (
     4  	"go/token"
     5  	"go/types"
     6  	"strings"
     7  	"testing"
     8  )
     9  
    10  func TestInspect(t *testing.T) {
    11  	tests := []struct {
    12  		name  string
    13  		src   string
    14  		doc   string
    15  		query string
    16  	}{
    17  		{
    18  			name: "local variable",
    19  			src: `
    20  			import (
    21  				"fmt"
    22  			)
    23  
    24  			func sum(x, y int) int { return x + y }
    25  			func f(x int) (y int) {
    26  				s := sum(x, x)
    27  				return [cur]s*x
    28  			}`,
    29  			doc: "var s int",
    30  		},
    31  		{
    32  			name: "local const",
    33  			src: `
    34  			func f(x int) (y int) {
    35  				const s = 10
    36  				return [cur]s*x
    37  			}`,
    38  			doc: "const s untyped int",
    39  		},
    40  		{
    41  			name: "global_variable",
    42  			src: `
    43  			import (
    44  				"fmt"
    45  			)
    46  
    47  			var (
    48  				s int
    49  			)
    50  
    51  			func sum(x, y int) { [cur]s = x + y }`,
    52  			doc: "var s int",
    53  		},
    54  		{
    55  			name: "global_const",
    56  			src: `
    57  			const myVal = 10
    58  			x := [cur]myVal * 10
    59  			`,
    60  			doc: "const myVal untyped int",
    61  		},
    62  		{
    63  			name: "func",
    64  			src: `
    65  			func fn(x int) int { return x * x }
    66  			[cur]fn(10)
    67  			`,
    68  			doc: "func fn(x int) int",
    69  		}, {
    70  			name: "func_args",
    71  			src: `
    72  			func fn(x int) int { return x * x }
    73  			fn([cur]`,
    74  			doc: "func fn(x int) int",
    75  		}, {
    76  			name: "func_args_closed",
    77  			src: `
    78  			func fn(x int) int { return x * x }
    79  			fn([cur])
    80  			`,
    81  			doc: "func fn(x int) int",
    82  		}, {
    83  			name: "func_args_after_close",
    84  			src: `
    85  			func fn(x int) int { return x * x }
    86  			fn()[cur]
    87  			`,
    88  		}, {
    89  			name: "func_args_id",
    90  			src: `
    91  			func fn(x int) int { return x * x }
    92  			n := 10
    93  			fn(n[cur])
    94  			`,
    95  			doc: "var n int",
    96  		}, {
    97  			name: "func_args_comma",
    98  			src: `
    99  			func fn(x int) int { return x * x }
   100  			n := 10
   101  			fn(n,[cur])
   102  			`,
   103  			doc: "func fn(x int) int",
   104  		}, {
   105  			name: "func_args_nested",
   106  			src: `
   107  			func fn(x int) int { return x * x }
   108  			func xy(a int) int { return x * x }
   109  			fn(xy([cur]
   110  			`,
   111  			doc: "func xy(a int) int",
   112  		}, {
   113  			name: "func_args_selector",
   114  			src: `
   115  			import "bytes"
   116  			bytes.Compare(nil,[cur])`,
   117  			query: "bytes.Compare",
   118  		}, {
   119  			name: "method",
   120  			src: `
   121  			type typ int
   122  			func (t typ) Int() int { return int(t) }
   123  
   124  			x := typ(123)
   125  			x.[cur]Int()`,
   126  			// TODO: Includes a receiver.
   127  			doc: "func Int() int",
   128  		},
   129  		{
   130  			name: "interface_method",
   131  			src: `
   132  			type hello interface {
   133  				sayHello(name string)
   134  			}
   135  			var h hello
   136  			h.[cur]sayHello()`,
   137  			// TODO: Includes a receiver.
   138  			doc: "func sayHello(name string)",
   139  		},
   140  		{
   141  			name: "custom_type_var",
   142  			src: `
   143  			type message string
   144  			var m message
   145  			[cur]m`,
   146  			// TODO: Remove "cmd/hello.".
   147  			doc: "var m cmd/hello.message",
   148  		},
   149  		{
   150  			name: "package",
   151  			src: `
   152  			import (
   153  				"fmt"
   154  			)
   155  
   156  			[cur]fmt.Println(0, 1)`,
   157  			query: "fmt",
   158  		},
   159  		{
   160  			name: "renamed package",
   161  			src: `
   162  			import (
   163  				pkg "fmt"
   164  			)
   165  
   166  			[cur]pkg.Println(0, 1)`,
   167  			query: "fmt",
   168  		},
   169  		{
   170  			name: "package var",
   171  			src: `
   172  			import (
   173  				"fmt"
   174  				"os"
   175  			)
   176  
   177  			fmt.Fprintln(os.[cur]Stderr, "error")`,
   178  			query: "os.Stderr",
   179  		},
   180  		{
   181  			name: "package const",
   182  			src: `
   183  			import (
   184  				"io"
   185  			)
   186  
   187  			x := io.[cur]SeekStart`,
   188  			query: "io.SeekStart",
   189  		},
   190  		{
   191  			name: "package func",
   192  			src: `
   193  			import (
   194  				"fmt"
   195  			)
   196  
   197  			fmt.P[cur]rintln(0, 1)`,
   198  			query: "fmt.Println",
   199  		},
   200  		{
   201  			name: "method",
   202  			src: `
   203  			import (
   204  				"bytes"
   205  			)
   206  
   207  			var buf bytes.Buffer
   208  			buf.[cur]Len()`,
   209  			query: "bytes.Buffer.Len",
   210  		},
   211  		{
   212  			name: "renamed pkg method",
   213  			src: `
   214  			import (
   215  				b "bytes"
   216  			)
   217  
   218  			var buf b.Buffer
   219  			buf.[cur]Len()`,
   220  			query: "bytes.Buffer.Len",
   221  		}, {
   222  			name: "package_interface_method",
   223  			src: `
   224  			import (
   225  				"bytes"
   226  				"io"
   227  			)
   228  
   229  			var buf bytes.Buffer
   230  			var r io.Reader = &buf
   231  			r.[cur]Read(nil)`,
   232  			query: "io.Reader.Read",
   233  		}, {
   234  			name: "type",
   235  			src: `
   236  			import (
   237  				"flag"
   238  			)
   239  
   240  			f := flag.F[cur]lag{}`,
   241  			query: "flag.Flag",
   242  		}, {
   243  			// This fails with go1.8 expectedly.
   244  			name: "field",
   245  			src: `
   246  			import (
   247  				"flag"
   248  			)
   249  
   250  			f := flag.Flag{[cur]Name: "myflag"}`,
   251  			query: "flag.Flag.Name",
   252  		}, {
   253  			name: "local_field_def",
   254  			src:  "type st struct { [cur]val int }",
   255  			doc:  "var val int",
   256  		}, {
   257  			name: "local_field_ref",
   258  			src: `
   259  			type st struct { val int }
   260  			var x st
   261  			x.[cur]val`,
   262  			doc: "var val int",
   263  		}, {
   264  			name: "invalid_type",
   265  			src: `
   266  			var x foobar
   267  			[cur]x + 10`,
   268  		},
   269  		{
   270  			name: "invalid_const_val",
   271  			src: `
   272  			func sum(x, y int) int { return x + y }
   273  			const x = sum(10, 20)
   274  			[cur]x + 10`,
   275  			// TODO: Fix this
   276  			doc: "const x invalid type",
   277  		},
   278  		{
   279  			name: "invalid syntax",
   280  			src:  `[cur]x := 3 +`,
   281  		},
   282  		{
   283  			name: "invalid_syntax_after_cur",
   284  			src: `[cur]x := 3 + 4
   285  			y := x +`,
   286  			doc: "var x int",
   287  		},
   288  		{
   289  			name: "right_after_id",
   290  			src: `
   291  			func f(x int) (y int) {
   292  				s := x+1
   293  				return s[cur]*x
   294  			}`,
   295  			doc: "var s int",
   296  		},
   297  		{
   298  			name: "typename_struct",
   299  			src: `
   300  			type mytype struct {
   301  				X int
   302  				Y string
   303  			}
   304  			v := [cur]mytype{}`,
   305  			doc: "type mytype struct{X int; Y string}",
   306  		},
   307  		{
   308  			name: "typename_interface",
   309  			src: `
   310  			type mytype interface {
   311  				Method(x int)
   312  			}
   313  			v := [cur]mytype(nil)`,
   314  			doc: "type mytype interface{Method(x int)}",
   315  		},
   316  		{
   317  			name: "typename_in_var",
   318  			src: `
   319  			type message string
   320  			var m [cur]message`,
   321  			doc: "type message string",
   322  		},
   323  		// def_ prefix tests test Inspect on identifiers that define objects.
   324  		{
   325  			name: "def_global_variable",
   326  			src:  `var [cur]x = 10`,
   327  			doc:  "var x int",
   328  		},
   329  		{
   330  			name: "def_func",
   331  			src: `
   332  			func [cur]myFunc(x int) (y int) { return x * 2 }
   333  			myFunc(10)`,
   334  			doc: "func myFunc(x int) (y int)",
   335  		},
   336  		{
   337  			name: "def_method",
   338  			src: `
   339  			type myType int
   340  			func (myType) [cur]myMethod() {}`,
   341  			// TODO: Print the receiver
   342  			doc: "func myMethod()",
   343  		},
   344  		{
   345  			name: "def_type",
   346  			src:  `type [cur]myType int`,
   347  			doc:  "type myType int",
   348  		}, {
   349  			name: "lgo_context",
   350  			src:  `_[cur]ctx.Done()`,
   351  			doc:  "var _ctx github.com/yunabe/lgo/core.LgoContext",
   352  		}, {
   353  			name:  "lgo_context_method",
   354  			src:   `_ctx.[cur]Done()`,
   355  			query: "context.Context.Done",
   356  		}, {
   357  			name: "lgo_context_infunc",
   358  			src: `
   359  			func f() {
   360  				_ctx.[cur]Done()
   361  			}`,
   362  			query: "context.Context.Done",
   363  		}, {
   364  			name: "builtin_error_method",
   365  			src: `
   366  			var err error
   367  			err.Error[cur]
   368  			`,
   369  			query: "builtin.error.Error",
   370  		}, {
   371  			name: "builtin_func",
   372  			src: `
   373  			var s []string
   374  			s = ap[cur]pend(s, "hello")`,
   375  			query: "builtin.append",
   376  		}, {
   377  			name:  "builtin_panic",
   378  			src:   `pa[cur]nic("panic")`,
   379  			query: "builtin.panic",
   380  		}, {
   381  			name:  "builtin_type",
   382  			src:   `var f fl[cur]oat64`,
   383  			query: "builtin.float64",
   384  		},
   385  	}
   386  	for _, tt := range tests {
   387  		t.Run(tt.name, func(t *testing.T) {
   388  			src := tt.src
   389  			pos := token.Pos(strings.Index(src, "[cur]") + 1)
   390  			if pos == token.NoPos {
   391  				t.Error("[cur] not found in src")
   392  				return
   393  			}
   394  
   395  			obj, local := inspectObject(strings.Replace(src, "[cur]", "", -1), pos, &Config{})
   396  			doc, query := getDocOrGoDocQuery(obj, local)
   397  			var queryStr string
   398  			if query != nil {
   399  				queryStr = query.pkg
   400  				if len(query.ids) > 0 {
   401  					queryStr += "." + strings.Join(query.ids, ".")
   402  				}
   403  			}
   404  			if tt.doc != doc {
   405  				t.Errorf("Expected %q but got %q", tt.doc, doc)
   406  			}
   407  			if tt.query != queryStr {
   408  				t.Errorf("Expected %q but got %q", tt.query, queryStr)
   409  			}
   410  		})
   411  	}
   412  }
   413  
   414  func TestInspectWithOlds(t *testing.T) {
   415  	result := Convert(`
   416  	x := 10
   417  	X := x + 10
   418  	`, &Config{LgoPkgPath: "lgo/pkg0"})
   419  	if result.Err != nil {
   420  		t.Error(result.Err)
   421  		return
   422  	}
   423  	olds := []types.Object{result.Pkg.Scope().Lookup("x"),
   424  		result.Pkg.Scope().Lookup("X"),
   425  	}
   426  	tests := []struct{ id, query string }{
   427  		{"x", "lgo/pkg0.Def_x"},
   428  		{"X", "lgo/pkg0.X"},
   429  	}
   430  	for _, tt := range tests {
   431  		src := `y := x + X`
   432  		doc, query := InspectIdent(src, token.Pos(strings.Index(src, tt.id)+1), &Config{
   433  			Olds:      olds,
   434  			DefPrefix: "Def_",
   435  			// RefPrefix is not used.
   436  			RefPrefix: "Ref_",
   437  		})
   438  		if doc != "" {
   439  			t.Errorf("Expected an empty doc for %s but got %q", tt.id, doc)
   440  		}
   441  		if query != tt.query {
   442  			t.Errorf("Expected %q for %s but got %q", tt.query, tt.id, query)
   443  		}
   444  	}
   445  }
   446  
   447  func TestInspectUnnamedStruct(t *testing.T) {
   448  	result := Convert(`
   449  	func Gen() struct{Val int} {
   450  		return struct{Val int}{123}
   451  	}
   452  	`, &Config{LgoPkgPath: "lgo/pkg0"})
   453  	if result.Err != nil {
   454  		t.Error(result.Err)
   455  		return
   456  	}
   457  	olds := []types.Object{
   458  		result.Pkg.Scope().Lookup("Gen"),
   459  	}
   460  	src := `Gen().Val`
   461  	doc, query := InspectIdent(src, token.Pos(strings.Index(src, "Val")+1), &Config{
   462  		Olds:      olds,
   463  		DefPrefix: "Def_",
   464  		// RefPrefix is not used.
   465  		RefPrefix: "Ref_",
   466  	})
   467  	if doc != "" {
   468  		t.Errorf("Unexpected non-empty doc: %q", doc)
   469  	}
   470  	if query != "" {
   471  		t.Errorf("Unexpected non-empty query: %q", query)
   472  	}
   473  }