aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/html
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/html')
-rw-r--r--libgo/go/html/fuzz.go1
-rw-r--r--libgo/go/html/template/attr.go6
-rw-r--r--libgo/go/html/template/content.go12
-rw-r--r--libgo/go/html/template/content_test.go4
-rw-r--r--libgo/go/html/template/context.go4
-rw-r--r--libgo/go/html/template/css.go4
-rw-r--r--libgo/go/html/template/error.go2
-rw-r--r--libgo/go/html/template/escape.go79
-rw-r--r--libgo/go/html/template/escape_test.go36
-rw-r--r--libgo/go/html/template/example_test.go2
-rw-r--r--libgo/go/html/template/exec_test.go38
-rw-r--r--libgo/go/html/template/html.go12
-rw-r--r--libgo/go/html/template/js.go16
-rw-r--r--libgo/go/html/template/js_test.go14
-rw-r--r--libgo/go/html/template/template.go8
-rw-r--r--libgo/go/html/template/template_test.go2
-rw-r--r--libgo/go/html/template/url.go14
-rw-r--r--libgo/go/html/template/url_test.go2
18 files changed, 175 insertions, 81 deletions
diff --git a/libgo/go/html/fuzz.go b/libgo/go/html/fuzz.go
index ecaf4f9..cd70f97 100644
--- a/libgo/go/html/fuzz.go
+++ b/libgo/go/html/fuzz.go
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build gofuzz
-// +build gofuzz
package html
diff --git a/libgo/go/html/template/attr.go b/libgo/go/html/template/attr.go
index 22922e6..6c52211 100644
--- a/libgo/go/html/template/attr.go
+++ b/libgo/go/html/template/attr.go
@@ -143,12 +143,12 @@ func attrType(name string) contentType {
// widely applied.
// Treat data-action as URL below.
name = name[5:]
- } else if colon := strings.IndexRune(name, ':'); colon != -1 {
- if name[:colon] == "xmlns" {
+ } else if prefix, short, ok := strings.Cut(name, ":"); ok {
+ if prefix == "xmlns" {
return contentTypeURL
}
// Treat svg:href and xlink:href as href below.
- name = name[colon+1:]
+ name = short
}
if t, ok := attrTypeMap[name]; ok {
return t
diff --git a/libgo/go/html/template/content.go b/libgo/go/html/template/content.go
index 6ba87a9..b104267 100644
--- a/libgo/go/html/template/content.go
+++ b/libgo/go/html/template/content.go
@@ -112,16 +112,16 @@ const (
// indirect returns the value, after dereferencing as many times
// as necessary to reach the base type (or nil).
-func indirect(a interface{}) interface{} {
+func indirect(a any) any {
if a == nil {
return nil
}
- if t := reflect.TypeOf(a); t.Kind() != reflect.Ptr {
+ if t := reflect.TypeOf(a); t.Kind() != reflect.Pointer {
// Avoid creating a reflect.Value if it's not a pointer.
return a
}
v := reflect.ValueOf(a)
- for v.Kind() == reflect.Ptr && !v.IsNil() {
+ for v.Kind() == reflect.Pointer && !v.IsNil() {
v = v.Elem()
}
return v.Interface()
@@ -135,12 +135,12 @@ var (
// indirectToStringerOrError returns the value, after dereferencing as many times
// as necessary to reach the base type (or nil) or an implementation of fmt.Stringer
// or error,
-func indirectToStringerOrError(a interface{}) interface{} {
+func indirectToStringerOrError(a any) any {
if a == nil {
return nil
}
v := reflect.ValueOf(a)
- for !v.Type().Implements(fmtStringerType) && !v.Type().Implements(errorType) && v.Kind() == reflect.Ptr && !v.IsNil() {
+ for !v.Type().Implements(fmtStringerType) && !v.Type().Implements(errorType) && v.Kind() == reflect.Pointer && !v.IsNil() {
v = v.Elem()
}
return v.Interface()
@@ -148,7 +148,7 @@ func indirectToStringerOrError(a interface{}) interface{} {
// stringify converts its arguments to a string and the type of the content.
// All pointers are dereferenced, as in the text/template package.
-func stringify(args ...interface{}) (string, contentType) {
+func stringify(args ...any) (string, contentType) {
if len(args) == 1 {
switch s := indirect(args[0]).(type) {
case string:
diff --git a/libgo/go/html/template/content_test.go b/libgo/go/html/template/content_test.go
index b7a39d4..497264e 100644
--- a/libgo/go/html/template/content_test.go
+++ b/libgo/go/html/template/content_test.go
@@ -12,7 +12,7 @@ import (
)
func TestTypedContent(t *testing.T) {
- data := []interface{}{
+ data := []any{
`<b> "foo%" O'Reilly &bar;`,
CSS(`a[href =~ "//example.com"]#foo`),
HTML(`Hello, <b>World</b> &amp;tc!`),
@@ -449,7 +449,7 @@ func TestEscapingNilNonemptyInterfaces(t *testing.T) {
// A non-empty interface should print like an empty interface.
want := new(bytes.Buffer)
- data := struct{ E interface{} }{}
+ data := struct{ E any }{}
tmpl.Execute(want, data)
if !bytes.Equal(want.Bytes(), got.Bytes()) {
diff --git a/libgo/go/html/template/context.go b/libgo/go/html/template/context.go
index f7d4849..aaa7d08 100644
--- a/libgo/go/html/template/context.go
+++ b/libgo/go/html/template/context.go
@@ -6,6 +6,7 @@ package template
import (
"fmt"
+ "text/template/parse"
)
// context describes the state an HTML parser must be in when it reaches the
@@ -22,6 +23,7 @@ type context struct {
jsCtx jsCtx
attr attr
element element
+ n parse.Node // for range break/continue
err *Error
}
@@ -141,6 +143,8 @@ const (
// stateError is an infectious error state outside any valid
// HTML/CSS/JS construct.
stateError
+ // stateDead marks unreachable code after a {{break}} or {{continue}}.
+ stateDead
)
// isComment is true for any state that contains content meant for template
diff --git a/libgo/go/html/template/css.go b/libgo/go/html/template/css.go
index eb92fc9..890a0c6 100644
--- a/libgo/go/html/template/css.go
+++ b/libgo/go/html/template/css.go
@@ -155,7 +155,7 @@ func isCSSSpace(b byte) bool {
}
// cssEscaper escapes HTML and CSS special characters using \<hex>+ escapes.
-func cssEscaper(args ...interface{}) string {
+func cssEscaper(args ...any) string {
s, _ := stringify(args...)
var b strings.Builder
r, w, written := rune(0), 0, 0
@@ -218,7 +218,7 @@ var mozBindingBytes = []byte("mozbinding")
// (inherit, blue), and colors (#888).
// It filters out unsafe values, such as those that affect token boundaries,
// and anything that might execute scripts.
-func cssValueFilter(args ...interface{}) string {
+func cssValueFilter(args ...any) string {
s, t := stringify(args...)
if t == contentTypeCSS {
return s
diff --git a/libgo/go/html/template/error.go b/libgo/go/html/template/error.go
index 0e52706..6bb5a20 100644
--- a/libgo/go/html/template/error.go
+++ b/libgo/go/html/template/error.go
@@ -228,6 +228,6 @@ func (e *Error) Error() string {
// errorf creates an error given a format string f and args.
// The template Name still needs to be supplied.
-func errorf(k ErrorCode, node parse.Node, line int, f string, args ...interface{}) *Error {
+func errorf(k ErrorCode, node parse.Node, line int, f string, args ...any) *Error {
return &Error{k, node, "", line, fmt.Sprintf(f, args...)}
}
diff --git a/libgo/go/html/template/escape.go b/libgo/go/html/template/escape.go
index 8739735..2b11526 100644
--- a/libgo/go/html/template/escape.go
+++ b/libgo/go/html/template/escape.go
@@ -45,7 +45,7 @@ func escapeTemplate(tmpl *Template, node parse.Node, name string) error {
// evalArgs formats the list of arguments into a string. It is equivalent to
// fmt.Sprint(args...), except that it deferences all pointers.
-func evalArgs(args ...interface{}) string {
+func evalArgs(args ...any) string {
// Optimization for simple common case of a single string argument.
if len(args) == 1 {
if s, ok := args[0].(string); ok {
@@ -97,6 +97,15 @@ type escaper struct {
actionNodeEdits map[*parse.ActionNode][]string
templateNodeEdits map[*parse.TemplateNode]string
textNodeEdits map[*parse.TextNode][]byte
+ // rangeContext holds context about the current range loop.
+ rangeContext *rangeContext
+}
+
+// rangeContext holds information about the current range loop.
+type rangeContext struct {
+ outer *rangeContext // outer loop
+ breaks []context // context at each break action
+ continues []context // context at each continue action
}
// makeEscaper creates a blank escaper for the given set.
@@ -109,6 +118,7 @@ func makeEscaper(n *nameSpace) escaper {
map[*parse.ActionNode][]string{},
map[*parse.TemplateNode]string{},
map[*parse.TextNode][]byte{},
+ nil,
}
}
@@ -124,8 +134,16 @@ func (e *escaper) escape(c context, n parse.Node) context {
switch n := n.(type) {
case *parse.ActionNode:
return e.escapeAction(c, n)
+ case *parse.BreakNode:
+ c.n = n
+ e.rangeContext.breaks = append(e.rangeContext.breaks, c)
+ return context{state: stateDead}
case *parse.CommentNode:
return c
+ case *parse.ContinueNode:
+ c.n = n
+ e.rangeContext.continues = append(e.rangeContext.breaks, c)
+ return context{state: stateDead}
case *parse.IfNode:
return e.escapeBranch(c, &n.BranchNode, "if")
case *parse.ListNode:
@@ -427,6 +445,12 @@ func join(a, b context, node parse.Node, nodeName string) context {
if b.state == stateError {
return b
}
+ if a.state == stateDead {
+ return b
+ }
+ if b.state == stateDead {
+ return a
+ }
if a.eq(b) {
return a
}
@@ -466,14 +490,27 @@ func join(a, b context, node parse.Node, nodeName string) context {
// escapeBranch escapes a branch template node: "if", "range" and "with".
func (e *escaper) escapeBranch(c context, n *parse.BranchNode, nodeName string) context {
+ if nodeName == "range" {
+ e.rangeContext = &rangeContext{outer: e.rangeContext}
+ }
c0 := e.escapeList(c, n.List)
- if nodeName == "range" && c0.state != stateError {
+ if nodeName == "range" {
+ if c0.state != stateError {
+ c0 = joinRange(c0, e.rangeContext)
+ }
+ e.rangeContext = e.rangeContext.outer
+ if c0.state == stateError {
+ return c0
+ }
+
// The "true" branch of a "range" node can execute multiple times.
// We check that executing n.List once results in the same context
// as executing n.List twice.
+ e.rangeContext = &rangeContext{outer: e.rangeContext}
c1, _ := e.escapeListConditionally(c0, n.List, nil)
c0 = join(c0, c1, n, nodeName)
if c0.state == stateError {
+ e.rangeContext = e.rangeContext.outer
// Make clear that this is a problem on loop re-entry
// since developers tend to overlook that branch when
// debugging templates.
@@ -481,11 +518,39 @@ func (e *escaper) escapeBranch(c context, n *parse.BranchNode, nodeName string)
c0.err.Description = "on range loop re-entry: " + c0.err.Description
return c0
}
+ c0 = joinRange(c0, e.rangeContext)
+ e.rangeContext = e.rangeContext.outer
+ if c0.state == stateError {
+ return c0
+ }
}
c1 := e.escapeList(c, n.ElseList)
return join(c0, c1, n, nodeName)
}
+func joinRange(c0 context, rc *rangeContext) context {
+ // Merge contexts at break and continue statements into overall body context.
+ // In theory we could treat breaks differently from continues, but for now it is
+ // enough to treat them both as going back to the start of the loop (which may then stop).
+ for _, c := range rc.breaks {
+ c0 = join(c0, c, c.n, "range")
+ if c0.state == stateError {
+ c0.err.Line = c.n.(*parse.BreakNode).Line
+ c0.err.Description = "at range loop break: " + c0.err.Description
+ return c0
+ }
+ }
+ for _, c := range rc.continues {
+ c0 = join(c0, c, c.n, "range")
+ if c0.state == stateError {
+ c0.err.Line = c.n.(*parse.ContinueNode).Line
+ c0.err.Description = "at range loop continue: " + c0.err.Description
+ return c0
+ }
+ }
+ return c0
+}
+
// escapeList escapes a list template node.
func (e *escaper) escapeList(c context, n *parse.ListNode) context {
if n == nil {
@@ -493,6 +558,9 @@ func (e *escaper) escapeList(c context, n *parse.ListNode) context {
}
for _, m := range n.Nodes {
c = e.escape(c, m)
+ if c.state == stateDead {
+ break
+ }
}
return c
}
@@ -503,6 +571,7 @@ func (e *escaper) escapeList(c context, n *parse.ListNode) context {
// which is the same as whether e was updated.
func (e *escaper) escapeListConditionally(c context, n *parse.ListNode, filter func(*escaper, context) bool) (context, bool) {
e1 := makeEscaper(e.ns)
+ e1.rangeContext = e.rangeContext
// Make type inferences available to f.
for k, v := range e.output {
e1.output[k] = v
@@ -865,7 +934,7 @@ func HTMLEscapeString(s string) string {
// HTMLEscaper returns the escaped HTML equivalent of the textual
// representation of its arguments.
-func HTMLEscaper(args ...interface{}) string {
+func HTMLEscaper(args ...any) string {
return template.HTMLEscaper(args...)
}
@@ -881,12 +950,12 @@ func JSEscapeString(s string) string {
// JSEscaper returns the escaped JavaScript equivalent of the textual
// representation of its arguments.
-func JSEscaper(args ...interface{}) string {
+func JSEscaper(args ...any) string {
return template.JSEscaper(args...)
}
// URLQueryEscaper returns the escaped value of the textual representation of
// its arguments in a form suitable for embedding in a URL query.
-func URLQueryEscaper(args ...interface{}) string {
+func URLQueryEscaper(args ...any) string {
return template.URLQueryEscaper(args...)
}
diff --git a/libgo/go/html/template/escape_test.go b/libgo/go/html/template/escape_test.go
index fbc84a7..58f3f27 100644
--- a/libgo/go/html/template/escape_test.go
+++ b/libgo/go/html/template/escape_test.go
@@ -35,8 +35,8 @@ func TestEscape(t *testing.T) {
A, E []string
B, M json.Marshaler
N int
- U interface{} // untyped nil
- Z *int // typed nil
+ U any // untyped nil
+ Z *int // typed nil
W HTML
}{
F: false,
@@ -858,7 +858,7 @@ func TestEscapeSet(t *testing.T) {
// pred is a template function that returns the predecessor of a
// natural number for testing recursive templates.
- fns := FuncMap{"pred": func(a ...interface{}) (interface{}, error) {
+ fns := FuncMap{"pred": func(a ...any) (any, error) {
if len(a) == 1 {
if i, _ := a[0].(int); i > 0 {
return i - 1, nil
@@ -920,6 +920,22 @@ func TestErrors(t *testing.T) {
"<a href='/foo?{{range .Items}}&{{.K}}={{.V}}{{end}}'>",
"",
},
+ {
+ "{{range .Items}}<a{{if .X}}{{end}}>{{end}}",
+ "",
+ },
+ {
+ "{{range .Items}}<a{{if .X}}{{end}}>{{continue}}{{end}}",
+ "",
+ },
+ {
+ "{{range .Items}}<a{{if .X}}{{end}}>{{break}}{{end}}",
+ "",
+ },
+ {
+ "{{range .Items}}<a{{if .X}}{{end}}>{{if .X}}{{break}}{{end}}{{end}}",
+ "",
+ },
// Error cases.
{
"{{if .Cond}}<a{{end}}",
@@ -956,6 +972,14 @@ func TestErrors(t *testing.T) {
"z:2:8: on range loop re-entry: {{range}} branches",
},
{
+ "{{range .Items}}<a{{if .X}}{{break}}{{end}}>{{end}}",
+ "z:1:29: at range loop break: {{range}} branches end in different contexts",
+ },
+ {
+ "{{range .Items}}<a{{if .X}}{{continue}}{{end}}>{{end}}",
+ "z:1:29: at range loop continue: {{range}} branches end in different contexts",
+ },
+ {
"<a b=1 c={{.H}}",
"z: ends in a non-text context: {stateAttr delimSpaceOrTagEnd",
},
@@ -1764,7 +1788,7 @@ func TestEscapeSetErrorsNotIgnorable(t *testing.T) {
}
func TestRedundantFuncs(t *testing.T) {
- inputs := []interface{}{
+ inputs := []any{
"\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f" +
"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" +
` !"#$%&'()*+,-./` +
@@ -1784,9 +1808,9 @@ func TestRedundantFuncs(t *testing.T) {
}
for n0, m := range redundantFuncs {
- f0 := funcMap[n0].(func(...interface{}) string)
+ f0 := funcMap[n0].(func(...any) string)
for n1 := range m {
- f1 := funcMap[n1].(func(...interface{}) string)
+ f1 := funcMap[n1].(func(...any) string)
for _, input := range inputs {
want := f0(input)
if got := f1(want); want != got {
diff --git a/libgo/go/html/template/example_test.go b/libgo/go/html/template/example_test.go
index 6cf936f..605b25f 100644
--- a/libgo/go/html/template/example_test.go
+++ b/libgo/go/html/template/example_test.go
@@ -98,7 +98,7 @@ func Example_autoescaping() {
func Example_escape() {
const s = `"Fran & Freddie's Diner" <tasty@example.com>`
- v := []interface{}{`"Fran & Freddie's Diner"`, ' ', `<tasty@example.com>`}
+ v := []any{`"Fran & Freddie's Diner"`, ' ', `<tasty@example.com>`}
fmt.Println(template.HTMLEscapeString(s))
template.HTMLEscape(os.Stdout, []byte(s))
diff --git a/libgo/go/html/template/exec_test.go b/libgo/go/html/template/exec_test.go
index 8885873..6cf710e 100644
--- a/libgo/go/html/template/exec_test.go
+++ b/libgo/go/html/template/exec_test.go
@@ -49,7 +49,7 @@ type T struct {
MSI map[string]int
MSIone map[string]int // one element, for deterministic output
MSIEmpty map[string]int
- MXI map[interface{}]int
+ MXI map[any]int
MII map[int]int
MI32S map[int32]string
MI64S map[int64]string
@@ -59,11 +59,11 @@ type T struct {
MUI8S map[uint8]string
SMSI []map[string]int
// Empty interfaces; used to see if we can dig inside one.
- Empty0 interface{} // nil
- Empty1 interface{}
- Empty2 interface{}
- Empty3 interface{}
- Empty4 interface{}
+ Empty0 any // nil
+ Empty1 any
+ Empty2 any
+ Empty3 any
+ Empty4 any
// Non-empty interfaces.
NonEmptyInterface I
NonEmptyInterfacePtS *I
@@ -141,7 +141,7 @@ var tVal = &T{
SB: []bool{true, false},
MSI: map[string]int{"one": 1, "two": 2, "three": 3},
MSIone: map[string]int{"one": 1},
- MXI: map[interface{}]int{"one": 1},
+ MXI: map[any]int{"one": 1},
MII: map[int]int{1: 1},
MI32S: map[int32]string{1: "one", 2: "two"},
MI64S: map[int64]string{2: "i642", 3: "i643"},
@@ -212,7 +212,7 @@ func (t *T) Method2(a uint16, b string) string {
return fmt.Sprintf("Method2: %d %s", a, b)
}
-func (t *T) Method3(v interface{}) string {
+func (t *T) Method3(v any) string {
return fmt.Sprintf("Method3: %v", v)
}
@@ -252,7 +252,7 @@ func (u *U) TrueFalse(b bool) string {
return ""
}
-func typeOf(arg interface{}) string {
+func typeOf(arg any) string {
return fmt.Sprintf("%T", arg)
}
@@ -260,7 +260,7 @@ type execTest struct {
name string
input string
output string
- data interface{}
+ data any
ok bool
}
@@ -393,7 +393,7 @@ var execTests = []execTest{
{".VariadicFuncInt", "{{call .VariadicFuncInt 33 `he` `llo`}}", "33=&lt;he&#43;llo&gt;", tVal, true},
{"if .BinaryFunc call", "{{ if .BinaryFunc}}{{call .BinaryFunc `1` `2`}}{{end}}", "[1=2]", tVal, true},
{"if not .BinaryFunc call", "{{ if not .BinaryFunc}}{{call .BinaryFunc `1` `2`}}{{else}}No{{end}}", "No", tVal, true},
- {"Interface Call", `{{stringer .S}}`, "foozle", map[string]interface{}{"S": bytes.NewBufferString("foozle")}, true},
+ {"Interface Call", `{{stringer .S}}`, "foozle", map[string]any{"S": bytes.NewBufferString("foozle")}, true},
{".ErrFunc", "{{call .ErrFunc}}", "bla", tVal, true},
{"call nil", "{{call nil}}", "", tVal, false},
@@ -567,6 +567,8 @@ var execTests = []execTest{
{"range empty no else", "{{range .SIEmpty}}-{{.}}-{{end}}", "", tVal, true},
{"range []int else", "{{range .SI}}-{{.}}-{{else}}EMPTY{{end}}", "-3--4--5-", tVal, true},
{"range empty else", "{{range .SIEmpty}}-{{.}}-{{else}}EMPTY{{end}}", "EMPTY", tVal, true},
+ {"range []int break else", "{{range .SI}}-{{.}}-{{break}}NOTREACHED{{else}}EMPTY{{end}}", "-3-", tVal, true},
+ {"range []int continue else", "{{range .SI}}-{{.}}-{{continue}}NOTREACHED{{else}}EMPTY{{end}}", "-3--4--5-", tVal, true},
{"range []bool", "{{range .SB}}-{{.}}-{{end}}", "-true--false-", tVal, true},
{"range []int method", "{{range .SI | .MAdd .I}}-{{.}}-{{end}}", "-20--21--22-", tVal, true},
{"range map", "{{range .MSI}}-{{.}}-{{end}}", "-1--3--2-", tVal, true},
@@ -738,7 +740,7 @@ func add(args ...int) int {
return sum
}
-func echo(arg interface{}) interface{} {
+func echo(arg any) any {
return arg
}
@@ -757,7 +759,7 @@ func stringer(s fmt.Stringer) string {
return s.String()
}
-func mapOfThree() interface{} {
+func mapOfThree() any {
return map[string]int{"three": 3}
}
@@ -1436,7 +1438,7 @@ func TestBlock(t *testing.T) {
func TestEvalFieldErrors(t *testing.T) {
tests := []struct {
name, src string
- value interface{}
+ value any
want string
}{
{
@@ -1579,7 +1581,7 @@ func TestInterfaceValues(t *testing.T) {
for _, tt := range tests {
tmpl := Must(New("tmpl").Parse(tt.text))
var buf bytes.Buffer
- err := tmpl.Execute(&buf, map[string]interface{}{
+ err := tmpl.Execute(&buf, map[string]any{
"PlusOne": func(n int) int {
return n + 1
},
@@ -1608,7 +1610,7 @@ func TestInterfaceValues(t *testing.T) {
// Check that panics during calls are recovered and returned as errors.
func TestExecutePanicDuringCall(t *testing.T) {
- funcs := map[string]interface{}{
+ funcs := map[string]any{
"doPanic": func() string {
panic("custom panic string")
},
@@ -1616,7 +1618,7 @@ func TestExecutePanicDuringCall(t *testing.T) {
tests := []struct {
name string
input string
- data interface{}
+ data any
wantErr string
}{
{
@@ -1814,7 +1816,7 @@ func TestRecursiveExecuteViaMethod(t *testing.T) {
func TestTemplateFuncsAfterClone(t *testing.T) {
s := `{{ f . }}`
want := "test"
- orig := New("orig").Funcs(map[string]interface{}{
+ orig := New("orig").Funcs(map[string]any{
"f": func(in string) string {
return in
},
diff --git a/libgo/go/html/template/html.go b/libgo/go/html/template/html.go
index 356b829..19bd0cc 100644
--- a/libgo/go/html/template/html.go
+++ b/libgo/go/html/template/html.go
@@ -12,7 +12,7 @@ import (
)
// htmlNospaceEscaper escapes for inclusion in unquoted attribute values.
-func htmlNospaceEscaper(args ...interface{}) string {
+func htmlNospaceEscaper(args ...any) string {
s, t := stringify(args...)
if t == contentTypeHTML {
return htmlReplacer(stripTags(s), htmlNospaceNormReplacementTable, false)
@@ -21,7 +21,7 @@ func htmlNospaceEscaper(args ...interface{}) string {
}
// attrEscaper escapes for inclusion in quoted attribute values.
-func attrEscaper(args ...interface{}) string {
+func attrEscaper(args ...any) string {
s, t := stringify(args...)
if t == contentTypeHTML {
return htmlReplacer(stripTags(s), htmlNormReplacementTable, true)
@@ -30,7 +30,7 @@ func attrEscaper(args ...interface{}) string {
}
// rcdataEscaper escapes for inclusion in an RCDATA element body.
-func rcdataEscaper(args ...interface{}) string {
+func rcdataEscaper(args ...any) string {
s, t := stringify(args...)
if t == contentTypeHTML {
return htmlReplacer(s, htmlNormReplacementTable, true)
@@ -39,7 +39,7 @@ func rcdataEscaper(args ...interface{}) string {
}
// htmlEscaper escapes for inclusion in HTML text.
-func htmlEscaper(args ...interface{}) string {
+func htmlEscaper(args ...any) string {
s, t := stringify(args...)
if t == contentTypeHTML {
return s
@@ -225,7 +225,7 @@ func stripTags(html string) string {
// htmlNameFilter accepts valid parts of an HTML attribute or tag name or
// a known-safe HTML attribute.
-func htmlNameFilter(args ...interface{}) string {
+func htmlNameFilter(args ...any) string {
s, t := stringify(args...)
if t == contentTypeHTMLAttr {
return s
@@ -260,6 +260,6 @@ func htmlNameFilter(args ...interface{}) string {
// content interpolated into comments.
// This approach is equally valid whether or not static comment content is
// removed from the template.
-func commentEscaper(args ...interface{}) string {
+func commentEscaper(args ...any) string {
return ""
}
diff --git a/libgo/go/html/template/js.go b/libgo/go/html/template/js.go
index ea9c183..50523d0 100644
--- a/libgo/go/html/template/js.go
+++ b/libgo/go/html/template/js.go
@@ -122,7 +122,7 @@ var jsonMarshalType = reflect.TypeOf((*json.Marshaler)(nil)).Elem()
// indirectToJSONMarshaler returns the value, after dereferencing as many times
// as necessary to reach the base type (or nil) or an implementation of json.Marshal.
-func indirectToJSONMarshaler(a interface{}) interface{} {
+func indirectToJSONMarshaler(a any) any {
// text/template now supports passing untyped nil as a func call
// argument, so we must support it. Otherwise we'd panic below, as one
// cannot call the Type or Interface methods on an invalid
@@ -132,7 +132,7 @@ func indirectToJSONMarshaler(a interface{}) interface{} {
}
v := reflect.ValueOf(a)
- for !v.Type().Implements(jsonMarshalType) && v.Kind() == reflect.Ptr && !v.IsNil() {
+ for !v.Type().Implements(jsonMarshalType) && v.Kind() == reflect.Pointer && !v.IsNil() {
v = v.Elem()
}
return v.Interface()
@@ -140,8 +140,8 @@ func indirectToJSONMarshaler(a interface{}) interface{} {
// jsValEscaper escapes its inputs to a JS Expression (section 11.14) that has
// neither side-effects nor free variables outside (NaN, Infinity).
-func jsValEscaper(args ...interface{}) string {
- var a interface{}
+func jsValEscaper(args ...any) string {
+ var a any
if len(args) == 1 {
a = indirectToJSONMarshaler(args[0])
switch t := a.(type) {
@@ -224,7 +224,7 @@ func jsValEscaper(args ...interface{}) string {
// jsStrEscaper produces a string that can be included between quotes in
// JavaScript source, in JavaScript embedded in an HTML5 <script> element,
// or in an HTML5 event handler attribute such as onclick.
-func jsStrEscaper(args ...interface{}) string {
+func jsStrEscaper(args ...any) string {
s, t := stringify(args...)
if t == contentTypeJSStr {
return replace(s, jsStrNormReplacementTable)
@@ -236,7 +236,7 @@ func jsStrEscaper(args ...interface{}) string {
// specials so the result is treated literally when included in a regular
// expression literal. /foo{{.X}}bar/ matches the string "foo" followed by
// the literal text of {{.X}} followed by the string "bar".
-func jsRegexpEscaper(args ...interface{}) string {
+func jsRegexpEscaper(args ...any) string {
s, _ := stringify(args...)
s = replace(s, jsRegexpReplacementTable)
if s == "" {
@@ -398,9 +398,7 @@ func isJSType(mimeType string) bool {
// https://tools.ietf.org/html/rfc4329#section-3
// https://www.ietf.org/rfc/rfc4627.txt
// discard parameters
- if i := strings.Index(mimeType, ";"); i >= 0 {
- mimeType = mimeType[:i]
- }
+ mimeType, _, _ = strings.Cut(mimeType, ";")
mimeType = strings.ToLower(mimeType)
mimeType = strings.TrimSpace(mimeType)
switch mimeType {
diff --git a/libgo/go/html/template/js_test.go b/libgo/go/html/template/js_test.go
index d7ee47b..56579d8 100644
--- a/libgo/go/html/template/js_test.go
+++ b/libgo/go/html/template/js_test.go
@@ -103,7 +103,7 @@ func TestNextJsCtx(t *testing.T) {
func TestJSValEscaper(t *testing.T) {
tests := []struct {
- x interface{}
+ x any
js string
}{
{int(42), " 42 "},
@@ -140,8 +140,8 @@ func TestJSValEscaper(t *testing.T) {
// "\v" == "v" on IE 6 so use "\u000b" instead.
{"\t\x0b", `"\t\u000b"`},
{struct{ X, Y int }{1, 2}, `{"X":1,"Y":2}`},
- {[]interface{}{}, "[]"},
- {[]interface{}{42, "foo", nil}, `[42,"foo",null]`},
+ {[]any{}, "[]"},
+ {[]any{42, "foo", nil}, `[42,"foo",null]`},
{[]string{"<!--", "</script>", "-->"}, `["\u003c!--","\u003c/script\u003e","--\u003e"]`},
{"<!--", `"\u003c!--"`},
{"-->", `"--\u003e"`},
@@ -158,7 +158,7 @@ func TestJSValEscaper(t *testing.T) {
}
// Make sure that escaping corner cases are not broken
// by nesting.
- a := []interface{}{test.x}
+ a := []any{test.x}
want := "[" + strings.TrimSpace(test.js) + "]"
if js := jsValEscaper(a); js != want {
t.Errorf("%+v: want\n\t%q\ngot\n\t%q", a, want, js)
@@ -168,7 +168,7 @@ func TestJSValEscaper(t *testing.T) {
func TestJSStrEscaper(t *testing.T) {
tests := []struct {
- x interface{}
+ x any
esc string
}{
{"", ``},
@@ -223,7 +223,7 @@ func TestJSStrEscaper(t *testing.T) {
func TestJSRegexpEscaper(t *testing.T) {
tests := []struct {
- x interface{}
+ x any
esc string
}{
{"", `(?:)`},
@@ -278,7 +278,7 @@ func TestEscapersOnLower7AndSelectHighCodepoints(t *testing.T) {
tests := []struct {
name string
- escaper func(...interface{}) string
+ escaper func(...any) string
escaped string
}{
{
diff --git a/libgo/go/html/template/template.go b/libgo/go/html/template/template.go
index 69312d3..7eba716 100644
--- a/libgo/go/html/template/template.go
+++ b/libgo/go/html/template/template.go
@@ -117,7 +117,7 @@ func (t *Template) escape() error {
// the output writer.
// A template may be executed safely in parallel, although if parallel
// executions share a Writer the output may be interleaved.
-func (t *Template) Execute(wr io.Writer, data interface{}) error {
+func (t *Template) Execute(wr io.Writer, data any) error {
if err := t.escape(); err != nil {
return err
}
@@ -131,7 +131,7 @@ func (t *Template) Execute(wr io.Writer, data interface{}) error {
// the output writer.
// A template may be executed safely in parallel, although if parallel
// executions share a Writer the output may be interleaved.
-func (t *Template) ExecuteTemplate(wr io.Writer, name string, data interface{}) error {
+func (t *Template) ExecuteTemplate(wr io.Writer, name string, data any) error {
tmpl, err := t.lookupAndEscapeTemplate(name)
if err != nil {
return err
@@ -335,7 +335,7 @@ func (t *Template) Name() string {
// terminates and Execute returns that error. FuncMap has the same base type
// as FuncMap in "text/template", copied here so clients need not import
// "text/template".
-type FuncMap map[string]interface{}
+type FuncMap map[string]any
// Funcs adds the elements of the argument map to the template's function map.
// It must be called before the template is parsed.
@@ -486,7 +486,7 @@ func parseGlob(t *Template, pattern string) (*Template, error) {
// IsTrue reports whether the value is 'true', in the sense of not the zero of its type,
// and whether the value has a meaningful truth value. This is the definition of
// truth used by if and other such actions.
-func IsTrue(val interface{}) (truth, ok bool) {
+func IsTrue(val any) (truth, ok bool) {
return template.IsTrue(val)
}
diff --git a/libgo/go/html/template/template_test.go b/libgo/go/html/template/template_test.go
index 1f2c888..99a1091 100644
--- a/libgo/go/html/template/template_test.go
+++ b/libgo/go/html/template/template_test.go
@@ -206,7 +206,7 @@ func (c *testCase) mustNotParse(t *Template, text string) {
}
}
-func (c *testCase) mustExecute(t *Template, val interface{}, want string) {
+func (c *testCase) mustExecute(t *Template, val any, want string) {
var buf bytes.Buffer
err := t.Execute(&buf, val)
if err != nil {
diff --git a/libgo/go/html/template/url.go b/libgo/go/html/template/url.go
index 6f8185a..9390558 100644
--- a/libgo/go/html/template/url.go
+++ b/libgo/go/html/template/url.go
@@ -32,7 +32,7 @@ import (
// To allow URLs containing other schemes to bypass this filter, developers must
// explicitly indicate that such a URL is expected and safe by encapsulating it
// in a template.URL value.
-func urlFilter(args ...interface{}) string {
+func urlFilter(args ...any) string {
s, t := stringify(args...)
if t == contentTypeURL {
return s
@@ -46,9 +46,7 @@ func urlFilter(args ...interface{}) string {
// isSafeURL is true if s is a relative URL or if URL has a protocol in
// (http, https, mailto).
func isSafeURL(s string) bool {
- if i := strings.IndexRune(s, ':'); i >= 0 && !strings.ContainsRune(s[:i], '/') {
-
- protocol := s[:i]
+ if protocol, _, ok := strings.Cut(s, ":"); ok && !strings.Contains(protocol, "/") {
if !strings.EqualFold(protocol, "http") && !strings.EqualFold(protocol, "https") && !strings.EqualFold(protocol, "mailto") {
return false
}
@@ -58,7 +56,7 @@ func isSafeURL(s string) bool {
// urlEscaper produces an output that can be embedded in a URL query.
// The output can be embedded in an HTML attribute without further escaping.
-func urlEscaper(args ...interface{}) string {
+func urlEscaper(args ...any) string {
return urlProcessor(false, args...)
}
@@ -67,13 +65,13 @@ func urlEscaper(args ...interface{}) string {
// The normalizer does not encode all HTML specials. Specifically, it does not
// encode '&' so correct embedding in an HTML attribute requires escaping of
// '&' to '&amp;'.
-func urlNormalizer(args ...interface{}) string {
+func urlNormalizer(args ...any) string {
return urlProcessor(true, args...)
}
// urlProcessor normalizes (when norm is true) or escapes its input to produce
// a valid hierarchical or opaque URL part.
-func urlProcessor(norm bool, args ...interface{}) string {
+func urlProcessor(norm bool, args ...any) string {
s, t := stringify(args...)
if t == contentTypeURL {
norm = true
@@ -143,7 +141,7 @@ func processURLOnto(s string, norm bool, b *bytes.Buffer) bool {
// Filters and normalizes srcset values which are comma separated
// URLs followed by metadata.
-func srcsetFilterAndEscaper(args ...interface{}) string {
+func srcsetFilterAndEscaper(args ...any) string {
s, t := stringify(args...)
switch t {
case contentTypeSrcset:
diff --git a/libgo/go/html/template/url_test.go b/libgo/go/html/template/url_test.go
index 75c354e..a04f39c 100644
--- a/libgo/go/html/template/url_test.go
+++ b/libgo/go/html/template/url_test.go
@@ -48,7 +48,7 @@ func TestURLFilters(t *testing.T) {
tests := []struct {
name string
- escaper func(...interface{}) string
+ escaper func(...any) string
escaped string
}{
{