Skip to content

Commit 8e10f6a

Browse files
authored
feat: suggested fixes for key naming (#86)
1 parent b9cc6ac commit 8e10f6a

File tree

3 files changed

+109
-15
lines changed

3 files changed

+109
-15
lines changed

sloglint.go

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -320,11 +320,40 @@ func visit(pass *analysis.Pass, opts *Options, node ast.Node, stack []ast.Node)
320320
})
321321
}
322322

323+
checkKeysNaming(opts, pass, keys, attrs)
324+
325+
if len(opts.ForbiddenKeys) > 0 {
326+
forEachKey(pass.TypesInfo, keys, attrs, func(key ast.Expr) {
327+
if name, ok := getKeyName(key); ok && slices.Contains(opts.ForbiddenKeys, name) {
328+
pass.Reportf(key.Pos(), "%q key is forbidden and should not be used", name)
329+
}
330+
})
331+
}
332+
333+
if opts.ArgsOnSepLines && areArgsOnSameLine(pass.Fset, call, keys, attrs) {
334+
pass.Reportf(call.Pos(), "arguments should be put on separate lines")
335+
}
336+
}
337+
338+
func checkKeysNaming(opts *Options, pass *analysis.Pass, keys, attrs []ast.Expr) {
323339
checkKeyNamingCase := func(caseFn func(string) string, caseName string) {
324340
forEachKey(pass.TypesInfo, keys, attrs, func(key ast.Expr) {
325-
if name, ok := getKeyName(key); ok && name != caseFn(name) {
326-
pass.Reportf(call.Pos(), "keys should be written in %s", caseName)
341+
name, ok := getKeyName(key)
342+
if !ok || name == caseFn(name) {
343+
return
327344
}
345+
346+
pass.Report(analysis.Diagnostic{
347+
Pos: key.Pos(),
348+
Message: fmt.Sprintf("keys should be written in %s", caseName),
349+
SuggestedFixes: []analysis.SuggestedFix{{
350+
TextEdits: []analysis.TextEdit{{
351+
Pos: key.Pos(),
352+
End: key.End(),
353+
NewText: []byte(strconv.Quote(caseFn(name))),
354+
}},
355+
}},
356+
})
328357
})
329358
}
330359

@@ -338,18 +367,6 @@ func visit(pass *analysis.Pass, opts *Options, node ast.Node, stack []ast.Node)
338367
case pascalCase:
339368
checkKeyNamingCase(strcase.ToPascal, "PascalCase")
340369
}
341-
342-
if len(opts.ForbiddenKeys) > 0 {
343-
forEachKey(pass.TypesInfo, keys, attrs, func(key ast.Expr) {
344-
if name, ok := getKeyName(key); ok && slices.Contains(opts.ForbiddenKeys, name) {
345-
pass.Reportf(key.Pos(), "%q key is forbidden and should not be used", name)
346-
}
347-
})
348-
}
349-
350-
if opts.ArgsOnSepLines && areArgsOnSameLine(pass.Fset, call, keys, attrs) {
351-
pass.Reportf(call.Pos(), "arguments should be put on separate lines")
352-
}
353370
}
354371

355372
func checkDiscardHandler(opts *Options, pass *analysis.Pass, name string, call *ast.CallExpr) {

testdata/src/key_naming_case/key_naming_case.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ const (
1010
kebabKey = "foo-bar"
1111
)
1212

13-
func tests() {
13+
func _() {
1414
slog.Info("msg")
1515
slog.Info("msg", "foo_bar", 1)
1616
slog.Info("msg", snakeKey, 1)
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package key_naming_case
2+
3+
import (
4+
"context"
5+
"log/slog"
6+
)
7+
8+
const (
9+
snakeKey = "foo_bar"
10+
kebabKey = "foo-bar"
11+
)
12+
13+
func _() {
14+
slog.Info("msg")
15+
slog.Info("msg", "foo_bar", 1)
16+
slog.Info("msg", snakeKey, 1)
17+
slog.Info("msg", slog.Int("foo_bar", 1))
18+
slog.Info("msg", slog.Int(snakeKey, 1))
19+
slog.Info("msg", slog.Attr{})
20+
slog.Info("msg", slog.Attr{"foo_bar", slog.IntValue(1)})
21+
slog.Info("msg", slog.Attr{snakeKey, slog.IntValue(1)})
22+
slog.Info("msg", slog.Attr{Key: "foo_bar"})
23+
slog.Info("msg", slog.Attr{Key: snakeKey})
24+
slog.Info("msg", slog.Attr{Key: "foo_bar", Value: slog.IntValue(1)})
25+
slog.Info("msg", slog.Attr{Key: snakeKey, Value: slog.IntValue(1)})
26+
slog.Info("msg", slog.Attr{Value: slog.IntValue(1), Key: "foo_bar"})
27+
slog.Info("msg", slog.Attr{Value: slog.IntValue(1), Key: snakeKey})
28+
29+
slog.Info("msg", "foo_bar", 1) // want `keys should be written in snake_case`
30+
slog.Info("msg", "foo_bar", 1) // want `keys should be written in snake_case`
31+
slog.Info("msg", slog.Int("foo_bar", 1)) // want `keys should be written in snake_case`
32+
slog.Info("msg", slog.Int("foo_bar", 1)) // want `keys should be written in snake_case`
33+
slog.Info("msg", slog.Attr{"foo_bar", slog.IntValue(1)}) // want `keys should be written in snake_case`
34+
slog.Info("msg", slog.Attr{"foo_bar", slog.IntValue(1)}) // want `keys should be written in snake_case`
35+
slog.Info("msg", slog.Attr{Key: "foo_bar"}) // want `keys should be written in snake_case`
36+
slog.Info("msg", slog.Attr{Key: "foo_bar"}) // want `keys should be written in snake_case`
37+
slog.Info("msg", slog.Attr{Key: "foo_bar", Value: slog.IntValue(1)}) // want `keys should be written in snake_case`
38+
slog.Info("msg", slog.Attr{Key: "foo_bar", Value: slog.IntValue(1)}) // want `keys should be written in snake_case`
39+
slog.Info("msg", slog.Attr{Value: slog.IntValue(1), Key: "foo_bar"}) // want `keys should be written in snake_case`
40+
slog.Info("msg", slog.Attr{Value: slog.IntValue(1), Key: "foo_bar"}) // 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("foo_bar", 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("foo_bar", 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{"foo_bar", 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: "foo_bar"}).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: "foo_bar", 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: "foo_bar"}).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: "foo_bar"}) // want `keys should be written in snake_case`
72+
}
73+
74+
func issue35() {
75+
intAttr := slog.Int
76+
slog.Info("msg", intAttr("foo_bar", 1))
77+
}

0 commit comments

Comments
 (0)