From fcc6d71be2c3ce7d9305c04fc2e87af554571bac Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Mon, 22 Feb 2021 20:37:25 +0100 Subject: go.mod: update golangci-lint to v1.37 --- vendor/github.com/esimonov/ifshort/LICENSE | 21 ++ .../esimonov/ifshort/pkg/analyzer/analyzer.go | 247 ++++++++++++++++++++ .../esimonov/ifshort/pkg/analyzer/occurrences.go | 259 +++++++++++++++++++++ 3 files changed, 527 insertions(+) create mode 100644 vendor/github.com/esimonov/ifshort/LICENSE create mode 100644 vendor/github.com/esimonov/ifshort/pkg/analyzer/analyzer.go create mode 100644 vendor/github.com/esimonov/ifshort/pkg/analyzer/occurrences.go (limited to 'vendor/github.com/esimonov') diff --git a/vendor/github.com/esimonov/ifshort/LICENSE b/vendor/github.com/esimonov/ifshort/LICENSE new file mode 100644 index 000000000..a04e339c0 --- /dev/null +++ b/vendor/github.com/esimonov/ifshort/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Eugene Simonov + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/esimonov/ifshort/pkg/analyzer/analyzer.go b/vendor/github.com/esimonov/ifshort/pkg/analyzer/analyzer.go new file mode 100644 index 000000000..df3d1211a --- /dev/null +++ b/vendor/github.com/esimonov/ifshort/pkg/analyzer/analyzer.go @@ -0,0 +1,247 @@ +package analyzer + +import ( + "go/ast" + "go/token" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" + "golang.org/x/tools/go/ast/inspector" +) + +var maxDeclChars, maxDeclLines int + +const ( + maxDeclLinesUsage = `maximum length of variable declaration measured in number of lines, after which the linter won't suggest using short syntax. +Has precedence over max-decl-chars.` + maxDeclCharsUsage = `maximum length of variable declaration measured in number of characters, after which the linter won't suggest using short syntax.` +) + +func init() { + Analyzer.Flags.IntVar(&maxDeclLines, "max-decl-lines", 1, maxDeclLinesUsage) + Analyzer.Flags.IntVar(&maxDeclChars, "max-decl-chars", 30, maxDeclCharsUsage) +} + +// Analyzer is an analysis.Analyzer instance for ifshort linter. +var Analyzer = &analysis.Analyzer{ + Name: "ifshort", + Doc: "Checks that your code uses short syntax for if-statements whenever possible.", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer}, +} + +func run(pass *analysis.Pass) (interface{}, error) { + inspector := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) + nodeFilter := []ast.Node{ + (*ast.FuncDecl)(nil), + } + + inspector.Preorder(nodeFilter, func(node ast.Node) { + fdecl := node.(*ast.FuncDecl) + + /*if fdecl.Name.Name != "notUsed_BinaryExpressionInIndex_OK" { + return + }*/ + + if fdecl == nil || fdecl.Body == nil { + return + } + + candidates := getNamedOccurrenceMap(fdecl, pass) + + for _, stmt := range fdecl.Body.List { + candidates.checkStatement(stmt, token.NoPos) + } + + for varName := range candidates { + for marker, occ := range candidates[varName] { + // If two or more vars with the same scope marker - skip them. + if candidates.isFoundByScopeMarker(marker) { + continue + } + + pass.Reportf(occ.declarationPos, + "variable '%s' is only used in the if-statement (%s); consider using short syntax", + varName, pass.Fset.Position(occ.ifStmtPos)) + } + } + }) + return nil, nil +} + +func (nom namedOccurrenceMap) checkStatement(stmt ast.Stmt, ifPos token.Pos) { + switch v := stmt.(type) { + case *ast.AssignStmt: + for _, el := range v.Rhs { + nom.checkExpression(el, ifPos) + } + if isAssign(v.Tok) { + for _, el := range v.Lhs { + nom.checkExpression(el, ifPos) + } + } + case *ast.DeferStmt: + for _, a := range v.Call.Args { + nom.checkExpression(a, ifPos) + } + case *ast.ExprStmt: + if callExpr, ok := v.X.(*ast.CallExpr); ok { + nom.checkExpression(callExpr, ifPos) + } + case *ast.ForStmt: + for _, el := range v.Body.List { + nom.checkStatement(el, ifPos) + } + + if bexpr, ok := v.Cond.(*ast.BinaryExpr); ok { + nom.checkExpression(bexpr.X, ifPos) + nom.checkExpression(bexpr.Y, ifPos) + } + + nom.checkStatement(v.Post, ifPos) + case *ast.GoStmt: + for _, a := range v.Call.Args { + nom.checkExpression(a, token.NoPos) + } + case *ast.IfStmt: + for _, el := range v.Body.List { + nom.checkStatement(el, v.If) + } + + switch cond := v.Cond.(type) { + case *ast.BinaryExpr: + nom.checkExpression(cond.X, v.If) + nom.checkExpression(cond.Y, v.If) + case *ast.CallExpr: + nom.checkExpression(cond, v.If) + } + + if init, ok := v.Init.(*ast.AssignStmt); ok { + for _, e := range init.Rhs { + nom.checkExpression(e, v.If) + } + } + case *ast.IncDecStmt: + nom.checkExpression(v.X, ifPos) + case *ast.RangeStmt: + nom.checkExpression(v.X, token.NoPos) + if v.Body != nil { + for _, e := range v.Body.List { + nom.checkStatement(e, ifPos) + } + } + case *ast.ReturnStmt: + for _, r := range v.Results { + nom.checkExpression(r, token.NoPos) + } + case *ast.SendStmt: + nom.checkExpression(v.Value, token.NoPos) + case *ast.SwitchStmt: + nom.checkExpression(v.Tag, token.NoPos) + + for _, el := range v.Body.List { + clauses, ok := el.(*ast.CaseClause) + if !ok { + continue + } + + for _, c := range clauses.List { + switch v := c.(type) { + case *ast.BinaryExpr: + nom.checkExpression(v.X, token.NoPos) + nom.checkExpression(v.Y, token.NoPos) + case *ast.Ident: + nom.checkExpression(v, token.NoPos) + } + } + + for _, c := range clauses.Body { + if est, ok := c.(*ast.ExprStmt); ok { + nom.checkExpression(est.X, token.NoPos) + } + + switch v := c.(type) { + case *ast.AssignStmt: + for _, el := range v.Rhs { + nom.checkExpression(el, token.NoPos) + } + case *ast.ExprStmt: + nom.checkExpression(v.X, token.NoPos) + } + } + } + } +} + +func (nom namedOccurrenceMap) checkExpression(candidate ast.Expr, ifPos token.Pos) { + switch v := candidate.(type) { + case *ast.BinaryExpr: + nom.checkExpression(v.X, ifPos) + case *ast.CallExpr: + for _, arg := range v.Args { + nom.checkExpression(arg, ifPos) + } + if fun, ok := v.Fun.(*ast.SelectorExpr); ok { + nom.checkExpression(fun.X, ifPos) + } + case *ast.CompositeLit: + for _, el := range v.Elts { + kv, ok := el.(*ast.KeyValueExpr) + if !ok { + continue + } + if ident, ok := kv.Key.(*ast.Ident); ok { + nom.checkExpression(ident, ifPos) + } + if ident, ok := kv.Value.(*ast.Ident); ok { + nom.checkExpression(ident, ifPos) + } + } + case *ast.FuncLit: + for _, el := range v.Body.List { + nom.checkStatement(el, ifPos) + } + case *ast.Ident: + if nom[v.Name].isEmponymousKey(ifPos) { + return + } + + scopeMarker1 := nom[v.Name].getScopeMarkerForPosition(v.Pos()) + + delete(nom[v.Name], scopeMarker1) + + for k := range nom { + for scopeMarker2 := range nom[k] { + if scopeMarker1 == scopeMarker2 { + delete(nom[k], scopeMarker2) + } + } + } + case *ast.IndexExpr: + nom.checkExpression(v.X, ifPos) + switch index := v.Index.(type) { + case *ast.BinaryExpr: + nom.checkExpression(index.X, ifPos) + case *ast.Ident: + nom.checkExpression(index, ifPos) + } + case *ast.SelectorExpr: + nom.checkExpression(v.X, ifPos) + case *ast.SliceExpr: + nom.checkExpression(v.High, ifPos) + nom.checkExpression(v.Low, ifPos) + nom.checkExpression(v.X, ifPos) + case *ast.TypeAssertExpr: + nom.checkExpression(v.X, ifPos) + case *ast.UnaryExpr: + nom.checkExpression(v.X, ifPos) + } +} + +func isAssign(tok token.Token) bool { + return (tok == token.ASSIGN || + tok == token.ADD_ASSIGN || tok == token.SUB_ASSIGN || + tok == token.MUL_ASSIGN || tok == token.QUO_ASSIGN || tok == token.REM_ASSIGN || + tok == token.AND_ASSIGN || tok == token.OR_ASSIGN || tok == token.XOR_ASSIGN || tok == token.AND_NOT_ASSIGN || + tok == token.SHL_ASSIGN || tok == token.SHR_ASSIGN) +} diff --git a/vendor/github.com/esimonov/ifshort/pkg/analyzer/occurrences.go b/vendor/github.com/esimonov/ifshort/pkg/analyzer/occurrences.go new file mode 100644 index 000000000..34224c93a --- /dev/null +++ b/vendor/github.com/esimonov/ifshort/pkg/analyzer/occurrences.go @@ -0,0 +1,259 @@ +package analyzer + +import ( + "go/ast" + "go/token" + "time" + + "golang.org/x/tools/go/analysis" +) + +// occurrence is a variable occurrence. +type occurrence struct { + declarationPos token.Pos + ifStmtPos token.Pos +} + +func (occ *occurrence) isComplete() bool { + return occ.ifStmtPos != token.NoPos && occ.declarationPos != token.NoPos +} + +// scopeMarkeredOccurences is a map of scope markers to variable occurrences. +type scopeMarkeredOccurences map[int64]occurrence + +func (smo scopeMarkeredOccurences) getGreatestMarker() int64 { + var maxScopeMarker int64 + + for marker := range smo { + if marker > maxScopeMarker { + maxScopeMarker = marker + } + } + return maxScopeMarker +} + +// find scope marker of the greatest token.Pos that is smaller than provided. +func (smo scopeMarkeredOccurences) getScopeMarkerForPosition(pos token.Pos) int64 { + var m int64 + var foundPos token.Pos + + for marker, occ := range smo { + if occ.declarationPos < pos && occ.declarationPos >= foundPos { + m = marker + foundPos = occ.declarationPos + } + } + return m +} + +func (smo scopeMarkeredOccurences) isEmponymousKey(pos token.Pos) bool { + if pos == token.NoPos { + return false + } + + for _, occ := range smo { + if occ.ifStmtPos == pos { + return true + } + } + return false +} + +// namedOccurrenceMap is a map of variable names to scopeMarkeredOccurences. +type namedOccurrenceMap map[string]scopeMarkeredOccurences + +func getNamedOccurrenceMap(fdecl *ast.FuncDecl, pass *analysis.Pass) namedOccurrenceMap { + nom := namedOccurrenceMap(map[string]scopeMarkeredOccurences{}) + + if fdecl == nil || fdecl.Body == nil { + return nom + } + + for _, stmt := range fdecl.Body.List { + switch v := stmt.(type) { + case *ast.AssignStmt: + nom.addFromAssignment(pass, v) + case *ast.IfStmt: + nom.addFromCondition(v) + nom.addFromIfClause(v) + nom.addFromElseClause(v) + } + } + + candidates := namedOccurrenceMap(map[string]scopeMarkeredOccurences{}) + + for varName, markeredOccs := range nom { + for marker, occ := range markeredOccs { + if !occ.isComplete() && !nom.isFoundByScopeMarker(marker) { + continue + } + if _, ok := candidates[varName]; !ok { + candidates[varName] = scopeMarkeredOccurences{ + marker: occ, + } + } else { + candidates[varName][marker] = occ + } + } + } + return candidates +} + +func (nom namedOccurrenceMap) isFoundByScopeMarker(scopeMarker int64) bool { + var i int + + for _, markeredOccs := range nom { + for marker := range markeredOccs { + if marker == scopeMarker { + i++ + } + } + } + return i >= 2 +} + +func (nom namedOccurrenceMap) addFromAssignment(pass *analysis.Pass, assignment *ast.AssignStmt) { + if assignment.Tok != token.DEFINE { + return + } + + scopeMarker := time.Now().UnixNano() + + for i, el := range assignment.Lhs { + ident, ok := el.(*ast.Ident) + if !ok { + continue + } + + if ident.Name == "_" || ident.Obj == nil || isUnshortenableAssignment(ident.Obj.Decl) { + continue + } + + if markeredOccs, ok := nom[ident.Name]; ok { + markeredOccs[scopeMarker] = occurrence{ + declarationPos: ident.Pos(), + } + nom[ident.Name] = markeredOccs + } else { + newOcc := occurrence{} + if areFlagSettingsSatisfied(pass, assignment, i) { + newOcc.declarationPos = ident.Pos() + } + nom[ident.Name] = scopeMarkeredOccurences{scopeMarker: newOcc} + } + } +} + +func isUnshortenableAssignment(decl interface{}) bool { + assign, ok := decl.(*ast.AssignStmt) + if !ok { + return false + } + + for _, el := range assign.Rhs { + u, ok := el.(*ast.UnaryExpr) + if !ok { + continue + } + + if u.Op == token.AND { + if _, ok := u.X.(*ast.CompositeLit); ok { + return true + } + } + } + return false +} + +func areFlagSettingsSatisfied(pass *analysis.Pass, assignment *ast.AssignStmt, i int) bool { + lh := assignment.Lhs[i] + rh := assignment.Rhs[len(assignment.Rhs)-1] + + if len(assignment.Rhs) == len(assignment.Lhs) { + rh = assignment.Rhs[i] + } + + if pass.Fset.Position(rh.End()).Line-pass.Fset.Position(rh.Pos()).Line > maxDeclLines { + return false + } + if int(rh.End()-lh.Pos()) > maxDeclChars { + return false + } + return true +} + +func (nom namedOccurrenceMap) addFromCondition(stmt *ast.IfStmt) { + switch v := stmt.Cond.(type) { + case *ast.BinaryExpr: + for _, v := range [2]ast.Expr{v.X, v.Y} { + switch e := v.(type) { + case *ast.Ident: + nom.addFromIdent(stmt.If, e) + case *ast.SelectorExpr: + nom.addFromIdent(stmt.If, e.X) + } + } + case *ast.Ident: + nom.addFromIdent(stmt.If, v) + case *ast.CallExpr: + for _, a := range v.Args { + switch e := a.(type) { + case *ast.Ident: + nom.addFromIdent(stmt.If, e) + case *ast.CallExpr: + nom.addFromCallExpr(stmt.If, e) + } + } + } +} + +func (nom namedOccurrenceMap) addFromIfClause(stmt *ast.IfStmt) { + nom.addFromBlockStmt(stmt.Body, stmt.If) +} + +func (nom namedOccurrenceMap) addFromElseClause(stmt *ast.IfStmt) { + nom.addFromBlockStmt(stmt.Else, stmt.If) +} + +func (nom namedOccurrenceMap) addFromBlockStmt(stmt ast.Stmt, ifPos token.Pos) { + blockStmt, ok := stmt.(*ast.BlockStmt) + if !ok { + return + } + + for _, el := range blockStmt.List { + exptStmt, ok := el.(*ast.ExprStmt) + if !ok { + continue + } + + if callExpr, ok := exptStmt.X.(*ast.CallExpr); ok { + nom.addFromCallExpr(ifPos, callExpr) + } + } +} + +func (nom namedOccurrenceMap) addFromCallExpr(ifPos token.Pos, callExpr *ast.CallExpr) { + for _, arg := range callExpr.Args { + nom.addFromIdent(ifPos, arg) + } +} + +func (nom namedOccurrenceMap) addFromIdent(ifPos token.Pos, v ast.Expr) { + ident, ok := v.(*ast.Ident) + if !ok { + return + } + + if markeredOccs, ok := nom[ident.Name]; ok { + marker := nom[ident.Name].getGreatestMarker() + + occ := markeredOccs[marker] + if occ.isComplete() { + return + } + + occ.ifStmtPos = ifPos + nom[ident.Name][marker] = occ + } +} -- cgit mrf-deployment