chore: enable forcetypeassert linter, fix issues (#38804)

Enable [`forcetypeassert`](https://github.com/gostaticanalysis/forcetypeassert)
linter to prevent unchecked type assertions. ~650 issues fixed, most
fixes were clean, some use `setting.PanicInDevOrTesting`.

The only behaviour changes are where code would previously send a 500 error
or panic, a 4xx error is now emitted.

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
silverwind
2026-08-09 12:25:06 +02:00
committed by GitHub
parent fac8bf2eca
commit 76a81b24f9
248 changed files with 1110 additions and 995 deletions
+3 -2
View File
@@ -20,6 +20,7 @@ import (
"gitea.dev/modules/util"
"golang.org/x/crypto/ssh"
"xorm.io/builder"
)
// AuthorizedStringCommentPrefix is a magic tag
@@ -162,8 +163,8 @@ func appendAuthorizedKeysToFile(keys ...*PublicKey) error {
// RegeneratePublicKeys regenerates the authorized_keys file
func RegeneratePublicKeys(ctx context.Context, t io.Writer) error {
if err := db.GetEngine(ctx).Where("type != ?", KeyTypePrincipal).Iterate(new(PublicKey), func(idx int, bean any) (err error) {
return WriteAuthorizedStringForValidKey(bean.(*PublicKey), t)
if err := db.Iterate(ctx, builder.Neq{"type": KeyTypePrincipal}, func(ctx context.Context, key *PublicKey) error {
return WriteAuthorizedStringForValidKey(key, t)
}); err != nil {
return err
}
+16 -20
View File
@@ -98,20 +98,12 @@ type RegisterableSource interface {
var registeredConfigs = map[Type]func() Config{}
// RegisterTypeConfig register a config for a provided type
func RegisterTypeConfig(typ Type, exemplar Config) {
if reflect.TypeOf(exemplar).Kind() == reflect.Pointer {
// Pointer:
registeredConfigs[typ] = func() Config {
return reflect.New(reflect.ValueOf(exemplar).Elem().Type()).Interface().(Config)
}
return
}
// Not a Pointer
registeredConfigs[typ] = func() Config {
return reflect.New(reflect.TypeOf(exemplar)).Elem().Interface().(Config)
}
// RegisterTypeConfig register a config for a provided type, the exemplar argument only serves type inference
func RegisterTypeConfig[T interface {
*E
Config
}, E any](typ Type, _ T) {
registeredConfigs[typ] = func() Config { return T(new(E)) }
}
// Source represents an external way for authorizing users.
@@ -188,6 +180,16 @@ func (source *Source) IsSSPI() bool {
return source.Type == SSPI
}
// MustSourceCfg returns the source's config as T. The registry populates Cfg from the
// source type, so a mismatch is a programming error the caller can't recover from.
func MustSourceCfg[T Config](source *Source) T {
cfg, ok := source.Cfg.(T)
if !ok {
panic(fmt.Errorf("auth source %q (id=%d, type=%s) has config %T, expected %s", source.Name, source.ID, source.Type, source.Cfg, reflect.TypeFor[T]()))
}
return cfg
}
// HasTLS returns true of this source supports TLS.
func (source *Source) HasTLS() bool {
hasTLSer, ok := source.Cfg.(HasTLSer)
@@ -371,12 +373,6 @@ type ErrSourceAlreadyExist struct {
Name string
}
// IsErrSourceAlreadyExist checks if an error is a ErrSourceAlreadyExist.
func IsErrSourceAlreadyExist(err error) bool {
_, ok := err.(ErrSourceAlreadyExist)
return ok
}
func (err ErrSourceAlreadyExist) Error() string {
return fmt.Sprintf("login source already exists [name: %s]", err.Name)
}
+3 -1
View File
@@ -11,6 +11,7 @@ import (
"gitea.dev/models/unittest"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestInTransaction(t *testing.T) {
@@ -107,7 +108,8 @@ func TestContextSafety(t *testing.T) {
_ = db.GetEngine(ctx).Iterate(&TestModel1{}, func(i int, bean any) error {
// here: db.GetEngine(ctx) is always the unclosed "Iterate" *Session with autoResetStatement=false,
// and the internal states (including "cond" and others) are always there and not be reset in this callback.
m1 := bean.(*TestModel1)
m1, ok := bean.(*TestModel1)
require.True(t, ok)
assert.EqualValues(t, i+1, m1.ID)
// here: XORM bug, it fails because the SQL becomes "WHERE id=-1", "WHERE id=-1 AND id=-2", "WHERE id=-1 AND id=-2 AND id=-3" ...
-6
View File
@@ -14,12 +14,6 @@ type ErrCancelled struct {
Message string
}
// IsErrCancelled checks if an error is a ErrCancelled.
func IsErrCancelled(err error) bool {
_, ok := err.(ErrCancelled)
return ok
}
func (err ErrCancelled) Error() string {
return "Cancelled: " + err.Message
}
+9
View File
@@ -239,6 +239,15 @@ func GetPackageDescriptorWithCache(ctx context.Context, pv *PackageVersion, c *c
}, nil
}
// DescriptorMetadata returns the descriptor's metadata, which getPackageDescriptor has created from the package type
func DescriptorMetadata[T interface{ *E }, E any](pd *PackageDescriptor) T {
metadata, ok := pd.Metadata.(T)
if !ok {
panic(fmt.Errorf("package %s of type %s has metadata type %T instead of %T", pd.Package.Name, pd.Package.Type, pd.Metadata, metadata))
}
return metadata
}
// GetPackageFileDescriptor gets a package file descriptor for a package file
func GetPackageFileDescriptor(ctx context.Context, pf *PackageFile) (*PackageFileDescriptor, error) {
return getPackageFileDescriptor(ctx, pf, cache.NewEphemeralCache())
+21 -8
View File
@@ -5,6 +5,7 @@ package repo
import (
"context"
"fmt"
"gitea.dev/models/db"
"gitea.dev/models/perm"
@@ -295,44 +296,56 @@ func (r *RepoUnit) Unit() unit.Unit {
return unit.Units[r.Type]
}
// unitConfig returns the unit's config, which BeforeSet has created from the unit type
func unitConfig[T interface {
*E
convert.Conversion
}, E any](r *RepoUnit) T {
config, ok := r.Config.(T)
if !ok {
panic(fmt.Errorf("repo unit %d of type %s has config type %T instead of %T", r.ID, r.Type.LogString(), r.Config, config))
}
return config
}
// CodeConfig returns config for unit.TypeCode
func (r *RepoUnit) CodeConfig() *UnitConfig {
return r.Config.(*UnitConfig)
return unitConfig[*UnitConfig](r)
}
// PullRequestsConfig returns config for unit.TypePullRequests
func (r *RepoUnit) PullRequestsConfig() *PullRequestsConfig {
return r.Config.(*PullRequestsConfig)
return unitConfig[*PullRequestsConfig](r)
}
// ReleasesConfig returns config for unit.TypeReleases
func (r *RepoUnit) ReleasesConfig() *UnitConfig {
return r.Config.(*UnitConfig)
return unitConfig[*UnitConfig](r)
}
// ExternalWikiConfig returns config for unit.TypeExternalWiki
func (r *RepoUnit) ExternalWikiConfig() *ExternalWikiConfig {
return r.Config.(*ExternalWikiConfig)
return unitConfig[*ExternalWikiConfig](r)
}
// IssuesConfig returns config for unit.TypeIssues
func (r *RepoUnit) IssuesConfig() *IssuesConfig {
return r.Config.(*IssuesConfig)
return unitConfig[*IssuesConfig](r)
}
// ExternalTrackerConfig returns config for unit.TypeExternalTracker
func (r *RepoUnit) ExternalTrackerConfig() *ExternalTrackerConfig {
return r.Config.(*ExternalTrackerConfig)
return unitConfig[*ExternalTrackerConfig](r)
}
// ActionsConfig returns config for unit.ActionsConfig
func (r *RepoUnit) ActionsConfig() *ActionsConfig {
return r.Config.(*ActionsConfig)
return unitConfig[*ActionsConfig](r)
}
// ProjectsConfig returns config for unit.ProjectsConfig
func (r *RepoUnit) ProjectsConfig() *ProjectsConfig {
return r.Config.(*ProjectsConfig)
return unitConfig[*ProjectsConfig](r)
}
func getUnitsByRepoID(ctx context.Context, repoID int64) (units []*RepoUnit, err error) {
+16 -12
View File
@@ -34,7 +34,8 @@ type FixtureItem struct {
type fixturesLoaderInternal struct {
xormEngine *xorm.Engine
tableSyncMap sync.Map
tableSyncMu sync.Mutex
tableSynced map[string]bool
db *sql.DB
dbType schemas.DBType
fixtures map[string]*FixtureItem
@@ -152,32 +153,35 @@ func (f *fixturesLoaderInternal) Load() error {
ctx := context.WithValue(context.Background(), db.ContextKeyTestFixtures, true)
f.tableSyncMu.Lock()
defer f.tableSyncMu.Unlock()
for _, fixture := range f.fixtures {
synced, existing := f.tableSyncMap.Load(fixture.tableName)
if synced == true || !existing {
synced, existing := f.tableSynced[fixture.tableName]
if synced || !existing {
continue
}
if err := f.loadFixtures(tx, fixture); err != nil {
return fmt.Errorf("failed to load fixtures from %s: %w", fixture.fileFullPath, err)
}
f.tableSyncMap.Store(fixture.tableName, true)
f.tableSynced[fixture.tableName] = true
}
if err = tx.Commit(); err != nil {
return err
}
f.tableSyncMap.Range(func(k, v any) bool {
tableName, synced := k.(string), v.(bool)
for tableName, synced := range f.tableSynced {
if !synced && f.fixtures[tableName] == nil {
_, _ = f.xormEngine.Context(ctx).Exec("DELETE FROM `" + tableName + "`")
}
f.tableSyncMap.Store(tableName, true)
return true
})
f.tableSynced[tableName] = true
}
return nil
}
func (f *fixturesLoaderInternal) MarkTableChanged(tableName string) {
f.tableSyncMap.Store(tableName, false)
f.tableSyncMu.Lock()
defer f.tableSyncMu.Unlock()
f.tableSynced[tableName] = false
}
func FixturesFileFullPaths(dir string, files []string) (map[string]*FixtureItem, error) {
@@ -212,7 +216,7 @@ func NewFixturesLoader(x *xorm.Engine, opts FixturesOptions) (FixturesLoader, er
return nil, fmt.Errorf("failed to get fixtures files: %w", err)
}
f := &fixturesLoaderInternal{xormEngine: x, db: x.DB().DB, dbType: x.Dialect().URI().DBType, fixtures: fixtureItems}
f := &fixturesLoaderInternal{xormEngine: x, db: x.DB().DB, dbType: x.Dialect().URI().DBType, fixtures: fixtureItems, tableSynced: map[string]bool{}}
switch f.dbType {
case schemas.SQLITE:
f.quoteObject = func(s string) string { return fmt.Sprintf(`"%s"`, s) }
@@ -233,7 +237,7 @@ func NewFixturesLoader(x *xorm.Engine, opts FixturesOptions) (FixturesLoader, er
xormBeans, _ := db.NamesToBean()
for _, bean := range xormBeans {
beanTableName := x.TableName(bean)
f.tableSyncMap.Store(trimTableNameQuotes(beanTableName), false)
f.tableSynced[trimTableNameQuotes(beanTableName)] = false
}
return f, nil
}