Repository: traefik/yaegi (Go)

Issue:
Call flow graph generation panics on var declaration when wrapped as main
The following code triggers a panic in `(*Interpreter).cfg` when called by `(*Interpreter).Eval`:

```go
i := ""
var j int
```

```
$ yaegi
> i := ""; var j int
1:41: CFG post-order panic: runtime error: invalid memory address or nil pointer dereference
goroutine 21 [running]:
runtime/debug.Stack(0x1, 0xc000200a00, 0x40)
        /goroot/src/runtime/debug/stack.go:24 +0x9f
github.com/traefik/yaegi/interp.(*Interpreter).eval.func1(0xc00020ff78)
        /workdir/yaegi/interp/interp.go:491 +0xc5
panic(0x1ab7500, 0xc0001a1da0)
        /goroot/src/runtime/panic.go:969 +0x175
github.com/traefik/yaegi/interp.(*Interpreter).cfg.func2.1(0xc0001c7200)
        /workdir/yaegi/interp/cfg.go:468 +0xb3
panic(0x1a1fd60, 0x214bd30)
        /goroot/src/runtime/panic.go:969 +0x175
github.com/traefik/yaegi/interp.(*Interpreter).cfg.func2(0xc0001c7200)
        /workdir/yaegi/interp/cfg.go:1903 +0x1963
github.com/traefik/yaegi/interp.(*node).Walk(0xc0001c7200, 0xc00020fdf8, 0xc00020fdc8)
        /workdir/yaegi/interp/interp.go:231 +0xa5
github.com/traefik/yaegi/interp.(*node).Walk(0xc0001c7100, 0xc00020fdf8, 0xc00020fdc8)
        /workdir/yaegi/interp/interp.go:228 +0x66
github.com/traefik/yaegi/interp.(*node).Walk(0xc0001c7000, 0xc00020fdf8, 0xc00020fdc8)
        /workdir/yaegi/interp/interp.go:228 +0x66
github.com/traefik/yaegi/interp.(*node).Walk(0xc0001c6c00, 0xc00020fdf8, 0xc00020fdc8)
        /workdir/yaegi/interp/interp.go:228 +0x66
github.com/traefik/yaegi/interp.(*Interpreter).cfg(0xc000372000, 0xc0001c6c00, 0xc00036e820, 0x4, 0xc00036e820, 0x4, 0x0, 0x0, 0xc0001c6c00)
        /workdir/yaegi/interp/cfg.go:63 +0x20f
github.com/traefik/yaegi/interp.(*Interpreter).eval(0xc000372000, 0xc0001c2780, 0x13, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, ...)
        /workdir/yaegi/interp/interp.go:518 +0x2f3
github.com/traefik/yaegi/interp.(*Interpreter).Eval(...)
        /workdir/yaegi/interp/interp.go:389
github.com/traefik/yaegi/interp.(*Interpreter).EvalWithContext.func1(0xc000181aa0, 0xc000372000, 0xc0001c2780, 0x13, 0xc0001a1b40, 0xc000230750)
        /workdir/yaegi/interp/interp.go:612 +0x85
created by github.com/traefik/yaegi/interp.(*Interpreter).EvalWithContext
        /workdir/yaegi/interp/interp.go:610 +0x150
```


Discussion:

Patch:
diff --git a/.github/workflows/go-cross.yml b/.github/workflows/go-cross.yml
index 6ed8d978d..079136702 100644
--- a/.github/workflows/go-cross.yml
+++ b/.github/workflows/go-cross.yml
@@ -18,13 +18,14 @@ jobs:
     strategy:
       matrix:
         go-version: [ 1.15, 1.16 ]
-        os: [ubuntu-latest, macos-latest, windows-latest]
+#        os: [ubuntu-latest, macos-latest, windows-latest]
+        os: [ubuntu-latest, windows-latest]
 
         include:
           - os: ubuntu-latest
             go-path-suffix: /go
-          - os: macos-latest
-            go-path-suffix: /go
+#          - os: macos-latest
+#            go-path-suffix: /go
           - os: windows-latest
             go-path-suffix: \go
 
diff --git a/interp/ast.go b/interp/ast.go
index 502a04617..966a5ab9a 100644
--- a/interp/ast.go
+++ b/interp/ast.go
@@ -716,7 +716,7 @@ func (interp *Interpreter) ast(src, name string, inc bool) (string, *node, error
 			n := addChild(&root, anc, pos, identExpr, aNop)
 			n.ident = a.Name
 			st.push(n, nod)
-			if n.anc.kind == defineStmt && n.anc.nright == 0 {
+			if n.anc.kind == defineStmt && n.anc.anc.kind == constDecl && n.anc.nright == 0 {
 				// Implicit assign expression (in a ConstDecl block).
 				// Clone assign source and type from previous
 				a := n.anc
@@ -858,7 +858,8 @@ func (interp *Interpreter) ast(src, name string, inc bool) (string, *node, error
 		case *ast.ValueSpec:
 			kind := valueSpec
 			act := aNop
-			if a.Values != nil {
+			switch {
+			case a.Values != nil:
 				if len(a.Names) > 1 && len(a.Values) == 1 {
 					if anc.node.kind == constDecl || anc.node.kind == varDecl {
 						kind = defineXStmt
@@ -874,7 +875,9 @@ func (interp *Interpreter) ast(src, name string, inc bool) (string, *node, error
 					}
 					act = aAssign
 				}
-			} else if anc.node.kind == constDecl {
+			case anc.node.kind == constDecl:
+				kind, act = defineStmt, aAssign
+			case anc.node.kind == varDecl && anc.node.anc.kind != fileStmt:
 				kind, act = defineStmt, aAssign
 			}
 			n := addChild(&root, anc, pos, kind, act)
diff --git a/interp/cfg.go b/interp/cfg.go
index a86af4a14..32991805c 100644
--- a/interp/cfg.go
+++ b/interp/cfg.go
@@ -614,6 +614,8 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
 				case src.kind == basicLit:
 					// Assign to nil.
 					src.rval = reflect.New(dest.typ.TypeOf()).Elem()
+				case n.nright == 0:
+					n.gen = reset
 				}
 
 				n.typ = dest.typ


Test patch:
diff --git a/interp/interp_eval_test.go b/interp/interp_eval_test.go
index 76ec58971..593762d14 100644
--- a/interp/interp_eval_test.go
+++ b/interp/interp_eval_test.go
@@ -182,6 +182,15 @@ func TestEvalDecl(t *testing.T) {
 	})
 }
 
+func TestEvalDeclWithExpr(t *testing.T) {
+	i := interp.New(interp.Options{})
+	runTests(t, i, []testCase{
+		{src: `a1 := ""; var a2 int; a2 = 2`, res: "2"},
+		{src: `b1 := ""; const b2 = 2; b2`, res: "2"},
+		{src: `c1 := ""; var c2, c3 [8]byte; c3[3]`, res: "0"},
+	})
+}
+
 func TestEvalFunc(t *testing.T) {
 	i := interp.New(interp.Options{})
 	runTests(t, i, []testCase{