Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement -context-only #14

Merged
merged 4 commits into from
Oct 22, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ The linter has several options, so you can adjust it to your own code style.
* Enforce using either key-value pairs or attributes for the entire project (optional)
* Enforce using constants instead of raw keys (optional)
* Enforce putting arguments on separate lines (optional)
* Enforce using methods that take a context (optional)

## 📦 Install

Expand Down Expand Up @@ -98,5 +99,22 @@ slog.Info("a user has logged in",
)
```

### Context only

The `-context-only` flag causes `sloglint` to report the use of any methods that do not take a `context.Context`.

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

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

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

The `slog.Handler` implementations in the standard library do not currently utilise the given
context. However, third-party implementations can create `slog.Attr` instances from the context.

[1]: https://github.com/go-simpler/sloglint/releases
[2]: https://github.com/go-simpler/sloggen
9 changes: 9 additions & 0 deletions sloglint.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type Options struct {
AttrOnly bool // Enforce using attributes only (incompatible with KVOnly).
NoRawKeys bool // Enforce using constants instead of raw keys.
ArgsOnSepLines bool // Enforce putting arguments on separate lines.
ContextOnly bool // Enforce using methods that take a context.
}

// New creates a new sloglint analyzer.
Expand Down Expand Up @@ -58,6 +59,7 @@ func flags(opts *Options) flag.FlagSet {
boolVar(&opts.AttrOnly, "attr-only", "enforce using attributes only (incompatible with -kv-only)")
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")
boolVar(&opts.ContextOnly, "context-only", "enforce using methods that take a context")

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

if opts.ContextOnly {
arg, ok := pass.TypesInfo.Types[call.Args[0]]
if ok && arg.Type.String() != "context.Context" {
pass.Reportf(call.Pos(), "methods that do not take a context should not be used")
tmzane marked this conversation as resolved.
Show resolved Hide resolved
}
}

// 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 @@ -34,4 +34,9 @@ func TestAnalyzer(t *testing.T) {
analyzer := sloglint.New(&sloglint.Options{ArgsOnSepLines: true})
analysistest.Run(t, testdata, analyzer, "args_on_sep_lines")
})

t.Run("context only", func(t *testing.T) {
analyzer := sloglint.New(&sloglint.Options{ContextOnly: true})
analysistest.Run(t, testdata, analyzer, "context_only")
})
}
35 changes: 35 additions & 0 deletions testdata/src/context_only/context_only.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package context_only

import (
"context"
"io"
"log/slog"
)

func tests() {
ctx := context.Background()

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

slog.Log(context.Background(), slog.LevelInfo, "msg")
slog.DebugContext(context.TODO(), "msg")
slog.InfoContext(context.WithoutCancel(ctx), "msg")
slog.WarnContext(ctx, "msg")
slog.ErrorContext(ctx, "msg")

logger := slog.New(slog.NewJSONHandler(io.Discard, &slog.HandlerOptions{}))

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

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