github.com/haalcala/mattermost-server-change-repo@v0.0.0-20210713015153-16753fbeee5f/plugin/checker/check_helpers.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See LICENSE.txt for license information. 3 4 package main 5 6 import ( 7 "fmt" 8 "go/ast" 9 "go/token" 10 "go/types" 11 12 "github.com/pkg/errors" 13 14 "github.com/mattermost/mattermost-server/v5/plugin/checker/internal/asthelpers" 15 "github.com/mattermost/mattermost-server/v5/plugin/checker/internal/version" 16 ) 17 18 func checkHelpersVersionComments(pkgPath string) (result, error) { 19 pkg, err := asthelpers.GetPackage(pkgPath) 20 if err != nil { 21 return result{}, err 22 } 23 24 api, apiIdent, err := asthelpers.FindInterfaceWithIdent("API", pkg.Syntax) 25 if err != nil { 26 return result{}, err 27 } 28 29 apiObj := pkg.TypesInfo.ObjectOf(apiIdent) 30 if apiObj == nil { 31 return result{}, errors.New("could not find type object for API interface") 32 } 33 34 helpers, err := asthelpers.FindInterface("Helpers", pkg.Syntax) 35 if err != nil { 36 return result{}, err 37 } 38 39 apiVersions := mapMinimumVersionsByMethodName(api.Methods.List) 40 41 helpersPositions := mapPositionsByMethodName(helpers.Methods.List) 42 helpersVersions := mapMinimumVersionsByMethodName(helpers.Methods.List) 43 44 implMethods := asthelpers.FindReceiverMethods("HelpersImpl", pkg.Syntax) 45 implVersions := mapEffectiveVersionByMethod(pkg.TypesInfo, apiObj.Type(), apiVersions, implMethods) 46 47 return validateMethods(pkg.Fset, helpersPositions, helpersVersions, implVersions), nil 48 } 49 50 func validateMethods( 51 fset *token.FileSet, 52 helpersPositions map[string]token.Pos, 53 helpersVersions map[string]version.V, 54 implVersions map[string]version.V, 55 ) result { 56 var res result 57 58 for name, helperVer := range helpersVersions { 59 pos := helpersPositions[name] 60 61 implVer, ok := implVersions[name] 62 if !ok { 63 res.Errors = append(res.Errors, renderWithFilePosition( 64 fset, 65 pos, 66 fmt.Sprintf("missing implementation for method %s", name)), 67 ) 68 continue 69 } 70 71 if helperVer == "" { 72 res.Errors = append(res.Errors, renderWithFilePosition( 73 fset, 74 pos, 75 fmt.Sprintf("missing a minimum server version comment on method %s", name)), 76 ) 77 continue 78 } 79 80 if helperVer == implVer { 81 continue 82 } 83 84 if helperVer.LessThan(implVer) { 85 res.Errors = append(res.Errors, renderWithFilePosition( 86 fset, 87 pos, 88 fmt.Sprintf("documented minimum server version too low on method %s", name)), 89 ) 90 } else { 91 res.Warnings = append(res.Warnings, renderWithFilePosition( 92 fset, 93 pos, 94 fmt.Sprintf("documented minimum server version too high on method %s", name)), 95 ) 96 } 97 } 98 99 return res 100 } 101 102 func mapEffectiveVersionByMethod(info *types.Info, apiType types.Type, versions map[string]version.V, methods []*ast.FuncDecl) map[string]version.V { 103 effectiveVersions := map[string]version.V{} 104 for _, m := range methods { 105 apiMethodsCalled := asthelpers.FindMethodsCalledOnType(info, apiType, m) 106 effectiveVersions[m.Name.Name] = getEffectiveMinimumVersion(versions, apiMethodsCalled) 107 } 108 return effectiveVersions 109 } 110 111 func mapMinimumVersionsByMethodName(methods []*ast.Field) map[string]version.V { 112 versions := map[string]version.V{} 113 for _, m := range methods { 114 versions[m.Names[0].Name] = version.V(version.ExtractMinimumVersionFromComment(m.Doc.Text())) 115 } 116 return versions 117 } 118 119 func mapPositionsByMethodName(methods []*ast.Field) map[string]token.Pos { 120 pos := map[string]token.Pos{} 121 for _, m := range methods { 122 pos[m.Names[0].Name] = m.Pos() 123 } 124 return pos 125 } 126 127 func getEffectiveMinimumVersion(info map[string]version.V, methods []string) version.V { 128 var highest version.V 129 for _, m := range methods { 130 if current, ok := info[m]; ok { 131 if current.GreaterThanOrEqualTo(highest) { 132 highest = current 133 } 134 } 135 } 136 return highest 137 }