github.com/munnerz/test-infra@v0.0.0-20190108210205-ce3d181dc989/prow/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 "github.com/golang/lint" 22 "github.com/sirupsen/logrus" 23 "regexp" 24 "strings" 25 ) 26 27 var ( 28 lintErrorfRegex = regexp.MustCompile(`should replace errors\.New\(fmt\.Sprintf\(\.\.\.\)\) with fmt\.Errorf\(\.\.\.\)`) 29 lintNamesUnderscoreRegex = regexp.MustCompile("don't use underscores in Go names; (.*) should be (.*)") 30 lintNamesAllCapsRegex = regexp.MustCompile("don't use ALL_CAPS in Go names; use CamelCase") 31 lintStutterRegex = regexp.MustCompile("name will be used as [^.]+\\.(.*) by other packages, and that stutters; consider calling this (.*)") 32 lintRangesRegex = regexp.MustCompile("should omit (?:2nd )?values? from range; this loop is equivalent to \\x60(for .*) ...\\x60") 33 ) 34 35 var lintHandlers = [...]func(lint.Problem) string{ 36 fixErrorf, 37 fixNameUnderscore, 38 fixNameAllCaps, 39 fixStutter, 40 fixRanges, 41 } 42 43 // SuggestCodeChange returns code suggestions for a given lint.Problem 44 // Returns empty string if no suggestion can be given 45 func SuggestCodeChange(p lint.Problem) string { 46 var suggestion = "" 47 for _, handler := range lintHandlers { 48 suggestion = handler(p) 49 if suggestion != "" { 50 return formatSuggestion(suggestion) 51 } 52 } 53 return "" 54 } 55 56 func fixNameUnderscore(p lint.Problem) string { 57 matches := lintNamesUnderscoreRegex.FindStringSubmatch(p.Text) 58 if len(matches) < 3 { 59 return "" 60 } 61 underscoreRe := regexp.MustCompile(`[A-Za-z]+(_[A-Za-z0-9]+)+`) 62 namesWithUnderscore := underscoreRe.FindStringSubmatch(matches[1]) 63 suggestion := strings.Replace(p.LineText, namesWithUnderscore[0], matches[2], -1) 64 if suggestion == p.LineText { 65 return "" 66 } 67 return suggestion 68 } 69 70 func fixNameAllCaps(p lint.Problem) string { 71 result := "" 72 matches := lintNamesAllCapsRegex.FindStringSubmatch(p.Text) 73 if len(matches) == 0 { 74 return result 75 } 76 // Identify all caps names 77 reAllCaps := regexp.MustCompile(`[A-Z]+(_[A-Z0-9]+)*`) 78 result = reAllCaps.ReplaceAllStringFunc(p.LineText, func(oldName string) string { 79 return strings.Replace(strings.Title(strings.Replace(strings.ToLower(oldName), "_", " ", -1)), " ", "", -1) 80 }) 81 if result == p.LineText { 82 return "" 83 } 84 return result 85 } 86 87 func fixStutter(p lint.Problem) string { 88 matches := lintStutterRegex.FindStringSubmatch(p.Text) 89 if len(matches) < 3 { 90 return "" 91 } 92 suggestion := strings.Replace(p.LineText, matches[1], matches[2], -1) 93 if suggestion == p.LineText { 94 return "" 95 } 96 return suggestion 97 } 98 99 func fixErrorf(p lint.Problem) string { 100 matches := lintErrorfRegex.FindStringSubmatch(p.Text) 101 if len(matches) != 1 { 102 return "" 103 } 104 parameterText := "" 105 count := 0 106 parameterTextBeginning := "errors.New(fmt.Sprintf(" 107 parameterTextBeginningInd := strings.Index(p.LineText, parameterTextBeginning) 108 if parameterTextBeginningInd < 0 { 109 logrus.Infof("Cannot find \"errors.New(fmt.Sprintf(\" in problem line text %s", p.LineText) 110 return "" 111 } 112 for _, char := range p.LineText[parameterTextBeginningInd+len(parameterTextBeginning):] { 113 if char == '(' { 114 count++ 115 } 116 if char == ')' { 117 count-- 118 if count < 0 { 119 break 120 } 121 } 122 parameterText += string(char) 123 } 124 if count > 0 { 125 return "" 126 } 127 toReplace := fmt.Sprintf("errors.New(fmt.Sprintf(%s))", parameterText) 128 replacement := fmt.Sprintf("fmt.Errorf(%s)", parameterText) 129 suggestion := strings.Replace(p.LineText, toReplace, replacement, -1) 130 if suggestion == p.LineText { 131 return "" 132 } 133 return suggestion 134 } 135 136 func fixRanges(p lint.Problem) string { 137 matches := lintRangesRegex.FindStringSubmatch(p.Text) 138 if len(matches) != 2 { 139 return "" 140 } 141 reValuesToOmit := regexp.MustCompile(`for (([ [A-Za-z0-9]+[,]?]?(, _ :?= ))|(_ = )|(_, _ = ))range`) 142 valuesToOmit := reValuesToOmit.FindStringSubmatch(p.LineText) 143 if len(valuesToOmit) == 0 { 144 return "" 145 } 146 suggestion := strings.Replace(p.LineText, valuesToOmit[0], matches[1], -1) 147 if suggestion == p.LineText { 148 return "" 149 } 150 return suggestion 151 } 152 153 func formatSuggestion(s string) string { 154 return "```suggestion\n" + s + "```\n" 155 }