diff --git a/.golangci.yml b/.golangci.yml index 71a2d10..f88d854 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,24 +1,255 @@ --- +# Linters that should pass for all FerretDB and tools code. + run: timeout: 3m -# https://golangci-lint.run/usage/linters/ linters-settings: + # asciicheck + depguard: + rules: + old-packages: + files: + - $all + deny: + - pkg: golang.org/x/net/context + desc: use `context` package instead + - pkg: golang.org/x/exp/slices + desc: use `slices` package instead + - pkg: maps + desc: use `golang.org/x/exp/maps` package instead + - pkg: github.com/jackc/pgconn + desc: use `github.com/jackc/pgx/v5/pgconn` package instead + - pkg: github.com/jackc/pgproto3 + desc: use `github.com/jackc/pgx/v5/pgproto3` package instead + - pkg: github.com/jackc/pgtype + desc: use `github.com/jackc/pgx/v5/pgtype` package instead + - pkg: github.com/jackc/pgx$ + desc: use `github.com/jackc/pgx/v5` package instead + - pkg: github.com/jackc/pgx/v4 + desc: use `github.com/jackc/pgx/v5` package instead + + wire-in-tools: + files: + - "**/tools/**/*.go" + deny: + - pkg: github.com/FerretDB/FerretDB/internal/ + desc: tools should not import FerretDB code + + types: + files: + - "**/internal/util/logging/*.go" + deny: + - pkg: github.com/FerretDB/FerretDB/internal/types + sjson: + files: + - $all + - "!**/internal/backends/sqlite/*.go" + - "!**/internal/backends/postgresql/*.go" + - "!**/internal/backends/postgresql/metadata/*.go" + - "!**/internal/backends/mysql/*.go" + - "!**/internal/backends/mysql/metadata/*.go" + - "!**/internal/backends/hana/*.go" + deny: + - pkg: github.com/FerretDB/FerretDB/internal/handler/sjson + common: + files: + - "**/internal/backends/**.go" + - "**/internal/clientconn/cursor/*.go" + deny: + - pkg: github.com/FerretDB/FerretDB/internal/handler/common + handlererrors: + files: + - $all + - "!**/internal/clientconn/*.go" + - "!**/internal/handler/**.go" + deny: + - pkg: github.com/FerretDB/FerretDB/internal/handler/handlererrors + backends: + files: + - "**/internal/clientconn/cursor/*.go" + deny: + - pkg: github.com/FerretDB/FerretDB/internal/backends + exhaustive: + default-signifies-exhaustive: false + forbidigo: + forbid: + - p: ^\Qpgxpool.Pool.AcquireFunc\E$ + pkg: ^\Qgithub.com/jackc/pgx/v5/pgxpool\E$ + msg: Use `Acquire` with `defer Release()` instead. + + # integration tests use a different configuration file + - p: ^\Qbson.E\E$ + pkg: ^\Qgo.mongodb.org/mongo-driver/bson\E$ + msg: Use `*types.Document` or `*types.Array` instead. + - p: ^\Qprimitive.E\E$ + pkg: ^\Qgo.mongodb.org/mongo-driver/bson/primitive\E$ + msg: Use `*types.Document` or `*types.Array` instead. + - p: ^\Qbson.D\E$ + pkg: ^\Qgo.mongodb.org/mongo-driver/bson\E$ + msg: Use `*types.Document` instead. + - p: ^\Qprimitive.D\E$ + pkg: ^\Qgo.mongodb.org/mongo-driver/bson/primitive\E$ + msg: Use `*types.Document` instead. + - p: ^\Qbson.M\E$ + pkg: ^\Qgo.mongodb.org/mongo-driver/bson\E$ + msg: Use `*types.Document` instead. + - p: ^\Qprimitive.M\E$ + pkg: ^\Qgo.mongodb.org/mongo-driver/bson/primitive\E$ + msg: Use `*types.Document` instead. + - p: ^\Qbson.A\E$ + pkg: ^\Qgo.mongodb.org/mongo-driver/bson\E$ + msg: Use `*types.Array` instead. + - p: ^\Qprimitive.A\E$ + pkg: ^\Qgo.mongodb.org/mongo-driver/bson/primitive\E$ + msg: Use `*types.Array` instead. + exclude-godoc-examples: true + analyze-types: true + gci: + sections: + - standard + - default + - prefix(github.com/FerretDB/FerretDB) + goconst: + min-occurrences: 5 + ignore-tests: true + godot: + scope: toplevel + exclude: + - check interfaces + capital: true + godox: + keywords: + - BUG + - FIXME + - HACK + goheader: + # Please do not update it, including bumping a year. + template: |- + Copyright 2021 FerretDB Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + goimports: + local-prefixes: github.com/FerretDB/FerretDB + gomodguard: + blocked: + modules: [] + gosimple: + checks: ["all"] govet: enable-all: true - settings: - shadow: - strict: true + disable: + - fieldalignment + - nilness + - shadow + importas: + no-unaliased: false + no-extra-aliases: true + alias: + - pkg: github.com/prometheus/client_model/go + alias: dto + - pkg: modernc.org/sqlite + alias: sqlite3 + - pkg: modernc.org/sqlite/lib + alias: sqlite3lib + - pkg: go.opentelemetry.io/otel/attribute + alias: otelattribute + - pkg: go.opentelemetry.io/otel/codes + alias: otelcodes + - pkg: go.opentelemetry.io/otel/trace + alias: oteltrace + - pkg: go.opentelemetry.io/otel/sdk/resource + alias: otelsdkresource + - pkg: go.opentelemetry.io/otel/sdk/trace + alias: otelsdktrace + - pkg: go.opentelemetry.io/otel/semconv/v1.21.0 + alias: otelsemconv + - pkg: github.com/FerretDB/FerretDB/internal/backends/mysql/metadata/pool + alias: mysqlpool + # ineffassign + lll: + line-length: 130 + tab-width: 4 + misspell: + ignore-words: + - guiness # present in the test dataset + extra-words: # https://go.dev/wiki/Spelling, https://github.com/golangci/misspell/issues/11 + - typo: "iff" + correction: "if" + - typo: "cancelation" + correction: "cancellation" + nolintlint: + allow-unused: false + allow-no-explanation: [] + require-explanation: true + require-specific: true + revive: + ignore-generated-header: true + severity: error + rules: [] + sloglint: + no-mixed-args: true + kv-only: false + attr-only: true + no-global: "all" + context: "scope" + static-msg: false # TODO https://github.com/FerretDB/FerretDB/issues/3421 + no-raw-keys: false # TODO https://github.com/FerretDB/FerretDB/issues/3421 + key-naming-case: snake + args-on-sep-lines: false + staticcheck: + checks: + - all + - -SA1019 # ignore deprecation errors in existing code; new code is checked by the other configuration + # unused + whitespace: + multi-if: false + multi-func: false linters: disable-all: true enable: - # TODO https://github.com/FerretDB/wire/issues/4 + - asciicheck + - depguard + - exhaustive + - forbidigo + - gci + - gochecksumtype + - goconst + - godot + - godox + - goheader + - goimports + - gomodguard + - gosimple - govet + - importas + - ineffassign + - lll + - misspell + - nolintlint + - revive + - sloglint + - staticcheck + - unused + - whitespace issues: max-issues-per-linter: 0 max-same-issues: 0 exclude-use-default: false - exclude-rules: [] + exclude-rules: + # we don't want to introduce that constant + - linters: [goconst] + text: "^string `_id` has \\d+ occurrences, make it a constant" \ No newline at end of file diff --git a/internal/bsonproto/binary.go b/internal/bsonproto/binary.go index 7971577..dd68fb2 100644 --- a/internal/bsonproto/binary.go +++ b/internal/bsonproto/binary.go @@ -28,7 +28,7 @@ const ( // BinaryGeneric represents a BSON Binary generic subtype. BinaryGeneric = BinarySubtype(0x00) // generic - // BinaryFunction represents a BSON Binary function subtype + // BinaryFunction represents a BSON Binary function subtype. BinaryFunction = BinarySubtype(0x01) // function // BinaryGenericOld represents a BSON Binary generic-old subtype. @@ -63,7 +63,7 @@ func SizeBinary(v Binary) int { // EncodeBinary encodes [Binary] value v into b. // -// b must be at least len(v.B)+5 ([SizeBinary]) bytes long; otherwise, EncodeBinary will panic. +// "b" must be at least len(v.B)+5 ([SizeBinary]) bytes long; otherwise, EncodeBinary will panic. // Only b[0:len(v.B)+5] bytes are modified. func EncodeBinary(b []byte, v Binary) { i := len(v.B) diff --git a/internal/bsonproto/bool.go b/internal/bsonproto/bool.go index a314ca4..b1bd4b8 100644 --- a/internal/bsonproto/bool.go +++ b/internal/bsonproto/bool.go @@ -23,7 +23,7 @@ const SizeBool = 1 // EncodeBool encodes bool value v into b. // -// b must be at least 1 ([SizeBool]) byte long; otherwise, EncodeBool will panic. +// "b" must be at least 1 ([SizeBool]) byte long; otherwise, EncodeBool will panic. // Only b[0] is modified. func EncodeBool(b []byte, v bool) { if v { diff --git a/internal/bsonproto/bsonproto.go b/internal/bsonproto/bsonproto.go index eb4e84f..b7388df 100644 --- a/internal/bsonproto/bsonproto.go +++ b/internal/bsonproto/bsonproto.go @@ -69,7 +69,7 @@ func SizeAny(v any) int { // Encode encodes value v into b. // -// b must be at least Size(v) bytes long; otherwise, Encode will panic. +// "b" must be at least Size(v) bytes long; otherwise, Encode will panic. // Only b[0:Size(v)] bytes are modified. func Encode[T ScalarType](b []byte, v T) { EncodeAny(b, v) @@ -77,7 +77,7 @@ func Encode[T ScalarType](b []byte, v T) { // EncodeAny encodes value v into b. // -// b must be at least Size(v) bytes long; otherwise, EncodeAny will panic. +// "b" must be at least Size(v) bytes long; otherwise, EncodeAny will panic. // Only b[0:Size(v)] bytes are modified. // // It panics if v is not a [ScalarType] (including CString). diff --git a/internal/bsonproto/cstring.go b/internal/bsonproto/cstring.go index 1805962..3c47458 100644 --- a/internal/bsonproto/cstring.go +++ b/internal/bsonproto/cstring.go @@ -26,7 +26,7 @@ func SizeCString(v string) int { // EncodeCString encodes cstring value v into b. // -// b must be at least len(v)+1 ([SizeCString]) bytes long; otherwise, EncodeString will panic. +// "b" must be at least len(v)+1 ([SizeCString]) bytes long; otherwise, EncodeString will panic. // Only b[0:len(v)+1] bytes are modified. func EncodeCString(b []byte, v string) { // ensure b length early diff --git a/internal/bsonproto/decimal128.go b/internal/bsonproto/decimal128.go index 2224ccb..e2d46ec 100644 --- a/internal/bsonproto/decimal128.go +++ b/internal/bsonproto/decimal128.go @@ -30,7 +30,7 @@ const SizeDecimal128 = 16 // EncodeDecimal128 encodes [Decimal128] value v into b. // -// b must be at least 16 ([SizeDecimal128]) bytes long; otherwise, EncodeDecimal128 will panic. +// "b" must be at least 16 ([SizeDecimal128]) bytes long; otherwise, EncodeDecimal128 will panic. // Only b[0:16] bytes are modified. func EncodeDecimal128(b []byte, v Decimal128) { binary.LittleEndian.PutUint64(b, uint64(v.L)) @@ -44,7 +44,10 @@ func DecodeDecimal128(b []byte) (Decimal128, error) { var res Decimal128 if len(b) < SizeDecimal128 { - return res, fmt.Errorf("DecodeDecimal128: expected at least %d bytes, got %d: %w", SizeDecimal128, len(b), ErrDecodeShortInput) + return res, fmt.Errorf( + "DecodeDecimal128: expected at least %d bytes, got %d: %w", + SizeDecimal128, len(b), ErrDecodeShortInput, + ) } res.L = binary.LittleEndian.Uint64(b[:8]) diff --git a/internal/bsonproto/float64.go b/internal/bsonproto/float64.go index 4c6cb06..54fa859 100644 --- a/internal/bsonproto/float64.go +++ b/internal/bsonproto/float64.go @@ -25,7 +25,7 @@ const SizeFloat64 = 8 // EncodeFloat64 encodes float64 value v into b. // -// b must be at least 8 ([SizeFloat64]) bytes long; otherwise, EncodeFloat64 will panic. +// "b" must be at least 8 ([SizeFloat64]) bytes long; otherwise, EncodeFloat64 will panic. // Only b[0:8] bytes are modified. // // Infinities, NaNs, negative zeros are preserved. diff --git a/internal/bsonproto/int32.go b/internal/bsonproto/int32.go index a348cdc..bfc346d 100644 --- a/internal/bsonproto/int32.go +++ b/internal/bsonproto/int32.go @@ -24,7 +24,7 @@ const SizeInt32 = 4 // EncodeInt32 encodes int32 value v into b. // -// b must be at least 4 ([SizeInt32]) bytes long; otherwise, EncodeInt32 will panic. +// "b" must be at least 4 ([SizeInt32]) bytes long; otherwise, EncodeInt32 will panic. // Only b[0:4] bytes are modified. func EncodeInt32(b []byte, v int32) { binary.LittleEndian.PutUint32(b, uint32(v)) diff --git a/internal/bsonproto/int64.go b/internal/bsonproto/int64.go index 8567726..65dc517 100644 --- a/internal/bsonproto/int64.go +++ b/internal/bsonproto/int64.go @@ -24,7 +24,7 @@ const SizeInt64 = 8 // EncodeInt64 encodes int64 value v into b. // -// b must be at least 8 ([SizeInt64]) bytes long; otherwise, EncodeInt64 will panic. +// "b" must be at least 8 ([SizeInt64]) bytes long; otherwise, EncodeInt64 will panic. // Only b[0:8] bytes are modified. func EncodeInt64(b []byte, v int64) { binary.LittleEndian.PutUint64(b, uint64(v)) diff --git a/internal/bsonproto/objectid.go b/internal/bsonproto/objectid.go index 16d715e..942a824 100644 --- a/internal/bsonproto/objectid.go +++ b/internal/bsonproto/objectid.go @@ -26,7 +26,7 @@ const SizeObjectID = 12 // EncodeObjectID encodes [ObjectID] value v into b. // -// b must be at least 12 ([SizeObjectID]) bytes long; otherwise, EncodeObjectID will panic. +// "b" must be at least 12 ([SizeObjectID]) bytes long; otherwise, EncodeObjectID will panic. // Only b[0:12] bytes are modified. func EncodeObjectID(b []byte, v ObjectID) { _ = b[11] @@ -40,7 +40,10 @@ func DecodeObjectID(b []byte) (ObjectID, error) { var res ObjectID if len(b) < SizeObjectID { - return res, fmt.Errorf("DecodeObjectID: expected at least %d bytes, got %d: %w", SizeObjectID, len(b), ErrDecodeShortInput) + return res, fmt.Errorf( + "DecodeObjectID: expected at least %d bytes, got %d: %w", + SizeObjectID, len(b), ErrDecodeShortInput, + ) } copy(res[:], b) diff --git a/internal/bsonproto/regex.go b/internal/bsonproto/regex.go index 4afc2d7..edf87e3 100644 --- a/internal/bsonproto/regex.go +++ b/internal/bsonproto/regex.go @@ -31,7 +31,7 @@ func SizeRegex(v Regex) int { // EncodeRegex encodes [Regex] value v into b. // -// b must be at least len(v.Pattern)+len(v.Options)+2 ([SizeRegex]) bytes long; otherwise, EncodeRegex will panic. +// "b" must be at least len(v.Pattern)+len(v.Options)+2 ([SizeRegex]) bytes long; otherwise, EncodeRegex will panic. // Only b[0:len(v.Pattern)+len(v.Options)+2] bytes are modified. func EncodeRegex(b []byte, v Regex) { // ensure b length early diff --git a/internal/bsonproto/string.go b/internal/bsonproto/string.go index 35f5482..dbb97f3 100644 --- a/internal/bsonproto/string.go +++ b/internal/bsonproto/string.go @@ -26,7 +26,7 @@ func SizeString(v string) int { // EncodeString encodes string value v into b. // -// b must be at least len(v)+5 ([SizeString]) bytes long; otherwise, EncodeString will panic. +// "b" must be at least len(v)+5 ([SizeString]) bytes long; otherwise, EncodeString will panic. // Only b[0:len(v)+5] bytes are modified. func EncodeString(b []byte, v string) { i := len(v) + 1 diff --git a/internal/bsonproto/time.go b/internal/bsonproto/time.go index d3ebb10..8631df7 100644 --- a/internal/bsonproto/time.go +++ b/internal/bsonproto/time.go @@ -25,7 +25,7 @@ const SizeTime = 8 // EncodeTime encodes [time.Time] value v into b. // -// b must be at least 8 ([SizeTime]) byte long; otherwise, EncodeTime will panic. +// "b" must be at least 8 ([SizeTime]) byte long; otherwise, EncodeTime will panic. // Only b[0:8] bytes are modified. func EncodeTime(b []byte, v time.Time) { binary.LittleEndian.PutUint64(b, uint64(v.UnixMilli())) diff --git a/internal/bsonproto/timestamp.go b/internal/bsonproto/timestamp.go index 7dd6170..07b7c6a 100644 --- a/internal/bsonproto/timestamp.go +++ b/internal/bsonproto/timestamp.go @@ -27,7 +27,7 @@ const SizeTimestamp = 8 // EncodeTimestamp encodes [Timestamp] value v into b. // -// b must be at least 8 ([SizeTimestamp]) bytes long; otherwise, EncodeTimestamp will panic. +// "b" must be at least 8 ([SizeTimestamp]) bytes long; otherwise, EncodeTimestamp will panic. // Only b[0:8] bytes are modified. func EncodeTimestamp(b []byte, v Timestamp) { binary.LittleEndian.PutUint64(b, uint64(v)) @@ -38,7 +38,10 @@ func EncodeTimestamp(b []byte, v Timestamp) { // If there is not enough bytes, DecodeTimestamp will return a wrapped [ErrDecodeShortInput]. func DecodeTimestamp(b []byte) (Timestamp, error) { if len(b) < SizeTimestamp { - return 0, fmt.Errorf("DecodeTimestamp: expected at least %d bytes, got %d: %w", SizeTimestamp, len(b), ErrDecodeShortInput) + return 0, fmt.Errorf( + "DecodeTimestamp: expected at least %d bytes, got %d: %w", + SizeTimestamp, len(b), ErrDecodeShortInput, + ) } return Timestamp(binary.LittleEndian.Uint64(b)), nil diff --git a/wireclient/wireclient.go b/wireclient/wireclient.go index 7a124c6..b537407 100644 --- a/wireclient/wireclient.go +++ b/wireclient/wireclient.go @@ -108,7 +108,7 @@ func Connect(ctx context.Context, uri string, l *slog.Logger) (*Conn, error) { // ConnectPing uses a combination of [Connect] and [Conn.Ping] to establish a working connection. // -// nil is returned on context expiration. +// Nil is returned on context expiration. func ConnectPing(ctx context.Context, uri string, l *slog.Logger) *Conn { for ctx.Err() == nil { conn, err := Connect(ctx, uri, l) @@ -309,7 +309,7 @@ func (c *Conn) Login(ctx context.Context, username, password, authDB string) err "$db", authDB, ) - for step := range 3 { + for step := 0; step < 3; step++ { c.l.DebugContext( ctx, "Login", slog.Int("step", step), slog.Bool("done", conv.Done()), slog.Bool("valid", conv.Valid()),