TEXT() passes user patterns straight to the pattern engine. The fuzzer found that lower-casing non-ASCII text shifts byte offsets; the lexer now lower-cases ASCII only.
- commit
- b4f3e8533868cd9c89d3cd2bcc4a9eace9887ac2
- parent
- e42bce492d0ed1d8896211c3db8f0bb52b7db069
- author
- Dami <dami.etoile@gmail.com>
| M | internal/ | 16 | ++++++++++++++++ |
| M | internal/ | 13 | ++++++++++++- |
| A | internal/ | 3 | +++ |
@@ -27,6 +27,22 @@ func TestFormattingReplacesCells(t *testing.T) {
2727 }2828 }2929 30+// FuzzFormatPattern checks that any pattern and value render without31+// panicking, as TEXT() passes user patterns straight through.32+func FuzzFormatPattern(f *testing.F) {33+ for _, p := range []string{"#,##0.00", "0.0%", "0.00E+00", `"$"#,##0;(#,##0)`, "m/d/yyyy h:mm am/pm", "[h]:mm:ss.00", `\`, `"`, "[", "*", "_"} {34+ f.Add(1234.5, p)35+ f.Add(-1e300, p)36+ }37+ f.Fuzz(func(t *testing.T, v float64, pat string) {38+ if len(pat) > 200 {39+ return40+ }41+ FormatPattern(v, pat)42+ Display(Value{Kind: Number, Num: v}, Format{Kind: FmtCustom, Pattern: pat}, 12)43+ })44+}45+3046 func TestBatchJoinsFormattingSteps(t *testing.T) {3147 s := New()3248 r := NewRect(at("A1"), at("B2"))@@ -62,7 +62,7 @@ func splitSections(pat string) []string {
6262 func lexPattern(sec string) []ptok {6363 var toks []ptok6464 lit := func(s string) { toks = append(toks, ptok{kind: ptLit, s: s}) }65- lower := strings.ToLower(sec)65+ lower := asciiLower(sec) // same byte offsets as sec6666 for i := 0; i < len(sec); {6767 c := sec[i]6868 lc := lower[i]@@ -148,6 +148,17 @@ func lexPattern(sec string) []ptok {
148148 return toks149149 }150150 151+// asciiLower lower-cases ASCII letters only, keeping byte offsets.152+func asciiLower(s string) string {153+ b := []byte(s)154+ for i, c := range b {155+ if c >= 'A' && c <= 'Z' {156+ b[i] = c + 'a' - 'A'157+ }158+ }159+ return string(b)160+}161+151162 // isDatePattern reports whether a section formats dates and times.152163 func isDatePattern(toks []ptok) bool {153164 for _, t := range toks {