st1013.go (4303B)
1 package st1013 2 3 import ( 4 "fmt" 5 "go/ast" 6 "go/constant" 7 "strconv" 8 9 "honnef.co/go/tools/analysis/code" 10 "honnef.co/go/tools/analysis/edit" 11 "honnef.co/go/tools/analysis/facts/generated" 12 "honnef.co/go/tools/analysis/lint" 13 "honnef.co/go/tools/analysis/report" 14 "honnef.co/go/tools/config" 15 "honnef.co/go/tools/pattern" 16 17 "golang.org/x/tools/go/analysis" 18 ) 19 20 var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ 21 Analyzer: &analysis.Analyzer{ 22 Name: "ST1013", 23 Run: run, 24 Requires: append([]*analysis.Analyzer{generated.Analyzer, config.Analyzer}, code.RequiredAnalyzers...), 25 }, 26 Doc: &lint.RawDocumentation{ 27 Title: `Should use constants for HTTP error codes, not magic numbers`, 28 Text: `HTTP has a tremendous number of status codes. While some of those are 29 well known (200, 400, 404, 500), most of them are not. The \'net/http\' 30 package provides constants for all status codes that are part of the 31 various specifications. It is recommended to use these constants 32 instead of hard-coding magic numbers, to vastly improve the 33 readability of your code.`, 34 Since: "2019.1", 35 Options: []string{"http_status_code_whitelist"}, 36 MergeIf: lint.MergeIfAny, 37 }, 38 }) 39 40 var Analyzer = SCAnalyzer.Analyzer 41 42 var query = pattern.MustParse(` 43 (CallExpr 44 (Symbol 45 name@(Or 46 "net/http.Error" 47 "net/http.Redirect" 48 "net/http.StatusText" 49 "net/http.RedirectHandler")) 50 args)`) 51 52 func run(pass *analysis.Pass) (any, error) { 53 whitelist := map[string]bool{} 54 for _, code := range config.For(pass).HTTPStatusCodeWhitelist { 55 whitelist[code] = true 56 } 57 for _, m := range code.Matches(pass, query) { 58 var arg int 59 switch m.State["name"].(string) { 60 case "net/http.Error": 61 arg = 2 62 case "net/http.Redirect": 63 arg = 3 64 case "net/http.StatusText": 65 arg = 0 66 case "net/http.RedirectHandler": 67 arg = 1 68 default: 69 continue 70 } 71 args := m.State["args"].([]ast.Expr) 72 if arg >= len(args) { 73 continue 74 } 75 tv, ok := code.IntegerLiteral(pass, args[arg]) 76 if !ok { 77 continue 78 } 79 n, ok := constant.Int64Val(tv.Value) 80 if !ok { 81 continue 82 } 83 if whitelist[strconv.FormatInt(n, 10)] { 84 continue 85 } 86 87 s, ok := httpStatusCodes[n] 88 if !ok { 89 continue 90 } 91 lit := args[arg] 92 report.Report(pass, lit, fmt.Sprintf("should use constant http.%s instead of numeric literal %d", s, n), 93 report.FilterGenerated(), 94 report.Fixes(edit.Fix(fmt.Sprintf("Use http.%s instead of %d", s, n), edit.ReplaceWithString(lit, "http."+s)))) 95 } 96 return nil, nil 97 } 98 99 var httpStatusCodes = map[int64]string{ 100 100: "StatusContinue", 101 101: "StatusSwitchingProtocols", 102 102: "StatusProcessing", 103 200: "StatusOK", 104 201: "StatusCreated", 105 202: "StatusAccepted", 106 203: "StatusNonAuthoritativeInfo", 107 204: "StatusNoContent", 108 205: "StatusResetContent", 109 206: "StatusPartialContent", 110 207: "StatusMultiStatus", 111 208: "StatusAlreadyReported", 112 226: "StatusIMUsed", 113 300: "StatusMultipleChoices", 114 301: "StatusMovedPermanently", 115 302: "StatusFound", 116 303: "StatusSeeOther", 117 304: "StatusNotModified", 118 305: "StatusUseProxy", 119 307: "StatusTemporaryRedirect", 120 308: "StatusPermanentRedirect", 121 400: "StatusBadRequest", 122 401: "StatusUnauthorized", 123 402: "StatusPaymentRequired", 124 403: "StatusForbidden", 125 404: "StatusNotFound", 126 405: "StatusMethodNotAllowed", 127 406: "StatusNotAcceptable", 128 407: "StatusProxyAuthRequired", 129 408: "StatusRequestTimeout", 130 409: "StatusConflict", 131 410: "StatusGone", 132 411: "StatusLengthRequired", 133 412: "StatusPreconditionFailed", 134 413: "StatusRequestEntityTooLarge", 135 414: "StatusRequestURITooLong", 136 415: "StatusUnsupportedMediaType", 137 416: "StatusRequestedRangeNotSatisfiable", 138 417: "StatusExpectationFailed", 139 418: "StatusTeapot", 140 422: "StatusUnprocessableEntity", 141 423: "StatusLocked", 142 424: "StatusFailedDependency", 143 426: "StatusUpgradeRequired", 144 428: "StatusPreconditionRequired", 145 429: "StatusTooManyRequests", 146 431: "StatusRequestHeaderFieldsTooLarge", 147 451: "StatusUnavailableForLegalReasons", 148 500: "StatusInternalServerError", 149 501: "StatusNotImplemented", 150 502: "StatusBadGateway", 151 503: "StatusServiceUnavailable", 152 504: "StatusGatewayTimeout", 153 505: "StatusHTTPVersionNotSupported", 154 506: "StatusVariantAlsoNegotiates", 155 507: "StatusInsufficientStorage", 156 508: "StatusLoopDetected", 157 510: "StatusNotExtended", 158 511: "StatusNetworkAuthenticationRequired", 159 }