sigs.k8s.io/prow@v0.0.0-20240503223140-c5e374dc7eb1/pkg/plugins/golint/suggestion/golint_suggestion.go (about) 1 /* 2 Copyright 2018 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package suggestion 18 19 import ( 20 "fmt" 21 "regexp" 22 "strings" 23 24 "github.com/sirupsen/logrus" 25 "golang.org/x/lint" 26 "golang.org/x/text/cases" 27 "golang.org/x/text/language" 28 ) 29 30 var ( 31 lintErrorfRegex = regexp.MustCompile(`should replace errors\.New\(fmt\.Sprintf\(\.\.\.\)\) with fmt\.Errorf\(\.\.\.\)`) 32 lintNamesUnderscoreRegex = regexp.MustCompile("don't use underscores in Go names; (.*) should be (.*)") 33 lintNamesAllCapsRegex = regexp.MustCompile("don't use ALL_CAPS in Go names; use CamelCase") 34 lintStutterRegex = regexp.MustCompile(`name will be used as [^.]+\.(.*) by other packages, and that stutters; consider calling this (.*)`) 35 lintRangesRegex = regexp.MustCompile(`should omit (?:2nd )?values? from range; this loop is equivalent to \x60(for .*) ...\x60`) 36 lintVarDeclRegex = regexp.MustCompile("should (?:omit type|drop) (.*) from declaration of (?:.*); (?:it will be inferred from the right-hand side|it is the zero value)") 37 ) 38 39 var lintHandlersMap = map[*regexp.Regexp]func(lint.Problem, []string) string{ 40 lintErrorfRegex: fixErrorf, 41 lintNamesUnderscoreRegex: fixNameUnderscore, 42 lintNamesAllCapsRegex: fixNameAllCaps, 43 lintStutterRegex: fixStutter, 44 lintRangesRegex: fixRanges, 45 lintVarDeclRegex: fixVarDecl, 46 } 47 48 // SuggestCodeChange returns code suggestions for a given lint.Problem 49 // Returns empty string if no suggestion can be given 50 func SuggestCodeChange(p lint.Problem) string { 51 var suggestion string 52 for regex, handler := range lintHandlersMap { 53 matches := regex.FindStringSubmatch(p.Text) 54 suggestion = handler(p, matches) 55 if suggestion != "" && suggestion != p.LineText { 56 return formatSuggestion(suggestion) 57 } 58 } 59 return "" 60 } 61 62 func fixNameUnderscore(p lint.Problem, matches []string) string { 63 if len(matches) < 3 { 64 return "" 65 } 66 underscoreRe := regexp.MustCompile(`[A-Za-z]+(_[A-Za-z0-9]+)+`) 67 namesWithUnderscore := underscoreRe.FindStringSubmatch(matches[1]) 68 suggestion := strings.Replace(p.LineText, namesWithUnderscore[0], matches[2], -1) 69 return suggestion 70 } 71 72 func fixNameAllCaps(p lint.Problem, matches []string) string { 73 result := "" 74 if len(matches) == 0 { 75 return result 76 } 77 // Identify all caps names 78 reAllCaps := regexp.MustCompile(`[A-Z]+(_[A-Z0-9]+)*`) 79 result = reAllCaps.ReplaceAllStringFunc(p.LineText, func(oldName string) string { 80 return strings.Replace(cases.Title(language.English).String(strings.Replace(strings.ToLower(oldName), "_", " ", -1)), " ", "", -1) 81 }) 82 return result 83 } 84 85 func fixStutter(p lint.Problem, matches []string) string { 86 if len(matches) < 3 { 87 return "" 88 } 89 suggestion := strings.Replace(p.LineText, matches[1], matches[2], -1) 90 return suggestion 91 } 92 93 func fixErrorf(p lint.Problem, matches []string) string { 94 if len(matches) != 1 { 95 return "" 96 } 97 parameterText := "" 98 count := 0 99 parameterTextBeginning := "errors.New(fmt.Sprintf(" 100 parameterTextBeginningInd := strings.Index(p.LineText, parameterTextBeginning) 101 if parameterTextBeginningInd < 0 { 102 logrus.Infof("Cannot find \"errors.New(fmt.Sprintf(\" in problem line text %s", p.LineText) 103 return "" 104 } 105 for _, char := range p.LineText[parameterTextBeginningInd+len(parameterTextBeginning):] { 106 if char == '(' { 107 count++ 108 } 109 if char == ')' { 110 count-- 111 if count < 0 { 112 break 113 } 114 } 115 parameterText += string(char) 116 } 117 if count > 0 { 118 return "" 119 } 120 toReplace := fmt.Sprintf("errors.New(fmt.Sprintf(%s))", parameterText) 121 replacement := fmt.Sprintf("fmt.Errorf(%s)", parameterText) 122 suggestion := strings.Replace(p.LineText, toReplace, replacement, -1) 123 return suggestion 124 } 125 126 func fixRanges(p lint.Problem, matches []string) string { 127 if len(matches) != 2 { 128 return "" 129 } 130 reValuesToOmit := regexp.MustCompile(`for (([ [A-Za-z0-9]+[,]?]?(, _ :?= ))|(_ = )|(_, _ = ))range`) 131 valuesToOmit := reValuesToOmit.FindStringSubmatch(p.LineText) 132 if len(valuesToOmit) == 0 { 133 return "" 134 } 135 suggestion := strings.Replace(p.LineText, valuesToOmit[0], matches[1], -1) 136 return suggestion 137 } 138 139 func fixVarDecl(p lint.Problem, matches []string) string { 140 if len(matches) != 2 { 141 return "" 142 } 143 suggestion := strings.Replace(p.LineText, " "+matches[1], "", -1) 144 return suggestion 145 } 146 147 func formatSuggestion(s string) string { 148 return "```suggestion\n" + s + "```\n" 149 }