expvar: don't crash if map value set to nil

Fixes #52719

Change-Id: Ib032193d00664090c47ae92e7d59674ec2d0165a
Reviewed-on: https://go-review.googlesource.com/c/go/+/408677
Reviewed-by: Alan Donovan <adonovan@google.com>
Auto-Submit: Ian Lance Taylor <iant@golang.org>
Reviewed-by: Russ Cox <rsc@golang.org>
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Jonathan Amsterdam <jba@google.com>
diff --git a/src/expvar/expvar.go b/src/expvar/expvar.go
index 5629f89..08cd055 100644
--- a/src/expvar/expvar.go
+++ b/src/expvar/expvar.go
@@ -118,7 +118,12 @@
 		if !first {
 			fmt.Fprintf(&b, ", ")
 		}
-		fmt.Fprintf(&b, "%q: %v", kv.Key, kv.Value)
+		fmt.Fprintf(&b, "%q: ", kv.Key)
+		if kv.Value != nil {
+			fmt.Fprintf(&b, "%v", kv.Value)
+		} else {
+			fmt.Fprint(&b, "null")
+		}
 		first = false
 	})
 	fmt.Fprintf(&b, "}")
@@ -224,7 +229,8 @@
 	defer v.keysMu.RUnlock()
 	for _, k := range v.keys {
 		i, _ := v.m.Load(k)
-		f(KeyValue{k, i.(Var)})
+		val, _ := i.(Var)
+		f(KeyValue{k, val})
 	}
 }
 
diff --git a/src/expvar/expvar_test.go b/src/expvar/expvar_test.go
index ba95a36..552bae8 100644
--- a/src/expvar/expvar_test.go
+++ b/src/expvar/expvar_test.go
@@ -261,6 +261,29 @@
 	}
 }
 
+func TestMapNil(t *testing.T) {
+	RemoveAll()
+	const key = "key"
+	m := NewMap("issue527719")
+	m.Set(key, nil)
+	s := m.String()
+	var j any
+	if err := json.Unmarshal([]byte(s), &j); err != nil {
+		t.Fatalf("m.String() == %q isn't valid JSON: %v", s, err)
+	}
+	m2, ok := j.(map[string]any)
+	if !ok {
+		t.Fatalf("m.String() produced %T, wanted a map", j)
+	}
+	v, ok := m2[key]
+	if !ok {
+		t.Fatalf("missing %q in %v", key, m2)
+	}
+	if v != nil {
+		t.Fatalf("m[%q] = %v, want nil", key, v)
+	}
+}
+
 func BenchmarkMapSet(b *testing.B) {
 	m := new(Map).Init()