From 892a7c9c69bf2a5256de3c3e566520441df23bb0 Mon Sep 17 00:00:00 2001 From: Ben Schumacher Date: Tue, 4 Nov 2025 12:09:11 +0100 Subject: [PATCH] Use golangci-lints's build-in modernize linter (#34341) --- server/.golangci.yml | 1 + server/Makefile | 13 ++----------- server/channels/api4/user_test.go | 6 +++--- server/channels/api4/websocket_norace_test.go | 1 - server/channels/app/webhub_fuzz.go | 1 - server/channels/store/layer_generators/main.go | 3 +-- server/channels/store/sqlstore/role_store.go | 6 +++--- server/channels/store/sqlstore/store.go | 8 ++++---- server/cmd/mattermost/main_test.go | 1 - server/cmd/mmctl/commands/enterprise.go | 1 - server/cmd/mmctl/commands/main_test.go | 1 - server/cmd/mmctl/commands/mmctl_e2e_test.go | 1 - server/cmd/mmctl/commands/mmctl_unit_test.go | 1 - server/cmd/mmctl/commands/utils_unix.go | 1 - server/config/environment.go | 4 ++-- .../elasticsearch/elasticsearch/elasticsearch.go | 7 ++++--- server/platform/services/upgrader/upgrader.go | 1 - server/public/model/access_policy_test.go | 7 ++++--- server/public/model/channel_count.go | 7 ++++--- server/public/model/feature_flags.go | 2 +- server/public/model/permission.go | 10 ++++++---- server/public/model/utils.go | 13 +++++++------ server/public/plugin/client_rpc.go | 4 ++-- server/public/utils/json_test.go | 2 +- 24 files changed, 45 insertions(+), 57 deletions(-) diff --git a/server/.golangci.yml b/server/.golangci.yml index e75eb1ca69c..659d8aad0f5 100644 --- a/server/.golangci.yml +++ b/server/.golangci.yml @@ -8,6 +8,7 @@ linters: - ineffassign - makezero - misspell + - modernize - revive - staticcheck - unconvert diff --git a/server/Makefile b/server/Makefile index 2e993030fb7..37280fbf342 100644 --- a/server/Makefile +++ b/server/Makefile @@ -322,22 +322,13 @@ golang-versions: ## Install Golang versions used for compatibility testing (e.g. export GO_COMPATIBILITY_TEST_VERSIONS="${GO_COMPATIBILITY_TEST_VERSIONS}" golangci-lint: ## Run golangci-lint on codebase - $(GO) install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.5.0 + $(GO) install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.6.0 ifeq ($(BUILD_ENTERPRISE_READY),true) $(GOBIN)/golangci-lint run ./... ./public/... $(BUILD_ENTERPRISE_DIR)/... else $(GOBIN)/golangci-lint run ./... ./public/... endif -modernize: ## Run modernize linter on codebase -## https://pkg.go.dev/golang.org/x/tools/gopls/internal/analysis/modernize is not support in golangci-lint. -## We need to invoke it directly. -ifeq ($(BUILD_ENTERPRISE_READY),true) - go run golang.org/x/tools/gopls/internal/analysis/modernize/cmd/modernize@v0.19.1 -test ./... ./public/... $(BUILD_ENTERPRISE_DIR)/... -else - go run golang.org/x/tools/gopls/internal/analysis/modernize/cmd/modernize@v0.19.1 -test ./... ./public/... -endif - i18n-extract: ## Extract strings for translation from the source code cd ../tools/mmgotool && $(GO) install . $(GOBIN)/mmgotool i18n extract --portal-dir="" @@ -431,7 +422,7 @@ ifeq ($(BUILD_ENTERPRISE_READY),true) endif endif -check-style: plugin-checker vet modernize golangci-lint ## Runs style/lint checks +check-style: plugin-checker vet golangci-lint ## Runs style/lint checks gotestsum: $(GO) install gotest.tools/gotestsum@v1.11.0 diff --git a/server/channels/api4/user_test.go b/server/channels/api4/user_test.go index 09b9adda364..8ba7163145d 100644 --- a/server/channels/api4/user_test.go +++ b/server/channels/api4/user_test.go @@ -3831,11 +3831,11 @@ func TestResetPassword(t *testing.T) { resp, err = th.Client.ResetPassword(context.Background(), "junk", "newpwd") require.Error(t, err) CheckBadRequestStatus(t, resp) - code := "" + var code strings.Builder for range model.TokenSize { - code += "a" + code.WriteString("a") } - resp, err = th.Client.ResetPassword(context.Background(), code, "newpwd") + resp, err = th.Client.ResetPassword(context.Background(), code.String(), "newpwd") require.Error(t, err) CheckBadRequestStatus(t, resp) _, err = th.Client.ResetPassword(context.Background(), recoveryToken.Token, "newpwd") diff --git a/server/channels/api4/websocket_norace_test.go b/server/channels/api4/websocket_norace_test.go index 08a74adce76..ce0b95690db 100644 --- a/server/channels/api4/websocket_norace_test.go +++ b/server/channels/api4/websocket_norace_test.go @@ -2,7 +2,6 @@ // See LICENSE.txt for license information. //go:build !race -// +build !race package api4 diff --git a/server/channels/app/webhub_fuzz.go b/server/channels/app/webhub_fuzz.go index c195f9abc73..b1e898d06a2 100644 --- a/server/channels/app/webhub_fuzz.go +++ b/server/channels/app/webhub_fuzz.go @@ -1,5 +1,4 @@ //go:build gofuzz -// +build gofuzz // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. diff --git a/server/channels/store/layer_generators/main.go b/server/channels/store/layer_generators/main.go index b846c18539d..9c244cef93e 100644 --- a/server/channels/store/layer_generators/main.go +++ b/server/channels/store/layer_generators/main.go @@ -149,8 +149,7 @@ func extractStoreMetadata() (*storeMetadata, error) { metadata.Methods[methodName] = extractMethodMetadata(method, src) } } - } else if strings.HasSuffix(x.Name.Name, "Store") { - subStoreName := strings.TrimSuffix(x.Name.Name, "Store") + } else if subStoreName, ok := strings.CutSuffix(x.Name.Name, "Store"); ok { metadata.SubStores[subStoreName] = subStore{Methods: map[string]methodData{}} for _, method := range x.Type.(*ast.InterfaceType).Methods.List { methodName := method.Names[0].Name diff --git a/server/channels/store/sqlstore/role_store.go b/server/channels/store/sqlstore/role_store.go index 544ae27744e..809d4982ced 100644 --- a/server/channels/store/sqlstore/role_store.go +++ b/server/channels/store/sqlstore/role_store.go @@ -46,11 +46,11 @@ type channelRolesPermissions struct { func NewRoleFromModel(role *model.Role) *Role { permissionsMap := make(map[string]bool) - permissions := "" + var permissions strings.Builder for _, permission := range role.Permissions { if !permissionsMap[permission] { - permissions += fmt.Sprintf(" %v", permission) + permissions.WriteString(fmt.Sprintf(" %v", permission)) permissionsMap[permission] = true } } @@ -63,7 +63,7 @@ func NewRoleFromModel(role *model.Role) *Role { CreateAt: role.CreateAt, UpdateAt: role.UpdateAt, DeleteAt: role.DeleteAt, - Permissions: permissions, + Permissions: permissions.String(), SchemeManaged: role.SchemeManaged, BuiltIn: role.BuiltIn, } diff --git a/server/channels/store/sqlstore/store.go b/server/channels/store/sqlstore/store.go index 4701a0259f7..093e5b5313e 100644 --- a/server/channels/store/sqlstore/store.go +++ b/server/channels/store/sqlstore/store.go @@ -940,15 +940,15 @@ func (ss *SqlStore) hasLicense() bool { func convertMySQLFullTextColumnsToPostgres(columnNames string) string { columns := strings.Split(columnNames, ", ") - concatenatedColumnNames := "" + var concatenatedColumnNames strings.Builder for i, c := range columns { - concatenatedColumnNames += c + concatenatedColumnNames.WriteString(c) if i < len(columns)-1 { - concatenatedColumnNames += " || ' ' || " + concatenatedColumnNames.WriteString(" || ' ' || ") } } - return concatenatedColumnNames + return concatenatedColumnNames.String() } // IsDuplicate checks whether an error is a duplicate key error, which comes when processes are competing on creating the same diff --git a/server/cmd/mattermost/main_test.go b/server/cmd/mattermost/main_test.go index 8ec8a0bea4a..e6fa87e1e12 100644 --- a/server/cmd/mattermost/main_test.go +++ b/server/cmd/mattermost/main_test.go @@ -2,7 +2,6 @@ // See LICENSE.txt for license information. //go:build maincoverage -// +build maincoverage package main diff --git a/server/cmd/mmctl/commands/enterprise.go b/server/cmd/mmctl/commands/enterprise.go index 1fbe5bbdaa4..af334a4b706 100644 --- a/server/cmd/mmctl/commands/enterprise.go +++ b/server/cmd/mmctl/commands/enterprise.go @@ -2,7 +2,6 @@ // See LICENSE.txt for license information. //go:build enterprise -// +build enterprise // This file is needed to ensure the enterprise code get complied // when running tests. See https://mattermost.atlassian.net/browse/MM-54929 diff --git a/server/cmd/mmctl/commands/main_test.go b/server/cmd/mmctl/commands/main_test.go index 16a8d667342..8657d44cdca 100644 --- a/server/cmd/mmctl/commands/main_test.go +++ b/server/cmd/mmctl/commands/main_test.go @@ -2,7 +2,6 @@ // See LICENSE.txt for license information. //go:build e2e -// +build e2e package commands diff --git a/server/cmd/mmctl/commands/mmctl_e2e_test.go b/server/cmd/mmctl/commands/mmctl_e2e_test.go index affb5d6bf06..ae962abedf5 100644 --- a/server/cmd/mmctl/commands/mmctl_e2e_test.go +++ b/server/cmd/mmctl/commands/mmctl_e2e_test.go @@ -2,7 +2,6 @@ // See LICENSE.txt for license information. //go:build e2e -// +build e2e package commands diff --git a/server/cmd/mmctl/commands/mmctl_unit_test.go b/server/cmd/mmctl/commands/mmctl_unit_test.go index 949934d4b72..f3d0b4cbc95 100644 --- a/server/cmd/mmctl/commands/mmctl_unit_test.go +++ b/server/cmd/mmctl/commands/mmctl_unit_test.go @@ -2,7 +2,6 @@ // See LICENSE.txt for license information. //go:build unit -// +build unit package commands diff --git a/server/cmd/mmctl/commands/utils_unix.go b/server/cmd/mmctl/commands/utils_unix.go index dbcce64b542..12f0d49b299 100644 --- a/server/cmd/mmctl/commands/utils_unix.go +++ b/server/cmd/mmctl/commands/utils_unix.go @@ -2,7 +2,6 @@ // See LICENSE.txt for license information. //go:build linux || darwin || freebsd -// +build linux darwin freebsd package commands diff --git a/server/config/environment.go b/server/config/environment.go index 576769cd868..55007223643 100644 --- a/server/config/environment.go +++ b/server/config/environment.go @@ -73,7 +73,7 @@ func applyEnvKey(key, value string, rValueSubject reflect.Value) { rFieldValue.Set(reflect.ValueOf(intVal)) } case reflect.Slice: - if rFieldValue.Type() == reflect.TypeOf(json.RawMessage{}) { + if rFieldValue.Type() == reflect.TypeFor[json.RawMessage]() { rFieldValue.Set(reflect.ValueOf([]byte(value))) break } @@ -100,7 +100,7 @@ func applyEnvironmentMap(inputConfig *model.Config, env map[string]string) *mode // generateEnvironmentMap creates a map[string]any containing true at the leaves mirroring the // configuration structure so the client can know which env variables are overridden func generateEnvironmentMap(env map[string]string, filter func(reflect.StructField) bool) map[string]any { - rType := reflect.TypeOf(model.Config{}) + rType := reflect.TypeFor[model.Config]() return generateEnvironmentMapWithBaseKey(env, rType, "MM", filter) } diff --git a/server/enterprise/elasticsearch/elasticsearch/elasticsearch.go b/server/enterprise/elasticsearch/elasticsearch/elasticsearch.go index e35c4003776..bcaf285dcc3 100644 --- a/server/enterprise/elasticsearch/elasticsearch/elasticsearch.go +++ b/server/enterprise/elasticsearch/elasticsearch/elasticsearch.go @@ -1350,7 +1350,8 @@ func (es *ElasticsearchInterfaceImpl) PurgeIndexes(rctx request.CTX) *model.AppE } indexPrefix := *es.Platform.Config().ElasticsearchSettings.IndexPrefix - indexesToDelete := indexPrefix + "*" + var indexesToDelete strings.Builder + indexesToDelete.WriteString(indexPrefix + "*") if ignorePurgeIndexes := *es.Platform.Config().ElasticsearchSettings.IgnoredPurgeIndexes; ignorePurgeIndexes != "" { // we are checking if provided indexes exist. If an index doesn't exist, @@ -1365,14 +1366,14 @@ func (es *ElasticsearchInterfaceImpl) PurgeIndexes(rctx request.CTX) *model.AppE rctx.Logger().Warn("Elasticsearch index get error", mlog.String("index", ignorePurgeIndex), mlog.Err(err)) continue } - indexesToDelete += ",-" + strings.TrimSpace(ignorePurgeIndex) + indexesToDelete.WriteString(",-" + strings.TrimSpace(ignorePurgeIndex)) } } ctx, cancel := context.WithTimeout(context.Background(), time.Duration(*es.Platform.Config().ElasticsearchSettings.RequestTimeoutSeconds)*time.Second) defer cancel() - _, err := es.client.Indices.Delete(indexesToDelete).Do(ctx) + _, err := es.client.Indices.Delete(indexesToDelete.String()).Do(ctx) if err != nil { rctx.Logger().Error("Elastic Search PurgeIndexes Error", mlog.Err(err)) return model.NewAppError("Elasticsearch.PurgeIndexes", "ent.elasticsearch.purge_index.delete_failed", nil, "", http.StatusInternalServerError).Wrap(err) diff --git a/server/platform/services/upgrader/upgrader.go b/server/platform/services/upgrader/upgrader.go index d14c8ee5636..358e24d4045 100644 --- a/server/platform/services/upgrader/upgrader.go +++ b/server/platform/services/upgrader/upgrader.go @@ -2,7 +2,6 @@ // See LICENSE.txt for license information. //go:build !linux -// +build !linux package upgrader diff --git a/server/public/model/access_policy_test.go b/server/public/model/access_policy_test.go index 06827f41776..eb6f3e2bb84 100644 --- a/server/public/model/access_policy_test.go +++ b/server/public/model/access_policy_test.go @@ -4,6 +4,7 @@ package model import ( + "strings" "testing" "github.com/stretchr/testify/require" @@ -56,15 +57,15 @@ func TestAccessPolicyVersionV0_1(t *testing.T) { }) t.Run("parent policy with too long name", func(t *testing.T) { - longName := "" + var longName strings.Builder for i := 0; i <= MaxPolicyNameLength; i++ { - longName += "a" + longName.WriteString("a") } policy := &AccessControlPolicy{ ID: NewId(), Type: AccessControlPolicyTypeParent, - Name: longName, + Name: longName.String(), Revision: 1, Version: AccessControlPolicyVersionV0_1, Rules: []AccessControlPolicyRule{{Actions: []string{"read"}, Expression: "user.role == 'admin'"}}, diff --git a/server/public/model/channel_count.go b/server/public/model/channel_count.go index f7c50fdfb36..4da39083465 100644 --- a/server/public/model/channel_count.go +++ b/server/public/model/channel_count.go @@ -8,6 +8,7 @@ import ( "fmt" "sort" "strconv" + "strings" ) type ChannelCounts struct { @@ -24,12 +25,12 @@ func (o *ChannelCounts) Etag() string { } sort.Strings(ids) - str := "" + var str strings.Builder for _, id := range ids { - str += id + strconv.FormatInt(o.Counts[id], 10) + str.WriteString(id + strconv.FormatInt(o.Counts[id], 10)) } - md5Counts := fmt.Sprintf("%x", md5.Sum([]byte(str))) + md5Counts := fmt.Sprintf("%x", md5.Sum([]byte(str.String()))) var update int64 for _, u := range o.UpdateTimes { diff --git a/server/public/model/feature_flags.go b/server/public/model/feature_flags.go index 08ea3b34934..a537cb64670 100644 --- a/server/public/model/feature_flags.go +++ b/server/public/model/feature_flags.go @@ -130,7 +130,7 @@ func (f *FeatureFlags) SetDefaults() { // Supports boolean and string feature flags. func (f *FeatureFlags) ToMap() map[string]string { refStructVal := reflect.ValueOf(*f) - refStructType := reflect.TypeOf(*f) + refStructType := reflect.TypeFor[FeatureFlags]() ret := make(map[string]string) for i := 0; i < refStructVal.NumField(); i++ { refFieldVal := refStructVal.Field(i) diff --git a/server/public/model/permission.go b/server/public/model/permission.go index e973222a679..815c08c3fd5 100644 --- a/server/public/model/permission.go +++ b/server/public/model/permission.go @@ -5,6 +5,7 @@ package model import ( "net/http" + "strings" ) const ( @@ -2658,12 +2659,13 @@ func MakePermissionError(s *Session, permissions []*Permission) *AppError { } func MakePermissionErrorForUser(userId string, permissions []*Permission) *AppError { - permissionsStr := "permission=" + var permissionsStr strings.Builder + permissionsStr.WriteString("permission=") for i, permission := range permissions { - permissionsStr += permission.Id + permissionsStr.WriteString(permission.Id) if i != len(permissions)-1 { - permissionsStr += "," + permissionsStr.WriteString(",") } } - return NewAppError("Permissions", "api.context.permissions.app_error", nil, "userId="+userId+", "+permissionsStr, http.StatusForbidden) + return NewAppError("Permissions", "api.context.permissions.app_error", nil, "userId="+userId+", "+permissionsStr.String(), http.StatusForbidden) } diff --git a/server/public/model/utils.go b/server/public/model/utils.go index e9b2e7f89de..7d2e42608cc 100644 --- a/server/public/model/utils.go +++ b/server/public/model/utils.go @@ -692,13 +692,14 @@ func IsValidAlphaNumHyphenUnderscorePlus(s string) bool { } func Etag(parts ...any) string { - etag := CurrentVersion + var etag strings.Builder + etag.WriteString(CurrentVersion) for _, part := range parts { - etag += fmt.Sprintf(".%v", part) + etag.WriteString(fmt.Sprintf(".%v", part)) } - return etag + return etag.String() } var ( @@ -712,7 +713,7 @@ func ParseHashtags(text string) (string, string) { words := strings.Fields(text) hashtagString := "" - plainString := "" + var plainString strings.Builder for _, word := range words { // trim off surrounding punctuation word = puncStart.ReplaceAllString(word, "") @@ -724,7 +725,7 @@ func ParseHashtags(text string) (string, string) { if validHashtag.MatchString(word) { hashtagString += " " + word } else { - plainString += " " + word + plainString.WriteString(" " + word) } } @@ -738,7 +739,7 @@ func ParseHashtags(text string) (string, string) { } } - return strings.TrimSpace(hashtagString), strings.TrimSpace(plainString) + return strings.TrimSpace(hashtagString), strings.TrimSpace(plainString.String()) } func ClearMentionTags(post string) string { diff --git a/server/public/plugin/client_rpc.go b/server/public/plugin/client_rpc.go index 2e288d89ddf..dec858eb2dd 100644 --- a/server/public/plugin/client_rpc.go +++ b/server/public/plugin/client_rpc.go @@ -189,9 +189,9 @@ func (g *hooksRPCClient) Implemented() (impl []string, err error) { // Implemented replies with the names of the hooks that are implemented. func (s *hooksRPCServer) Implemented(args struct{}, reply *[]string) error { - ifaceType := reflect.TypeOf((*Hooks)(nil)).Elem() + ifaceType := reflect.TypeFor[Hooks]() implType := reflect.TypeOf(s.impl) - selfType := reflect.TypeOf(s) + selfType := reflect.TypeFor[*hooksRPCServer]() var methods []string for i := 0; i < ifaceType.NumMethod(); i++ { method := ifaceType.Method(i) diff --git a/server/public/utils/json_test.go b/server/public/utils/json_test.go index 41cec656016..826070b7925 100644 --- a/server/public/utils/json_test.go +++ b/server/public/utils/json_test.go @@ -52,7 +52,7 @@ func TestHumanizeJsonError(t *testing.T) { []byte("line 1\nline 2\nline 3"), &json.UnmarshalTypeError{ Value: "bool", - Type: reflect.TypeOf(testType{}), + Type: reflect.TypeFor[testType](), Offset: 17, Struct: "struct", Field: "field",