Skip to content

Commit 6dd777b

Browse files
authored
feat: support With and LogAttrs functions (#41)
1 parent 38a4f9e commit 6dd777b

File tree

10 files changed

+155
-62
lines changed

10 files changed

+155
-62
lines changed

sloglint.go

+43-32
Original file line numberDiff line numberDiff line change
@@ -114,25 +114,33 @@ func flags(opts *Options) flag.FlagSet {
114114
return *fset
115115
}
116116

117-
var slogFuncs = map[string]int{ // funcName:argsPos
118-
"log/slog.Log": 3,
119-
"log/slog.Debug": 1,
120-
"log/slog.Info": 1,
121-
"log/slog.Warn": 1,
122-
"log/slog.Error": 1,
123-
"log/slog.DebugContext": 2,
124-
"log/slog.InfoContext": 2,
125-
"log/slog.WarnContext": 2,
126-
"log/slog.ErrorContext": 2,
127-
"(*log/slog.Logger).Log": 3,
128-
"(*log/slog.Logger).Debug": 1,
129-
"(*log/slog.Logger).Info": 1,
130-
"(*log/slog.Logger).Warn": 1,
131-
"(*log/slog.Logger).Error": 1,
132-
"(*log/slog.Logger).DebugContext": 2,
133-
"(*log/slog.Logger).InfoContext": 2,
134-
"(*log/slog.Logger).WarnContext": 2,
135-
"(*log/slog.Logger).ErrorContext": 2,
117+
var slogFuncs = map[string]struct {
118+
argsPos int
119+
skipContextCheck bool
120+
}{
121+
// funcName: {argsPos, skipContextCheck}
122+
"log/slog.With": {argsPos: 0, skipContextCheck: true},
123+
"log/slog.Log": {argsPos: 3},
124+
"log/slog.LogAttrs": {argsPos: 3},
125+
"log/slog.Debug": {argsPos: 1},
126+
"log/slog.Info": {argsPos: 1},
127+
"log/slog.Warn": {argsPos: 1},
128+
"log/slog.Error": {argsPos: 1},
129+
"log/slog.DebugContext": {argsPos: 2},
130+
"log/slog.InfoContext": {argsPos: 2},
131+
"log/slog.WarnContext": {argsPos: 2},
132+
"log/slog.ErrorContext": {argsPos: 2},
133+
"(*log/slog.Logger).With": {argsPos: 0, skipContextCheck: true},
134+
"(*log/slog.Logger).Log": {argsPos: 3},
135+
"(*log/slog.Logger).LogAttrs": {argsPos: 3},
136+
"(*log/slog.Logger).Debug": {argsPos: 1},
137+
"(*log/slog.Logger).Info": {argsPos: 1},
138+
"(*log/slog.Logger).Warn": {argsPos: 1},
139+
"(*log/slog.Logger).Error": {argsPos: 1},
140+
"(*log/slog.Logger).DebugContext": {argsPos: 2},
141+
"(*log/slog.Logger).InfoContext": {argsPos: 2},
142+
"(*log/slog.Logger).WarnContext": {argsPos: 2},
143+
"(*log/slog.Logger).ErrorContext": {argsPos: 2},
136144
}
137145

138146
var attrFuncs = map[string]struct{}{
@@ -183,7 +191,7 @@ func visit(pass *analysis.Pass, opts *Options, node ast.Node, stack []ast.Node)
183191
}
184192

185193
name := fn.FullName()
186-
argsPos, ok := slogFuncs[name]
194+
funcInfo, ok := slogFuncs[name]
187195
if !ok {
188196
return
189197
}
@@ -199,25 +207,28 @@ func visit(pass *analysis.Pass, opts *Options, node ast.Node, stack []ast.Node)
199207
}
200208
}
201209

202-
switch opts.ContextOnly {
203-
case "all":
204-
typ := pass.TypesInfo.TypeOf(call.Args[0])
205-
if typ != nil && typ.String() != "context.Context" {
206-
pass.Reportf(call.Pos(), "%sContext should be used instead", fn.Name())
207-
}
208-
case "scope":
209-
typ := pass.TypesInfo.TypeOf(call.Args[0])
210-
if typ != nil && typ.String() != "context.Context" && hasContextInScope(pass.TypesInfo, stack) {
211-
pass.Reportf(call.Pos(), "%sContext should be used instead", fn.Name())
210+
// NOTE: "With" functions are not checked for context.Context.
211+
if !funcInfo.skipContextCheck {
212+
switch opts.ContextOnly {
213+
case "all":
214+
typ := pass.TypesInfo.TypeOf(call.Args[0])
215+
if typ != nil && typ.String() != "context.Context" {
216+
pass.Reportf(call.Pos(), "%sContext should be used instead", fn.Name())
217+
}
218+
case "scope":
219+
typ := pass.TypesInfo.TypeOf(call.Args[0])
220+
if typ != nil && typ.String() != "context.Context" && hasContextInScope(pass.TypesInfo, stack) {
221+
pass.Reportf(call.Pos(), "%sContext should be used instead", fn.Name())
222+
}
212223
}
213224
}
214225

215-
if opts.StaticMsg && !staticMsg(call.Args[argsPos-1]) {
226+
if opts.StaticMsg && !staticMsg(call.Args[funcInfo.argsPos-1]) {
216227
pass.Reportf(call.Pos(), "message should be a string literal or a constant")
217228
}
218229

219230
// NOTE: we assume that the arguments have already been validated by govet.
220-
args := call.Args[argsPos:]
231+
args := call.Args[funcInfo.argsPos:]
221232
if len(args) == 0 {
222233
return
223234
}

testdata/src/args_on_sep_lines/args_on_sep_lines.go

+13
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,16 @@ func tests() {
1616
slog.Int("bar", 2),
1717
)
1818

19+
slog.With(
20+
"foo", 1,
21+
"bar", 2,
22+
).Info("msg")
23+
24+
slog.With(
25+
slog.Int("foo", 1),
26+
slog.Int("bar", 2),
27+
).Info("msg")
28+
1929
slog.Info("msg", "foo", 1, "bar", 2) // want `arguments should be put on separate lines`
2030
slog.Info("msg", slog.Int("foo", 1), slog.Int("bar", 2)) // want `arguments should be put on separate lines`
2131

@@ -31,4 +41,7 @@ func tests() {
3141
slog.Info("msg", // want `arguments should be put on separate lines`
3242
slog.Int("foo", 1), slog.Int("bar", 2),
3343
)
44+
45+
slog.With("msg", "foo", 1, "bar", 2).Info("msg") // want `arguments should be put on separate lines`
46+
slog.With("msg", slog.Int("foo", 1), slog.Int("bar", 2)).Info("msg") // want `arguments should be put on separate lines`
3447
}

testdata/src/attr_only/attr_only.go

+5-3
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@ func tests() {
66
slog.Info("msg")
77
slog.Info("msg", slog.Int("foo", 1))
88
slog.Info("msg", slog.Int("foo", 1), slog.Int("bar", 2))
9+
slog.With(slog.Int("foo", 1), slog.Int("bar", 2)).Info("msg")
910

10-
slog.Info("msg", "foo", 1) // want `key-value pairs should not be used`
11-
slog.Info("msg", "foo", 1, "bar", 2) // want `key-value pairs should not be used`
12-
slog.Info("msg", "foo", 1, slog.Int("bar", 2)) // want `key-value pairs should not be used`
11+
slog.Info("msg", "foo", 1) // want `key-value pairs should not be used`
12+
slog.Info("msg", "foo", 1, "bar", 2) // want `key-value pairs should not be used`
13+
slog.Info("msg", "foo", 1, slog.Int("bar", 2)) // want `key-value pairs should not be used`
14+
slog.With("foo", 1, slog.Int("bar", 2)).Info("msg") // want `key-value pairs should not be used`
1315
}

testdata/src/context_only_all/context_only_all.go

+6-4
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ func tests(ctx context.Context) {
1111
slog.InfoContext(ctx, "msg")
1212
slog.WarnContext(ctx, "msg")
1313
slog.ErrorContext(ctx, "msg")
14+
slog.With("key", "value").ErrorContext(ctx, "msg")
1415

1516
slog.Debug("msg") // want `DebugContext should be used instead`
1617
slog.Info("msg") // want `InfoContext should be used instead`
@@ -24,8 +25,9 @@ func tests(ctx context.Context) {
2425
logger.WarnContext(ctx, "msg")
2526
logger.ErrorContext(ctx, "msg")
2627

27-
logger.Debug("msg") // want `DebugContext should be used instead`
28-
logger.Info("msg") // want `InfoContext should be used instead`
29-
logger.Warn("msg") // want `WarnContext should be used instead`
30-
logger.Error("msg") // want `ErrorContext should be used instead`
28+
logger.Debug("msg") // want `DebugContext should be used instead`
29+
logger.Info("msg") // want `InfoContext should be used instead`
30+
logger.Warn("msg") // want `WarnContext should be used instead`
31+
logger.Error("msg") // want `ErrorContext should be used instead`
32+
logger.With("key", "value").Error("msg") // want `ErrorContext should be used instead`
3133
}

testdata/src/forbidden_keys/forbidden_keys.go

+20-9
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,29 @@ const (
99
func tests() {
1010
slog.Info("msg")
1111
slog.Info("msg", "foo-bar", 1)
12+
slog.With("foo-bar", 1).Info("msg")
1213
slog.Info("msg", "foo_bar", 1) // want `"foo_bar" key is forbidden and should not be used`
1314
slog.Info("msg", snakeKey, 1) // want `"foo_bar" key is forbidden and should not be used`
1415
slog.Info("msg", slog.Int("foo_bar", 1)) // want `"foo_bar" key is forbidden and should not be used`
1516
slog.Info("msg", slog.Int(snakeKey, 1)) // want `"foo_bar" key is forbidden and should not be used`
1617
slog.Info("msg", slog.Attr{})
17-
slog.Info("msg", slog.Attr{"foo_bar", slog.IntValue(1)}) // want `"foo_bar" key is forbidden and should not be used`
18-
slog.Info("msg", slog.Attr{snakeKey, slog.IntValue(1)}) // want `"foo_bar" key is forbidden and should not be used`
19-
slog.Info("msg", slog.Attr{Key: "foo_bar"}) // want `"foo_bar" key is forbidden and should not be used`
20-
slog.Info("msg", slog.Attr{Key: snakeKey}) // want `"foo_bar" key is forbidden and should not be used`
21-
slog.Info("msg", slog.Attr{Key: "foo_bar", Value: slog.IntValue(1)}) // want `"foo_bar" key is forbidden and should not be used`
22-
slog.Info("msg", slog.Attr{Key: snakeKey, Value: slog.IntValue(1)}) // want `"foo_bar" key is forbidden and should not be used`
23-
slog.Info("msg", slog.Attr{Value: slog.IntValue(1), Key: "foo_bar"}) // want `"foo_bar" key is forbidden and should not be used`
24-
slog.Info("msg", slog.Attr{Value: slog.IntValue(1), Key: snakeKey}) // want `"foo_bar" key is forbidden and should not be used`
25-
slog.Info("msg", slog.Attr{Value: slog.IntValue(1), Key: `foo_bar`}) // want `"foo_bar" key is forbidden and should not be used`
18+
slog.Info("msg", slog.Attr{"foo_bar", slog.IntValue(1)}) // want `"foo_bar" key is forbidden and should not be used`
19+
slog.Info("msg", slog.Attr{snakeKey, slog.IntValue(1)}) // want `"foo_bar" key is forbidden and should not be used`
20+
slog.Info("msg", slog.Attr{Key: "foo_bar"}) // want `"foo_bar" key is forbidden and should not be used`
21+
slog.Info("msg", slog.Attr{Key: snakeKey}) // want `"foo_bar" key is forbidden and should not be used`
22+
slog.Info("msg", slog.Attr{Key: "foo_bar", Value: slog.IntValue(1)}) // want `"foo_bar" key is forbidden and should not be used`
23+
slog.Info("msg", slog.Attr{Key: snakeKey, Value: slog.IntValue(1)}) // want `"foo_bar" key is forbidden and should not be used`
24+
slog.Info("msg", slog.Attr{Value: slog.IntValue(1), Key: "foo_bar"}) // want `"foo_bar" key is forbidden and should not be used`
25+
slog.Info("msg", slog.Attr{Value: slog.IntValue(1), Key: snakeKey}) // want `"foo_bar" key is forbidden and should not be used`
26+
slog.Info("msg", slog.Attr{Value: slog.IntValue(1), Key: `foo_bar`}) // want `"foo_bar" key is forbidden and should not be used`
27+
slog.With(slog.Attr{"foo_bar", slog.IntValue(1)}).Info("msg") // want `"foo_bar" key is forbidden and should not be used`
28+
slog.With(slog.Attr{snakeKey, slog.IntValue(1)}).Info("msg") // want `"foo_bar" key is forbidden and should not be used`
29+
slog.With(slog.Attr{Key: "foo_bar"}).Info("msg") // want `"foo_bar" key is forbidden and should not be used`
30+
slog.With(slog.Attr{Key: snakeKey}).Info("msg") // want `"foo_bar" key is forbidden and should not be used`
31+
slog.With(slog.Attr{Key: "foo_bar", Value: slog.IntValue(1)}).Info("msg") // want `"foo_bar" key is forbidden and should not be used`
32+
slog.With(slog.Attr{Key: snakeKey, Value: slog.IntValue(1)}).Info("msg") // want `"foo_bar" key is forbidden and should not be used`
33+
slog.With(slog.Attr{Value: slog.IntValue(1), Key: "foo_bar"}).Info("msg") // want `"foo_bar" key is forbidden and should not be used`
34+
slog.With(slog.Attr{Value: slog.IntValue(1), Key: snakeKey}).Info("msg") // want `"foo_bar" key is forbidden and should not be used`
35+
slog.With(slog.Attr{Value: slog.IntValue(1), Key: `foo_bar`}).Info("msg") // want `"foo_bar" key is forbidden and should not be used`
36+
2637
}

testdata/src/key_naming_case/key_naming_case.go

+35-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package key_naming_case
22

3-
import "log/slog"
3+
import (
4+
"context"
5+
"log/slog"
6+
)
47

58
const (
69
snakeKey = "foo_bar"
@@ -35,6 +38,37 @@ func tests() {
3538
slog.Info("msg", slog.Attr{Key: kebabKey, Value: slog.IntValue(1)}) // want `keys should be written in snake_case`
3639
slog.Info("msg", slog.Attr{Value: slog.IntValue(1), Key: "foo-bar"}) // want `keys should be written in snake_case`
3740
slog.Info("msg", slog.Attr{Value: slog.IntValue(1), Key: kebabKey}) // want `keys should be written in snake_case`
41+
42+
// With snake_case key
43+
slog.Info("msg")
44+
slog.With("foo_bar", 1).Info("msg")
45+
slog.With(snakeKey, 1).Info("msg")
46+
slog.With(slog.Int("foo_bar", 1)).Info("msg")
47+
slog.With(slog.Int(snakeKey, 1)).Info("msg")
48+
slog.With(slog.Attr{}).Info("msg")
49+
slog.With(slog.Attr{"foo_bar", slog.IntValue(1)}).Info("msg")
50+
slog.With(slog.Attr{snakeKey, slog.IntValue(1)}).Info("msg")
51+
slog.With(slog.Attr{Key: "foo_bar"}).Info("msg")
52+
slog.With(slog.Attr{Key: snakeKey}).Info("msg")
53+
slog.With(slog.Attr{Key: "foo_bar", Value: slog.IntValue(1)}).Info("msg")
54+
slog.With(slog.Attr{Key: snakeKey, Value: slog.IntValue(1)}).Info("msg")
55+
slog.With(slog.Attr{Value: slog.IntValue(1), Key: "foo_bar"}).Info("msg")
56+
slog.With(slog.Attr{Value: slog.IntValue(1), Key: snakeKey}).Info("msg")
57+
58+
slog.With("foo-bar", 1).Info("msg") // want `keys should be written in snake_case`
59+
slog.With(kebabKey, 1).Info("msg") // want `keys should be written in snake_case`
60+
slog.With(slog.Int("foo-bar", 1)).Info("msg") // want `keys should be written in snake_case`
61+
slog.With(slog.Int(kebabKey, 1)).Info("msg") // want `keys should be written in snake_case`
62+
slog.With(slog.Attr{"foo-bar", slog.IntValue(1)}).Info("msg") // want `keys should be written in snake_case`
63+
slog.With(slog.Attr{kebabKey, slog.IntValue(1)}).Info("msg") // want `keys should be written in snake_case`
64+
slog.With(slog.Attr{Key: "foo-bar"}).Info("msg") // want `keys should be written in snake_case`
65+
slog.With(slog.Attr{Key: kebabKey}).Info("msg") // want `keys should be written in snake_case`
66+
slog.With(slog.Attr{Key: "foo-bar", Value: slog.IntValue(1)}).Info("msg") // want `keys should be written in snake_case`
67+
slog.With(slog.Attr{Key: kebabKey, Value: slog.IntValue(1)}).Info("msg") // want `keys should be written in snake_case`
68+
slog.With(slog.Attr{Value: slog.IntValue(1), Key: "foo-bar"}).Info("msg") // want `keys should be written in snake_case`
69+
slog.With(slog.Attr{Value: slog.IntValue(1), Key: kebabKey}).Info("msg") // want `keys should be written in snake_case`
70+
71+
slog.LogAttrs(context.TODO(), slog.LevelInfo, "msg", slog.Attr{Value: slog.IntValue(1), Key: kebabKey}) // want `keys should be written in snake_case`
3872
}
3973

4074
func issue35() {

testdata/src/kv_only/kv_only.go

+8-3
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,13 @@ func tests() {
66
slog.Info("msg")
77
slog.Info("msg", "foo", 1)
88
slog.Info("msg", "foo", 1, "bar", 2)
9+
slog.With("foo", 1).Info("msg")
10+
slog.With("foo", 1, "bar", 2).Info("msg")
911

10-
slog.Info("msg", slog.Int("foo", 1)) // want `attributes should not be used`
11-
slog.Info("msg", slog.Int("foo", 1), slog.Int("bar", 2)) // want `attributes should not be used`
12-
slog.Info("msg", "foo", 1, slog.Int("bar", 2)) // want `attributes should not be used`
12+
slog.Info("msg", slog.Int("foo", 1)) // want `attributes should not be used`
13+
slog.Info("msg", slog.Int("foo", 1), slog.Int("bar", 2)) // want `attributes should not be used`
14+
slog.Info("msg", "foo", 1, slog.Int("bar", 2)) // want `attributes should not be used`
15+
slog.With(slog.Int("foo", 1)).Info("msg") // want `attributes should not be used`
16+
slog.With(slog.Int("foo", 1), slog.Int("bar", 2)).Info("msg") // want `attributes should not be used`
17+
slog.With("foo", 1, slog.Int("bar", 2)).Info("msg") // want `attributes should not be used`
1318
}

testdata/src/no_global_default/no_global_default.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import "log/slog"
55
var logger = slog.New(nil)
66

77
func tests() {
8-
slog.Info("msg") // want `default logger should not be used`
8+
slog.Info("msg") // want `default logger should not be used`
9+
slog.With("key", "value") // want `default logger should not be used`
910
logger.Info("msg")
11+
logger.With("key", "value")
1012
}

testdata/src/no_mixed_args/no_mixed_args.go

+15-9
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ func tests() {
1414
slog.Info("msg", "foo", 1, "bar", 2)
1515
slog.Info("msg", slog.Int("foo", 1))
1616
slog.Info("msg", slog.Int("foo", 1), slog.Int("bar", 2))
17+
slog.With("foo", 1, "bar", 2).Info("msg")
18+
slog.With(slog.Int("foo", 1)).Info("msg")
19+
slog.With(slog.Int("foo", 1), slog.Int("bar", 2)).Info("msg")
1720

1821
slog.Log(ctx, slog.LevelInfo, "msg", "foo", 1, slog.Int("bar", 2)) // want `key-value pairs and attributes should not be mixed`
1922
slog.Debug("msg", "foo", 1, slog.Int("bar", 2)) // want `key-value pairs and attributes should not be mixed`
@@ -24,14 +27,17 @@ func tests() {
2427
slog.InfoContext(ctx, "msg", "foo", 1, slog.Int("bar", 2)) // want `key-value pairs and attributes should not be mixed`
2528
slog.WarnContext(ctx, "msg", "foo", 1, slog.Int("bar", 2)) // want `key-value pairs and attributes should not be mixed`
2629
slog.ErrorContext(ctx, "msg", "foo", 1, slog.Int("bar", 2)) // want `key-value pairs and attributes should not be mixed`
30+
slog.With("foo", 1, slog.Int("bar", 2)).ErrorContext(ctx, "msg") // want `key-value pairs and attributes should not be mixed`
2731

28-
logger.Log(ctx, slog.LevelInfo, "msg", "foo", 1, slog.Int("bar", 2)) // want `key-value pairs and attributes should not be mixed`
29-
logger.Debug("msg", "foo", 1, slog.Int("bar", 2)) // want `key-value pairs and attributes should not be mixed`
30-
logger.Info("msg", "foo", 1, slog.Int("bar", 2)) // want `key-value pairs and attributes should not be mixed`
31-
logger.Warn("msg", "foo", 1, slog.Int("bar", 2)) // want `key-value pairs and attributes should not be mixed`
32-
logger.Error("msg", "foo", 1, slog.Int("bar", 2)) // want `key-value pairs and attributes should not be mixed`
33-
logger.DebugContext(ctx, "msg", "foo", 1, slog.Int("bar", 2)) // want `key-value pairs and attributes should not be mixed`
34-
logger.InfoContext(ctx, "msg", "foo", 1, slog.Int("bar", 2)) // want `key-value pairs and attributes should not be mixed`
35-
logger.WarnContext(ctx, "msg", "foo", 1, slog.Int("bar", 2)) // want `key-value pairs and attributes should not be mixed`
36-
logger.ErrorContext(ctx, "msg", "foo", 1, slog.Int("bar", 2)) // want `key-value pairs and attributes should not be mixed`
32+
logger.Log(ctx, slog.LevelInfo, "msg", "foo", 1, slog.Int("bar", 2)) // want `key-value pairs and attributes should not be mixed`
33+
logger.Debug("msg", "foo", 1, slog.Int("bar", 2)) // want `key-value pairs and attributes should not be mixed`
34+
logger.Info("msg", "foo", 1, slog.Int("bar", 2)) // want `key-value pairs and attributes should not be mixed`
35+
logger.Warn("msg", "foo", 1, slog.Int("bar", 2)) // want `key-value pairs and attributes should not be mixed`
36+
logger.Error("msg", "foo", 1, slog.Int("bar", 2)) // want `key-value pairs and attributes should not be mixed`
37+
logger.DebugContext(ctx, "msg", "foo", 1, slog.Int("bar", 2)) // want `key-value pairs and attributes should not be mixed`
38+
logger.InfoContext(ctx, "msg", "foo", 1, slog.Int("bar", 2)) // want `key-value pairs and attributes should not be mixed`
39+
logger.WarnContext(ctx, "msg", "foo", 1, slog.Int("bar", 2)) // want `key-value pairs and attributes should not be mixed`
40+
logger.ErrorContext(ctx, "msg", "foo", 1, slog.Int("bar", 2)) // want `key-value pairs and attributes should not be mixed`
41+
logger.With("foo", 1, slog.Int("bar", 2)).ErrorContext(ctx, "msg") // want `key-value pairs and attributes should not be mixed`
42+
logger.With("foo", 1).ErrorContext(ctx, "msg", "foo", 1, slog.Int("bar", 2)) // want `key-value pairs and attributes should not be mixed`
3743
}

testdata/src/no_raw_keys/no_raw_keys.go

+7
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,11 @@ func tests() {
2626
slog.Info("msg", slog.Attr{Key: "foo"}) // want `raw keys should not be used`
2727
slog.Info("msg", slog.Attr{Key: "foo", Value: slog.IntValue(1)}) // want `raw keys should not be used`
2828
slog.Info("msg", slog.Attr{Value: slog.IntValue(1), Key: "foo"}) // want `raw keys should not be used`
29+
30+
slog.With("foo", 1).Info("msg") // want `raw keys should not be used`
31+
slog.With(slog.Int("foo", 1)).Info("msg") // want `raw keys should not be used`
32+
slog.With(slog.Attr{"foo", slog.IntValue(1)}).Info("msg") // want `raw keys should not be used`
33+
slog.With(slog.Attr{Key: "foo"}).Info("msg") // want `raw keys should not be used`
34+
slog.With(slog.Attr{Key: "foo", Value: slog.IntValue(1)}).Info("msg") // want `raw keys should not be used`
35+
slog.With(slog.Attr{Value: slog.IntValue(1), Key: "foo"}).Info("msg") // want `raw keys should not be used`
2936
}

0 commit comments

Comments
 (0)