src/fmt/scan_test.go GO 1,335 lines View on github.com → Search inside
1// Copyright 2009 The Go Authors. All rights reserved.2// Use of this source code is governed by a BSD-style3// license that can be found in the LICENSE file.45package fmt_test67import (8	"bufio"9	"bytes"10	"errors"11	. "fmt"12	"io"13	"math"14	"reflect"15	"regexp"16	"strings"17	"testing"18	"testing/iotest"19	"unicode/utf8"20)2122type ScanTest struct {23	text string24	in   any25	out  any26}2728type ScanfTest struct {29	format string30	text   string31	in     any32	out    any33}3435type ScanfMultiTest struct {36	format string37	text   string38	in     []any39	out    []any40	err    string41}4243var (44	boolVal              bool45	intVal               int46	int8Val              int847	int16Val             int1648	int32Val             int3249	int64Val             int6450	uintVal              uint51	uint8Val             uint852	uint16Val            uint1653	uint32Val            uint3254	uint64Val            uint6455	uintptrVal           uintptr56	float32Val           float3257	float64Val           float6458	stringVal            string59	bytesVal             []byte60	runeVal              rune61	complex64Val         complex6462	complex128Val        complex12863	renamedBoolVal       renamedBool64	renamedIntVal        renamedInt65	renamedInt8Val       renamedInt866	renamedInt16Val      renamedInt1667	renamedInt32Val      renamedInt3268	renamedInt64Val      renamedInt6469	renamedUintVal       renamedUint70	renamedUint8Val      renamedUint871	renamedUint16Val     renamedUint1672	renamedUint32Val     renamedUint3273	renamedUint64Val     renamedUint6474	renamedUintptrVal    renamedUintptr75	renamedStringVal     renamedString76	renamedBytesVal      renamedBytes77	renamedFloat32Val    renamedFloat3278	renamedFloat64Val    renamedFloat6479	renamedComplex64Val  renamedComplex6480	renamedComplex128Val renamedComplex12881)8283// Xs accepts any non-empty run of the verb character84type Xs string8586func (x *Xs) Scan(state ScanState, verb rune) error {87	tok, err := state.Token(true, func(r rune) bool { return r == verb })88	if err != nil {89		return err90	}91	s := string(tok)92	if !regexp.MustCompile("^" + string(verb) + "+$").MatchString(s) {93		return errors.New("syntax error for xs")94	}95	*x = Xs(s)96	return nil97}9899var xVal Xs100101// IntString accepts an integer followed immediately by a string.102// It tests the embedding of a scan within a scan.103type IntString struct {104	i int105	s string106}107108func (s *IntString) Scan(state ScanState, verb rune) error {109	if _, err := Fscan(state, &s.i); err != nil {110		return err111	}112113	tok, err := state.Token(true, nil)114	if err != nil {115		return err116	}117	s.s = string(tok)118	return nil119}120121var intStringVal IntString122123var scanTests = []ScanTest{124	// Basic types125	{"T\n", &boolVal, true},  // boolean test vals toggle to be sure they are written126	{"F\n", &boolVal, false}, // restored to zero value127	{"21\n", &intVal, 21},128	{"2_1\n", &intVal, 21},129	{"0\n", &intVal, 0},130	{"000\n", &intVal, 0},131	{"0x10\n", &intVal, 0x10},132	{"0x_1_0\n", &intVal, 0x10},133	{"-0x10\n", &intVal, -0x10},134	{"0377\n", &intVal, 0377},135	{"0_3_7_7\n", &intVal, 0377},136	{"0o377\n", &intVal, 0377},137	{"0o_3_7_7\n", &intVal, 0377},138	{"-0377\n", &intVal, -0377},139	{"-0o377\n", &intVal, -0377},140	{"0\n", &uintVal, uint(0)},141	{"000\n", &uintVal, uint(0)},142	{"0x10\n", &uintVal, uint(0x10)},143	{"0377\n", &uintVal, uint(0377)},144	{"22\n", &int8Val, int8(22)},145	{"23\n", &int16Val, int16(23)},146	{"24\n", &int32Val, int32(24)},147	{"25\n", &int64Val, int64(25)},148	{"127\n", &int8Val, int8(127)},149	{"-21\n", &intVal, -21},150	{"-22\n", &int8Val, int8(-22)},151	{"-23\n", &int16Val, int16(-23)},152	{"-24\n", &int32Val, int32(-24)},153	{"-25\n", &int64Val, int64(-25)},154	{"-128\n", &int8Val, int8(-128)},155	{"+21\n", &intVal, +21},156	{"+22\n", &int8Val, int8(+22)},157	{"+23\n", &int16Val, int16(+23)},158	{"+24\n", &int32Val, int32(+24)},159	{"+25\n", &int64Val, int64(+25)},160	{"+127\n", &int8Val, int8(+127)},161	{"26\n", &uintVal, uint(26)},162	{"27\n", &uint8Val, uint8(27)},163	{"28\n", &uint16Val, uint16(28)},164	{"29\n", &uint32Val, uint32(29)},165	{"30\n", &uint64Val, uint64(30)},166	{"31\n", &uintptrVal, uintptr(31)},167	{"255\n", &uint8Val, uint8(255)},168	{"32767\n", &int16Val, int16(32767)},169	{"2.3\n", &float64Val, 2.3},170	{"2.3e1\n", &float32Val, float32(2.3e1)},171	{"2.3e2\n", &float64Val, 2.3e2},172	{"2.3p2\n", &float64Val, 2.3 * 4},173	{"2.3p+2\n", &float64Val, 2.3 * 4},174	{"2.3p+66\n", &float64Val, 2.3 * (1 << 66)},175	{"2.3p-66\n", &float64Val, 2.3 / (1 << 66)},176	{"0x2.3p-66\n", &float64Val, float64(0x23) / (1 << 70)},177	{"2_3.4_5\n", &float64Val, 23.45},178	{"2.35\n", &stringVal, "2.35"},179	{"2345678\n", &bytesVal, []byte("2345678")},180	{"(3.4e1-2i)\n", &complex128Val, 3.4e1 - 2i},181	{"-3.45e1-3i\n", &complex64Val, complex64(-3.45e1 - 3i)},182	{"-.45e1-1e2i\n", &complex128Val, complex128(-.45e1 - 100i)},183	{"-.4_5e1-1E2i\n", &complex128Val, complex128(-.45e1 - 100i)},184	{"0x1.0p1+0x1.0P2i\n", &complex128Val, complex128(2 + 4i)},185	{"-0x1p1-0x1p2i\n", &complex128Val, complex128(-2 - 4i)},186	{"-0x1ep-1-0x1p2i\n", &complex128Val, complex128(-15 - 4i)},187	{"-0x1_Ep-1-0x1p0_2i\n", &complex128Val, complex128(-15 - 4i)},188	{"hello\n", &stringVal, "hello"},189190	// Carriage-return followed by newline. (We treat \r\n as \n always.)191	{"hello\r\n", &stringVal, "hello"},192	{"27\r\n", &uint8Val, uint8(27)},193194	// Renamed types195	{"true\n", &renamedBoolVal, renamedBool(true)},196	{"F\n", &renamedBoolVal, renamedBool(false)},197	{"101\n", &renamedIntVal, renamedInt(101)},198	{"102\n", &renamedIntVal, renamedInt(102)},199	{"103\n", &renamedUintVal, renamedUint(103)},200	{"104\n", &renamedUintVal, renamedUint(104)},201	{"105\n", &renamedInt8Val, renamedInt8(105)},202	{"106\n", &renamedInt16Val, renamedInt16(106)},203	{"107\n", &renamedInt32Val, renamedInt32(107)},204	{"108\n", &renamedInt64Val, renamedInt64(108)},205	{"109\n", &renamedUint8Val, renamedUint8(109)},206	{"110\n", &renamedUint16Val, renamedUint16(110)},207	{"111\n", &renamedUint32Val, renamedUint32(111)},208	{"112\n", &renamedUint64Val, renamedUint64(112)},209	{"113\n", &renamedUintptrVal, renamedUintptr(113)},210	{"114\n", &renamedStringVal, renamedString("114")},211	{"115\n", &renamedBytesVal, renamedBytes([]byte("115"))},212213	// Custom scanners.214	{"  vvv ", &xVal, Xs("vvv")},215	{" 1234hello", &intStringVal, IntString{1234, "hello"}},216217	// Fixed bugs218	{"2147483648\n", &int64Val, int64(2147483648)}, // was: integer overflow219}220221var scanfTests = []ScanfTest{222	{"%v", "TRUE\n", &boolVal, true},223	{"%t", "false\n", &boolVal, false},224	{"%v", "-71\n", &intVal, -71},225	{"%v", "-7_1\n", &intVal, -71},226	{"%v", "0b111\n", &intVal, 7},227	{"%v", "0b_1_1_1\n", &intVal, 7},228	{"%v", "0377\n", &intVal, 0377},229	{"%v", "0_3_7_7\n", &intVal, 0377},230	{"%v", "0o377\n", &intVal, 0377},231	{"%v", "0o_3_7_7\n", &intVal, 0377},232	{"%v", "0x44\n", &intVal, 0x44},233	{"%v", "0x_4_4\n", &intVal, 0x44},234	{"%d", "72\n", &intVal, 72},235	{"%c", "a\n", &runeVal, 'a'},236	{"%c", "\u5072\n", &runeVal, '\u5072'},237	{"%c", "\u1234\n", &runeVal, '\u1234'},238	{"%d", "73\n", &int8Val, int8(73)},239	{"%d", "+74\n", &int16Val, int16(74)},240	{"%d", "75\n", &int32Val, int32(75)},241	{"%d", "76\n", &int64Val, int64(76)},242	{"%b", "1001001\n", &intVal, 73},243	{"%o", "075\n", &intVal, 075},244	{"%x", "a75\n", &intVal, 0xa75},245	{"%v", "71\n", &uintVal, uint(71)},246	{"%d", "72\n", &uintVal, uint(72)},247	{"%d", "7_2\n", &uintVal, uint(7)}, // only %v takes underscores248	{"%d", "73\n", &uint8Val, uint8(73)},249	{"%d", "74\n", &uint16Val, uint16(74)},250	{"%d", "75\n", &uint32Val, uint32(75)},251	{"%d", "76\n", &uint64Val, uint64(76)},252	{"%d", "77\n", &uintptrVal, uintptr(77)},253	{"%b", "1001001\n", &uintVal, uint(73)},254	{"%b", "100_1001\n", &uintVal, uint(4)},255	{"%o", "075\n", &uintVal, uint(075)},256	{"%o", "07_5\n", &uintVal, uint(07)}, // only %v takes underscores257	{"%x", "a75\n", &uintVal, uint(0xa75)},258	{"%x", "A75\n", &uintVal, uint(0xa75)},259	{"%x", "A7_5\n", &uintVal, uint(0xa7)}, // only %v takes underscores260	{"%U", "U+1234\n", &intVal, int(0x1234)},261	{"%U", "U+4567\n", &uintVal, uint(0x4567)},262263	{"%e", "2.3\n", &float64Val, 2.3},264	{"%E", "2.3e1\n", &float32Val, float32(2.3e1)},265	{"%f", "2.3e2\n", &float64Val, 2.3e2},266	{"%g", "2.3p2\n", &float64Val, 2.3 * 4},267	{"%G", "2.3p+2\n", &float64Val, 2.3 * 4},268	{"%v", "2.3p+66\n", &float64Val, 2.3 * (1 << 66)},269	{"%f", "2.3p-66\n", &float64Val, 2.3 / (1 << 66)},270	{"%G", "0x2.3p-66\n", &float64Val, float64(0x23) / (1 << 70)},271	{"%E", "2_3.4_5\n", &float64Val, 23.45},272273	// Strings274	{"%s", "using-%s\n", &stringVal, "using-%s"},275	{"%x", "7573696e672d2578\n", &stringVal, "using-%x"},276	{"%X", "7573696E672D2558\n", &stringVal, "using-%X"},277	{"%q", `"quoted\twith\\do\u0075bl\x65s"` + "\n", &stringVal, "quoted\twith\\doubles"},278	{"%q", "`quoted with backs`\n", &stringVal, "quoted with backs"},279280	// Byte slices281	{"%s", "bytes-%s\n", &bytesVal, []byte("bytes-%s")},282	{"%x", "62797465732d2578\n", &bytesVal, []byte("bytes-%x")},283	{"%X", "62797465732D2558\n", &bytesVal, []byte("bytes-%X")},284	{"%q", `"bytes\rwith\vdo\u0075bl\x65s"` + "\n", &bytesVal, []byte("bytes\rwith\vdoubles")},285	{"%q", "`bytes with backs`\n", &bytesVal, []byte("bytes with backs")},286287	// Renamed types288	{"%v\n", "true\n", &renamedBoolVal, renamedBool(true)},289	{"%t\n", "F\n", &renamedBoolVal, renamedBool(false)},290	{"%v", "101\n", &renamedIntVal, renamedInt(101)},291	{"%c", "\u0101\n", &renamedIntVal, renamedInt('\u0101')},292	{"%o", "0146\n", &renamedIntVal, renamedInt(102)},293	{"%v", "103\n", &renamedUintVal, renamedUint(103)},294	{"%d", "104\n", &renamedUintVal, renamedUint(104)},295	{"%d", "105\n", &renamedInt8Val, renamedInt8(105)},296	{"%d", "106\n", &renamedInt16Val, renamedInt16(106)},297	{"%d", "107\n", &renamedInt32Val, renamedInt32(107)},298	{"%d", "108\n", &renamedInt64Val, renamedInt64(108)},299	{"%x", "6D\n", &renamedUint8Val, renamedUint8(109)},300	{"%o", "0156\n", &renamedUint16Val, renamedUint16(110)},301	{"%d", "111\n", &renamedUint32Val, renamedUint32(111)},302	{"%d", "112\n", &renamedUint64Val, renamedUint64(112)},303	{"%d", "113\n", &renamedUintptrVal, renamedUintptr(113)},304	{"%s", "114\n", &renamedStringVal, renamedString("114")},305	{"%q", "\"1155\"\n", &renamedBytesVal, renamedBytes([]byte("1155"))},306	{"%g", "116e1\n", &renamedFloat32Val, renamedFloat32(116e1)},307	{"%g", "-11.7e+1", &renamedFloat64Val, renamedFloat64(-11.7e+1)},308	{"%g", "11+6e1i\n", &renamedComplex64Val, renamedComplex64(11 + 6e1i)},309	{"%g", "-11.+7e+1i", &renamedComplex128Val, renamedComplex128(-11. + 7e+1i)},310311	// Interesting formats312	{"here is\tthe value:%d", "here is   the\tvalue:118\n", &intVal, 118},313	{"%% %%:%d", "% %:119\n", &intVal, 119},314	{"%d%%", "42%", &intVal, 42}, // %% at end of string.315316	// Corner cases317	{"%x", "FFFFFFFF\n", &uint32Val, uint32(0xFFFFFFFF)},318319	// Custom scanner.320	{"%s", "  sss ", &xVal, Xs("sss")},321	{"%2s", "sssss", &xVal, Xs("ss")},322323	// Fixed bugs324	{"%d\n", "27\n", &intVal, 27},         // ok325	{"%d\n", "28 \n", &intVal, 28},        // was: "unexpected newline"326	{"%v", "0", &intVal, 0},               // was: "EOF"; 0 was taken as base prefix and not counted.327	{"%v", "0", &uintVal, uint(0)},        // was: "EOF"; 0 was taken as base prefix and not counted.328	{"%c", " ", &uintVal, uint(' ')},      // %c must accept a blank.329	{"%c", "\t", &uintVal, uint('\t')},    // %c must accept any space.330	{"%c", "\n", &uintVal, uint('\n')},    // %c must accept any space.331	{"%d%%", "23%\n", &uintVal, uint(23)}, // %% matches literal %.332	{"%%%d", "%23\n", &uintVal, uint(23)}, // %% matches literal %.333334	// space handling335	{"%d", "27", &intVal, 27},336	{"%d", "27 ", &intVal, 27},337	{"%d", " 27", &intVal, 27},338	{"%d", " 27 ", &intVal, 27},339340	{"X%d", "X27", &intVal, 27},341	{"X%d", "X27 ", &intVal, 27},342	{"X%d", "X 27", &intVal, 27},343	{"X%d", "X 27 ", &intVal, 27},344345	{"X %d", "X27", &intVal, nil},  // expected space in input to match format346	{"X %d", "X27 ", &intVal, nil}, // expected space in input to match format347	{"X %d", "X 27", &intVal, 27},348	{"X %d", "X 27 ", &intVal, 27},349350	{"%dX", "27X", &intVal, 27},351	{"%dX", "27 X", &intVal, nil}, // input does not match format352	{"%dX", " 27X", &intVal, 27},353	{"%dX", " 27 X", &intVal, nil}, // input does not match format354355	{"%d X", "27X", &intVal, nil}, // expected space in input to match format356	{"%d X", "27 X", &intVal, 27},357	{"%d X", " 27X", &intVal, nil}, // expected space in input to match format358	{"%d X", " 27 X", &intVal, 27},359360	{"X %d X", "X27X", &intVal, nil},  // expected space in input to match format361	{"X %d X", "X27 X", &intVal, nil}, // expected space in input to match format362	{"X %d X", "X 27X", &intVal, nil}, // expected space in input to match format363	{"X %d X", "X 27 X", &intVal, 27},364365	{"X %s X", "X27X", &stringVal, nil},  // expected space in input to match format366	{"X %s X", "X27 X", &stringVal, nil}, // expected space in input to match format367	{"X %s X", "X 27X", &stringVal, nil}, // unexpected EOF368	{"X %s X", "X 27 X", &stringVal, "27"},369370	{"X%sX", "X27X", &stringVal, nil},   // unexpected EOF371	{"X%sX", "X27 X", &stringVal, nil},  // input does not match format372	{"X%sX", "X 27X", &stringVal, nil},  // unexpected EOF373	{"X%sX", "X 27 X", &stringVal, nil}, // input does not match format374375	{"X%s", "X27", &stringVal, "27"},376	{"X%s", "X27 ", &stringVal, "27"},377	{"X%s", "X 27", &stringVal, "27"},378	{"X%s", "X 27 ", &stringVal, "27"},379380	{"X%dX", "X27X", &intVal, 27},381	{"X%dX", "X27 X", &intVal, nil}, // input does not match format382	{"X%dX", "X 27X", &intVal, 27},383	{"X%dX", "X 27 X", &intVal, nil}, // input does not match format384385	{"X%dX", "X27X", &intVal, 27},386	{"X%dX", "X27X ", &intVal, 27},387	{"X%dX", " X27X", &intVal, nil},  // input does not match format388	{"X%dX", " X27X ", &intVal, nil}, // input does not match format389390	{"X%dX\n", "X27X", &intVal, 27},391	{"X%dX \n", "X27X ", &intVal, 27},392	{"X%dX\n", "X27X\n", &intVal, 27},393	{"X%dX\n", "X27X \n", &intVal, 27},394395	{"X%dX \n", "X27X", &intVal, 27},396	{"X%dX \n", "X27X ", &intVal, 27},397	{"X%dX \n", "X27X\n", &intVal, 27},398	{"X%dX \n", "X27X \n", &intVal, 27},399400	{"X%c", "X\n", &runeVal, '\n'},401	{"X%c", "X \n", &runeVal, ' '},402	{"X %c", "X!", &runeVal, nil},  // expected space in input to match format403	{"X %c", "X\n", &runeVal, nil}, // newline in input does not match format404	{"X %c", "X !", &runeVal, '!'},405	{"X %c", "X \n", &runeVal, '\n'},406407	{" X%dX", "X27X", &intVal, nil},  // expected space in input to match format408	{" X%dX", "X27X ", &intVal, nil}, // expected space in input to match format409	{" X%dX", " X27X", &intVal, 27},410	{" X%dX", " X27X ", &intVal, 27},411412	{"X%dX ", "X27X", &intVal, 27},413	{"X%dX ", "X27X ", &intVal, 27},414	{"X%dX ", " X27X", &intVal, nil},  // input does not match format415	{"X%dX ", " X27X ", &intVal, nil}, // input does not match format416417	{" X%dX ", "X27X", &intVal, nil},  // expected space in input to match format418	{" X%dX ", "X27X ", &intVal, nil}, // expected space in input to match format419	{" X%dX ", " X27X", &intVal, 27},420	{" X%dX ", " X27X ", &intVal, 27},421422	{"%d\nX", "27\nX", &intVal, 27},423	{"%dX\n X", "27X\n X", &intVal, 27},424}425426var overflowTests = []ScanTest{427	{"128", &int8Val, 0},428	{"32768", &int16Val, 0},429	{"-129", &int8Val, 0},430	{"-32769", &int16Val, 0},431	{"256", &uint8Val, 0},432	{"65536", &uint16Val, 0},433	{"1e100", &float32Val, 0},434	{"1e500", &float64Val, 0},435	{"(1e100+0i)", &complex64Val, 0},436	{"(1+1e100i)", &complex64Val, 0},437	{"(1-1e500i)", &complex128Val, 0},438}439440var truth bool441var i, j, k int442var f float64443var s, t string444var c complex128445var x, y Xs446var z IntString447var r1, r2, r3 rune448449var multiTests = []ScanfMultiTest{450	{"", "", []any{}, []any{}, ""},451	{"%d", "23", args(&i), args(23), ""},452	{"%2s%3s", "22333", args(&s, &t), args("22", "333"), ""},453	{"%2d%3d", "44555", args(&i, &j), args(44, 555), ""},454	{"%2d.%3d", "66.777", args(&i, &j), args(66, 777), ""},455	{"%d, %d", "23, 18", args(&i, &j), args(23, 18), ""},456	{"%3d22%3d", "33322333", args(&i, &j), args(333, 333), ""},457	{"%6vX=%3fY", "3+2iX=2.5Y", args(&c, &f), args((3 + 2i), 2.5), ""},458	{"%d%s", "123abc", args(&i, &s), args(123, "abc"), ""},459	{"%c%c%c", "2\u50c2X", args(&r1, &r2, &r3), args('2', '\u50c2', 'X'), ""},460	{"%5s%d", " 1234567 ", args(&s, &i), args("12345", 67), ""},461	{"%5s%d", " 12 34 567 ", args(&s, &i), args("12", 34), ""},462463	// Custom scanners.464	{"%e%f", "eefffff", args(&x, &y), args(Xs("ee"), Xs("fffff")), ""},465	{"%4v%s", "12abcd", args(&z, &s), args(IntString{12, "ab"}, "cd"), ""},466467	// Errors468	{"%t", "23 18", args(&i), nil, "bad verb"},469	{"%d %d %d", "23 18", args(&i, &j), args(23, 18), "too few operands"},470	{"%d %d", "23 18 27", args(&i, &j, &k), args(23, 18), "too many operands"},471	{"%c", "\u0100", args(&int8Val), nil, "overflow"},472	{"X%d", "10X", args(&intVal), nil, "input does not match format"},473	{"%d%", "42%", args(&intVal), args(42), "missing verb: % at end of format string"},474	{"%d% ", "42%", args(&intVal), args(42), "too few operands for format '% '"}, // Slightly odd error, but correct.475	{"%%%d", "xxx 42", args(&intVal), args(42), "missing literal %"},476	{"%%%d", "x42", args(&intVal), args(42), "missing literal %"},477	{"%%%d", "42", args(&intVal), args(42), "missing literal %"},478479	// Bad UTF-8: should see every byte.480	{"%c%c%c", "\xc2X\xc2", args(&r1, &r2, &r3), args(utf8.RuneError, 'X', utf8.RuneError), ""},481482	// Fixed bugs483	{"%v%v", "FALSE23", args(&truth, &i), args(false, 23), ""},484}485486var readers = []struct {487	name string488	f    func(string) io.Reader489}{490	{"StringReader", func(s string) io.Reader {491		return strings.NewReader(s)492	}},493	{"ReaderOnly", func(s string) io.Reader {494		return struct{ io.Reader }{strings.NewReader(s)}495	}},496	{"OneByteReader", func(s string) io.Reader {497		return iotest.OneByteReader(strings.NewReader(s))498	}},499	{"DataErrReader", func(s string) io.Reader {500		return iotest.DataErrReader(strings.NewReader(s))501	}},502}503504func testScan(t *testing.T, f func(string) io.Reader, scan func(r io.Reader, a ...any) (int, error)) {505	for _, test := range scanTests {506		r := f(test.text)507		n, err := scan(r, test.in)508		if err != nil {509			m := ""510			if n > 0 {511				m = Sprintf(" (%d fields ok)", n)512			}513			t.Errorf("got error scanning %q: %s%s", test.text, err, m)514			continue515		}516		if n != 1 {517			t.Errorf("count error on entry %q: got %d", test.text, n)518			continue519		}520		// The incoming value may be a pointer521		v := reflect.ValueOf(test.in)522		if p := v; p.Kind() == reflect.Pointer {523			v = p.Elem()524		}525		val := v.Interface()526		if !reflect.DeepEqual(val, test.out) {527			t.Errorf("scanning %q: expected %#v got %#v, type %T", test.text, test.out, val, val)528		}529	}530}531532func TestScan(t *testing.T) {533	for _, r := range readers {534		t.Run(r.name, func(t *testing.T) {535			testScan(t, r.f, Fscan)536		})537	}538}539540func TestScanln(t *testing.T) {541	for _, r := range readers {542		t.Run(r.name, func(t *testing.T) {543			testScan(t, r.f, Fscanln)544		})545	}546}547548func TestScanf(t *testing.T) {549	for _, test := range scanfTests {550		n, err := Sscanf(test.text, test.format, test.in)551		if err != nil {552			if test.out != nil {553				t.Errorf("Sscanf(%q, %q): unexpected error: %v", test.text, test.format, err)554			}555			continue556		}557		if test.out == nil {558			t.Errorf("Sscanf(%q, %q): unexpected success", test.text, test.format)559			continue560		}561		if n != 1 {562			t.Errorf("Sscanf(%q, %q): parsed %d field, want 1", test.text, test.format, n)563			continue564		}565		// The incoming value may be a pointer566		v := reflect.ValueOf(test.in)567		if p := v; p.Kind() == reflect.Pointer {568			v = p.Elem()569		}570		val := v.Interface()571		if !reflect.DeepEqual(val, test.out) {572			t.Errorf("Sscanf(%q, %q): parsed value %T(%#v), want %T(%#v)", test.text, test.format, val, val, test.out, test.out)573		}574	}575}576577func TestScanOverflow(t *testing.T) {578	// different machines and different types report errors with different strings.579	re := regexp.MustCompile("overflow|too large|out of range|not representable")580	for _, test := range overflowTests {581		_, err := Sscan(test.text, test.in)582		if err == nil {583			t.Errorf("expected overflow scanning %q", test.text)584			continue585		}586		if !re.MatchString(err.Error()) {587			t.Errorf("expected overflow error scanning %q: %s", test.text, err)588		}589	}590}591592func verifyNaN(str string, t *testing.T) {593	var f float64594	var f32 float32595	var f64 float64596	text := str + " " + str + " " + str597	n, err := Fscan(strings.NewReader(text), &f, &f32, &f64)598	if err != nil {599		t.Errorf("got error scanning %q: %s", text, err)600	}601	if n != 3 {602		t.Errorf("count error scanning %q: got %d", text, n)603	}604	if !math.IsNaN(float64(f)) || !math.IsNaN(float64(f32)) || !math.IsNaN(f64) {605		t.Errorf("didn't get NaNs scanning %q: got %g %g %g", text, f, f32, f64)606	}607}608609func TestNaN(t *testing.T) {610	for _, s := range []string{"nan", "NAN", "NaN"} {611		verifyNaN(s, t)612	}613}614615func verifyInf(str string, t *testing.T) {616	var f float64617	var f32 float32618	var f64 float64619	text := str + " " + str + " " + str620	n, err := Fscan(strings.NewReader(text), &f, &f32, &f64)621	if err != nil {622		t.Errorf("got error scanning %q: %s", text, err)623	}624	if n != 3 {625		t.Errorf("count error scanning %q: got %d", text, n)626	}627	sign := 1628	if str[0] == '-' {629		sign = -1630	}631	if !math.IsInf(float64(f), sign) || !math.IsInf(float64(f32), sign) || !math.IsInf(f64, sign) {632		t.Errorf("didn't get right Infs scanning %q: got %g %g %g", text, f, f32, f64)633	}634}635636func TestInf(t *testing.T) {637	for _, s := range []string{"inf", "+inf", "-inf", "INF", "-INF", "+INF", "Inf", "-Inf", "+Inf"} {638		verifyInf(s, t)639	}640}641642func testScanfMulti(t *testing.T, f func(string) io.Reader) {643	sliceType := reflect.TypeOf(make([]any, 1))644	for _, test := range multiTests {645		r := f(test.text)646		n, err := Fscanf(r, test.format, test.in...)647		if err != nil {648			if test.err == "" {649				t.Errorf("got error scanning (%q, %q): %q", test.format, test.text, err)650			} else if !strings.Contains(err.Error(), test.err) {651				t.Errorf("got wrong error scanning (%q, %q): %q; expected %q", test.format, test.text, err, test.err)652			}653			continue654		}655		if test.err != "" {656			t.Errorf("expected error %q error scanning (%q, %q)", test.err, test.format, test.text)657		}658		if n != len(test.out) {659			t.Errorf("count error on entry (%q, %q): expected %d got %d", test.format, test.text, len(test.out), n)660			continue661		}662		// Convert the slice of pointers into a slice of values663		resultVal := reflect.MakeSlice(sliceType, n, n)664		for i := 0; i < n; i++ {665			v := reflect.ValueOf(test.in[i]).Elem()666			resultVal.Index(i).Set(v)667		}668		result := resultVal.Interface()669		if !reflect.DeepEqual(result, test.out) {670			t.Errorf("scanning (%q, %q): expected %#v got %#v", test.format, test.text, test.out, result)671		}672	}673}674675func TestScanfMulti(t *testing.T) {676	for _, r := range readers {677		t.Run(r.name, func(t *testing.T) {678			testScanfMulti(t, r.f)679		})680	}681}682683func TestScanMultiple(t *testing.T) {684	var a int685	var s string686	n, err := Sscan("123abc", &a, &s)687	if n != 2 {688		t.Errorf("Sscan count error: expected 2: got %d", n)689	}690	if err != nil {691		t.Errorf("Sscan expected no error; got %s", err)692	}693	if a != 123 || s != "abc" {694		t.Errorf("Sscan wrong values: got (%d %q) expected (123 \"abc\")", a, s)695	}696	n, err = Sscan("asdf", &s, &a)697	if n != 1 {698		t.Errorf("Sscan count error: expected 1: got %d", n)699	}700	if err == nil {701		t.Errorf("Sscan expected error; got none: %s", err)702	}703	if s != "asdf" {704		t.Errorf("Sscan wrong values: got %q expected \"asdf\"", s)705	}706}707708// Empty strings are not valid input when scanning a string.709func TestScanEmpty(t *testing.T) {710	var s1, s2 string711	n, err := Sscan("abc", &s1, &s2)712	if n != 1 {713		t.Errorf("Sscan count error: expected 1: got %d", n)714	}715	if err == nil {716		t.Error("Sscan <one item> expected error; got none")717	}718	if s1 != "abc" {719		t.Errorf("Sscan wrong values: got %q expected \"abc\"", s1)720	}721	n, err = Sscan("", &s1, &s2)722	if n != 0 {723		t.Errorf("Sscan count error: expected 0: got %d", n)724	}725	if err == nil {726		t.Error("Sscan <empty> expected error; got none")727	}728	// Quoted empty string is OK.729	n, err = Sscanf(`""`, "%q", &s1)730	if n != 1 {731		t.Errorf("Sscanf count error: expected 1: got %d", n)732	}733	if err != nil {734		t.Errorf("Sscanf <empty> expected no error with quoted string; got %s", err)735	}736}737738func TestScanNotPointer(t *testing.T) {739	r := strings.NewReader("1")740	var a int741	_, err := Fscan(r, a)742	if err == nil {743		t.Error("expected error scanning non-pointer")744	} else if !strings.Contains(err.Error(), "pointer") {745		t.Errorf("expected pointer error scanning non-pointer, got: %s", err)746	}747}748749func TestScanlnNoNewline(t *testing.T) {750	var a int751	_, err := Sscanln("1 x\n", &a)752	if err == nil {753		t.Error("expected error scanning string missing newline")754	} else if !strings.Contains(err.Error(), "newline") {755		t.Errorf("expected newline error scanning string missing newline, got: %s", err)756	}757}758759func TestScanlnWithMiddleNewline(t *testing.T) {760	r := strings.NewReader("123\n456\n")761	var a, b int762	_, err := Fscanln(r, &a, &b)763	if err == nil {764		t.Error("expected error scanning string with extra newline")765	} else if !strings.Contains(err.Error(), "newline") {766		t.Errorf("expected newline error scanning string with extra newline, got: %s", err)767	}768}769770// eofCounter is a special Reader that counts reads at end of file.771type eofCounter struct {772	reader   *strings.Reader773	eofCount int774}775776func (ec *eofCounter) Read(b []byte) (n int, err error) {777	n, err = ec.reader.Read(b)778	if n == 0 {779		ec.eofCount++780	}781	return782}783784// TestEOF verifies that when we scan, we see at most EOF once per call to a785// Scan function, and then only when it's really an EOF.786func TestEOF(t *testing.T) {787	ec := &eofCounter{strings.NewReader("123\n"), 0}788	var a int789	n, err := Fscanln(ec, &a)790	if err != nil {791		t.Error("unexpected error", err)792	}793	if n != 1 {794		t.Error("expected to scan one item, got", n)795	}796	if ec.eofCount != 0 {797		t.Error("expected zero EOFs", ec.eofCount)798		ec.eofCount = 0 // reset for next test799	}800	n, err = Fscanln(ec, &a)801	if err == nil {802		t.Error("expected error scanning empty string")803	}804	if n != 0 {805		t.Error("expected to scan zero items, got", n)806	}807	if ec.eofCount != 1 {808		t.Error("expected one EOF, got", ec.eofCount)809	}810}811812// TestEOFAtEndOfInput verifies that we see an EOF error if we run out of input.813// This was a buglet: we used to get "expected integer".814func TestEOFAtEndOfInput(t *testing.T) {815	var i, j int816	n, err := Sscanf("23", "%d %d", &i, &j)817	if n != 1 || i != 23 {818		t.Errorf("Sscanf expected one value of 23; got %d %d", n, i)819	}820	if err != io.EOF {821		t.Errorf("Sscanf expected EOF; got %q", err)822	}823	n, err = Sscan("234", &i, &j)824	if n != 1 || i != 234 {825		t.Errorf("Sscan expected one value of 234; got %d %d", n, i)826	}827	if err != io.EOF {828		t.Errorf("Sscan expected EOF; got %q", err)829	}830	// Trailing space is tougher.831	n, err = Sscan("234 ", &i, &j)832	if n != 1 || i != 234 {833		t.Errorf("Sscan expected one value of 234; got %d %d", n, i)834	}835	if err != io.EOF {836		t.Errorf("Sscan expected EOF; got %q", err)837	}838}839840var eofTests = []struct {841	format string842	v      any843}{844	{"%s", &stringVal},845	{"%q", &stringVal},846	{"%x", &stringVal},847	{"%v", &stringVal},848	{"%v", &bytesVal},849	{"%v", &intVal},850	{"%v", &uintVal},851	{"%v", &boolVal},852	{"%v", &float32Val},853	{"%v", &complex64Val},854	{"%v", &renamedStringVal},855	{"%v", &renamedBytesVal},856	{"%v", &renamedIntVal},857	{"%v", &renamedUintVal},858	{"%v", &renamedBoolVal},859	{"%v", &renamedFloat32Val},860	{"%v", &renamedComplex64Val},861}862863func TestEOFAllTypes(t *testing.T) {864	for i, test := range eofTests {865		if _, err := Sscanf("", test.format, test.v); err != io.EOF {866			t.Errorf("#%d: %s %T not eof on empty string: %s", i, test.format, test.v, err)867		}868		if _, err := Sscanf("   ", test.format, test.v); err != io.EOF {869			t.Errorf("#%d: %s %T not eof on trailing blanks: %s", i, test.format, test.v, err)870		}871	}872}873874// TestUnreadRuneWithBufio verifies that, at least when using bufio, successive875// calls to Fscan do not lose runes.876func TestUnreadRuneWithBufio(t *testing.T) {877	r := bufio.NewReader(strings.NewReader("123αb"))878	var i int879	var a string880	n, err := Fscanf(r, "%d", &i)881	if n != 1 || err != nil {882		t.Errorf("reading int expected one item, no errors; got %d %q", n, err)883	}884	if i != 123 {885		t.Errorf("expected 123; got %d", i)886	}887	n, err = Fscanf(r, "%s", &a)888	if n != 1 || err != nil {889		t.Errorf("reading string expected one item, no errors; got %d %q", n, err)890	}891	if a != "αb" {892		t.Errorf("expected αb; got %q", a)893	}894}895896type TwoLines string897898// Scan attempts to read two lines into the object. Scanln should prevent this899// because it stops at newline; Scan and Scanf should be fine.900func (t *TwoLines) Scan(state ScanState, verb rune) error {901	chars := make([]rune, 0, 100)902	for nlCount := 0; nlCount < 2; {903		c, _, err := state.ReadRune()904		if err != nil {905			return err906		}907		chars = append(chars, c)908		if c == '\n' {909			nlCount++910		}911	}912	*t = TwoLines(string(chars))913	return nil914}915916func TestMultiLine(t *testing.T) {917	input := "abc\ndef\n"918	// Sscan should work919	var tscan TwoLines920	n, err := Sscan(input, &tscan)921	if n != 1 {922		t.Errorf("Sscan: expected 1 item; got %d", n)923	}924	if err != nil {925		t.Errorf("Sscan: expected no error; got %s", err)926	}927	if string(tscan) != input {928		t.Errorf("Sscan: expected %q; got %q", input, tscan)929	}930	// Sscanf should work931	var tscanf TwoLines932	n, err = Sscanf(input, "%s", &tscanf)933	if n != 1 {934		t.Errorf("Sscanf: expected 1 item; got %d", n)935	}936	if err != nil {937		t.Errorf("Sscanf: expected no error; got %s", err)938	}939	if string(tscanf) != input {940		t.Errorf("Sscanf: expected %q; got %q", input, tscanf)941	}942	// Sscanln should not work943	var tscanln TwoLines944	n, err = Sscanln(input, &tscanln)945	if n != 0 {946		t.Errorf("Sscanln: expected 0 items; got %d: %q", n, tscanln)947	}948	if err == nil {949		t.Error("Sscanln: expected error; got none")950	} else if err != io.ErrUnexpectedEOF {951		t.Errorf("Sscanln: expected io.ErrUnexpectedEOF (ha!); got %s", err)952	}953}954955// TestLineByLineFscanf tests that Fscanf does not read past newline. Issue956// 3481.957func TestLineByLineFscanf(t *testing.T) {958	r := struct{ io.Reader }{strings.NewReader("1\n2\n")}959	var i, j int960	n, err := Fscanf(r, "%v\n", &i)961	if n != 1 || err != nil {962		t.Fatalf("first read: %d %q", n, err)963	}964	n, err = Fscanf(r, "%v\n", &j)965	if n != 1 || err != nil {966		t.Fatalf("second read: %d %q", n, err)967	}968	if i != 1 || j != 2 {969		t.Errorf("wrong values; wanted 1 2 got %d %d", i, j)970	}971}972973// TestScanStateCount verifies the correct byte count is returned. Issue 8512.974975// runeScanner implements the Scanner interface for TestScanStateCount.976type runeScanner struct {977	rune rune978	size int979}980981func (rs *runeScanner) Scan(state ScanState, verb rune) error {982	r, size, err := state.ReadRune()983	rs.rune = r984	rs.size = size985	return err986}987988func TestScanStateCount(t *testing.T) {989	var a, b, c runeScanner990	n, err := Sscanf("12➂", "%c%c%c", &a, &b, &c)991	if err != nil {992		t.Fatal(err)993	}994	if n != 3 {995		t.Fatalf("expected 3 items consumed, got %d", n)996	}997	if a.rune != '1' || b.rune != '2' || c.rune != '➂' {998		t.Errorf("bad scan rune: %q %q %q should be '1' '2' '➂'", a.rune, b.rune, c.rune)999	}1000	if a.size != 1 || b.size != 1 || c.size != 3 {1001		t.Errorf("bad scan size: %d %d %d should be 1 1 3", a.size, b.size, c.size)1002	}1003}10041005// RecursiveInt accepts a string matching %d.%d.%d....1006// and parses it into a linked list.1007// It allows us to benchmark recursive descent style scanners.1008type RecursiveInt struct {1009	i    int1010	next *RecursiveInt1011}10121013func (r *RecursiveInt) Scan(state ScanState, verb rune) (err error) {1014	_, err = Fscan(state, &r.i)1015	if err != nil {1016		return1017	}1018	next := new(RecursiveInt)1019	_, err = Fscanf(state, ".%v", next)1020	if err != nil {1021		if err == io.ErrUnexpectedEOF {1022			err = nil1023		}1024		return1025	}1026	r.next = next1027	return1028}10291030// scanInts performs the same scanning task as RecursiveInt.Scan1031// but without recurring through scanner, so we can compare1032// performance more directly.1033func scanInts(r *RecursiveInt, b *bytes.Buffer) (err error) {1034	r.next = nil1035	_, err = Fscan(b, &r.i)1036	if err != nil {1037		return1038	}1039	c, _, err := b.ReadRune()1040	if err != nil {1041		if err == io.EOF {1042			err = nil1043		}1044		return1045	}1046	if c != '.' {1047		return1048	}1049	next := new(RecursiveInt)1050	err = scanInts(next, b)1051	if err == nil {1052		r.next = next1053	}1054	return1055}10561057func makeInts(n int) []byte {1058	var buf bytes.Buffer1059	Fprintf(&buf, "1")1060	for i := 1; i < n; i++ {1061		Fprintf(&buf, ".%d", i+1)1062	}1063	return buf.Bytes()1064}10651066func TestScanInts(t *testing.T) {1067	testScanInts(t, scanInts)1068	testScanInts(t, func(r *RecursiveInt, b *bytes.Buffer) (err error) {1069		_, err = Fscan(b, r)1070		return1071	})1072}10731074// 800 is small enough to not overflow the stack when using gccgo on a1075// platform that does not support split stack.1076const intCount = 80010771078func testScanInts(t *testing.T, scan func(*RecursiveInt, *bytes.Buffer) error) {1079	r := new(RecursiveInt)1080	ints := makeInts(intCount)1081	buf := bytes.NewBuffer(ints)1082	err := scan(r, buf)1083	if err != nil {1084		t.Error("unexpected error", err)1085	}1086	i := 11087	for ; r != nil; r = r.next {1088		if r.i != i {1089			t.Fatalf("bad scan: expected %d got %d", i, r.i)1090		}1091		i++1092	}1093	if i-1 != intCount {1094		t.Fatalf("bad scan count: expected %d got %d", intCount, i-1)1095	}1096}10971098func BenchmarkScanInts(b *testing.B) {1099	b.StopTimer()1100	ints := makeInts(intCount)1101	var r RecursiveInt1102	for i := 0; i < b.N; i++ {1103		buf := bytes.NewBuffer(ints)1104		b.StartTimer()1105		scanInts(&r, buf)1106		b.StopTimer()1107	}1108}11091110func BenchmarkScanRecursiveInt(b *testing.B) {1111	b.StopTimer()1112	ints := makeInts(intCount)1113	var r RecursiveInt1114	for i := 0; i < b.N; i++ {1115		buf := bytes.NewBuffer(ints)1116		b.StartTimer()1117		Fscan(buf, &r)1118		b.StopTimer()1119	}1120}11211122func BenchmarkScanRecursiveIntReaderWrapper(b *testing.B) {1123	b.StopTimer()1124	ints := makeInts(intCount)1125	var r RecursiveInt1126	for i := 0; i < b.N; i++ {1127		buf := struct{ io.Reader }{strings.NewReader(string(ints))}1128		b.StartTimer()1129		Fscan(buf, &r)1130		b.StopTimer()1131	}1132}11331134// Issue 9124.1135// %x on bytes couldn't handle non-space bytes terminating the scan.1136func TestHexBytes(t *testing.T) {1137	var a, b []byte1138	n, err := Sscanf("00010203", "%x", &a)1139	if n != 1 || err != nil {1140		t.Errorf("simple: got count, err = %d, %v; expected 1, nil", n, err)1141	}1142	check := func(msg string, x []byte) {1143		if len(x) != 4 {1144			t.Errorf("%s: bad length %d", msg, len(x))1145		}1146		for i, b := range x {1147			if int(b) != i {1148				t.Errorf("%s: bad x[%d] = %x", msg, i, x[i])1149			}1150		}1151	}1152	check("simple", a)1153	a = nil11541155	n, err = Sscanf("00010203 00010203", "%x %x", &a, &b)1156	if n != 2 || err != nil {1157		t.Errorf("simple pair: got count, err = %d, %v; expected 2, nil", n, err)1158	}1159	check("simple pair a", a)1160	check("simple pair b", b)1161	a = nil1162	b = nil11631164	n, err = Sscanf("00010203:", "%x", &a)1165	if n != 1 || err != nil {1166		t.Errorf("colon: got count, err = %d, %v; expected 1, nil", n, err)1167	}1168	check("colon", a)1169	a = nil11701171	n, err = Sscanf("00010203:00010203", "%x:%x", &a, &b)1172	if n != 2 || err != nil {1173		t.Errorf("colon pair: got count, err = %d, %v; expected 2, nil", n, err)1174	}1175	check("colon pair a", a)1176	check("colon pair b", b)1177	a = nil1178	b = nil11791180	// This one fails because there is a hex byte after the data,1181	// that is, an odd number of hex input bytes.1182	n, err = Sscanf("000102034:", "%x", &a)1183	if n != 0 || err == nil {1184		t.Errorf("odd count: got count, err = %d, %v; expected 0, error", n, err)1185	}1186}11871188func TestScanNewlinesAreSpaces(t *testing.T) {1189	var a, b int1190	var tests = []struct {1191		name  string1192		text  string1193		count int1194	}{1195		{"newlines", "1\n2\n", 2},1196		{"no final newline", "1\n2", 2},1197		{"newlines with spaces ", "1  \n  2  \n", 2},1198		{"no final newline with spaces", "1  \n  2", 2},1199	}1200	for _, test := range tests {1201		n, err := Sscan(test.text, &a, &b)1202		if n != test.count {1203			t.Errorf("%s: expected to scan %d item(s), scanned %d", test.name, test.count, n)1204		}1205		if err != nil {1206			t.Errorf("%s: unexpected error: %s", test.name, err)1207		}1208	}1209}12101211func TestScanlnNewlinesTerminate(t *testing.T) {1212	var a, b int1213	var tests = []struct {1214		name  string1215		text  string1216		count int1217		ok    bool1218	}{1219		{"one line one item", "1\n", 1, false},1220		{"one line two items with spaces ", "   1 2    \n", 2, true},1221		{"one line two items no newline", "   1 2", 2, true},1222		{"two lines two items", "1\n2\n", 1, false},1223	}1224	for _, test := range tests {1225		n, err := Sscanln(test.text, &a, &b)1226		if n != test.count {1227			t.Errorf("%s: expected to scan %d item(s), scanned %d", test.name, test.count, n)1228		}1229		if test.ok && err != nil {1230			t.Errorf("%s: unexpected error: %s", test.name, err)1231		}1232		if !test.ok && err == nil {1233			t.Errorf("%s: expected error; got none", test.name)1234		}1235	}1236}12371238func TestScanfNewlineMatchFormat(t *testing.T) {1239	var a, b int1240	var tests = []struct {1241		name   string1242		text   string1243		format string1244		count  int1245		ok     bool1246	}{1247		{"newline in both", "1\n2", "%d\n%d\n", 2, true},1248		{"newline in input", "1\n2", "%d %d", 1, false},1249		{"space-newline in input", "1 \n2", "%d %d", 1, false},1250		{"newline in format", "1 2", "%d\n%d", 1, false},1251		{"space-newline in format", "1 2", "%d \n%d", 1, false},1252		{"space-newline in both", "1 \n2", "%d \n%d", 2, true},1253		{"extra space in format", "1\n2", "%d\n %d", 2, true},1254		{"two extra spaces in format", "1\n2", "%d \n %d", 2, true},1255		{"space vs newline 0000", "1\n2", "%d\n%d", 2, true},1256		{"space vs newline 0001", "1\n2", "%d\n %d", 2, true},1257		{"space vs newline 0010", "1\n2", "%d \n%d", 2, true},1258		{"space vs newline 0011", "1\n2", "%d \n %d", 2, true},1259		{"space vs newline 0100", "1\n 2", "%d\n%d", 2, true},1260		{"space vs newline 0101", "1\n 2", "%d\n%d ", 2, true},1261		{"space vs newline 0110", "1\n 2", "%d \n%d", 2, true},1262		{"space vs newline 0111", "1\n 2", "%d \n %d", 2, true},1263		{"space vs newline 1000", "1 \n2", "%d\n%d", 2, true},1264		{"space vs newline 1001", "1 \n2", "%d\n %d", 2, true},1265		{"space vs newline 1010", "1 \n2", "%d \n%d", 2, true},1266		{"space vs newline 1011", "1 \n2", "%d \n %d", 2, true},1267		{"space vs newline 1100", "1 \n 2", "%d\n%d", 2, true},1268		{"space vs newline 1101", "1 \n 2", "%d\n %d", 2, true},1269		{"space vs newline 1110", "1 \n 2", "%d \n%d", 2, true},1270		{"space vs newline 1111", "1 \n 2", "%d \n %d", 2, true},1271		{"space vs newline no-percent 0000", "1\n2", "1\n2", 0, true},1272		{"space vs newline no-percent 0001", "1\n2", "1\n 2", 0, true},1273		{"space vs newline no-percent 0010", "1\n2", "1 \n2", 0, true},1274		{"space vs newline no-percent 0011", "1\n2", "1 \n 2", 0, true},1275		{"space vs newline no-percent 0100", "1\n 2", "1\n2", 0, false},  // fails: space after nl in input but not pattern1276		{"space vs newline no-percent 0101", "1\n 2", "1\n2 ", 0, false}, // fails: space after nl in input but not pattern1277		{"space vs newline no-percent 0110", "1\n 2", "1 \n2", 0, false}, // fails: space after nl in input but not pattern1278		{"space vs newline no-percent 0111", "1\n 2", "1 \n 2", 0, true},1279		{"space vs newline no-percent 1000", "1 \n2", "1\n2", 0, true},1280		{"space vs newline no-percent 1001", "1 \n2", "1\n 2", 0, true},1281		{"space vs newline no-percent 1010", "1 \n2", "1 \n2", 0, true},1282		{"space vs newline no-percent 1011", "1 \n2", "1 \n 2", 0, true},1283		{"space vs newline no-percent 1100", "1 \n 2", "1\n2", 0, false}, // fails: space after nl in input but not pattern1284		{"space vs newline no-percent 1101", "1 \n 2", "1\n 2", 0, true},1285		{"space vs newline no-percent 1110", "1 \n 2", "1 \n2", 0, false}, // fails: space after nl in input but not pattern1286		{"space vs newline no-percent 1111", "1 \n 2", "1 \n 2", 0, true},1287	}1288	for _, test := range tests {1289		var n int1290		var err error1291		if strings.Contains(test.format, "%") {1292			n, err = Sscanf(test.text, test.format, &a, &b)1293		} else {1294			n, err = Sscanf(test.text, test.format)1295		}1296		if n != test.count {1297			t.Errorf("%s: expected to scan %d item(s), scanned %d", test.name, test.count, n)1298		}1299		if test.ok && err != nil {1300			t.Errorf("%s: unexpected error: %s", test.name, err)1301		}1302		if !test.ok && err == nil {1303			t.Errorf("%s: expected error; got none", test.name)1304		}1305	}1306}13071308// Test for issue 12090: Was unreading at EOF, double-scanning a byte.13091310type hexBytes [2]byte13111312func (h *hexBytes) Scan(ss ScanState, verb rune) error {1313	var b []byte1314	_, err := Fscanf(ss, "%4x", &b)1315	if err != nil {1316		panic(err) // Really shouldn't happen.1317	}1318	copy((*h)[:], b)1319	return err1320}13211322func TestHexByte(t *testing.T) {1323	var h hexBytes1324	n, err := Sscanln("0123\n", &h)1325	if err != nil {1326		t.Fatal(err)1327	}1328	if n != 1 {1329		t.Fatalf("expected 1 item; scanned %d", n)1330	}1331	if h[0] != 0x01 || h[1] != 0x23 {1332		t.Fatalf("expected 0123 got %x", h)1333	}1334}

Code quality findings 8

Ensure errors are handled or logged
warning correctness unhandled-error
if err != nil {
Ensure errors are handled or logged
warning correctness unhandled-error
if err != nil {
Ensure errors are handled or logged
warning correctness unhandled-error
if err != nil {
Ensure errors are handled or logged
warning correctness unhandled-error
if err != nil {
Regexp compiled inside function; compile once at package level to avoid recompilation on each call
info performance regexp-compile-in-func
if !regexp.MustCompile("^" + string(verb) + "+$").MatchString(s) {
Regexp compiled inside function; compile once at package level to avoid recompilation on each call
info performance regexp-compile-in-func
re := regexp.MustCompile("overflow|too large|out of range|not representable")
Range over slice copies each element by value; use index or pointer receiver for large structs to avoid copies
info performance copy-large-struct
for i, test := range eofTests {
Range over slice copies each element by value; use index or pointer receiver for large structs to avoid copies
info performance copy-large-struct
for i, b := range x {

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.