// zeroConst returns a new "zero" constant of the specified type, // which must not be an array or struct type: the zero values of // aggregates are well-defined but cannot be represented by Const. // func zeroConst(t types.Type) *Const { switch t := t.(type) { case *types.Basic: switch { case t.Info()&types.IsBoolean != 0: return NewConst(exact.MakeBool(false), t) case t.Info()&types.IsNumeric != 0: return NewConst(exact.MakeInt64(0), t) case t.Info()&types.IsString != 0: return NewConst(exact.MakeString(""), t) case t.Kind() == types.UnsafePointer: fallthrough case t.Kind() == types.UntypedNil: return nilConst(t) default: panic(fmt.Sprint("zeroConst for unexpected type:", t)) } case *types.Pointer, *types.Slice, *types.Interface, *types.Chan, *types.Map, *types.Signature: return nilConst(t) case *types.Named: return NewConst(zeroConst(t.Underlying()).Value, t) case *types.Array, *types.Struct, *types.Tuple: panic(fmt.Sprint("zeroConst applied to aggregate:", t)) } panic(fmt.Sprint("zeroConst: unexpected ", t)) }
// Conversion type-checks the conversion T(x). // The result is in x. func (check *Checker) conversion(x *operand, T Type) { constArg := x.mode == constant var ok bool switch { case constArg && isConstType(T): // constant conversion switch t := T.Underlying().(*Basic); { case representableConst(x.val, check.conf, t.kind, &x.val): ok = true case x.isInteger() && isString(t): codepoint := int64(-1) if i, ok := exact.Int64Val(x.val); ok { codepoint = i } // If codepoint < 0 the absolute value is too large (or unknown) for // conversion. This is the same as converting any other out-of-range // value - let string(codepoint) do the work. x.val = exact.MakeString(string(codepoint)) ok = true } case x.convertibleTo(check.conf, T): // non-constant conversion x.mode = value ok = true } if !ok { check.errorf(x.pos(), "cannot convert %s to %s", x, T) x.mode = invalid return } // The conversion argument types are final. For untyped values the // conversion provides the type, per the spec: "A constant may be // given a type explicitly by a constant declaration or conversion,...". final := x.typ if isUntyped(x.typ) { final = T // - For conversions to interfaces, use the argument's default type. // - For conversions of untyped constants to non-constant types, also // use the default type (e.g., []byte("foo") should report string // not []byte as type for the constant "foo"). // - Keep untyped nil for untyped nil arguments. if IsInterface(T) || constArg && !isConstType(T) { final = defaultType(x.typ) } check.updateExprType(x.expr, final, true) } x.typ = T }
// stringConst returns a 'string' constant that evaluates to s. func stringConst(s string) *Const { return NewConst(exact.MakeString(s), tString) }
// CreateTestMainPackage creates and returns a synthetic "main" // package that runs all the tests of the supplied packages, similar // to the one that would be created by the 'go test' tool. // // It returns nil if the program contains no tests. // func (prog *Program) CreateTestMainPackage(pkgs ...*Package) *Package { pkgs, tests, benchmarks, examples := FindTests(pkgs) if len(pkgs) == 0 { return nil } testmain := &Package{ Prog: prog, Members: make(map[string]Member), values: make(map[types.Object]Value), Object: types.NewPackage("test$main", "main"), } // Build package's init function. init := &Function{ name: "init", Signature: new(types.Signature), Synthetic: "package initializer", Pkg: testmain, Prog: prog, } init.startBody() if testMainStartBodyHook != nil { testMainStartBodyHook(init) } // Initialize packages to test. var pkgpaths []string for _, pkg := range pkgs { var v Call v.Call.Value = pkg.init v.setType(types.NewTuple()) init.emit(&v) pkgpaths = append(pkgpaths, pkg.Object.Path()) } sort.Strings(pkgpaths) init.emit(new(Return)) init.finishBody() testmain.init = init testmain.Object.MarkComplete() testmain.Members[init.name] = init // For debugging convenience, define an unexported const // that enumerates the packages. packagesConst := types.NewConst(token.NoPos, testmain.Object, "packages", tString, exact.MakeString(strings.Join(pkgpaths, " "))) memberFromObject(testmain, packagesConst, nil) // Create main *types.Func and *ssa.Function mainFunc := types.NewFunc(token.NoPos, testmain.Object, "main", new(types.Signature)) memberFromObject(testmain, mainFunc, nil) main := testmain.Func("main") main.Synthetic = "test main function" main.startBody() if testMainStartBodyHook != nil { testMainStartBodyHook(main) } if testingPkg := prog.ImportedPackage("testing"); testingPkg != nil { testingMain := testingPkg.Func("Main") testingMainParams := testingMain.Signature.Params() // The generated code is as if compiled from this: // // func main() { // match := func(_, _ string) (bool, error) { return true, nil } // tests := []testing.InternalTest{{"TestFoo", TestFoo}, ...} // benchmarks := []testing.InternalBenchmark{...} // examples := []testing.InternalExample{...} // testing.Main(match, tests, benchmarks, examples) // } matcher := &Function{ name: "matcher", Signature: testingMainParams.At(0).Type().(*types.Signature), Synthetic: "test matcher predicate", parent: main, Pkg: testmain, Prog: prog, } main.AnonFuncs = append(main.AnonFuncs, matcher) matcher.startBody() matcher.emit(&Return{Results: []Value{vTrue, nilConst(types.Universe.Lookup("error").Type())}}) matcher.finishBody() // Emit call: testing.Main(matcher, tests, benchmarks, examples). var c Call c.Call.Value = testingMain c.Call.Args = []Value{ matcher, testMainSlice(main, tests, testingMainParams.At(1).Type()), testMainSlice(main, benchmarks, testingMainParams.At(2).Type()), testMainSlice(main, examples, testingMainParams.At(3).Type()), } emitTailCall(main, &c) } else { // The program does not import "testing", but FindTests // returned non-nil, which must mean there were Examples // but no Tests or Benchmarks. // We'll simply call them from testmain.main; this will // ensure they don't panic, but will not check any // "Output:" comments. for _, eg := range examples { var c Call c.Call.Value = eg c.setType(types.NewTuple()) main.emit(&c) } main.emit(&Return{}) main.currentBlock = nil } main.finishBody() testmain.Members["main"] = main if prog.mode&PrintPackages != 0 { printMu.Lock() testmain.WriteTo(os.Stdout) printMu.Unlock() } if prog.mode&SanityCheckFunctions != 0 { sanityCheckPackage(testmain) } prog.packages[testmain.Object] = testmain return testmain }