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  }