/* * rewrite t.Fail() or any other *testing.T method by replacing with T().Fail() * This function receives a selector expression (eg: t.Fail()) and * the name of the *testing.T param from the function declaration. Rewrites the * selector expression in place if the target was a *testing.T */ func replaceTestingTsMethodCalls(selectorExpr *ast.SelectorExpr, testingT string) { ident, ok := selectorExpr.X.(*ast.Ident) if !ok { return } if ident.Name == testingT { selectorExpr.X = newGinkgoTFromIdent(ident) } }
// compiles a selector statement like x.sel func (w *World) compileSelectorStmt(n *ast.SelectorExpr) Expr { x := w.compileExpr(n.X) t := x.Type() if t == nil { panic(err(n.Pos(), "void does not have member", n.Sel.Name)) } sel := strings.ToLower(n.Sel.Name) N := "" for i := 0; i < t.NumMethod(); i++ { name := t.Method(i).Name if strings.ToLower(name) == sel && unicode.IsUpper(rune(name[0])) && !strings.HasSuffix(name, GoExclusiveMethodSuffix) { N = t.Method(i).Name break } } if N == "" { panic(err(n.Pos(), t, "has no method", n.Sel.Name)) } return &selector{x, N} }
func (check *checker) selector(x *operand, e *ast.SelectorExpr) { // these must be declared before the "goto Error" statements var ( obj Object index []int indirect bool ) sel := e.Sel.Name // If the identifier refers to a package, handle everything here // so we don't need a "package" mode for operands: package names // can only appear in qualified identifiers which are mapped to // selector expressions. if ident, ok := e.X.(*ast.Ident); ok { if pkg, _ := check.scope.LookupParent(ident.Name).(*PkgName); pkg != nil { check.recordUse(ident, pkg) pkg.used = true exp := pkg.pkg.scope.Lookup(sel) if exp == nil { if !pkg.pkg.fake { check.errorf(e.Pos(), "%s not declared by package %s", sel, ident) } goto Error } if !exp.Exported() { check.errorf(e.Pos(), "%s not exported by package %s", sel, ident) // ok to continue } check.recordSelection(e, PackageObj, nil, exp, nil, false) // Simplified version of the code for *ast.Idents: // - imported objects are always fully initialized switch exp := exp.(type) { case *Const: assert(exp.Val() != nil) x.mode = constant x.typ = exp.typ x.val = exp.val case *TypeName: x.mode = typexpr x.typ = exp.typ case *Var: x.mode = variable x.typ = exp.typ case *Func: x.mode = value x.typ = exp.typ case *Builtin: x.mode = builtin x.typ = exp.typ x.id = exp.id default: unreachable() } x.expr = e return } } check.exprOrType(x, e.X) if x.mode == invalid { goto Error } obj, index, indirect = LookupFieldOrMethod(x.typ, check.pkg, sel) if obj == nil { if index != nil { // TODO(gri) should provide actual type where the conflict happens check.invalidOp(e.Pos(), "ambiguous selector %s", sel) } else { check.invalidOp(e.Pos(), "%s has no field or method %s", x, sel) } goto Error } if x.mode == typexpr { // method expression m, _ := obj.(*Func) if m == nil { check.invalidOp(e.Pos(), "%s has no method %s", x, sel) goto Error } // verify that m is in the method set of x.typ if !indirect && ptrRecv(m) { check.invalidOp(e.Pos(), "%s is not in method set of %s", sel, x.typ) goto Error } check.recordSelection(e, MethodExpr, x.typ, m, index, indirect) // the receiver type becomes the type of the first function // argument of the method expression's function type var params []*Var sig := m.typ.(*Signature) if sig.params != nil { params = sig.params.vars } x.mode = value x.typ = &Signature{ params: NewTuple(append([]*Var{NewVar(token.NoPos, check.pkg, "", x.typ)}, params...)...), results: sig.results, variadic: sig.variadic, } check.addDeclDep(m) } else { // regular selector switch obj := obj.(type) { case *Var: check.recordSelection(e, FieldVal, x.typ, obj, index, indirect) if x.mode == variable || indirect { x.mode = variable } else { x.mode = value } x.typ = obj.typ case *Func: // TODO(gri) This code appears elsewhere, too. Factor! // verify that obj is in the method set of x.typ (or &(x.typ) if x is addressable) // // spec: "A method call x.m() is valid if the method set of (the type of) x // contains m and the argument list can be assigned to the parameter // list of m. If x is addressable and &x's method set contains m, x.m() // is shorthand for (&x).m()". if !indirect && x.mode != variable && ptrRecv(obj) { check.invalidOp(e.Pos(), "%s is not in method set of %s", sel, x) goto Error } check.recordSelection(e, MethodVal, x.typ, obj, index, indirect) if debug { // Verify that LookupFieldOrMethod and MethodSet.Lookup agree. typ := x.typ if x.mode == variable { // If typ is not an (unnamed) pointer or an interface, // use *typ instead, because the method set of *typ // includes the methods of typ. // Variables are addressable, so we can always take their // address. if _, ok := typ.(*Pointer); !ok && !isInterface(typ) { typ = &Pointer{base: typ} } } // If we created a synthetic pointer type above, we will throw // away the method set computed here after use. // TODO(gri) Method set computation should probably always compute // both, the value and the pointer receiver method set and represent // them in a single structure. // TODO(gri) Consider also using a method set cache for the lifetime // of checker once we rely on MethodSet lookup instead of individual // lookup. mset := NewMethodSet(typ) if m := mset.Lookup(check.pkg, sel); m == nil || m.obj != obj { check.dump("%s: (%s).%v -> %s", e.Pos(), typ, obj.name, m) check.dump("%s\n", mset) panic("method sets and lookup don't agree") } } x.mode = value // remove receiver sig := *obj.typ.(*Signature) sig.recv = nil x.typ = &sig default: unreachable() } } // everything went well x.expr = e return Error: x.mode = invalid x.expr = e }