Ensure errors are handled or logged
if err != nil {
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}
Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.