nilnil
Support type aliases by Go 1.23
nil 오류의 동시 반환이없고 잘못된 값이 있는지 확인합니다.
$ go install github.com/Antonboom/nilnil@latest
$ nilnil ./...
return nil, nil 관용적이지 않습니다. 개발자는 오류가 없으면 반환 값이 유효하며 추가 점검없이 사용할 수 있다는 사실에 익숙합니다.
user , err := getUser ()
if err != nil {
return err
}
if user != nil { // Ambiguous!
// Use user.
}기껏해야 이것은 같은 의견으로 이어집니다
// ...
// If this compilation unit has no line table, it returns nil, nil.
func ( d * Data ) LineReader ( cu * Entry ) ( * LineReader , error ) { 그것은 진정한 제약이 아닙니다. 그리고 최악의 경우 그러한 코드는 공황 으로 이어질 수 있습니다.
Sentinel 오류로 위의 예를 다시 작성하십시오.
user , err := getUser ()
if errors . Is ( err , errUserNotFound ) {
// Do something and return.
}
if err != nil {
return err
}
// Use user.때때로 사람들은 반대의 상황 (비 닐 오류와 동시에 유효한 값을 반환)을 반대 방지로 간주하여 찾기 어려운 버그로 이어질 수 있기 때문입니다.
예를 들어,이 Kubernetes 코드
func ( fh * fakeHistory ) UpdateControllerRevision ( revision * apps. ControllerRevision , newRevision int64 ) ( * apps. ControllerRevision , error ) {
clone := revision . DeepCopy ()
clone . Revision = newRevision
return clone , fh . indexer . Update ( clone )
}다음과 같이 다시 작성할 수 있습니다
func ( fh * fakeHistory ) UpdateControllerRevision ( revision * apps. ControllerRevision , newRevision int64 ) ( * apps. ControllerRevision , error ) {
clone := revision . DeepCopy ()
clone . Revision = newRevision
if err := fh . indexer . Update ( clone ); err != nil {
return nil , fmt . Errorf ( "update index: %w" , err )
}
return clone , nil
} 각 사례를 별도로 분석해야한다는 것을 이해하지만 Linter가 다시 생각하게 만들기를 바랍니다. 모호한 API를 사용해야합니까, 아니면 Sentinel 오류를 사용하여 수행하는 것이 더 낫습니까?
어쨌든 Linter를 활성화 할 수는 없습니다.
$ nilnil --checked-types chan,func,iface,map,ptr,uintptr,unsafeptr ./...
$ nilnil --detect-opposite ./...
$ nilnil --detect-opposite --checked-types ptr ./..https://golangci-lint.run/usage/linters/#nilnil
linters-settings :
nilnil :
# In addition, detect opposite situation (simultaneous return of non-nil error and valid value).
# Default: false
detect-opposite : true
# List of return types to check.
# Default: ["chan", "func", "iface", "map", "ptr", "uintptr", "unsafeptr"]
checked-types :
- chan
- func
- iface
- map
- ptr
- uintptr
- unsafeptr // BEFORE
func parsePublicKey ( algo PublicKeyAlgorithm , keyData * publicKeyInfo ) ( interface {}, error ) {
der := cryptobyte . String ( keyData . PublicKey . RightAlign ())
switch algo {
case RSA :
// ...
return pub , nil
case ECDSA :
// ...
return pub , nil
case Ed25519 :
// ...
return ed25519 . PublicKey ( der ), nil
case DSA :
// ...
return pub , nil
default :
return nil , nil
}
}
// AFTER
var errUnknownPublicKeyAlgo = errors . New ( "unknown public key algo" )
func parsePublicKey ( algo PublicKeyAlgorithm , keyData * publicKeyInfo ) ( interface {}, error ) {
der := cryptobyte . String ( keyData . PublicKey . RightAlign ())
switch algo {
case RSA :
// ...
return pub , nil
case ECDSA :
// ...
return pub , nil
case Ed25519 :
// ...
return ed25519 . PublicKey ( der ), nil
case DSA :
// ...
return pub , nil
default :
return nil , fmt . Errorf ( "%w: %v" , errUnknownPublicKeyAlgo , algo )
}
} // BEFORE
// As a special case, handleResponse may return (nil, nil) to skip the frame.
func ( rl * http2clientConnReadLoop ) handleResponse ( /* ... */ ) ( * Response , error ) {
if statusCode >= 100 && statusCode <= 199 {
return nil , nil
}
}
// ...
res , err := rl . handleResponse ( cs , f )
if err != nil {
return err
}
if res == nil {
// (nil, nil) special case. See handleResponse docs.
return nil
}
// AFTER
var errNeedSkipFrame = errors . New ( "need skip frame" )
// As a special case, handleResponse may return errNeedSkipFrame to skip the frame.
func ( rl * http2clientConnReadLoop ) handleResponse ( /* ... */ ) ( * Response , error ) {
if statusCode >= 100 && statusCode <= 199 {
return nil , errNeedSkipFrame
}
}
// ...
res , err := rl . handleResponse ( cs , f )
if errors . Is ( err , errNeedSkipFrame ) {
return nil
}
if err != nil {
return err
} // BEFORE
func ( s * Service ) StartStream ( ctx context. Context ) ( * Stream , error ) {
return nil , nil
}
// AFTER
func ( s * Service ) StartStream ( ctx context. Context ) ( * Stream , error ) {
return nil , errors . New ( "not implemented" )
} package ratelimiter
type RateLimiter struct {
// ...
}
func New () ( * RateLimiter , error ) {
// It's OK, RateLimiter is nil-safe.
// But it's better not to do it anyway.
return nil , nil
}
func ( r * RateLimiter ) Allow () bool {
if r == nil {
return true
}
return r . allow ()
}error 구현합니다.uinptr 및 unsafe.Pointer 포함), 함수 및 인터페이스 ( panic: invalid memory address or nil pointer dereference );panic: assignment to entry in nil map );fatal error: all goroutines are asleep - deadlock! )return nil, nil 만으로도 지원됩니다. $ cd $GOROOT /src
$ nilnil ./... 2>&1 | grep -v " _test.go "
/usr/local/go/src/internal/bisect/bisect.go:196:3: return both a ` nil ` error and an invalid value: use a sentinel error instead
/usr/local/go/src/net/fd_unix.go:71:3: return both a ` nil ` error and an invalid value: use a sentinel error instead
/usr/local/go/src/net/fd_unix.go:79:4: return both a ` nil ` error and an invalid value: use a sentinel error instead
/usr/local/go/src/net/fd_unix.go:156:4: return both a ` nil ` error and an invalid value: use a sentinel error instead
/usr/local/go/src/net/iprawsock_posix.go:36:3: return both a ` nil ` error and an invalid value: use a sentinel error instead
/usr/local/go/src/net/tcpsock_posix.go:38:3: return both a ` nil ` error and an invalid value: use a sentinel error instead
/usr/local/go/src/net/udpsock_posix.go:37:3: return both a ` nil ` error and an invalid value: use a sentinel error instead
/usr/local/go/src/net/unixsock_posix.go:92:3: return both a ` nil ` error and an invalid value: use a sentinel error instead
/usr/local/go/src/crypto/tls/key_agreement.go:46:2: return both a ` nil ` error and an invalid value: use a sentinel error instead
/usr/local/go/src/crypto/tls/ticket.go:355:3: return both a ` nil ` error and an invalid value: use a sentinel error instead
/usr/local/go/src/crypto/tls/ticket.go:359:3: return both a ` nil ` error and an invalid value: use a sentinel error instead
/usr/local/go/src/database/sql/driver/types.go:157:3: return both a ` nil ` error and an invalid value: use a sentinel error instead
/usr/local/go/src/database/sql/driver/types.go:232:3: return both a ` nil ` error and an invalid value: use a sentinel error instead
/usr/local/go/src/database/sql/driver/types.go:263:4: return both a ` nil ` error and an invalid value: use a sentinel error instead
/usr/local/go/src/database/sql/convert.go:548:3: return both a ` nil ` error and an invalid value: use a sentinel error instead
/usr/local/go/src/database/sql/sql.go:205:3: return both a ` nil ` error and an invalid value: use a sentinel error instead
/usr/local/go/src/database/sql/sql.go:231:3: return both a ` nil ` error and an invalid value: use a sentinel error instead
/usr/local/go/src/database/sql/sql.go:257:3: return both a ` nil ` error and an invalid value: use a sentinel error instead
/usr/local/go/src/database/sql/sql.go:284:3: return both a ` nil ` error and an invalid value: use a sentinel error instead
/usr/local/go/src/database/sql/sql.go:311:3: return both a ` nil ` error and an invalid value: use a sentinel error instead
/usr/local/go/src/database/sql/sql.go:337:3: return both a ` nil ` error and an invalid value: use a sentinel error instead
/usr/local/go/src/database/sql/sql.go:363:3: return both a ` nil ` error and an invalid value: use a sentinel error instead
/usr/local/go/src/database/sql/sql.go:389:3: return both a ` nil ` error and an invalid value: use a sentinel error instead
/usr/local/go/src/database/sql/sql.go:422:3: return both a ` nil ` error and an invalid value: use a sentinel error instead
/usr/local/go/src/debug/dwarf/entry.go:884:3: return both a ` nil ` error and an invalid value: use a sentinel error instead
/usr/local/go/src/debug/dwarf/line.go:146:3: return both a ` nil ` error and an invalid value: use a sentinel error instead
/usr/local/go/src/debug/dwarf/line.go:153:3: return both a ` nil ` error and an invalid value: use a sentinel error instead
/usr/local/go/src/debug/dwarf/typeunit.go:138:3: return both a ` nil ` error and an invalid value: use a sentinel error instead
/usr/local/go/src/debug/pe/file.go:470:3: return both a ` nil ` error and an invalid value: use a sentinel error instead
/usr/local/go/src/net/http/h2_bundle.go:9530:3: return both a ` nil ` error and an invalid value: use a sentinel error instead
/usr/local/go/src/net/http/transfer.go:765:3: return both a ` nil ` error and an invalid value: use a sentinel error instead
/usr/local/go/src/net/http/transfer.go:775:3: return both a ` nil ` error and an invalid value: use a sentinel error instead
/usr/local/go/src/net/http/transfer.go:798:3: return both a ` nil ` error and an invalid value: use a sentinel error instead
/usr/local/go/src/go/build/build.go:1442:3: return both a ` nil ` error and an invalid value: use a sentinel error instead
/usr/local/go/src/go/build/build.go:1453:3: return both a ` nil ` error and an invalid value: use a sentinel error instead
/usr/local/go/src/go/build/build.go:1457:3: return both a ` nil ` error and an invalid value: use a sentinel error instead
/usr/local/go/src/go/build/build.go:1491:3: return both a ` nil ` error and an invalid value: use a sentinel error instead
/usr/local/go/src/go/internal/gccgoimporter/ar.go:125:3: return both a ` nil ` error and an invalid value: use a sentinel error instead
/usr/local/go/src/image/jpeg/reader.go:622:5: return both a ` nil ` error and an invalid value: use a sentinel error instead
/usr/local/go/src/image/png/reader.go:434:4: return both a ` nil ` error and an invalid value: use a sentinel error instead
/usr/local/go/src/internal/profile/legacy_profile.go:1089:4: return both a ` nil ` error and an invalid value: use a sentinel error instead
/usr/local/go/src/net/internal/socktest/switch.go:142:3: return both a ` nil ` error and an invalid value: use a sentinel error instead