summaryrefslogtreecommitdiff
path: root/libgo/go/cmd/vet/main.go
blob: c50d4885a078745ffa2bc10b2a7c59c5469507d9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Vet is a simple checker for static errors in Go source code.
// See doc.go for more information.

package main

import (
	"bytes"
	"encoding/gob"
	"encoding/json"
	"flag"
	"fmt"
	"go/ast"
	"go/build"
	"go/importer"
	"go/parser"
	"go/printer"
	"go/token"
	"go/types"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"sort"
	"strconv"
	"strings"

	"cmd/internal/objabi"
)

// Important! If you add flags here, make sure to update cmd/go/internal/vet/vetflag.go.

var (
	verbose = flag.Bool("v", false, "verbose")
	source  = flag.Bool("source", false, "import from source instead of compiled object files")
	tags    = flag.String("tags", "", "space-separated list of build tags to apply when parsing")
	tagList = []string{} // exploded version of tags flag; set in main

	vcfg          vetConfig
	mustTypecheck bool
)

var exitCode = 0

// "-all" flag enables all non-experimental checks
var all = triStateFlag("all", unset, "enable all non-experimental checks")

// Flags to control which individual checks to perform.
var report = map[string]*triState{
	// Only unusual checks are written here.
	// Most checks that operate during the AST walk are added by register.
	"asmdecl":   triStateFlag("asmdecl", unset, "check assembly against Go declarations"),
	"buildtags": triStateFlag("buildtags", unset, "check that +build tags are valid"),
}

// experimental records the flags enabling experimental features. These must be
// requested explicitly; they are not enabled by -all.
var experimental = map[string]bool{}

// setTrueCount record how many flags are explicitly set to true.
var setTrueCount int

// dirsRun and filesRun indicate whether the vet is applied to directory or
// file targets. The distinction affects which checks are run.
var dirsRun, filesRun bool

// includesNonTest indicates whether the vet is applied to non-test targets.
// Certain checks are relevant only if they touch both test and non-test files.
var includesNonTest bool

// A triState is a boolean that knows whether it has been set to either true or false.
// It is used to identify if a flag appears; the standard boolean flag cannot
// distinguish missing from unset. It also satisfies flag.Value.
type triState int

const (
	unset triState = iota
	setTrue
	setFalse
)

func triStateFlag(name string, value triState, usage string) *triState {
	flag.Var(&value, name, usage)
	return &value
}

// triState implements flag.Value, flag.Getter, and flag.boolFlag.
// They work like boolean flags: we can say vet -printf as well as vet -printf=true
func (ts *triState) Get() interface{} {
	return *ts == setTrue
}

func (ts triState) isTrue() bool {
	return ts == setTrue
}

func (ts *triState) Set(value string) error {
	b, err := strconv.ParseBool(value)
	if err != nil {
		return err
	}
	if b {
		*ts = setTrue
		setTrueCount++
	} else {
		*ts = setFalse
	}
	return nil
}

func (ts *triState) String() string {
	switch *ts {
	case unset:
		return "true" // An unset flag will be set by -all, so defaults to true.
	case setTrue:
		return "true"
	case setFalse:
		return "false"
	}
	panic("not reached")
}

func (ts triState) IsBoolFlag() bool {
	return true
}

// vet tells whether to report errors for the named check, a flag name.
func vet(name string) bool {
	return report[name].isTrue()
}

// setExit sets the value for os.Exit when it is called, later. It
// remembers the highest value.
func setExit(err int) {
	if err > exitCode {
		exitCode = err
	}
}

var (
	// Each of these vars has a corresponding case in (*File).Visit.
	assignStmt    *ast.AssignStmt
	binaryExpr    *ast.BinaryExpr
	callExpr      *ast.CallExpr
	compositeLit  *ast.CompositeLit
	exprStmt      *ast.ExprStmt
	forStmt       *ast.ForStmt
	funcDecl      *ast.FuncDecl
	funcLit       *ast.FuncLit
	genDecl       *ast.GenDecl
	interfaceType *ast.InterfaceType
	rangeStmt     *ast.RangeStmt
	returnStmt    *ast.ReturnStmt
	structType    *ast.StructType

	// checkers is a two-level map.
	// The outer level is keyed by a nil pointer, one of the AST vars above.
	// The inner level is keyed by checker name.
	checkers    = make(map[ast.Node]map[string]func(*File, ast.Node))
	pkgCheckers = make(map[string]func(*Package))
	exporters   = make(map[string]func() interface{})
)

// The exporters data as written to the vetx output file.
type vetxExport struct {
	Name string
	Data interface{}
}

// Vet can provide its own "export information"
// about package A to future invocations of vet
// on packages importing A. If B imports A,
// then running "go vet B" actually invokes vet twice:
// first, it runs vet on A, in "vetx-only" mode, which
// skips most checks and only computes export data
// describing A. Then it runs vet on B, making A's vetx
// data available for consultation. The vet of B
// computes vetx data for B in addition to its
// usual vet checks.

// register registers the named check function,
// to be called with AST nodes of the given types.
// The registered functions are not called in vetx-only mode.
func register(name, usage string, fn func(*File, ast.Node), types ...ast.Node) {
	report[name] = triStateFlag(name, unset, usage)
	for _, typ := range types {
		m := checkers[typ]
		if m == nil {
			m = make(map[string]func(*File, ast.Node))
			checkers[typ] = m
		}
		m[name] = fn
	}
}

// registerPkgCheck registers a package-level checking function,
// to be invoked with the whole package being vetted
// before any of the per-node handlers.
// The registered function fn is called even in vetx-only mode
// (see comment above), so fn must take care not to report
// errors when vcfg.VetxOnly is true.
func registerPkgCheck(name string, fn func(*Package)) {
	pkgCheckers[name] = fn
}

// registerExport registers a function to return vetx export data
// that should be saved and provided to future invocations of vet
// when checking packages importing this one.
// The value returned by fn should be nil or else valid to encode using gob.
// Typically a registerExport call is paired with a call to gob.Register.
func registerExport(name string, fn func() interface{}) {
	exporters[name] = fn
}

// Usage is a replacement usage function for the flags package.
func Usage() {
	fmt.Fprintf(os.Stderr, "Usage of vet:\n")
	fmt.Fprintf(os.Stderr, "\tvet [flags] directory...\n")
	fmt.Fprintf(os.Stderr, "\tvet [flags] files... # Must be a single package\n")
	fmt.Fprintf(os.Stderr, "By default, -all is set and all non-experimental checks are run.\n")
	fmt.Fprintf(os.Stderr, "For more information run\n")
	fmt.Fprintf(os.Stderr, "\tgo doc cmd/vet\n\n")
	fmt.Fprintf(os.Stderr, "Flags:\n")
	flag.PrintDefaults()
	os.Exit(2)
}

// File is a wrapper for the state of a file used in the parser.
// The parse tree walkers are all methods of this type.
type File struct {
	pkg     *Package
	fset    *token.FileSet
	name    string
	content []byte
	file    *ast.File
	b       bytes.Buffer // for use by methods

	// Parsed package "foo" when checking package "foo_test"
	basePkg *Package

	// The keys are the objects that are receivers of a "String()
	// string" method. The value reports whether the method has a
	// pointer receiver.
	// This is used by the recursiveStringer method in print.go.
	stringerPtrs map[*ast.Object]bool

	// Registered checkers to run.
	checkers map[ast.Node][]func(*File, ast.Node)

	// Unreachable nodes; can be ignored in shift check.
	dead map[ast.Node]bool
}

func main() {
	objabi.AddVersionFlag()
	flag.Usage = Usage
	flag.Parse()

	// If any flag is set, we run only those checks requested.
	// If all flag is set true or if no flags are set true, set all the non-experimental ones
	// not explicitly set (in effect, set the "-all" flag).
	if setTrueCount == 0 || *all == setTrue {
		for name, setting := range report {
			if *setting == unset && !experimental[name] {
				*setting = setTrue
			}
		}
	}

	// Accept space-separated tags because that matches
	// the go command's other subcommands.
	// Accept commas because go tool vet traditionally has.
	tagList = strings.Fields(strings.Replace(*tags, ",", " ", -1))

	initPrintFlags()
	initUnusedFlags()

	if flag.NArg() == 0 {
		Usage()
	}

	// Special case for "go vet" passing an explicit configuration:
	// single argument ending in vet.cfg.
	// Once we have a more general mechanism for obtaining this
	// information from build tools like the go command,
	// vet should be changed to use it. This vet.cfg hack is an
	// experiment to learn about what form that information should take.
	if flag.NArg() == 1 && strings.HasSuffix(flag.Arg(0), "vet.cfg") {
		doPackageCfg(flag.Arg(0))
		os.Exit(exitCode)
	}

	for _, name := range flag.Args() {
		// Is it a directory?
		fi, err := os.Stat(name)
		if err != nil {
			warnf("error walking tree: %s", err)
			continue
		}
		if fi.IsDir() {
			dirsRun = true
		} else {
			filesRun = true
			if !strings.HasSuffix(name, "_test.go") {
				includesNonTest = true
			}
		}
	}
	if dirsRun && filesRun {
		Usage()
	}
	if dirsRun {
		for _, name := range flag.Args() {
			walkDir(name)
		}
		os.Exit(exitCode)
	}
	if doPackage(flag.Args(), nil) == nil {
		warnf("no files checked")
	}
	os.Exit(exitCode)
}

// prefixDirectory places the directory name on the beginning of each name in the list.
func prefixDirectory(directory string, names []string) {
	if directory != "." {
		for i, name := range names {
			names[i] = filepath.Join(directory, name)
		}
	}
}

// vetConfig is the JSON config struct prepared by the Go command.
type vetConfig struct {
	Compiler    string
	Dir         string
	ImportPath  string
	GoFiles     []string
	ImportMap   map[string]string
	PackageFile map[string]string
	Standard    map[string]bool
	PackageVetx map[string]string // map from import path to vetx data file
	VetxOnly    bool              // only compute vetx output; don't run ordinary checks
	VetxOutput  string            // file where vetx output should be written

	SucceedOnTypecheckFailure bool

	imp types.Importer
}

func (v *vetConfig) Import(path string) (*types.Package, error) {
	if v.imp == nil {
		v.imp = importer.For(v.Compiler, v.openPackageFile)
	}
	if path == "unsafe" {
		return v.imp.Import("unsafe")
	}
	p := v.ImportMap[path]
	if p == "" {
		return nil, fmt.Errorf("unknown import path %q", path)
	}
	if v.PackageFile[p] == "" {
		if v.Compiler == "gccgo" && v.Standard[path] {
			// gccgo doesn't have sources for standard library packages,
			// but the importer will do the right thing.
			return v.imp.Import(path)
		}
		return nil, fmt.Errorf("unknown package file for import %q", path)
	}
	return v.imp.Import(p)
}

func (v *vetConfig) openPackageFile(path string) (io.ReadCloser, error) {
	file := v.PackageFile[path]
	if file == "" {
		if v.Compiler == "gccgo" && v.Standard[path] {
			// The importer knows how to handle this.
			return nil, nil
		}
		// Note that path here has been translated via v.ImportMap,
		// unlike in the error in Import above. We prefer the error in
		// Import, but it's worth diagnosing this one too, just in case.
		return nil, fmt.Errorf("unknown package file for %q", path)
	}
	f, err := os.Open(file)
	if err != nil {
		return nil, err
	}
	return f, nil
}

// doPackageCfg analyzes a single package described in a config file.
func doPackageCfg(cfgFile string) {
	js, err := ioutil.ReadFile(cfgFile)
	if err != nil {
		errorf("%v", err)
	}
	if err := json.Unmarshal(js, &vcfg); err != nil {
		errorf("parsing vet config %s: %v", cfgFile, err)
	}
	stdImporter = &vcfg
	inittypes()
	mustTypecheck = true
	doPackage(vcfg.GoFiles, nil)
	if vcfg.VetxOutput != "" {
		out := make([]vetxExport, 0, len(exporters))
		for name, fn := range exporters {
			out = append(out, vetxExport{
				Name: name,
				Data: fn(),
			})
		}
		// Sort the data so that it is consistent across builds.
		sort.Slice(out, func(i, j int) bool {
			return out[i].Name < out[j].Name
		})
		var buf bytes.Buffer
		if err := gob.NewEncoder(&buf).Encode(out); err != nil {
			errorf("encoding vet output: %v", err)
			return
		}
		if err := ioutil.WriteFile(vcfg.VetxOutput, buf.Bytes(), 0666); err != nil {
			errorf("saving vet output: %v", err)
			return
		}
	}
}

// doPackageDir analyzes the single package found in the directory, if there is one,
// plus a test package, if there is one.
func doPackageDir(directory string) {
	context := build.Default
	if len(context.BuildTags) != 0 {
		warnf("build tags %s previously set", context.BuildTags)
	}
	context.BuildTags = append(tagList, context.BuildTags...)

	pkg, err := context.ImportDir(directory, 0)
	if err != nil {
		// If it's just that there are no go source files, that's fine.
		if _, nogo := err.(*build.NoGoError); nogo {
			return
		}
		// Non-fatal: we are doing a recursive walk and there may be other directories.
		warnf("cannot process directory %s: %s", directory, err)
		return
	}
	var names []string
	names = append(names, pkg.GoFiles...)
	names = append(names, pkg.CgoFiles...)
	names = append(names, pkg.TestGoFiles...) // These are also in the "foo" package.
	names = append(names, pkg.SFiles...)
	prefixDirectory(directory, names)
	basePkg := doPackage(names, nil)
	// Is there also a "foo_test" package? If so, do that one as well.
	if len(pkg.XTestGoFiles) > 0 {
		names = pkg.XTestGoFiles
		prefixDirectory(directory, names)
		doPackage(names, basePkg)
	}
}

type Package struct {
	path      string
	defs      map[*ast.Ident]types.Object
	uses      map[*ast.Ident]types.Object
	selectors map[*ast.SelectorExpr]*types.Selection
	types     map[ast.Expr]types.TypeAndValue
	spans     map[types.Object]Span
	files     []*File
	typesPkg  *types.Package
}

// doPackage analyzes the single package constructed from the named files.
// It returns the parsed Package or nil if none of the files have been checked.
func doPackage(names []string, basePkg *Package) *Package {
	var files []*File
	var astFiles []*ast.File
	fs := token.NewFileSet()
	for _, name := range names {
		data, err := ioutil.ReadFile(name)
		if err != nil {
			// Warn but continue to next package.
			warnf("%s: %s", name, err)
			return nil
		}
		var parsedFile *ast.File
		if strings.HasSuffix(name, ".go") {
			parsedFile, err = parser.ParseFile(fs, name, data, parser.ParseComments)
			if err != nil {
				warnf("%s: %s", name, err)
				return nil
			}
			astFiles = append(astFiles, parsedFile)
		}
		file := &File{
			fset:    fs,
			content: data,
			name:    name,
			file:    parsedFile,
			dead:    make(map[ast.Node]bool),
		}
		files = append(files, file)
	}
	if len(astFiles) == 0 {
		return nil
	}
	pkg := new(Package)
	pkg.path = astFiles[0].Name.Name
	pkg.files = files
	// Type check the package.
	errs := pkg.check(fs, astFiles)
	if errs != nil {
		if vcfg.SucceedOnTypecheckFailure {
			os.Exit(0)
		}
		if *verbose || mustTypecheck {
			for _, err := range errs {
				fmt.Fprintf(os.Stderr, "%v\n", err)
			}
			if mustTypecheck {
				// This message could be silenced, and we could just exit,
				// but it might be helpful at least at first to make clear that the
				// above errors are coming from vet and not the compiler
				// (they often look like compiler errors, such as "declared but not used").
				errorf("typecheck failures")
			}
		}
	}

	// Check.
	for _, file := range files {
		file.pkg = pkg
		file.basePkg = basePkg
	}
	for name, fn := range pkgCheckers {
		if vet(name) {
			fn(pkg)
		}
	}
	if vcfg.VetxOnly {
		return pkg
	}

	chk := make(map[ast.Node][]func(*File, ast.Node))
	for typ, set := range checkers {
		for name, fn := range set {
			if vet(name) {
				chk[typ] = append(chk[typ], fn)
			}
		}
	}
	for _, file := range files {
		checkBuildTag(file)
		file.checkers = chk
		if file.file != nil {
			file.walkFile(file.name, file.file)
		}
	}
	return pkg
}

func visit(path string, f os.FileInfo, err error) error {
	if err != nil {
		warnf("walk error: %s", err)
		return err
	}
	// One package per directory. Ignore the files themselves.
	if !f.IsDir() {
		return nil
	}
	doPackageDir(path)
	return nil
}

func (pkg *Package) hasFileWithSuffix(suffix string) bool {
	for _, f := range pkg.files {
		if strings.HasSuffix(f.name, suffix) {
			return true
		}
	}
	return false
}

// walkDir recursively walks the tree looking for Go packages.
func walkDir(root string) {
	filepath.Walk(root, visit)
}

// errorf formats the error to standard error, adding program
// identification and a newline, and exits.
func errorf(format string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, "vet: "+format+"\n", args...)
	os.Exit(2)
}

// warnf formats the error to standard error, adding program
// identification and a newline, but does not exit.
func warnf(format string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, "vet: "+format+"\n", args...)
	setExit(1)
}

// Println is fmt.Println guarded by -v.
func Println(args ...interface{}) {
	if !*verbose {
		return
	}
	fmt.Println(args...)
}

// Printf is fmt.Printf guarded by -v.
func Printf(format string, args ...interface{}) {
	if !*verbose {
		return
	}
	fmt.Printf(format+"\n", args...)
}

// Bad reports an error and sets the exit code..
func (f *File) Bad(pos token.Pos, args ...interface{}) {
	f.Warn(pos, args...)
	setExit(1)
}

// Badf reports a formatted error and sets the exit code.
func (f *File) Badf(pos token.Pos, format string, args ...interface{}) {
	f.Warnf(pos, format, args...)
	setExit(1)
}

// loc returns a formatted representation of the position.
func (f *File) loc(pos token.Pos) string {
	if pos == token.NoPos {
		return ""
	}
	// Do not print columns. Because the pos often points to the start of an
	// expression instead of the inner part with the actual error, the
	// precision can mislead.
	posn := f.fset.Position(pos)
	return fmt.Sprintf("%s:%d", posn.Filename, posn.Line)
}

// locPrefix returns a formatted representation of the position for use as a line prefix.
func (f *File) locPrefix(pos token.Pos) string {
	if pos == token.NoPos {
		return ""
	}
	return fmt.Sprintf("%s: ", f.loc(pos))
}

// Warn reports an error but does not set the exit code.
func (f *File) Warn(pos token.Pos, args ...interface{}) {
	fmt.Fprintf(os.Stderr, "%s%s", f.locPrefix(pos), fmt.Sprintln(args...))
}

// Warnf reports a formatted error but does not set the exit code.
func (f *File) Warnf(pos token.Pos, format string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, "%s%s\n", f.locPrefix(pos), fmt.Sprintf(format, args...))
}

// walkFile walks the file's tree.
func (f *File) walkFile(name string, file *ast.File) {
	Println("Checking file", name)
	ast.Walk(f, file)
}

// Visit implements the ast.Visitor interface.
func (f *File) Visit(node ast.Node) ast.Visitor {
	f.updateDead(node)
	var key ast.Node
	switch node.(type) {
	case *ast.AssignStmt:
		key = assignStmt
	case *ast.BinaryExpr:
		key = binaryExpr
	case *ast.CallExpr:
		key = callExpr
	case *ast.CompositeLit:
		key = compositeLit
	case *ast.ExprStmt:
		key = exprStmt
	case *ast.ForStmt:
		key = forStmt
	case *ast.FuncDecl:
		key = funcDecl
	case *ast.FuncLit:
		key = funcLit
	case *ast.GenDecl:
		key = genDecl
	case *ast.InterfaceType:
		key = interfaceType
	case *ast.RangeStmt:
		key = rangeStmt
	case *ast.ReturnStmt:
		key = returnStmt
	case *ast.StructType:
		key = structType
	}
	for _, fn := range f.checkers[key] {
		fn(f, node)
	}
	return f
}

// gofmt returns a string representation of the expression.
func (f *File) gofmt(x ast.Expr) string {
	f.b.Reset()
	printer.Fprint(&f.b, f.fset, x)
	return f.b.String()
}

// imported[path][key] is previously written export data.
var imported = make(map[string]map[string]interface{})

// readVetx reads export data written by a previous
// invocation of vet on an imported package (path).
// The key is the name passed to registerExport
// when the data was originally generated.
// readVetx returns nil if the data is unavailable.
func readVetx(path, key string) interface{} {
	if path == "unsafe" || vcfg.ImportPath == "" {
		return nil
	}
	m := imported[path]
	if m == nil {
		file := vcfg.PackageVetx[path]
		if file == "" {
			return nil
		}
		data, err := ioutil.ReadFile(file)
		if err != nil {
			return nil
		}
		var out []vetxExport
		err = gob.NewDecoder(bytes.NewReader(data)).Decode(&out)
		if err != nil {
			return nil
		}
		m = make(map[string]interface{})
		for _, x := range out {
			m[x.Name] = x.Data
		}
		imported[path] = m
	}
	return m[key]
}