gopls: add and enable the slog analyzer

This analyzer is included in go/vet, and so gopls should have it as
well.

Change-Id: Ib5cbee44a1f38c4aa45d75bcaa7a345d88099d8e
Reviewed-on: https://go-review.googlesource.com/c/tools/+/524764
Reviewed-by: Alan Donovan <adonovan@google.com>
Auto-Submit: Robert Findley <rfindley@google.com>
gopls-CI: kokoro <noreply+kokoro@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
diff --git a/gopls/doc/analyzers.md b/gopls/doc/analyzers.md
index 9a592d4..2ff9434 100644
--- a/gopls/doc/analyzers.md
+++ b/gopls/doc/analyzers.md
@@ -494,6 +494,24 @@
 
 **Enabled by default.**
 
+## **slog**
+
+check for invalid structured logging calls
+
+The slog checker looks for calls to functions from the log/slog
+package that take alternating key-value pairs. It reports calls
+where an argument in a key position is neither a string nor a
+slog.Attr, and where a final key is missing its value.
+For example,it would report
+
+	slog.Warn("message", 11, "k") // slog.Warn arg "11" should be a string or a slog.Attr
+
+and
+
+	slog.Info("message", "k1", v1, "k2") // call to slog.Info missing a final value
+
+**Enabled by default.**
+
 ## **sortslice**
 
 check the argument type of sort.Slice
diff --git a/gopls/internal/lsp/source/api_json.go b/gopls/internal/lsp/source/api_json.go
index 7635edb..60425db 100644
--- a/gopls/internal/lsp/source/api_json.go
+++ b/gopls/internal/lsp/source/api_json.go
@@ -359,6 +359,11 @@
 							Default: "true",
 						},
 						{
+							Name:    "\"slog\"",
+							Doc:     "check for invalid structured logging calls\n\nThe slog checker looks for calls to functions from the log/slog\npackage that take alternating key-value pairs. It reports calls\nwhere an argument in a key position is neither a string nor a\nslog.Attr, and where a final key is missing its value.\nFor example,it would report\n\n\tslog.Warn(\"message\", 11, \"k\") // slog.Warn arg \"11\" should be a string or a slog.Attr\n\nand\n\n\tslog.Info(\"message\", \"k1\", v1, \"k2\") // call to slog.Info missing a final value",
+							Default: "true",
+						},
+						{
 							Name:    "\"sortslice\"",
 							Doc:     "check the argument type of sort.Slice\n\nsort.Slice requires an argument of a slice type. Check that\nthe interface{} value passed to sort.Slice is actually a slice.",
 							Default: "true",
@@ -1071,6 +1076,12 @@
 			Default: true,
 		},
 		{
+			Name:    "slog",
+			Doc:     "check for invalid structured logging calls\n\nThe slog checker looks for calls to functions from the log/slog\npackage that take alternating key-value pairs. It reports calls\nwhere an argument in a key position is neither a string nor a\nslog.Attr, and where a final key is missing its value.\nFor example,it would report\n\n\tslog.Warn(\"message\", 11, \"k\") // slog.Warn arg \"11\" should be a string or a slog.Attr\n\nand\n\n\tslog.Info(\"message\", \"k1\", v1, \"k2\") // call to slog.Info missing a final value",
+			URL:     "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/slog",
+			Default: true,
+		},
+		{
 			Name:    "sortslice",
 			Doc:     "check the argument type of sort.Slice\n\nsort.Slice requires an argument of a slice type. Check that\nthe interface{} value passed to sort.Slice is actually a slice.",
 			URL:     "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/sortslice",
diff --git a/gopls/internal/lsp/source/options.go b/gopls/internal/lsp/source/options.go
index 67818fa..2b91f83 100644
--- a/gopls/internal/lsp/source/options.go
+++ b/gopls/internal/lsp/source/options.go
@@ -39,6 +39,7 @@
 	"golang.org/x/tools/go/analysis/passes/printf"
 	"golang.org/x/tools/go/analysis/passes/shadow"
 	"golang.org/x/tools/go/analysis/passes/shift"
+	"golang.org/x/tools/go/analysis/passes/slog"
 	"golang.org/x/tools/go/analysis/passes/sortslice"
 	"golang.org/x/tools/go/analysis/passes/stdmethods"
 	"golang.org/x/tools/go/analysis/passes/stringintconv"
@@ -1549,6 +1550,7 @@
 		nilfunc.Analyzer.Name:       {Analyzer: nilfunc.Analyzer, Enabled: true},
 		printf.Analyzer.Name:        {Analyzer: printf.Analyzer, Enabled: true},
 		shift.Analyzer.Name:         {Analyzer: shift.Analyzer, Enabled: true},
+		slog.Analyzer.Name:          {Analyzer: slog.Analyzer, Enabled: true},
 		stdmethods.Analyzer.Name:    {Analyzer: stdmethods.Analyzer, Enabled: true},
 		stringintconv.Analyzer.Name: {Analyzer: stringintconv.Analyzer, Enabled: true},
 		structtag.Analyzer.Name:     {Analyzer: structtag.Analyzer, Enabled: true},
diff --git a/gopls/internal/regtest/marker/testdata/diagnostics/analyzers.txt b/gopls/internal/regtest/marker/testdata/diagnostics/analyzers.txt
index 6e7e465..e98674b 100644
--- a/gopls/internal/regtest/marker/testdata/diagnostics/analyzers.txt
+++ b/gopls/internal/regtest/marker/testdata/diagnostics/analyzers.txt
@@ -1,24 +1,32 @@
 Test of warning diagnostics from various analyzers:
-tests, copylocks, printf, and timeformat.
+copylocks, printf, slog, tests, and timeformat.
 
 -- go.mod --
 module example.com
 go 1.12
 
+-- flags --
+-min_go=go1.21
+
 -- bad_test.go --
 package analyzer
 
 import (
 	"fmt"
+	"log/slog"
 	"sync"
 	"testing"
 	"time"
 )
 
-func Testbad(t *testing.T) { //@diag("", re"Testbad has malformed name: first letter after 'Test' must not be lowercase")
+// copylocks
+func _() {
 	var x sync.Mutex
 	_ = x //@diag("x", re"assignment copies lock value to _: sync.Mutex")
+}
 
+// printf
+func _() {
 	printfWrapper("%s") //@diag(re`printfWrapper\(.*\)`, re"example.com.printfWrapper format %s reads arg #1, but call has 0 args")
 }
 
@@ -26,7 +34,18 @@
 	fmt.Printf(format, args...)
 }
 
+// slog
+func _() {
+	slog.Info("msg", 1) //@diag("1", re`slog.Info arg "1" should be a string or a slog.Attr`)
+}
+
+// tests
+func Testbad(t *testing.T) { //@diag("", re"Testbad has malformed name: first letter after 'Test' must not be lowercase")
+}
+
+// timeformat
 func _() {
 	now := time.Now()
 	fmt.Println(now.Format("2006-02-01")) //@diag("2006-02-01", re"2006-02-01 should be 2006-01-02")
 }
+