// okPrintfArg compares the formatState to the arguments actually present, // reporting any discrepancies it can discern. If the final argument is ellipsissed, // there's little it can do for that. func (f *File) okPrintfArg(call *ast.CallExpr, state *formatState) (ok bool) { var v printVerb found := false // Linear scan is fast enough for a small list. for _, v = range printVerbs { if v.verb == state.verb { found = true break } } if !found { f.Badf(call.Pos(), "unrecognized printf verb %q", state.verb) return false } for _, flag := range state.flags { if !strings.ContainsRune(v.flags, rune(flag)) { f.Badf(call.Pos(), "unrecognized printf flag for verb %q: %q", state.verb, flag) return false } } // Verb is good. If len(state.argNums)>trueArgs, we have something like %.*s and all // but the final arg must be an integer. trueArgs := 1 if state.verb == '%' { trueArgs = 0 } nargs := len(state.argNums) for i := 0; i < nargs-trueArgs; i++ { argNum := state.argNums[i] if !f.argCanBeChecked(call, i, true, state) { return } arg := call.Args[argNum] if !f.matchArgType(argInt, nil, arg) { f.Badf(call.Pos(), "arg %s for * in printf format not of type int", f.gofmt(arg)) return false } } if state.verb == '%' { return true } argNum := state.argNums[len(state.argNums)-1] if !f.argCanBeChecked(call, len(state.argNums)-1, false, state) { return false } arg := call.Args[argNum] if !f.matchArgType(v.typ, nil, arg) { typeString := "" if typ := f.pkg.types[arg].Type; typ != nil { typeString = typ.String() } f.Badf(call.Pos(), "arg %s for printf verb %%%c of wrong type: %s", f.gofmt(arg), state.verb, typeString) return false } if v.typ&argString != 0 && v.verb != 'T' && !bytes.Contains(state.flags, []byte{'#'}) && f.recursiveStringer(arg) { f.Badf(call.Pos(), "arg %s for printf causes recursive call to String method", f.gofmt(arg)) return false } return true }
// argCanBeChecked reports whether the specified argument is statically present; // it may be beyond the list of arguments or in a terminal slice... argument, which // means we can't see it. func (f *File) argCanBeChecked(call *ast.CallExpr, formatArg int, isStar bool, state *formatState) bool { argNum := state.argNums[formatArg] if argNum < 0 { // Shouldn't happen, so catch it with prejudice. panic("negative arg num") } if argNum < len(call.Args)-1 { return true // Always OK. } if call.Ellipsis.IsValid() { return false // We just can't tell; there could be many more arguments. } if argNum < len(call.Args) { return true } // There are bad indexes in the format or there are fewer arguments than the format needs. verb := fmt.Sprintf("verb %%%c", state.verb) if isStar { verb = "indirect *" } // This is the argument number relative to the format: Printf("%s", "hi") will give 1 for the "hi". arg := argNum - state.firstArg + 1 // People think of arguments as 1-indexed. f.Badf(call.Pos(), "missing argument for %s %s: need %d, have %d", state.name, verb, arg, len(call.Args)-state.firstArg) return false }
// argCanBeChecked reports whether the specified argument is statically present; // it may be beyond the list of arguments or in a terminal slice... argument, which // means we can't see it. func (f *File) argCanBeChecked(call *ast.CallExpr, formatArg int, isStar bool, state *formatState) bool { argNum := state.argNums[formatArg] if argNum < 0 { // Shouldn't happen, so catch it with prejudice. panic("negative arg num") } if argNum == 0 { f.Badf(call.Pos(), `index value [0] for %s("%s"); indexes start at 1`, state.name, state.format) return false } if argNum < len(call.Args)-1 { return true // Always OK. } if call.Ellipsis.IsValid() { return false // We just can't tell; there could be many more arguments. } if argNum < len(call.Args) { return true } // There are bad indexes in the format or there are fewer arguments than the format needs. // This is the argument number relative to the format: Printf("%s", "hi") will give 1 for the "hi". arg := argNum - state.firstArg + 1 // People think of arguments as 1-indexed. f.Badf(call.Pos(), `missing argument for %s("%s"): format reads arg %d, have only %d args`, state.name, state.format, arg, len(call.Args)-state.firstArg) return false }
// conversion typechecks the type conversion conv to type typ. iota is the current // value of iota or -1 if iota doesn't have a value in the current context. The result // of the conversion is returned via x. If the conversion has type errors, the returned // x is marked as invalid (x.mode == invalid). // func (check *checker) conversion(x *operand, conv *ast.CallExpr, typ Type, iota int) { // all conversions have one argument if len(conv.Args) != 1 { check.invalidOp(conv.Pos(), "%s conversion requires exactly one argument", conv) goto Error } // evaluate argument check.expr(x, conv.Args[0], nil, iota) if x.mode == invalid { goto Error } if x.mode == constant && isConstType(typ) { // constant conversion // TODO(gri) implement this } else { // non-constant conversion if !x.isConvertible(typ) { check.invalidOp(conv.Pos(), "cannot convert %s to %s", x, typ) goto Error } x.mode = value } x.expr = conv x.typ = typ return Error: x.mode = invalid }
// conversion typechecks the type conversion conv to type typ. iota is the current // value of iota or -1 if iota doesn't have a value in the current context. The result // of the conversion is returned via x. If the conversion has type errors, the returned // x is marked as invalid (x.mode == invalid). // func (check *checker) conversion(x *operand, conv *ast.CallExpr, typ Type, iota int) { // all conversions have one argument if len(conv.Args) != 1 { check.invalidOp(conv.Pos(), "%s conversion requires exactly one argument", conv) goto Error } // evaluate argument check.expr(x, conv.Args[0], nil, iota) if x.mode == invalid { goto Error } if x.mode == constant && isConstType(typ) { // constant conversion typ := typ.Underlying().(*Basic) // For now just implement string(x) where x is an integer, // as a temporary work-around for issue 4982, which is a // common issue. if typ.kind == String { switch { case x.isInteger(): 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)) case isString(x.typ): // nothing to do default: goto ErrorMsg } } // TODO(gri) verify the remaining conversions. } else { // non-constant conversion if !x.isConvertible(check.ctxt, typ) { goto ErrorMsg } x.mode = value } // the conversion argument types are final; for now we just use x.typ // TODO(gri) Fix this. For untyped constants, the type should be typ. check.updateExprType(x.expr, x.typ, true) check.conversions[conv] = true // for cap/len checking x.expr = conv x.typ = typ return ErrorMsg: check.invalidOp(conv.Pos(), "cannot convert %s to %s", x, typ) Error: x.mode = invalid x.expr = conv }
// checkPrintf checks a call to a formatted print routine such as Printf. // call.Args[formatIndex] is (well, should be) the format argument. func (f *File) checkPrintf(call *ast.CallExpr, name string, formatIndex int) { if formatIndex >= len(call.Args) { f.Bad(call.Pos(), "too few arguments in call to", name) return } lit := f.pkg.types[call.Args[formatIndex]].Value if lit == nil { if *verbose { f.Warn(call.Pos(), "can't check non-constant format in call to", name) } return } if lit.Kind() != exact.String { f.Badf(call.Pos(), "constant %v not a string in call to %s", lit, name) return } format := exact.StringVal(lit) firstArg := formatIndex + 1 // Arguments are immediately after format string. if !strings.Contains(format, "%") { if len(call.Args) > firstArg { f.Badf(call.Pos(), "no formatting directive in %s call", name) } return } // Hard part: check formats against args. argNum := firstArg indexed := false for i, w := 0, 0; i < len(format); i += w { w = 1 if format[i] == '%' { state := f.parsePrintfVerb(call, name, format[i:], firstArg, argNum) if state == nil { return } w = len(state.format) if state.indexed { indexed = true } if !f.okPrintfArg(call, state) { // One error per format is enough. return } if len(state.argNums) > 0 { // Continue with the next sequential argument. argNum = state.argNums[len(state.argNums)-1] + 1 } } } // Dotdotdot is hard. if call.Ellipsis.IsValid() && argNum >= len(call.Args)-1 { return } // If the arguments were direct indexed, we assume the programmer knows what's up. // Otherwise, there should be no leftover arguments. if !indexed && argNum != len(call.Args) { expect := argNum - firstArg numArgs := len(call.Args) - firstArg f.Badf(call.Pos(), "wrong number of args for format in %s call: %d needed but %d args", name, expect, numArgs) } }
func (f *File) checkUnsafePointer(x *ast.CallExpr) { if !vet("unsafeptr") { return } if len(x.Args) != 1 { return } if f.hasBasicType(x.Fun, types.UnsafePointer) && f.hasBasicType(x.Args[0], types.Uintptr) && !f.isSafeUintptr(x.Args[0]) { f.Badf(x.Pos(), "possible misuse of unsafe.Pointer") } }
// checkPrintf checks a call to a formatted print routine such as Printf. // call.Args[formatIndex] is (well, should be) the format argument. func (f *File) checkPrintf(call *ast.CallExpr, name string, formatIndex int) { if formatIndex >= len(call.Args) { return } lit := f.literal(call.Args[formatIndex]) if lit == nil { if *verbose { f.Warn(call.Pos(), "can't check non-literal format in call to", name) } return } if lit.Kind != token.STRING { f.Badf(call.Pos(), "literal %v not a string in call to", lit.Value, name) } format, err := strconv.Unquote(lit.Value) if err != nil { // Shouldn't happen if parser returned no errors, but be safe. f.Badf(call.Pos(), "invalid quoted string literal") } firstArg := formatIndex + 1 // Arguments are immediately after format string. if !strings.Contains(format, "%") { if len(call.Args) > firstArg { f.Badf(call.Pos(), "no formatting directive in %s call", name) } return } // Hard part: check formats against args. argNum := firstArg for i, w := 0, 0; i < len(format); i += w { w = 1 if format[i] == '%' { verb, flags, nbytes, nargs := f.parsePrintfVerb(call, format[i:]) w = nbytes if verb == '%' { // "%%" does nothing interesting. continue } // If we've run out of args, print after loop will pick that up. if argNum+nargs <= len(call.Args) { f.checkPrintfArg(call, verb, flags, argNum, nargs) } argNum += nargs } } // TODO: Dotdotdot is hard. if call.Ellipsis.IsValid() && argNum != len(call.Args) { return } if argNum != len(call.Args) { expect := argNum - firstArg numArgs := len(call.Args) - firstArg f.Badf(call.Pos(), "wrong number of args for format in %s call: %d needed but %d args", name, expect, numArgs) } }
// checkPrint checks a call to an unformatted print routine such as Println. // call.Args[firstArg] is the first argument to be printed. func (f *File) checkPrint(call *ast.CallExpr, name string, firstArg int) { isLn := strings.HasSuffix(name, "ln") isF := strings.HasPrefix(name, "F") args := call.Args // check for Println(os.Stderr, ...) if firstArg == 0 && !isF && len(args) > 0 { if sel, ok := args[0].(*ast.SelectorExpr); ok { if x, ok := sel.X.(*ast.Ident); ok { if x.Name == "os" && strings.HasPrefix(sel.Sel.Name, "Std") { f.Badf(call.Pos(), "first argument to %s is %s.%s", name, x.Name, sel.Sel.Name) } } } } if len(args) <= firstArg { // If we have a call to a method called Error that satisfies the Error interface, // then it's ok. Otherwise it's something like (*T).Error from the testing package // and we need to check it. if name == "Error" && f.isErrorMethodCall(call) { return } // If it's an Error call now, it's probably for printing errors. if !isLn { // Check the signature to be sure: there are niladic functions called "error". if firstArg != 0 || f.numArgsInSignature(call) != firstArg { f.Badf(call.Pos(), "no args in %s call", name) } } return } arg := args[firstArg] if lit, ok := arg.(*ast.BasicLit); ok && lit.Kind == token.STRING { if strings.Contains(lit.Value, "%") { f.Badf(call.Pos(), "possible formatting directive in %s call", name) } } if isLn { // The last item, if a string, should not have a newline. arg = args[len(call.Args)-1] if lit, ok := arg.(*ast.BasicLit); ok && lit.Kind == token.STRING { if strings.HasSuffix(lit.Value, `\n"`) { f.Badf(call.Pos(), "%s call ends with newline", name) } } } for _, arg := range args { if f.recursiveStringer(arg) { f.Badf(call.Pos(), "arg %s for print causes recursive call to String method", f.gofmt(arg)) } } }
func (f *File) checkPrintfArg(call *ast.CallExpr, verb rune, flags []byte, argNum, nargs int) { // Linear scan is fast enough for a small list. for _, v := range printVerbs { if v.verb == verb { for _, flag := range flags { if !strings.ContainsRune(v.flags, rune(flag)) { f.Badf(call.Pos(), "unrecognized printf flag for verb %q: %q", verb, flag) return } } // Verb is good. If nargs>1, we have something like %.*s and all but the final // arg must be integer. for i := 0; i < nargs-1; i++ { if !f.matchArgType(argInt, call.Args[argNum+i]) { f.Badf(call.Pos(), "arg %s for * in printf format not of type int", f.gofmt(call.Args[argNum+i])) } } for _, v := range printVerbs { if v.verb == verb { arg := call.Args[argNum+nargs-1] if !f.matchArgType(v.typ, arg) { typeString := "" if typ := f.pkg.types[arg]; typ != nil { typeString = typ.String() } f.Badf(call.Pos(), "arg %s for printf verb %%%c of wrong type: %s", f.gofmt(arg), verb, typeString) } break } } return } } f.Badf(call.Pos(), "unrecognized printf verb %q", verb) }
func (f *File) checkPrintfVerb(call *ast.CallExpr, verb rune, flags []byte) { // Linear scan is fast enough for a small list. for _, v := range printVerbs { if v.verb == verb { for _, flag := range flags { if !strings.ContainsRune(v.flags, rune(flag)) { f.Badf(call.Pos(), "unrecognized printf flag for verb %q: %q", verb, flag) } } return } } f.Badf(call.Pos(), "unrecognized printf verb %q", verb) }
// parsePrintfVerb looks the formatting directive that begins the format string // and returns a formatState that encodes what the directive wants, without looking // at the actual arguments present in the call. The result is nil if there is an error. func (f *File) parsePrintfVerb(call *ast.CallExpr, name, format string, firstArg, argNum int) *formatState { state := &formatState{ format: format, name: name, flags: make([]byte, 0, 5), argNum: argNum, argNums: make([]int, 0, 1), nbytes: 1, // There's guaranteed to be a percent sign. indexed: false, firstArg: firstArg, file: f, call: call, } // There may be flags. state.parseFlags() indexPending := false // There may be an index. if !state.parseIndex() { return nil } // There may be a width. if !state.parseNum() { return nil } // There may be a precision. if !state.parsePrecision() { return nil } // Now a verb, possibly prefixed by an index (which we may already have). if !indexPending && !state.parseIndex() { return nil } if state.nbytes == len(state.format) { f.Badf(call.Pos(), "missing verb at end of format string in %s call", name) return nil } verb, w := utf8.DecodeRuneInString(state.format[state.nbytes:]) state.verb = verb state.nbytes += w if verb != '%' { state.argNums = append(state.argNums, state.argNum) } state.format = state.format[:state.nbytes] return state }
// checkPrintf checks a call to a formatted print routine such as Printf. // The skip argument records how many arguments to ignore; that is, // call.Args[skip] is (well, should be) the format argument. func (f *File) checkPrintf(call *ast.CallExpr, name string, skip int) { if len(call.Args) <= skip { return } lit := f.literal(call.Args[skip]) if lit == nil { if *verbose { f.Warn(call.Pos(), "can't check non-literal format in call to", name) } return } if lit.Kind != token.STRING { f.Badf(call.Pos(), "literal %v not a string in call to", lit.Value, name) } format, err := strconv.Unquote(lit.Value) if err != nil { f.Badf(call.Pos(), "invalid quoted string literal") } if !strings.Contains(format, "%") { if len(call.Args) > skip+1 { f.Badf(call.Pos(), "no formatting directive in %s call", name) } return } // Hard part: check formats against args. // Trivial but useful test: count. numArgs := 0 for i, w := 0, 0; i < len(format); i += w { w = 1 if format[i] == '%' { nbytes, nargs := f.parsePrintfVerb(call, format[i:]) w = nbytes numArgs += nargs } } expect := len(call.Args) - (skip + 1) // Don't be too strict on dotdotdot. if call.Ellipsis.IsValid() && numArgs >= expect { return } if numArgs != expect { f.Badf(call.Pos(), "wrong number of args in %s call: %d needed but %d args", name, numArgs, expect) } }
// checkPrint checks a call to an unformatted print routine such as Println. // The skip argument records how many arguments to ignore; that is, // call.Args[skip] is the first argument to be printed. func (f *File) checkPrint(call *ast.CallExpr, name string, skip int) { isLn := strings.HasSuffix(name, "ln") args := call.Args if len(args) <= skip { if *verbose && !isLn { f.Badf(call.Pos(), "no args in %s call", name) } return } arg := args[skip] if lit, ok := arg.(*ast.BasicLit); ok && lit.Kind == token.STRING { if strings.Contains(lit.Value, "%") { f.Badf(call.Pos(), "possible formatting directive in %s call", name) } } if isLn { // The last item, if a string, should not have a newline. arg = args[len(call.Args)-1] if lit, ok := arg.(*ast.BasicLit); ok && lit.Kind == token.STRING { if strings.HasSuffix(lit.Value, `\n"`) { f.Badf(call.Pos(), "%s call ends with newline", name) } } } }
func NewFunctionCall(fs *token.FileSet, n *ast.CallExpr) (*FunctionCall, error) { parts := make([]string, 0) ast.Inspect(n.Fun, func(n ast.Node) bool { if n == nil { return true } switch obj := n.(type) { case *ast.SelectorExpr: // do nothing, just avoid the default case break case *ast.Ident: parts = append(parts, obj.Name) default: return false } return true }) if len(parts) == 0 { return nil, ErrNotSupported } args := make([]string, len(n.Args)) for i, arg := range n.Args { ast.Inspect(arg, func(n ast.Node) bool { switch t := n.(type) { case *ast.Ident: args[i] = t.Name case *ast.BasicLit: args[i] = t.Value } return true }) } return &FunctionCall{ Line: fs.Position(n.Pos()).Line, Expr: strings.Join(parts, "."), Name: parts[len(parts)-1], Args: args, }, nil }
// conversion typechecks the type conversion conv to type typ. iota is the current // value of iota or -1 if iota doesn't have a value in the current context. The result // of the conversion is returned via x. If the conversion has type errors, the returned // x is marked as invalid (x.mode == invalid). // func (check *checker) conversion(x *operand, conv *ast.CallExpr, typ Type, iota int) { // all conversions have one argument if len(conv.Args) != 1 { check.invalidOp(conv.Pos(), "%s conversion requires exactly one argument", conv) goto Error } // evaluate argument check.expr(x, conv.Args[0], nil, iota) if x.mode == invalid { goto Error } // TODO(gri) fix this - implement all checks and constant evaluation x.mode = value x.expr = conv x.typ = typ return Error: x.mode = invalid }
// checkPrintf checks a call to a formatted print routine such as Printf. func (f *File) checkPrintf(call *ast.CallExpr, name string) { format, idx := formatString(f, call) if idx < 0 { if *verbose { f.Warn(call.Pos(), "can't check non-constant format in call to", name) } return } firstArg := idx + 1 // Arguments are immediately after format string. if !strings.Contains(format, "%") { if len(call.Args) > firstArg { f.Badf(call.Pos(), "no formatting directive in %s call", name) } return } // Hard part: check formats against args. argNum := firstArg maxArgNum := firstArg for i, w := 0, 0; i < len(format); i += w { w = 1 if format[i] == '%' { state := f.parsePrintfVerb(call, name, format[i:], firstArg, argNum) if state == nil { return } w = len(state.format) if !f.okPrintfArg(call, state) { // One error per format is enough. return } if len(state.argNums) > 0 { // Continue with the next sequential argument. argNum = state.argNums[len(state.argNums)-1] + 1 } for _, n := range state.argNums { if n >= maxArgNum { maxArgNum = n + 1 } } } } // Dotdotdot is hard. if call.Ellipsis.IsValid() && maxArgNum >= len(call.Args)-1 { return } // There should be no leftover arguments. if maxArgNum != len(call.Args) { expect := maxArgNum - firstArg numArgs := len(call.Args) - firstArg f.Badf(call.Pos(), "wrong number of args for format in %s call: %d needed but %d args", name, expect, numArgs) } }
func (w *World) compileSource(n *ast.CallExpr) Expr { if len(n.Args) != 1 { panic(err(n.Pos(), "source() needs 1 string argument, got", len(n.Args))) } arg := n.Args[0] if lit, ok := arg.(*ast.BasicLit); ok && lit.Kind == token.STRING { code, err1 := ioutil.ReadFile(lit.Value[1 : len(lit.Value)-1]) if err1 != nil { panic(err(n.Pos(), err1)) } block, err2 := w.Compile(string(code)) if err1 != nil { panic(err(n.Pos(), err2)) } return block } else { panic(err(n.Pos(), "source() needs literal string argument")) } }
// checkPrint checks a call to an unformatted print routine such as Println. // The skip argument records how many arguments to ignore; that is, // call.Args[skip] is the first argument to be printed. func (f *File) checkPrint(call *ast.CallExpr, name string, skip int) { isLn := strings.HasSuffix(name, "ln") isF := strings.HasPrefix(name, "F") args := call.Args // check for Println(os.Stderr, ...) if skip == 0 && !isF && len(args) > 0 { if sel, ok := args[0].(*ast.SelectorExpr); ok { if x, ok := sel.X.(*ast.Ident); ok { if x.Name == "os" && strings.HasPrefix(sel.Sel.Name, "Std") { f.Warnf(call.Pos(), "first argument to %s is %s.%s", name, x.Name, sel.Sel.Name) } } } } if len(args) <= skip { // TODO: check that the receiver of Error() is of type error. if !isLn && name != "Error" { f.Badf(call.Pos(), "no args in %s call", name) } return } arg := args[skip] if lit, ok := arg.(*ast.BasicLit); ok && lit.Kind == token.STRING { if strings.Contains(lit.Value, "%") { f.Badf(call.Pos(), "possible formatting directive in %s call", name) } } if isLn { // The last item, if a string, should not have a newline. arg = args[len(call.Args)-1] if lit, ok := arg.(*ast.BasicLit); ok && lit.Kind == token.STRING { if strings.HasSuffix(lit.Value, `\n"`) { f.Badf(call.Pos(), "%s call ends with newline", name) } } } }
// checkPrintf checks a call to a formatted print routine such as Printf. // The skip argument records how many arguments to ignore; that is, // call.Args[skip] is (well, should be) the format argument. func (f *File) checkPrintf(call *ast.CallExpr, name string, skip int) { if len(call.Args) <= skip { return } // Common case: literal is first argument. arg := call.Args[skip] lit, ok := arg.(*ast.BasicLit) if !ok { // Too hard to check. if *verbose { f.Warn(call.Pos(), "can't check non-literal format in call to", name) } return } if lit.Kind == token.STRING { if !strings.Contains(lit.Value, "%") { if len(call.Args) > skip+1 { f.Badf(call.Pos(), "no formatting directive in %s call", name) } return } } // Hard part: check formats against args. // Trivial but useful test: count. numArgs := 0 for i, w := 0, 0; i < len(lit.Value); i += w { w = 1 if lit.Value[i] == '%' { nbytes, nargs := f.parsePrintfVerb(call, lit.Value[i:]) w = nbytes numArgs += nargs } } expect := len(call.Args) - (skip + 1) if numArgs != expect { f.Badf(call.Pos(), "wrong number of args in %s call: %d needed but %d args", name, numArgs, expect) } }
func (w *World) compileCallExpr(n *ast.CallExpr) Expr { // compile function or method to be called var f Expr var fname string switch Fun := n.Fun.(type) { default: panic(err(n.Pos(), "not allowed:", typ(n.Fun))) case *ast.Ident: // function call fname = Fun.Name if fname == "source" { return w.compileSource(n) } f = w.compileExpr(Fun) case *ast.SelectorExpr: // method call f = w.compileSelectorStmt(Fun) fname = Fun.Sel.Name } if f.Type().Kind() != reflect.Func { panic(err(n.Pos(), "can not call", Format(n))) } // compile and check args args := make([]Expr, len(n.Args)) variadic := f.Type().IsVariadic() if !variadic && len(n.Args) != f.Type().NumIn() { panic(err(n.Pos(), fname, "needs", f.Type().NumIn(), "arguments, got", len(n.Args))) // TODO: varargs } for i := range args { if variadic { args[i] = w.compileExpr(n.Args[i]) // no type check or conversion } else { args[i] = typeConv(n.Args[i].Pos(), w.compileExpr(n.Args[i]), f.Type().In(i)) } } return &call{f, args} }
// builtin type-checks a call to the built-in specified by id and // returns true if the call is valid, with *x holding the result; // but x.expr is not set. If the call is invalid, the result is // false, and *x is undefined. // func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ bool) { // append is the only built-in that permits the use of ... for the last argument bin := predeclaredFuncs[id] if call.Ellipsis.IsValid() && id != _Append { check.invalidOp(call.Ellipsis, "invalid use of ... with built-in %s", bin.name) check.use(call.Args...) return } // For len(x) and cap(x) we need to know if x contains any function calls or // receive operations. Save/restore current setting and set hasCallOrRecv to // false for the evaluation of x so that we can check it afterwards. // Note: We must do this _before_ calling unpack because unpack evaluates the // first argument before we even call arg(x, 0)! if id == _Len || id == _Cap { defer func(b bool) { check.hasCallOrRecv = b }(check.hasCallOrRecv) check.hasCallOrRecv = false } // determine actual arguments var arg getter nargs := len(call.Args) switch id { default: // make argument getter arg, nargs, _ = unpack(func(x *operand, i int) { check.multiExpr(x, call.Args[i]) }, nargs, false) if arg == nil { return } // evaluate first argument, if present if nargs > 0 { arg(x, 0) if x.mode == invalid { return } } case _Make, _New, _Offsetof, _Trace: // arguments require special handling } // check argument count { msg := "" if nargs < bin.nargs { msg = "not enough" } else if !bin.variadic && nargs > bin.nargs { msg = "too many" } if msg != "" { check.invalidOp(call.Rparen, "%s arguments for %s (expected %d, found %d)", msg, call, bin.nargs, nargs) return } } switch id { case _Append: // append(s S, x ...T) S, where T is the element type of S // spec: "The variadic function append appends zero or more values x to s of type // S, which must be a slice type, and returns the resulting slice, also of type S. // The values x are passed to a parameter of type ...T where T is the element type // of S and the respective parameter passing rules apply." S := x.typ var T Type if s, _ := S.Underlying().(*Slice); s != nil { T = s.elem } else { check.invalidArg(x.pos(), "%s is not a slice", x) return } // remember arguments that have been evaluated already alist := []operand{*x} // spec: "As a special case, append also accepts a first argument assignable // to type []byte with a second argument of string type followed by ... . // This form appends the bytes of the string. if nargs == 2 && call.Ellipsis.IsValid() && x.assignableTo(check.conf, NewSlice(universeByte), nil) { arg(x, 1) if x.mode == invalid { return } if isString(x.typ) { if check.Types != nil { sig := makeSig(S, S, x.typ) sig.variadic = true check.recordBuiltinType(call.Fun, sig) } x.mode = value x.typ = S break } alist = append(alist, *x) // fallthrough } // check general case by creating custom signature sig := makeSig(S, S, NewSlice(T)) // []T required for variadic signature sig.variadic = true check.arguments(x, call, sig, func(x *operand, i int) { // only evaluate arguments that have not been evaluated before if i < len(alist) { *x = alist[i] return } arg(x, i) }, nargs) // ok to continue even if check.arguments reported errors x.mode = value x.typ = S if check.Types != nil { check.recordBuiltinType(call.Fun, sig) } case _Cap, _Len: // cap(x) // len(x) mode := invalid var typ Type var val constant.Value switch typ = implicitArrayDeref(x.typ.Underlying()); t := typ.(type) { case *Basic: if isString(t) && id == _Len { if x.mode == constant_ { mode = constant_ val = constant.MakeInt64(int64(len(constant.StringVal(x.val)))) } else { mode = value } } case *Array: mode = value // spec: "The expressions len(s) and cap(s) are constants // if the type of s is an array or pointer to an array and // the expression s does not contain channel receives or // function calls; in this case s is not evaluated." if !check.hasCallOrRecv { mode = constant_ val = constant.MakeInt64(t.len) } case *Slice, *Chan: mode = value case *Map: if id == _Len { mode = value } } if mode == invalid { check.invalidArg(x.pos(), "%s for %s", x, bin.name) return } x.mode = mode x.typ = Typ[Int] x.val = val if check.Types != nil && mode != constant_ { check.recordBuiltinType(call.Fun, makeSig(x.typ, typ)) } case _Close: // close(c) c, _ := x.typ.Underlying().(*Chan) if c == nil { check.invalidArg(x.pos(), "%s is not a channel", x) return } if c.dir == RecvOnly { check.invalidArg(x.pos(), "%s must not be a receive-only channel", x) return } x.mode = novalue if check.Types != nil { check.recordBuiltinType(call.Fun, makeSig(nil, c)) } case _Complex: // complex(x, y floatT) complexT var y operand arg(&y, 1) if y.mode == invalid { return } // convert or check untyped arguments d := 0 if isUntyped(x.typ) { d |= 1 } if isUntyped(y.typ) { d |= 2 } switch d { case 0: // x and y are typed => nothing to do case 1: // only x is untyped => convert to type of y check.convertUntyped(x, y.typ) case 2: // only y is untyped => convert to type of x check.convertUntyped(&y, x.typ) case 3: // x and y are untyped => // 1) if both are constants, convert them to untyped // floating-point numbers if possible, // 2) if one of them is not constant (possible because // it contains a shift that is yet untyped), convert // both of them to float64 since they must have the // same type to succeed (this will result in an error // because shifts of floats are not permitted) if x.mode == constant_ && y.mode == constant_ { toFloat := func(x *operand) { if isNumeric(x.typ) && constant.Sign(constant.Imag(x.val)) == 0 { x.typ = Typ[UntypedFloat] } } toFloat(x) toFloat(&y) } else { check.convertUntyped(x, Typ[Float64]) check.convertUntyped(&y, Typ[Float64]) // x and y should be invalid now, but be conservative // and check below } } if x.mode == invalid || y.mode == invalid { return } // both argument types must be identical if !Identical(x.typ, y.typ) { check.invalidArg(x.pos(), "mismatched types %s and %s", x.typ, y.typ) return } // the argument types must be of floating-point type if !isFloat(x.typ) { check.invalidArg(x.pos(), "arguments have type %s, expected floating-point", x.typ) return } // if both arguments are constants, the result is a constant if x.mode == constant_ && y.mode == constant_ { x.val = constant.BinaryOp(constant.ToFloat(x.val), token.ADD, constant.MakeImag(constant.ToFloat(y.val))) } else { x.mode = value } // determine result type var res BasicKind switch x.typ.Underlying().(*Basic).kind { case Float32: res = Complex64 case Float64: res = Complex128 case UntypedFloat: res = UntypedComplex default: unreachable() } resTyp := Typ[res] if check.Types != nil && x.mode != constant_ { check.recordBuiltinType(call.Fun, makeSig(resTyp, x.typ, x.typ)) } x.typ = resTyp case _Copy: // copy(x, y []T) int var dst Type if t, _ := x.typ.Underlying().(*Slice); t != nil { dst = t.elem } var y operand arg(&y, 1) if y.mode == invalid { return } var src Type switch t := y.typ.Underlying().(type) { case *Basic: if isString(y.typ) { src = universeByte } case *Slice: src = t.elem } if dst == nil || src == nil { check.invalidArg(x.pos(), "copy expects slice arguments; found %s and %s", x, &y) return } if !Identical(dst, src) { check.invalidArg(x.pos(), "arguments to copy %s and %s have different element types %s and %s", x, &y, dst, src) return } if check.Types != nil { check.recordBuiltinType(call.Fun, makeSig(Typ[Int], x.typ, y.typ)) } x.mode = value x.typ = Typ[Int] case _Delete: // delete(m, k) m, _ := x.typ.Underlying().(*Map) if m == nil { check.invalidArg(x.pos(), "%s is not a map", x) return } arg(x, 1) // k if x.mode == invalid { return } if !x.assignableTo(check.conf, m.key, nil) { check.invalidArg(x.pos(), "%s is not assignable to %s", x, m.key) return } x.mode = novalue if check.Types != nil { check.recordBuiltinType(call.Fun, makeSig(nil, m, m.key)) } case _Imag, _Real: // imag(complexT) floatT // real(complexT) floatT // convert or check untyped argument if isUntyped(x.typ) { if x.mode == constant_ { // an untyped constant number can alway be considered // as a complex constant if isNumeric(x.typ) { x.typ = Typ[UntypedComplex] } } else { // an untyped non-constant argument may appear if // it contains a (yet untyped non-constant) shift // expression: convert it to complex128 which will // result in an error (shift of complex value) check.convertUntyped(x, Typ[Complex128]) // x should be invalid now, but be conservative and check if x.mode == invalid { return } } } // the argument must be of complex type if !isComplex(x.typ) { check.invalidArg(x.pos(), "argument has type %s, expected complex type", x.typ) return } // if the argument is a constant, the result is a constant if x.mode == constant_ { if id == _Real { x.val = constant.Real(x.val) } else { x.val = constant.Imag(x.val) } } else { x.mode = value } // determine result type var res BasicKind switch x.typ.Underlying().(*Basic).kind { case Complex64: res = Float32 case Complex128: res = Float64 case UntypedComplex: res = UntypedFloat default: unreachable() } resTyp := Typ[res] if check.Types != nil && x.mode != constant_ { check.recordBuiltinType(call.Fun, makeSig(resTyp, x.typ)) } x.typ = resTyp case _Make: // make(T, n) // make(T, n, m) // (no argument evaluated yet) arg0 := call.Args[0] T := check.typ(arg0) if T == Typ[Invalid] { return } var min int // minimum number of arguments switch T.Underlying().(type) { case *Slice: min = 2 case *Map, *Chan: min = 1 default: check.invalidArg(arg0.Pos(), "cannot make %s; type must be slice, map, or channel", arg0) return } if nargs < min || min+1 < nargs { check.errorf(call.Pos(), "%s expects %d or %d arguments; found %d", call, min, min+1, nargs) return } var sizes []int64 // constant integer arguments, if any for _, arg := range call.Args[1:] { if s, ok := check.index(arg, -1); ok && s >= 0 { sizes = append(sizes, s) } } if len(sizes) == 2 && sizes[0] > sizes[1] { check.invalidArg(call.Args[1].Pos(), "length and capacity swapped") // safe to continue } x.mode = value x.typ = T if check.Types != nil { params := [...]Type{T, Typ[Int], Typ[Int]} check.recordBuiltinType(call.Fun, makeSig(x.typ, params[:1+len(sizes)]...)) } case _New: // new(T) // (no argument evaluated yet) T := check.typ(call.Args[0]) if T == Typ[Invalid] { return } x.mode = value x.typ = &Pointer{base: T} if check.Types != nil { check.recordBuiltinType(call.Fun, makeSig(x.typ, T)) } case _Panic: // panic(x) T := new(Interface) check.assignment(x, T, "argument to panic") if x.mode == invalid { return } x.mode = novalue if check.Types != nil { check.recordBuiltinType(call.Fun, makeSig(nil, T)) } case _Print, _Println: // print(x, y, ...) // println(x, y, ...) var params []Type if nargs > 0 { params = make([]Type, nargs) for i := 0; i < nargs; i++ { if i > 0 { arg(x, i) // first argument already evaluated } check.assignment(x, nil, "argument to "+predeclaredFuncs[id].name) if x.mode == invalid { // TODO(gri) "use" all arguments? return } params[i] = x.typ } } x.mode = novalue if check.Types != nil { check.recordBuiltinType(call.Fun, makeSig(nil, params...)) } case _Recover: // recover() interface{} x.mode = value x.typ = new(Interface) if check.Types != nil { check.recordBuiltinType(call.Fun, makeSig(x.typ)) } case _Alignof: // unsafe.Alignof(x T) uintptr check.assignment(x, nil, "argument to unsafe.Alignof") if x.mode == invalid { return } x.mode = constant_ x.val = constant.MakeInt64(check.conf.alignof(x.typ)) x.typ = Typ[Uintptr] // result is constant - no need to record signature case _Offsetof: // unsafe.Offsetof(x T) uintptr, where x must be a selector // (no argument evaluated yet) arg0 := call.Args[0] selx, _ := unparen(arg0).(*ast.SelectorExpr) if selx == nil { check.invalidArg(arg0.Pos(), "%s is not a selector expression", arg0) check.use(arg0) return } check.expr(x, selx.X) if x.mode == invalid { return } base := derefStructPtr(x.typ) sel := selx.Sel.Name obj, index, indirect := LookupFieldOrMethod(base, false, check.pkg, sel) switch obj.(type) { case nil: check.invalidArg(x.pos(), "%s has no single field %s", base, sel) return case *Func: // TODO(gri) Using derefStructPtr may result in methods being found // that don't actually exist. An error either way, but the error // message is confusing. See: https://play.golang.org/p/al75v23kUy , // but go/types reports: "invalid argument: x.m is a method value". check.invalidArg(arg0.Pos(), "%s is a method value", arg0) return } if indirect { check.invalidArg(x.pos(), "field %s is embedded via a pointer in %s", sel, base) return } // TODO(gri) Should we pass x.typ instead of base (and indirect report if derefStructPtr indirected)? check.recordSelection(selx, FieldVal, base, obj, index, false) offs := check.conf.offsetof(base, index) x.mode = constant_ x.val = constant.MakeInt64(offs) x.typ = Typ[Uintptr] // result is constant - no need to record signature case _Sizeof: // unsafe.Sizeof(x T) uintptr check.assignment(x, nil, "argument to unsafe.Sizeof") if x.mode == invalid { return } x.mode = constant_ x.val = constant.MakeInt64(check.conf.sizeof(x.typ)) x.typ = Typ[Uintptr] // result is constant - no need to record signature case _Assert: // assert(pred) causes a typechecker error if pred is false. // The result of assert is the value of pred if there is no error. // Note: assert is only available in self-test mode. if x.mode != constant_ || !isBoolean(x.typ) { check.invalidArg(x.pos(), "%s is not a boolean constant", x) return } if x.val.Kind() != constant.Bool { check.errorf(x.pos(), "internal error: value of %s should be a boolean constant", x) return } if !constant.BoolVal(x.val) { check.errorf(call.Pos(), "%s failed", call) // compile-time assertion failure - safe to continue } // result is constant - no need to record signature case _Trace: // trace(x, y, z, ...) dumps the positions, expressions, and // values of its arguments. The result of trace is the value // of the first argument. // Note: trace is only available in self-test mode. // (no argument evaluated yet) if nargs == 0 { check.dump("%s: trace() without arguments", call.Pos()) x.mode = novalue break } var t operand x1 := x for _, arg := range call.Args { check.rawExpr(x1, arg, nil) // permit trace for types, e.g.: new(trace(T)) check.dump("%s: %s", x1.pos(), x1) x1 = &t // use incoming x only for first argument } // trace is only available in test mode - no need to record signature default: unreachable() } return true }
// builtin type-checks a call to the built-in specified by id and // returns true if the call is valid, with *x holding the result; // but x.expr is not set. If the call is invalid, the result is // false, and *x is undefined. // func (check *checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ bool) { // append is the only built-in that permits the use of ... for the last argument bin := predeclaredFuncs[id] if call.Ellipsis.IsValid() && id != _Append { check.invalidOp(call.Ellipsis, "invalid use of ... with built-in %s", bin.name) check.use(call.Args) return } // determine actual arguments var arg getter nargs := len(call.Args) switch id { default: // make argument getter arg, nargs = unpack(func(x *operand, i int) { check.expr(x, call.Args[i]) }, nargs, false) // evaluate first argument, if present if nargs > 0 { arg(x, 0) if x.mode == invalid { return } } case _Make, _New, _Offsetof, _Trace: // arguments require special handling } // check argument count { msg := "" if nargs < bin.nargs { msg = "not enough" } else if !bin.variadic && nargs > bin.nargs { msg = "too many" } if msg != "" { check.invalidOp(call.Rparen, "%s arguments for %s (expected %d, found %d)", msg, call, bin.nargs, nargs) return } } switch id { case _Append: // append(s S, x ...T) S, where T is the element type of S // spec: "The variadic function append appends zero or more values x to s of type // S, which must be a slice type, and returns the resulting slice, also of type S. // The values x are passed to a parameter of type ...T where T is the element type // of S and the respective parameter passing rules apply." S := x.typ var T Type if s, _ := S.Underlying().(*Slice); s != nil { T = s.elem } else { check.invalidArg(x.pos(), "%s is not a slice", x) return } // remember arguments that have been evaluated already alist := []operand{*x} // spec: "As a special case, append also accepts a first argument assignable // to type []byte with a second argument of string type followed by ... . // This form appends the bytes of the string. if nargs == 2 && call.Ellipsis.IsValid() && x.isAssignableTo(check.conf, NewSlice(Typ[Byte])) { arg(x, 1) if x.mode == invalid { return } if isString(x.typ) { if check.Types != nil { sig := makeSig(S, S, NewSlice(Typ[Byte])) sig.isVariadic = true check.recordBuiltinType(call.Fun, sig) } x.mode = value x.typ = S break } alist = append(alist, *x) // fallthrough } // check general case by creating custom signature sig := makeSig(S, S, NewSlice(T)) // []T required for variadic signature sig.isVariadic = true check.arguments(x, call, sig, func(x *operand, i int) { // only evaluate arguments that have not been evaluated before if i < len(alist) { *x = alist[i] return } arg(x, i) }, nargs) // ok to continue even if check.arguments reported errors x.mode = value x.typ = S if check.Types != nil { check.recordBuiltinType(call.Fun, sig) } case _Cap, _Len: // cap(x) // len(x) mode := invalid var typ Type var val exact.Value switch typ = implicitArrayDeref(x.typ.Underlying()); t := typ.(type) { case *Basic: if isString(t) && id == _Len { if x.mode == constant { mode = constant val = exact.MakeInt64(int64(len(exact.StringVal(x.val)))) } else { mode = value } } case *Array: mode = value // spec: "The expressions len(s) and cap(s) are constants // if the type of s is an array or pointer to an array and // the expression s does not contain channel receives or // function calls; in this case s is not evaluated." if !check.containsCallsOrReceives(x.expr) { mode = constant val = exact.MakeInt64(t.len) } case *Slice, *Chan: mode = value case *Map: if id == _Len { mode = value } } if mode == invalid { check.invalidArg(x.pos(), "%s for %s", x, bin.name) return } x.mode = mode x.typ = Typ[Int] x.val = val if check.Types != nil && mode != constant { check.recordBuiltinType(call.Fun, makeSig(x.typ, typ)) } case _Close: // close(c) c, _ := x.typ.Underlying().(*Chan) if c == nil { check.invalidArg(x.pos(), "%s is not a channel", x) return } if c.dir&ast.SEND == 0 { check.invalidArg(x.pos(), "%s must not be a receive-only channel", x) return } x.mode = novalue if check.Types != nil { check.recordBuiltinType(call.Fun, makeSig(nil, c)) } case _Complex: // complex(x, y realT) complexT if !check.complexArg(x) { return } var y operand arg(&y, 1) if y.mode == invalid { return } if !check.complexArg(&y) { return } check.convertUntyped(x, y.typ) if x.mode == invalid { return } check.convertUntyped(&y, x.typ) if y.mode == invalid { return } if !IsIdentical(x.typ, y.typ) { check.invalidArg(x.pos(), "mismatched types %s and %s", x.typ, y.typ) return } if x.mode == constant && y.mode == constant { x.val = exact.BinaryOp(x.val, token.ADD, exact.MakeImag(y.val)) } else { x.mode = value } realT := x.typ complexT := Typ[Invalid] switch realT.Underlying().(*Basic).kind { case Float32: complexT = Typ[Complex64] case Float64: complexT = Typ[Complex128] case UntypedInt, UntypedRune, UntypedFloat: if x.mode == constant { realT = defaultType(realT).(*Basic) complexT = Typ[UntypedComplex] } else { // untyped but not constant; probably because one // operand is a non-constant shift of untyped lhs realT = Typ[Float64] complexT = Typ[Complex128] } default: check.invalidArg(x.pos(), "float32 or float64 arguments expected") return } x.typ = complexT if check.Types != nil && x.mode != constant { check.recordBuiltinType(call.Fun, makeSig(complexT, realT, realT)) } if x.mode != constant { // The arguments have now their final types, which at run- // time will be materialized. Update the expression trees. // If the current types are untyped, the materialized type // is the respective default type. // (If the result is constant, the arguments are never // materialized and there is nothing to do.) check.updateExprType(x.expr, realT, true) check.updateExprType(y.expr, realT, true) } case _Copy: // copy(x, y []T) int var dst Type if t, _ := x.typ.Underlying().(*Slice); t != nil { dst = t.elem } var y operand arg(&y, 1) if y.mode == invalid { return } var src Type switch t := y.typ.Underlying().(type) { case *Basic: if isString(y.typ) { src = Typ[Byte] } case *Slice: src = t.elem } if dst == nil || src == nil { check.invalidArg(x.pos(), "copy expects slice arguments; found %s and %s", x, &y) return } if !IsIdentical(dst, src) { check.invalidArg(x.pos(), "arguments to copy %s and %s have different element types %s and %s", x, &y, dst, src) return } x.mode = value x.typ = Typ[Int] if check.Types != nil { S := NewSlice(dst) check.recordBuiltinType(call.Fun, makeSig(x.typ, S, S)) } case _Delete: // delete(m, k) m, _ := x.typ.Underlying().(*Map) if m == nil { check.invalidArg(x.pos(), "%s is not a map", x) return } arg(x, 1) // k if x.mode == invalid { return } if !x.isAssignableTo(check.conf, m.key) { check.invalidArg(x.pos(), "%s is not assignable to %s", x, m.key) return } x.mode = novalue if check.Types != nil { check.recordBuiltinType(call.Fun, makeSig(nil, m, m.key)) } case _Imag, _Real: // imag(complexT) realT // real(complexT) realT if !isComplex(x.typ) { check.invalidArg(x.pos(), "%s must be a complex number", x) return } if x.mode == constant { if id == _Real { x.val = exact.Real(x.val) } else { x.val = exact.Imag(x.val) } } else { x.mode = value } var k BasicKind switch x.typ.Underlying().(*Basic).kind { case Complex64: k = Float32 case Complex128: k = Float64 case UntypedComplex: k = UntypedFloat default: unreachable() } if check.Types != nil && x.mode != constant { check.recordBuiltinType(call.Fun, makeSig(Typ[k], x.typ)) } x.typ = Typ[k] case _Make: // make(T, n) // make(T, n, m) // (no argument evaluated yet) arg0 := call.Args[0] T := check.typ(arg0, nil, false) if T == Typ[Invalid] { return } var min int // minimum number of arguments switch T.Underlying().(type) { case *Slice: min = 2 case *Map, *Chan: min = 1 default: check.invalidArg(arg0.Pos(), "cannot make %s; type must be slice, map, or channel", arg0) return } if nargs < min || min+1 < nargs { check.errorf(call.Pos(), "%s expects %d or %d arguments; found %d", call, min, min+1, nargs) return } var sizes []int64 // constant integer arguments, if any for _, arg := range call.Args[1:] { if s, ok := check.index(arg, -1); ok && s >= 0 { sizes = append(sizes, s) } } if len(sizes) == 2 && sizes[0] > sizes[1] { check.invalidArg(call.Args[1].Pos(), "length and capacity swapped") // safe to continue } x.mode = variable x.typ = T if check.Types != nil { params := [...]Type{T, Typ[Int], Typ[Int]} check.recordBuiltinType(call.Fun, makeSig(x.typ, params[:1+len(sizes)]...)) } case _New: // new(T) // (no argument evaluated yet) T := check.typ(call.Args[0], nil, false) if T == Typ[Invalid] { return } x.mode = variable x.typ = &Pointer{base: T} if check.Types != nil { check.recordBuiltinType(call.Fun, makeSig(x.typ, T)) } case _Panic: // panic(x) arg(x, 0) if x.mode == invalid { return } // TODO(gri) arguments must be assignable to _ x.mode = novalue if check.Types != nil { check.recordBuiltinType(call.Fun, makeSig(nil, new(Interface))) } case _Print, _Println: // print(x, y, ...) // println(x, y, ...) var params []Type if nargs > 0 { params = make([]Type, nargs) params[0] = x.typ // first argument already evaluated for i := 1; i < nargs; i++ { arg(x, i) if x.mode == invalid { return } params[i] = x.typ // TODO(gri) arguments must be assignable to _ } } x.mode = novalue if check.Types != nil { check.recordBuiltinType(call.Fun, makeSig(nil, params...)) } case _Recover: // recover() interface{} x.mode = value x.typ = new(Interface) if check.Types != nil { check.recordBuiltinType(call.Fun, makeSig(x.typ)) } case _Alignof: // unsafe.Alignof(x T) uintptr // TODO(gri) argument must be assignable to _ x.mode = constant x.val = exact.MakeInt64(check.conf.alignof(x.typ)) x.typ = Typ[Uintptr] // result is constant - no need to record signature case _Offsetof: // unsafe.Offsetof(x T) uintptr, where x must be a selector // (no argument evaluated yet) arg0 := call.Args[0] selx, _ := unparen(arg0).(*ast.SelectorExpr) if selx == nil { check.invalidArg(arg0.Pos(), "%s is not a selector expression", arg0) check.rawExpr(x, arg0, nil) // evaluate to avoid spurious "declared but not used" errors return } check.expr(x, selx.X) if x.mode == invalid { return } base := derefStructPtr(x.typ) sel := selx.Sel.Name obj, index, indirect := LookupFieldOrMethod(base, check.pkg, sel) switch obj.(type) { case nil: check.invalidArg(x.pos(), "%s has no single field %s", base, sel) return case *Func: check.invalidArg(arg0.Pos(), "%s is a method value", arg0) return } if indirect { check.invalidArg(x.pos(), "field %s is embedded via a pointer in %s", sel, base) return } // TODO(gri) Should we pass x.typ instead of base (and indirect report if derefStructPtr indirected)? check.recordSelection(selx, FieldVal, base, obj, index, false) offs := check.conf.offsetof(base, index) x.mode = constant x.val = exact.MakeInt64(offs) x.typ = Typ[Uintptr] // result is constant - no need to record signature case _Sizeof: // unsafe.Sizeof(x T) uintptr // TODO(gri) argument must be assignable to _ x.mode = constant x.val = exact.MakeInt64(check.conf.sizeof(x.typ)) x.typ = Typ[Uintptr] // result is constant - no need to record signature case _Assert: // assert(pred) causes a typechecker error if pred is false. // The result of assert is the value of pred if there is no error. // Note: assert is only available in self-test mode. if x.mode != constant || !isBoolean(x.typ) { check.invalidArg(x.pos(), "%s is not a boolean constant", x) return } if x.val.Kind() != exact.Bool { check.errorf(x.pos(), "internal error: value of %s should be a boolean constant", x) return } if !exact.BoolVal(x.val) { check.errorf(call.Pos(), "%s failed", call) // compile-time assertion failure - safe to continue } // result is constant - no need to record signature case _Trace: // trace(x, y, z, ...) dumps the positions, expressions, and // values of its arguments. The result of trace is the value // of the first argument. // Note: trace is only available in self-test mode. // (no argument evaluated yet) if nargs == 0 { check.dump("%s: trace() without arguments", call.Pos()) x.mode = novalue break } var t operand x1 := x for _, arg := range call.Args { check.rawExpr(x1, arg, nil) // permit trace for types, e.g.: new(trace(T)) check.dump("%s: %s", x1.pos(), x1) x1 = &t // use incoming x only for first argument } // trace is only available in test mode - no need to record signature default: unreachable() } return true }
// checkPrint checks a call to an unformatted print routine such as Println. func (f *File) checkPrint(call *ast.CallExpr, name string) { firstArg := 0 typ := f.pkg.types[call.Fun].Type if typ == nil { // Skip checking functions with unknown type. return } if sig, ok := typ.(*types.Signature); ok { if !sig.Variadic() { // Skip checking non-variadic functions. return } params := sig.Params() firstArg = params.Len() - 1 typ := params.At(firstArg).Type() typ = typ.(*types.Slice).Elem() it, ok := typ.(*types.Interface) if !ok || !it.Empty() { // Skip variadic functions accepting non-interface{} args. return } } args := call.Args if len(args) <= firstArg { // Skip calls without variadic args. return } args = args[firstArg:] // check for Println(os.Stderr, ...) if firstArg == 0 { if sel, ok := args[0].(*ast.SelectorExpr); ok { if x, ok := sel.X.(*ast.Ident); ok { if x.Name == "os" && strings.HasPrefix(sel.Sel.Name, "Std") { f.Badf(call.Pos(), "first argument to %s is %s.%s", name, x.Name, sel.Sel.Name) } } } } arg := args[0] if lit, ok := arg.(*ast.BasicLit); ok && lit.Kind == token.STRING { // Ignore trailing % character in lit.Value. // The % in "abc 0.0%" couldn't be a formatting directive. s := strings.TrimSuffix(lit.Value, `%"`) if strings.Contains(s, "%") { f.Badf(call.Pos(), "possible formatting directive in %s call", name) } } if strings.HasSuffix(name, "ln") { // The last item, if a string, should not have a newline. arg = args[len(args)-1] if lit, ok := arg.(*ast.BasicLit); ok && lit.Kind == token.STRING { if strings.HasSuffix(lit.Value, `\n"`) { f.Badf(call.Pos(), "%s call ends with newline", name) } } } for _, arg := range args { if f.isFunctionValue(arg) { f.Badf(call.Pos(), "arg %s in %s call is a function value, not a function call", f.gofmt(arg), name) } if f.recursiveStringer(arg) { f.Badf(call.Pos(), "arg %s in %s call causes recursive call to String method", f.gofmt(arg), name) } } }
// builtin typechecks a built-in call. The built-in type is bin, and iota is the current // value of iota or -1 if iota doesn't have a value in the current context. The result // of the call is returned via x. If the call has type errors, the returned x is marked // as invalid (x.mode == invalid). // func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *builtin, iota int) { args := call.Args id := bin.id // declare before goto's var arg0 ast.Expr var typ0 Type // check argument count n := len(args) msg := "" if n < bin.nargs { msg = "not enough" } else if !bin.isVariadic && n > bin.nargs { msg = "too many" } if msg != "" { check.invalidOp(call.Pos(), msg+"arguments for %s (expected %d, found %d)", call, bin.nargs, n) goto Error } // common case: evaluate first argument if present; // if it is an expression, x has the expression value if n > 0 { arg0 = args[0] switch id { case _Make, _New: // argument must be a type typ0 = check.typ(arg0, false) if typ0 == Typ[Invalid] { goto Error } case _Trace: // _Trace implementation does the work default: // argument must be an expression check.expr(x, arg0, nil, iota) if x.mode == invalid { goto Error } typ0 = underlying(x.typ) } } switch id { case _Append: s, ok := typ0.(*Slice) if !ok { check.invalidArg(x.pos(), "%s is not a typed slice", x) goto Error } for _, arg := range args[1:] { check.expr(x, arg, nil, iota) if x.mode == invalid { goto Error } // TODO(gri) check assignability } x.mode = value x.typ = s case _Cap, _Len: mode := invalid var val interface{} switch typ := implicitDeref(typ0).(type) { case *Basic: if isString(typ) && id == _Len { if x.mode == constant { mode = constant val = int64(len(x.val.(string))) } else { mode = value } } case *Array: mode = value if !containsCallsOrReceives(arg0) { mode = constant val = typ.Len } case *Slice, *Chan: mode = value case *Map: if id == _Len { mode = value } } if mode == invalid { check.invalidArg(x.pos(), "%s for %s", x, bin.name) goto Error } x.mode = mode x.typ = Typ[Int] x.val = val case _Close: ch, ok := typ0.(*Chan) if !ok { check.invalidArg(x.pos(), "%s is not a channel", x) goto Error } if ch.Dir&ast.SEND == 0 { check.invalidArg(x.pos(), "%s must not be a receive-only channel", x) goto Error } x.mode = novalue case _Complex: if !check.complexArg(x) { goto Error } var y operand check.expr(&y, args[1], nil, iota) if y.mode == invalid { goto Error } if !check.complexArg(&y) { goto Error } check.convertUntyped(x, y.typ) if x.mode == invalid { goto Error } check.convertUntyped(&y, x.typ) if y.mode == invalid { goto Error } if !isIdentical(x.typ, y.typ) { check.invalidArg(x.pos(), "mismatched types %s and %s", x.typ, y.typ) goto Error } if x.mode == constant && y.mode == constant { x.val = binaryOpConst(x.val, toImagConst(y.val), token.ADD, false) } else { x.mode = value } switch underlying(x.typ).(*Basic).Kind { case Float32: x.typ = Typ[Complex64] case Float64: x.typ = Typ[Complex128] case UntypedInt, UntypedRune, UntypedFloat: x.typ = Typ[UntypedComplex] default: check.invalidArg(x.pos(), "float32 or float64 arguments expected") goto Error } case _Copy: // TODO(gri) implements checks unimplemented() x.mode = value x.typ = Typ[Int] case _Delete: m, ok := typ0.(*Map) if !ok { check.invalidArg(x.pos(), "%s is not a map", x) goto Error } check.expr(x, args[1], nil, iota) if x.mode == invalid { goto Error } if !x.isAssignable(m.Key) { check.invalidArg(x.pos(), "%s is not assignable to %s", x, m.Key) goto Error } x.mode = novalue case _Imag, _Real: if !isComplex(typ0) { check.invalidArg(x.pos(), "%s must be a complex number", x) goto Error } if x.mode == constant { // nothing to do for x.val == 0 if !isZeroConst(x.val) { c := x.val.(complex) if id == _Real { x.val = c.re } else { x.val = c.im } } } else { x.mode = value } k := Invalid switch typ0.(*Basic).Kind { case Complex64: k = Float32 case Complex128: k = Float64 case UntypedComplex: k = UntypedFloat default: unreachable() } x.typ = Typ[k] case _Make: var min int // minimum number of arguments switch underlying(typ0).(type) { case *Slice: min = 2 case *Map, *Chan: min = 1 default: check.invalidArg(arg0.Pos(), "cannot make %s; type must be slice, map, or channel", arg0) goto Error } if n := len(args); n < min || min+1 < n { check.errorf(call.Pos(), "%s expects %d or %d arguments; found %d", call, min, min+1, n) goto Error } var sizes []interface{} // constant integer arguments, if any for _, arg := range args[1:] { check.expr(x, arg, nil, iota) if x.isInteger() { if x.mode == constant { if isNegConst(x.val) { check.invalidArg(x.pos(), "%s must not be negative", x) // safe to continue } else { sizes = append(sizes, x.val) // x.val >= 0 } } } else { check.invalidArg(x.pos(), "%s must be an integer", x) // safe to continue } } if len(sizes) == 2 && compareConst(sizes[0], sizes[1], token.GTR) { check.invalidArg(args[1].Pos(), "length and capacity swapped") // safe to continue } x.mode = variable x.typ = typ0 case _New: x.mode = variable x.typ = &Pointer{Base: typ0} case _Panic, _Print, _Println: x.mode = novalue case _Recover: x.mode = value x.typ = emptyInterface case _Alignof: x.mode = constant x.typ = Typ[Uintptr] // For now we return 1 always as it satisfies the spec's alignment guarantees. // TODO(gri) Extend typechecker API so that platform-specific values can be // provided. x.val = int64(1) case _Offsetof: if _, ok := unparen(x.expr).(*ast.SelectorExpr); !ok { check.invalidArg(x.pos(), "%s is not a selector", x) goto Error } x.mode = constant x.typ = Typ[Uintptr] // because of the size guarantees for basic types (> 0 for some), // returning 0 is only correct if two distinct non-zero size // structs can have the same address (the spec permits that) x.val = int64(0) case _Sizeof: // basic types with specified sizes have size guarantees; for all others we use 0 var size int64 if typ, ok := typ0.(*Basic); ok { size = typ.Size } x.mode = constant x.typ = Typ[Uintptr] x.val = size case _Assert: // assert(pred) causes a typechecker error if pred is false. // The result of assert is the value of pred if there is no error. // Note: assert is only available in self-test mode. if x.mode != constant || !isBoolean(typ0) { check.invalidArg(x.pos(), "%s is not a boolean constant", x) goto Error } pred, ok := x.val.(bool) if !ok { check.errorf(x.pos(), "internal error: value of %s should be a boolean constant", x) goto Error } if !pred { check.errorf(call.Pos(), "%s failed", call) // compile-time assertion failure - safe to continue } case _Trace: // trace(x, y, z, ...) dumps the positions, expressions, and // values of its arguments. The result of trace is the value // of the first argument. // Note: trace is only available in self-test mode. if len(args) == 0 { check.dump("%s: trace() without arguments", call.Pos()) x.mode = novalue x.expr = call return } var t operand x1 := x for _, arg := range args { check.rawExpr(x1, arg, nil, iota, true) // permit trace for types, e.g.: new(trace(T)) check.dump("%s: %s", x1.pos(), x1) x1 = &t // use incoming x only for first argument } default: check.invalidAST(call.Pos(), "unknown builtin id %d", id) goto Error } x.expr = call return Error: x.mode = invalid x.expr = call }
// builtin typechecks a built-in call. The built-in type is bin, and iota is the current // value of iota or -1 if iota doesn't have a value in the current context. The result // of the call is returned via x. If the call has type errors, the returned x is marked // as invalid (x.mode == invalid). // func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *builtin, iota int) { args := call.Args id := bin.id // declare before goto's var arg0 ast.Expr // first argument, if present // check argument count n := len(args) msg := "" if n < bin.nargs { msg = "not enough" } else if !bin.isVariadic && n > bin.nargs { msg = "too many" } if msg != "" { check.invalidOp(call.Pos(), msg+" arguments for %s (expected %d, found %d)", call, bin.nargs, n) goto Error } // common case: evaluate first argument if present; // if it is an expression, x has the expression value if n > 0 { arg0 = args[0] switch id { case _Make, _New, _Print, _Println, _Offsetof, _Trace: // respective cases below do the work default: // argument must be an expression check.expr(x, arg0, nil, iota) if x.mode == invalid { goto Error } } } switch id { case _Append: if _, ok := underlying(x.typ).(*Slice); !ok { check.invalidArg(x.pos(), "%s is not a typed slice", x) goto Error } resultTyp := x.typ for _, arg := range args[1:] { check.expr(x, arg, nil, iota) if x.mode == invalid { goto Error } // TODO(gri) check assignability } x.mode = value x.typ = resultTyp case _Cap, _Len: mode := invalid var val exact.Value switch typ := implicitArrayDeref(underlying(x.typ)).(type) { case *Basic: if isString(typ) && id == _Len { if x.mode == constant { mode = constant val = exact.MakeInt64(int64(len(exact.StringVal(x.val)))) } else { mode = value } } case *Array: mode = value // spec: "The expressions len(s) and cap(s) are constants // if the type of s is an array or pointer to an array and // the expression s does not contain channel receives or // function calls; in this case s is not evaluated." if !check.containsCallsOrReceives(arg0) { mode = constant val = exact.MakeInt64(typ.Len) } case *Slice, *Chan: mode = value case *Map: if id == _Len { mode = value } } if mode == invalid { check.invalidArg(x.pos(), "%s for %s", x, bin.name) goto Error } x.mode = mode x.typ = Typ[Int] x.val = val case _Close: ch, ok := underlying(x.typ).(*Chan) if !ok { check.invalidArg(x.pos(), "%s is not a channel", x) goto Error } if ch.Dir&ast.SEND == 0 { check.invalidArg(x.pos(), "%s must not be a receive-only channel", x) goto Error } x.mode = novalue case _Complex: if !check.complexArg(x) { goto Error } var y operand check.expr(&y, args[1], nil, iota) if y.mode == invalid { goto Error } if !check.complexArg(&y) { goto Error } check.convertUntyped(x, y.typ) if x.mode == invalid { goto Error } check.convertUntyped(&y, x.typ) if y.mode == invalid { goto Error } if !IsIdentical(x.typ, y.typ) { check.invalidArg(x.pos(), "mismatched types %s and %s", x.typ, y.typ) goto Error } typ := underlying(x.typ).(*Basic) if x.mode == constant && y.mode == constant { x.val = exact.BinaryOp(x.val, token.ADD, exact.MakeImag(y.val)) } else { x.mode = value } switch typ.Kind { case Float32: x.typ = Typ[Complex64] case Float64: x.typ = Typ[Complex128] case UntypedInt, UntypedRune, UntypedFloat: if x.mode == constant { typ = defaultType(typ).(*Basic) x.typ = Typ[UntypedComplex] } else { // untyped but not constant; probably because one // operand is a non-constant shift of untyped lhs typ = Typ[Float64] x.typ = Typ[Complex128] } default: check.invalidArg(x.pos(), "float32 or float64 arguments expected") goto Error } if x.mode != constant { // The arguments have now their final types, which at run- // time will be materialized. Update the expression trees. // If the current types are untyped, the materialized type // is the respective default type. // (If the result is constant, the arguments are never // materialized and there is nothing to do.) check.updateExprType(args[0], typ, true) check.updateExprType(args[1], typ, true) } case _Copy: var y operand check.expr(&y, args[1], nil, iota) if y.mode == invalid { goto Error } var dst, src Type if t, ok := underlying(x.typ).(*Slice); ok { dst = t.Elt } switch t := underlying(y.typ).(type) { case *Basic: if isString(y.typ) { src = Typ[Byte] } case *Slice: src = t.Elt } if dst == nil || src == nil { check.invalidArg(x.pos(), "copy expects slice arguments; found %s and %s", x, &y) goto Error } if !IsIdentical(dst, src) { check.invalidArg(x.pos(), "arguments to copy %s and %s have different element types %s and %s", x, &y, dst, src) goto Error } x.mode = value x.typ = Typ[Int] case _Delete: m, ok := underlying(x.typ).(*Map) if !ok { check.invalidArg(x.pos(), "%s is not a map", x) goto Error } check.expr(x, args[1], nil, iota) if x.mode == invalid { goto Error } if !x.isAssignable(check.ctxt, m.Key) { check.invalidArg(x.pos(), "%s is not assignable to %s", x, m.Key) goto Error } x.mode = novalue case _Imag, _Real: if !isComplex(x.typ) { check.invalidArg(x.pos(), "%s must be a complex number", x) goto Error } if x.mode == constant { if id == _Real { x.val = exact.Real(x.val) } else { x.val = exact.Imag(x.val) } } else { x.mode = value } k := Invalid switch underlying(x.typ).(*Basic).Kind { case Complex64: k = Float32 case Complex128: k = Float64 case UntypedComplex: k = UntypedFloat default: unreachable() } x.typ = Typ[k] case _Make: resultTyp := check.typ(arg0, false) if resultTyp == Typ[Invalid] { goto Error } var min int // minimum number of arguments switch underlying(resultTyp).(type) { case *Slice: min = 2 case *Map, *Chan: min = 1 default: check.invalidArg(arg0.Pos(), "cannot make %s; type must be slice, map, or channel", arg0) goto Error } if n := len(args); n < min || min+1 < n { check.errorf(call.Pos(), "%s expects %d or %d arguments; found %d", call, min, min+1, n) goto Error } var sizes []int64 // constant integer arguments, if any for _, arg := range args[1:] { if s, ok := check.index(arg, -1, iota); ok && s >= 0 { sizes = append(sizes, s) } } if len(sizes) == 2 && sizes[0] > sizes[1] { check.invalidArg(args[1].Pos(), "length and capacity swapped") // safe to continue } x.mode = variable x.typ = resultTyp case _New: resultTyp := check.typ(arg0, false) if resultTyp == Typ[Invalid] { goto Error } x.mode = variable x.typ = &Pointer{Base: resultTyp} case _Panic: x.mode = novalue case _Print, _Println: for _, arg := range args { check.expr(x, arg, nil, -1) if x.mode == invalid { goto Error } } x.mode = novalue case _Recover: x.mode = value x.typ = new(Interface) case _Alignof: x.mode = constant x.val = exact.MakeInt64(check.ctxt.alignof(x.typ)) x.typ = Typ[Uintptr] case _Offsetof: arg, ok := unparen(arg0).(*ast.SelectorExpr) if !ok { check.invalidArg(arg0.Pos(), "%s is not a selector expression", arg0) goto Error } check.expr(x, arg.X, nil, -1) if x.mode == invalid { goto Error } sel := arg.Sel.Name res := lookupField(x.typ, QualifiedName{check.pkg, arg.Sel.Name}) if res.index == nil { check.invalidArg(x.pos(), "%s has no single field %s", x, sel) goto Error } offs := check.ctxt.offsetof(deref(x.typ), res.index) if offs < 0 { check.invalidArg(x.pos(), "field %s is embedded via a pointer in %s", sel, x) goto Error } x.mode = constant x.val = exact.MakeInt64(offs) x.typ = Typ[Uintptr] case _Sizeof: x.mode = constant x.val = exact.MakeInt64(check.ctxt.sizeof(x.typ)) x.typ = Typ[Uintptr] case _Assert: // assert(pred) causes a typechecker error if pred is false. // The result of assert is the value of pred if there is no error. // Note: assert is only available in self-test mode. if x.mode != constant || !isBoolean(x.typ) { check.invalidArg(x.pos(), "%s is not a boolean constant", x) goto Error } if x.val.Kind() != exact.Bool { check.errorf(x.pos(), "internal error: value of %s should be a boolean constant", x) goto Error } if !exact.BoolVal(x.val) { check.errorf(call.Pos(), "%s failed", call) // compile-time assertion failure - safe to continue } case _Trace: // trace(x, y, z, ...) dumps the positions, expressions, and // values of its arguments. The result of trace is the value // of the first argument. // Note: trace is only available in self-test mode. if len(args) == 0 { check.dump("%s: trace() without arguments", call.Pos()) x.mode = novalue x.expr = call return } var t operand x1 := x for _, arg := range args { check.rawExpr(x1, arg, nil, iota, true) // permit trace for types, e.g.: new(trace(T)) check.dump("%s: %s", x1.pos(), x1) x1 = &t // use incoming x only for first argument } default: check.invalidAST(call.Pos(), "unknown builtin id %d", id) goto Error } x.expr = call return Error: x.mode = invalid x.expr = call }