From 493ec396fab32d9e8859e34ad497fdb0a910a33c Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Sun, 1 Jan 2023 17:49:11 -0700 Subject: lint: Turn on forcetypeassert --- .golangci.yml | 1 - lib/binstruct/size.go | 3 +- lib/btrfs/btrfsitem/items.go | 3 +- lib/btrfs/io2_lv.go | 18 ++++-- lib/btrfs/io4_fs.go | 129 ++++++++++++++++++++++++++++--------------- lib/containers/lru.go | 5 +- lib/containers/set.go | 7 ++- lib/containers/syncmap.go | 6 +- lib/textui/progress.go | 1 + 9 files changed, 116 insertions(+), 57 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 7dd7f00..d5c6e53 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -27,7 +27,6 @@ linters: - cyclop - exhaustive - exhaustruct - - forcetypeassert - funlen - gci - gochecknoglobals diff --git a/lib/binstruct/size.go b/lib/binstruct/size.go index 365da85..52fa380 100644 --- a/lib/binstruct/size.go +++ b/lib/binstruct/size.go @@ -1,4 +1,4 @@ -// Copyright (C) 2022 Luke Shumaker +// Copyright (C) 2022-2023 Luke Shumaker // // SPDX-License-Identifier: GPL-2.0-or-later @@ -30,6 +30,7 @@ var ( func staticSize(typ reflect.Type) (int, error) { if typ.Implements(staticSizerType) { + //nolint:forcetypeassert // Already did a type check via reflection. return reflect.New(typ).Elem().Interface().(StaticSizer).BinaryStaticSize(), nil } if typ.Implements(marshalerType) || typ.Implements(unmarshalerType) { diff --git a/lib/btrfs/btrfsitem/items.go b/lib/btrfs/btrfsitem/items.go index 29b3cb0..d300179 100644 --- a/lib/btrfs/btrfsitem/items.go +++ b/lib/btrfs/btrfsitem/items.go @@ -1,4 +1,4 @@ -// Copyright (C) 2022 Luke Shumaker +// Copyright (C) 2022-2023 Luke Shumaker // // SPDX-License-Identifier: GPL-2.0-or-later @@ -79,5 +79,6 @@ func UnmarshalItem(key btrfsprim.Key, csumType btrfssum.CSumType, dat []byte) It key.ItemType, len(dat), n), } } + //nolint:forcetypeassert // items_gen.go has all types in keytype2gotype implement the Item interface. return retPtr.Elem().Interface().(Item) } diff --git a/lib/btrfs/io2_lv.go b/lib/btrfs/io2_lv.go index 5580285..3f82cd0 100644 --- a/lib/btrfs/io2_lv.go +++ b/lib/btrfs/io2_lv.go @@ -1,4 +1,4 @@ -// Copyright (C) 2022 Luke Shumaker +// Copyright (C) 2022-2023 Luke Shumaker // // SPDX-License-Identifier: GPL-2.0-or-later @@ -171,10 +171,20 @@ func (fs *FS) initDev(ctx context.Context, sb btrfstree.Superblock) error { if item.Key.ItemType != btrfsitem.CHUNK_ITEM_KEY { return nil } - for _, mapping := range item.Body.(btrfsitem.Chunk).Mappings(item.Key) { - if err := fs.LV.AddMapping(mapping); err != nil { - return err + switch itemBody := item.Body.(type) { + case btrfsitem.Chunk: + for _, mapping := range itemBody.Mappings(item.Key) { + if err := fs.LV.AddMapping(mapping); err != nil { + return err + } } + case btrfsitem.Error: + // do nothing + default: + // This is a panic because the item decoder should not emit CHUNK_ITEM items as + // anything but btrfsitem.Chunk or btrfsitem.Error without this code also being + // updated. + panic(fmt.Errorf("should not happen: CHUNK_ITEM has unexpected item type: %T", itemBody)) } return nil }, diff --git a/lib/btrfs/io4_fs.go b/lib/btrfs/io4_fs.go index ea81bc2..d7c2770 100644 --- a/lib/btrfs/io4_fs.go +++ b/lib/btrfs/io4_fs.go @@ -1,4 +1,4 @@ -// Copyright (C) 2022 Luke Shumaker +// Copyright (C) 2022-2023 Luke Shumaker // // SPDX-License-Identifier: GPL-2.0-or-later @@ -152,17 +152,29 @@ func (sv *Subvolume) LoadFullInode(inode btrfsprim.ObjID) (*FullInode, error) { for _, item := range items { switch item.Key.ItemType { case btrfsitem.INODE_ITEM_KEY: - itemBody := item.Body.(btrfsitem.Inode) - if val.InodeItem != nil { - if !reflect.DeepEqual(itemBody, *val.InodeItem) { - val.Errs = append(val.Errs, fmt.Errorf("multiple inodes")) + switch itemBody := item.Body.(type) { + case btrfsitem.Inode: + if val.InodeItem != nil { + if !reflect.DeepEqual(itemBody, *val.InodeItem) { + val.Errs = append(val.Errs, fmt.Errorf("multiple inodes")) + } + continue } - continue + val.InodeItem = &itemBody + case btrfsitem.Error: + val.Errs = append(val.Errs, fmt.Errorf("malformed INODE_ITEM: %w", itemBody.Err)) + default: + panic(fmt.Errorf("should not happen: INODE_ITEM has unexpected item type: %T", itemBody)) } - val.InodeItem = &itemBody case btrfsitem.XATTR_ITEM_KEY: - itemBody := item.Body.(btrfsitem.DirEntry) - val.XAttrs[string(itemBody.Name)] = string(itemBody.Data) + switch itemBody := item.Body.(type) { + case btrfsitem.DirEntry: + val.XAttrs[string(itemBody.Name)] = string(itemBody.Data) + case btrfsitem.Error: + val.Errs = append(val.Errs, fmt.Errorf("malformed XATTR_ITEM: %w", itemBody.Err)) + default: + panic(fmt.Errorf("should not happen: XATTR_ITEM has unexpected item type: %T", itemBody)) + } default: val.OtherItems = append(val.OtherItems, item) } @@ -200,48 +212,66 @@ func (ret *Dir) populate() { for _, item := range ret.OtherItems { switch item.Key.ItemType { case btrfsitem.INODE_REF_KEY: - body := item.Body.(btrfsitem.InodeRefs) - if len(body) != 1 { - ret.Errs = append(ret.Errs, fmt.Errorf("INODE_REF item with %d entries on a directory", - len(body))) - continue - } - ref := InodeRef{ - Inode: btrfsprim.ObjID(item.Key.Offset), - InodeRef: body[0], - } - if ret.DotDot != nil { - if !reflect.DeepEqual(ref, *ret.DotDot) { - ret.Errs = append(ret.Errs, fmt.Errorf("multiple INODE_REF items on a directory")) + switch body := item.Body.(type) { + case btrfsitem.InodeRefs: + if len(body) != 1 { + ret.Errs = append(ret.Errs, fmt.Errorf("INODE_REF item with %d entries on a directory", + len(body))) + continue + } + ref := InodeRef{ + Inode: btrfsprim.ObjID(item.Key.Offset), + InodeRef: body[0], } - continue + if ret.DotDot != nil { + if !reflect.DeepEqual(ref, *ret.DotDot) { + ret.Errs = append(ret.Errs, fmt.Errorf("multiple INODE_REF items on a directory")) + } + continue + } + ret.DotDot = &ref + case btrfsitem.Error: + ret.Errs = append(ret.Errs, fmt.Errorf("malformed INODE_REF: %w", body.Err)) + default: + panic(fmt.Errorf("should not happen: INODE_REF has unexpected item type: %T", body)) } - ret.DotDot = &ref case btrfsitem.DIR_ITEM_KEY: - entry := item.Body.(btrfsitem.DirEntry) - namehash := btrfsitem.NameHash(entry.Name) - if namehash != item.Key.Offset { - ret.Errs = append(ret.Errs, fmt.Errorf("direntry crc32c mismatch: key=%#x crc32c(%q)=%#x", - item.Key.Offset, entry.Name, namehash)) - continue - } - if other, exists := ret.ChildrenByName[string(entry.Name)]; exists { - if !reflect.DeepEqual(entry, other) { - ret.Errs = append(ret.Errs, fmt.Errorf("multiple instances of direntry name %q", entry.Name)) + switch entry := item.Body.(type) { + case btrfsitem.DirEntry: + namehash := btrfsitem.NameHash(entry.Name) + if namehash != item.Key.Offset { + ret.Errs = append(ret.Errs, fmt.Errorf("direntry crc32c mismatch: key=%#x crc32c(%q)=%#x", + item.Key.Offset, entry.Name, namehash)) + continue + } + if other, exists := ret.ChildrenByName[string(entry.Name)]; exists { + if !reflect.DeepEqual(entry, other) { + ret.Errs = append(ret.Errs, fmt.Errorf("multiple instances of direntry name %q", entry.Name)) + } + continue } - continue + ret.ChildrenByName[string(entry.Name)] = entry + case btrfsitem.Error: + ret.Errs = append(ret.Errs, fmt.Errorf("malformed DIR_ITEM: %w", entry.Err)) + default: + panic(fmt.Errorf("should not happen: DIR_ITEM has unexpected item type: %T", entry)) } - ret.ChildrenByName[string(entry.Name)] = entry case btrfsitem.DIR_INDEX_KEY: index := item.Key.Offset - entry := item.Body.(btrfsitem.DirEntry) - if other, exists := ret.ChildrenByIndex[index]; exists { - if !reflect.DeepEqual(entry, other) { - ret.Errs = append(ret.Errs, fmt.Errorf("multiple instances of direntry index %v", index)) + switch entry := item.Body.(type) { + case btrfsitem.DirEntry: + if other, exists := ret.ChildrenByIndex[index]; exists { + if !reflect.DeepEqual(entry, other) { + ret.Errs = append(ret.Errs, fmt.Errorf("multiple instances of direntry index %v", index)) + } + continue } - continue + ret.ChildrenByIndex[index] = entry + case btrfsitem.Error: + ret.Errs = append(ret.Errs, fmt.Errorf("malformed DIR_INDEX: %w", entry.Err)) + default: + panic(fmt.Errorf("should not happen: DIR_INDEX has unexpected item type: %T", entry)) } - ret.ChildrenByIndex[index] = entry default: panic(fmt.Errorf("TODO: handle item type %v", item.Key.ItemType)) } @@ -317,10 +347,17 @@ func (ret *File) populate() { case btrfsitem.INODE_REF_KEY: // TODO case btrfsitem.EXTENT_DATA_KEY: - ret.Extents = append(ret.Extents, FileExtent{ - OffsetWithinFile: int64(item.Key.Offset), - FileExtent: item.Body.(btrfsitem.FileExtent), - }) + switch itemBody := item.Body.(type) { + case btrfsitem.FileExtent: + ret.Extents = append(ret.Extents, FileExtent{ + OffsetWithinFile: int64(item.Key.Offset), + FileExtent: itemBody, + }) + case btrfsitem.Error: + ret.Errs = append(ret.Errs, fmt.Errorf("malformed EXTENT_DATA: %w", itemBody.Err)) + default: + panic(fmt.Errorf("should not happen: EXTENT_DATA has unexpected item type: %T", itemBody)) + } default: panic(fmt.Errorf("TODO: handle item type %v", item.Key.ItemType)) } diff --git a/lib/containers/lru.go b/lib/containers/lru.go index a235a12..12446b0 100644 --- a/lib/containers/lru.go +++ b/lib/containers/lru.go @@ -1,4 +1,4 @@ -// Copyright (C) 2022 Luke Shumaker +// Copyright (C) 2022-2023 Luke Shumaker // // SPDX-License-Identifier: GPL-2.0-or-later @@ -44,6 +44,7 @@ func (c *LRUCache[K, V]) Get(key K) (value V, ok bool) { c.init() _value, ok := c.inner.Get(key) if ok { + //nolint:forcetypeassert // Typed wrapper around untyped lib. value = _value.(V) } return value, ok @@ -53,6 +54,7 @@ func (c *LRUCache[K, V]) Keys() []K { untyped := c.inner.Keys() typed := make([]K, len(untyped)) for i := range untyped { + //nolint:forcetypeassert // Typed wrapper around untyped lib. typed[i] = untyped[i].(K) } return typed @@ -65,6 +67,7 @@ func (c *LRUCache[K, V]) Peek(key K) (value V, ok bool) { c.init() _value, ok := c.inner.Peek(key) if ok { + //nolint:forcetypeassert // Typed wrapper around untyped lib. value = _value.(V) } return value, ok diff --git a/lib/containers/set.go b/lib/containers/set.go index 67ba7ac..4fc8aad 100644 --- a/lib/containers/set.go +++ b/lib/containers/set.go @@ -1,4 +1,4 @@ -// Copyright (C) 2022 Luke Shumaker +// Copyright (C) 2022-2023 Luke Shumaker // // SPDX-License-Identifier: GPL-2.0-or-later @@ -22,7 +22,10 @@ var ( _ lowmemjson.Decodable = (*Set[int])(nil) ) -func cast[T any](x any) T { return x.(T) } +func cast[T any](x any) T { + //nolint:forcetypeassert // Only called within a type switch. + return x.(T) +} func (o Set[T]) EncodeJSON(w io.Writer) error { var less func(a, b T) bool diff --git a/lib/containers/syncmap.go b/lib/containers/syncmap.go index fb7f59b..4af678f 100644 --- a/lib/containers/syncmap.go +++ b/lib/containers/syncmap.go @@ -1,4 +1,4 @@ -// Copyright (C) 2022 Luke Shumaker +// Copyright (C) 2022-2023 Luke Shumaker // // SPDX-License-Identifier: GPL-2.0-or-later @@ -18,6 +18,7 @@ func (m *SyncMap[K, V]) Delete(key K) { func (m *SyncMap[K, V]) Load(key K) (value V, ok bool) { _value, ok := m.inner.Load(key) if ok { + //nolint:forcetypeassert // Typed wrapper around untyped lib. value = _value.(V) } return value, ok @@ -25,17 +26,20 @@ func (m *SyncMap[K, V]) Load(key K) (value V, ok bool) { func (m *SyncMap[K, V]) LoadAndDelete(key K) (value V, loaded bool) { _value, ok := m.inner.LoadAndDelete(key) if ok { + //nolint:forcetypeassert // Typed wrapper around untyped lib. value = _value.(V) } return value, ok } func (m *SyncMap[K, V]) LoadOrStore(key K, value V) (actual V, loaded bool) { _actual, loaded := m.inner.LoadOrStore(key, value) + //nolint:forcetypeassert // Typed wrapper around untyped lib. actual = _actual.(V) return actual, loaded } func (m *SyncMap[K, V]) Range(f func(key K, value V) bool) { m.inner.Range(func(key, value any) bool { + //nolint:forcetypeassert // Typed wrapper around untyped lib. return f(key.(K), value.(V)) }) } diff --git a/lib/textui/progress.go b/lib/textui/progress.go index 56fda96..68d986f 100644 --- a/lib/textui/progress.go +++ b/lib/textui/progress.go @@ -56,6 +56,7 @@ func (p *Progress[T]) Done() { } func (p *Progress[T]) flush(force bool) { + //nolint:forcetypeassert // It wasn't worth it to me (yet?) to make a typed wrapper around atomic.Value. cur := p.cur.Load().(T) if !force && cur == p.oldStat { return -- cgit v1.1-4-g5e80