Skip to content

Commit

Permalink
feat: implement -context-only (#14)
Browse files Browse the repository at this point in the history
  • Loading branch information
mattdowdell authored Oct 22, 2023
1 parent bc45156 commit 1301dc6
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 0 deletions.
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ The linter has several options, so you can adjust it to your own code style.

* Forbid mixing key-value pairs and attributes within a single function call (default)
* Enforce using either key-value pairs or attributes for the entire project (optional)
* Enforce using methods that accept a context (optional)
* Enforce using constants instead of raw keys (optional)
* Enforce putting arguments on separate lines (optional)

Expand Down Expand Up @@ -53,6 +54,22 @@ In contrast, the `-attr-only` flag causes `sloglint` to report any use of key-va
slog.Info("a user has logged in", "user_id", 42) // sloglint: key-value pairs should not be used
```

### Context only

Some `slog.Handler` implementations make use of the given `context.Context` (e.g. to access context values).
For them to work properly, you need to pass a context to all logger calls.
The `-context-only` flag causes `sloglint` to report the use of methods without a context.

```go
slog.Info("a user has logged in") // sloglint: methods without a context should not be used
```

This report can be fixed by using the equivalent method with the `Context` suffix.

```go
slog.InfoContext(ctx, "a user has logged in")
```

### No raw keys

To prevent typos, you may want to forbid the use of raw keys altogether.
Expand Down
9 changes: 9 additions & 0 deletions sloglint.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
type Options struct {
KVOnly bool // Enforce using key-value pairs only (incompatible with AttrOnly).
AttrOnly bool // Enforce using attributes only (incompatible with KVOnly).
ContextOnly bool // Enforce using methods that accept a context.
NoRawKeys bool // Enforce using constants instead of raw keys.
ArgsOnSepLines bool // Enforce putting arguments on separate lines.
}
Expand Down Expand Up @@ -56,6 +57,7 @@ func flags(opts *Options) flag.FlagSet {

boolVar(&opts.KVOnly, "kv-only", "enforce using key-value pairs only (incompatible with -attr-only)")
boolVar(&opts.AttrOnly, "attr-only", "enforce using attributes only (incompatible with -kv-only)")
boolVar(&opts.ContextOnly, "context-only", "enforce using methods that accept a context")
boolVar(&opts.NoRawKeys, "no-raw-keys", "enforce using constants instead of raw keys")
boolVar(&opts.ArgsOnSepLines, "args-on-sep-lines", "enforce putting arguments on separate lines")

Expand Down Expand Up @@ -113,6 +115,13 @@ func run(pass *analysis.Pass, opts *Options) {
return
}

if opts.ContextOnly {
typ := pass.TypesInfo.TypeOf(call.Args[0])
if typ != nil && typ.String() != "context.Context" {
pass.Reportf(call.Pos(), "methods without a context should not be used")
}
}

// NOTE: we assume that the arguments have already been validated by govet.
args := call.Args[argsPos:]
if len(args) == 0 {
Expand Down
5 changes: 5 additions & 0 deletions sloglint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ func TestAnalyzer(t *testing.T) {
analysistest.Run(t, testdata, analyzer, "attr_only")
})

t.Run("context only", func(t *testing.T) {
analyzer := sloglint.New(&sloglint.Options{ContextOnly: true})
analysistest.Run(t, testdata, analyzer, "context_only")
})

t.Run("no raw keys", func(t *testing.T) {
analyzer := sloglint.New(&sloglint.Options{NoRawKeys: true})
analysistest.Run(t, testdata, analyzer, "no_raw_keys")
Expand Down
33 changes: 33 additions & 0 deletions testdata/src/context_only/context_only.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package context_only

import (
"context"
"log/slog"
)

func tests() {
logger := slog.New(nil)
ctx := context.Background()

slog.Log(ctx, slog.LevelInfo, "msg")
slog.DebugContext(ctx, "msg")
slog.InfoContext(ctx, "msg")
slog.WarnContext(ctx, "msg")
slog.ErrorContext(ctx, "msg")

logger.Log(ctx, slog.LevelInfo, "msg")
logger.DebugContext(ctx, "msg")
logger.InfoContext(ctx, "msg")
logger.WarnContext(ctx, "msg")
logger.ErrorContext(ctx, "msg")

slog.Debug("msg") // want `methods without a context should not be used`
slog.Info("msg") // want `methods without a context should not be used`
slog.Warn("msg") // want `methods without a context should not be used`
slog.Error("msg") // want `methods without a context should not be used`

logger.Debug("msg") // want `methods without a context should not be used`
logger.Info("msg") // want `methods without a context should not be used`
logger.Warn("msg") // want `methods without a context should not be used`
logger.Error("msg") // want `methods without a context should not be used`
}

0 comments on commit 1301dc6

Please sign in to comment.