From 7b4377ad9d8a7205416df8d6217ef2b010f89481 Mon Sep 17 00:00:00 2001 From: Taras Madan Date: Wed, 22 Jan 2025 16:07:17 +0100 Subject: vendor: delete --- vendor/github.com/ashanbrown/forbidigo/LICENSE | 13 - .../forbidigo/forbidigo/config_options.go | 129 ------- .../ashanbrown/forbidigo/forbidigo/forbidigo.go | 398 --------------------- .../ashanbrown/forbidigo/forbidigo/patterns.go | 127 ------- vendor/github.com/ashanbrown/makezero/LICENSE | 13 - .../ashanbrown/makezero/makezero/makezero.go | 230 ------------ 6 files changed, 910 deletions(-) delete mode 100644 vendor/github.com/ashanbrown/forbidigo/LICENSE delete mode 100644 vendor/github.com/ashanbrown/forbidigo/forbidigo/config_options.go delete mode 100644 vendor/github.com/ashanbrown/forbidigo/forbidigo/forbidigo.go delete mode 100644 vendor/github.com/ashanbrown/forbidigo/forbidigo/patterns.go delete mode 100644 vendor/github.com/ashanbrown/makezero/LICENSE delete mode 100644 vendor/github.com/ashanbrown/makezero/makezero/makezero.go (limited to 'vendor/github.com/ashanbrown') diff --git a/vendor/github.com/ashanbrown/forbidigo/LICENSE b/vendor/github.com/ashanbrown/forbidigo/LICENSE deleted file mode 100644 index dc1d47ad5..000000000 --- a/vendor/github.com/ashanbrown/forbidigo/LICENSE +++ /dev/null @@ -1,13 +0,0 @@ -Copyright 2019 Andrew Shannon Brown - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/vendor/github.com/ashanbrown/forbidigo/forbidigo/config_options.go b/vendor/github.com/ashanbrown/forbidigo/forbidigo/config_options.go deleted file mode 100644 index 3f0ed6682..000000000 --- a/vendor/github.com/ashanbrown/forbidigo/forbidigo/config_options.go +++ /dev/null @@ -1,129 +0,0 @@ -package forbidigo - -// Code generated by github.com/launchdarkly/go-options. DO NOT EDIT. - -import "fmt" - -import "github.com/google/go-cmp/cmp" - -type ApplyOptionFunc func(c *config) error - -func (f ApplyOptionFunc) apply(c *config) error { - return f(c) -} - -func newConfig(options ...Option) (config, error) { - var c config - err := applyConfigOptions(&c, options...) - return c, err -} - -func applyConfigOptions(c *config, options ...Option) error { - c.ExcludeGodocExamples = true - for _, o := range options { - if err := o.apply(c); err != nil { - return err - } - } - return nil -} - -type Option interface { - apply(*config) error -} - -type optionExcludeGodocExamplesImpl struct { - o bool -} - -func (o optionExcludeGodocExamplesImpl) apply(c *config) error { - c.ExcludeGodocExamples = o.o - return nil -} - -func (o optionExcludeGodocExamplesImpl) Equal(v optionExcludeGodocExamplesImpl) bool { - switch { - case !cmp.Equal(o.o, v.o): - return false - } - return true -} - -func (o optionExcludeGodocExamplesImpl) String() string { - name := "OptionExcludeGodocExamples" - - // hack to avoid go vet error about passing a function to Sprintf - var value interface{} = o.o - return fmt.Sprintf("%s: %+v", name, value) -} - -// OptionExcludeGodocExamples don't check inside Godoc examples (see https://blog.golang.org/examples) -func OptionExcludeGodocExamples(o bool) Option { - return optionExcludeGodocExamplesImpl{ - o: o, - } -} - -type optionIgnorePermitDirectivesImpl struct { - o bool -} - -func (o optionIgnorePermitDirectivesImpl) apply(c *config) error { - c.IgnorePermitDirectives = o.o - return nil -} - -func (o optionIgnorePermitDirectivesImpl) Equal(v optionIgnorePermitDirectivesImpl) bool { - switch { - case !cmp.Equal(o.o, v.o): - return false - } - return true -} - -func (o optionIgnorePermitDirectivesImpl) String() string { - name := "OptionIgnorePermitDirectives" - - // hack to avoid go vet error about passing a function to Sprintf - var value interface{} = o.o - return fmt.Sprintf("%s: %+v", name, value) -} - -// OptionIgnorePermitDirectives don't check for `permit` directives(for example, in favor of `nolint`) -func OptionIgnorePermitDirectives(o bool) Option { - return optionIgnorePermitDirectivesImpl{ - o: o, - } -} - -type optionAnalyzeTypesImpl struct { - o bool -} - -func (o optionAnalyzeTypesImpl) apply(c *config) error { - c.AnalyzeTypes = o.o - return nil -} - -func (o optionAnalyzeTypesImpl) Equal(v optionAnalyzeTypesImpl) bool { - switch { - case !cmp.Equal(o.o, v.o): - return false - } - return true -} - -func (o optionAnalyzeTypesImpl) String() string { - name := "OptionAnalyzeTypes" - - // hack to avoid go vet error about passing a function to Sprintf - var value interface{} = o.o - return fmt.Sprintf("%s: %+v", name, value) -} - -// OptionAnalyzeTypes enable to match canonical names for types and interfaces using type info -func OptionAnalyzeTypes(o bool) Option { - return optionAnalyzeTypesImpl{ - o: o, - } -} diff --git a/vendor/github.com/ashanbrown/forbidigo/forbidigo/forbidigo.go b/vendor/github.com/ashanbrown/forbidigo/forbidigo/forbidigo.go deleted file mode 100644 index a7a3ab591..000000000 --- a/vendor/github.com/ashanbrown/forbidigo/forbidigo/forbidigo.go +++ /dev/null @@ -1,398 +0,0 @@ -// Package forbidigo provides a linter for forbidding the use of specific identifiers -package forbidigo - -import ( - "bytes" - "fmt" - "go/ast" - "go/printer" - "go/token" - "go/types" - "log" - "regexp" - "strings" -) - -type Issue interface { - Details() string - Pos() token.Pos - Position() token.Position - String() string -} - -type UsedIssue struct { - identifier string - pattern string - pos token.Pos - position token.Position - customMsg string -} - -func (a UsedIssue) Details() string { - explanation := fmt.Sprintf(` because %q`, a.customMsg) - if a.customMsg == "" { - explanation = fmt.Sprintf(" by pattern `%s`", a.pattern) - } - return fmt.Sprintf("use of `%s` forbidden", a.identifier) + explanation -} - -func (a UsedIssue) Position() token.Position { - return a.position -} - -func (a UsedIssue) Pos() token.Pos { - return a.pos -} - -func (a UsedIssue) String() string { return toString(a) } - -func toString(i UsedIssue) string { - return fmt.Sprintf("%s at %s", i.Details(), i.Position()) -} - -type Linter struct { - cfg config - patterns []*pattern -} - -func DefaultPatterns() []string { - return []string{`^(fmt\.Print(|f|ln)|print|println)$`} -} - -//go:generate go-options config -type config struct { - // don't check inside Godoc examples (see https://blog.golang.org/examples) - ExcludeGodocExamples bool `options:",true"` - IgnorePermitDirectives bool // don't check for `permit` directives(for example, in favor of `nolint`) - AnalyzeTypes bool // enable to match canonical names for types and interfaces using type info -} - -func NewLinter(patterns []string, options ...Option) (*Linter, error) { - cfg, err := newConfig(options...) - if err != nil { - return nil, fmt.Errorf("failed to process options: %w", err) - } - - if len(patterns) == 0 { - patterns = DefaultPatterns() - } - compiledPatterns := make([]*pattern, 0, len(patterns)) - for _, ptrn := range patterns { - p, err := parse(ptrn) - if err != nil { - return nil, err - } - compiledPatterns = append(compiledPatterns, p) - } - return &Linter{ - cfg: cfg, - patterns: compiledPatterns, - }, nil -} - -type visitor struct { - cfg config - isTestFile bool // godoc only runs on test files - - linter *Linter - comments []*ast.CommentGroup - - runConfig RunConfig - issues []Issue -} - -// Deprecated: Run was the original entrypoint before RunWithConfig was introduced to support -// additional match patterns that need additional information. -func (l *Linter) Run(fset *token.FileSet, nodes ...ast.Node) ([]Issue, error) { - return l.RunWithConfig(RunConfig{Fset: fset}, nodes...) -} - -// RunConfig provides information that the linter needs for different kinds -// of match patterns. Ideally, all fields should get set. More fields may get -// added in the future as needed. -type RunConfig struct { - // FSet is required. - Fset *token.FileSet - - // TypesInfo is needed for expanding source code expressions. - // Nil disables that step, i.e. patterns match the literal source code. - TypesInfo *types.Info - - // DebugLog is used to print debug messages. May be nil. - DebugLog func(format string, args ...interface{}) -} - -func (l *Linter) RunWithConfig(config RunConfig, nodes ...ast.Node) ([]Issue, error) { - if config.DebugLog == nil { - config.DebugLog = func(format string, args ...interface{}) {} - } - var issues []Issue - for _, node := range nodes { - var comments []*ast.CommentGroup - isTestFile := false - isWholeFileExample := false - if file, ok := node.(*ast.File); ok { - comments = file.Comments - fileName := config.Fset.Position(file.Pos()).Filename - isTestFile = strings.HasSuffix(fileName, "_test.go") - - // From https://blog.golang.org/examples, a "whole file example" is: - // a file that ends in _test.go and contains exactly one example function, - // no test or benchmark functions, and at least one other package-level declaration. - if l.cfg.ExcludeGodocExamples && isTestFile && len(file.Decls) > 1 { - numExamples := 0 - numTestsAndBenchmarks := 0 - for _, decl := range file.Decls { - funcDecl, isFuncDecl := decl.(*ast.FuncDecl) - // consider only functions, not methods - if !isFuncDecl || funcDecl.Recv != nil || funcDecl.Name == nil { - continue - } - funcName := funcDecl.Name.Name - if strings.HasPrefix(funcName, "Test") || strings.HasPrefix(funcName, "Benchmark") { - numTestsAndBenchmarks++ - break // not a whole file example - } - if strings.HasPrefix(funcName, "Example") { - numExamples++ - } - } - - // if this is a whole file example, skip this node - isWholeFileExample = numExamples == 1 && numTestsAndBenchmarks == 0 - } - } - if isWholeFileExample { - continue - } - visitor := visitor{ - cfg: l.cfg, - isTestFile: isTestFile, - linter: l, - runConfig: config, - comments: comments, - } - ast.Walk(&visitor, node) - issues = append(issues, visitor.issues...) - } - return issues, nil -} - -func (v *visitor) Visit(node ast.Node) ast.Visitor { - switch node := node.(type) { - case *ast.FuncDecl: - // don't descend into godoc examples if we are ignoring them - isGodocExample := v.isTestFile && node.Recv == nil && node.Name != nil && strings.HasPrefix(node.Name.Name, "Example") - if isGodocExample && v.cfg.ExcludeGodocExamples { - return nil - } - ast.Walk(v, node.Type) - if node.Body != nil { - ast.Walk(v, node.Body) - } - return nil - // Ignore constant and type names - case *ast.ValueSpec: - // Look at only type and values for const and variable specs, and not names - if node.Type != nil { - ast.Walk(v, node.Type) - } - if node.Values != nil { - for _, x := range node.Values { - ast.Walk(v, x) - } - } - return nil - // Ignore import alias names - case *ast.ImportSpec: - return nil - // Ignore type names - case *ast.TypeSpec: - // Look at only type parameters for type spec - if node.TypeParams != nil { - ast.Walk(v, node.TypeParams) - } - ast.Walk(v, node.Type) - return nil - // Ignore field names - case *ast.Field: - if node.Type != nil { - ast.Walk(v, node.Type) - } - return nil - // The following two are handled below. - case *ast.SelectorExpr: - case *ast.Ident: - // Everything else isn't. - default: - return v - } - - // The text as it appears in the source is always used because issues - // use that. It's used for matching unless usage of type information - // is enabled. - srcText := v.textFor(node) - matchTexts, pkgText := v.expandMatchText(node, srcText) - v.runConfig.DebugLog("%s: match %v, package %q", v.runConfig.Fset.Position(node.Pos()), matchTexts, pkgText) - for _, p := range v.linter.patterns { - if p.matches(matchTexts) && - (p.Package == "" || p.pkgRe.MatchString(pkgText)) && - !v.permit(node) { - v.issues = append(v.issues, UsedIssue{ - identifier: srcText, // Always report the expression as it appears in the source code. - pattern: p.re.String(), - pos: node.Pos(), - position: v.runConfig.Fset.Position(node.Pos()), - customMsg: p.Msg, - }) - } - } - - // descend into the left-side of selectors - if selector, isSelector := node.(*ast.SelectorExpr); isSelector { - if _, leftSideIsIdentifier := selector.X.(*ast.Ident); !leftSideIsIdentifier { - return v - } - } - - return nil -} - -// textFor returns the expression as it appears in the source code (for -// example, .). -func (v *visitor) textFor(node ast.Node) string { - buf := new(bytes.Buffer) - if err := printer.Fprint(buf, v.runConfig.Fset, node); err != nil { - log.Fatalf("ERROR: unable to print node at %s: %s", v.runConfig.Fset.Position(node.Pos()), err) - } - return buf.String() -} - -// expandMatchText expands the selector in a selector expression to the full package -// name and (for variables) the type: -// -// - example.com/some/pkg.Function -// - example.com/some/pkg.CustomType.Method -// -// It updates the text to match against and fills the package string if possible, -// otherwise it just returns. -func (v *visitor) expandMatchText(node ast.Node, srcText string) (matchTexts []string, pkgText string) { - // The text to match against is the literal source code if we cannot - // come up with something different. - matchTexts = []string{srcText} - - if !v.cfg.AnalyzeTypes || v.runConfig.TypesInfo == nil { - return matchTexts, pkgText - } - - location := v.runConfig.Fset.Position(node.Pos()) - - switch node := node.(type) { - case *ast.Ident: - if object, ok := v.runConfig.TypesInfo.Uses[node]; !ok { - // No information about the identifier. Should - // not happen, but perhaps there were compile - // errors? - v.runConfig.DebugLog("%s: unknown identifier %q", location, srcText) - } else if pkg := object.Pkg(); pkg != nil { - pkgText = pkg.Path() - // if this is a method, don't include the package name - isMethod := false - if signature, ok := object.Type().(*types.Signature); ok && signature.Recv() != nil { - isMethod = true - } - v.runConfig.DebugLog("%s: identifier: %q -> %q in package %q", location, srcText, matchTexts, pkgText) - // match either with or without package name - if !isMethod { - matchTexts = []string{pkg.Name() + "." + srcText, srcText} - } - } else { - v.runConfig.DebugLog("%s: identifier: %q -> %q without package", location, srcText, matchTexts) - } - case *ast.SelectorExpr: - selector := node.X - field := node.Sel.Name - - // If we are lucky, the entire selector expression has a known - // type. We don't care about the value. - selectorText := v.textFor(node) - if typeAndValue, ok := v.runConfig.TypesInfo.Types[selector]; ok { - m, p, ok := typeNameWithPackage(typeAndValue.Type) - if !ok { - v.runConfig.DebugLog("%s: selector %q with supported type %T", location, selectorText, typeAndValue.Type) - } - matchTexts = []string{m + "." + field} - pkgText = p - v.runConfig.DebugLog("%s: selector %q with supported type %q: %q -> %q, package %q", location, selectorText, typeAndValue.Type.String(), srcText, matchTexts, pkgText) - } - // Some expressions need special treatment. - switch selector := selector.(type) { - case *ast.Ident: - if object, hasUses := v.runConfig.TypesInfo.Uses[selector]; hasUses { - switch object := object.(type) { - case *types.PkgName: - pkgText = object.Imported().Path() - matchTexts = []string{object.Imported().Name() + "." + field} - v.runConfig.DebugLog("%s: selector %q is package: %q -> %q, package %q", location, selectorText, srcText, matchTexts, pkgText) - case *types.Var: - if typeName, packageName, ok := typeNameWithPackage(object.Type()); ok { - matchTexts = []string{typeName + "." + field} - pkgText = packageName - v.runConfig.DebugLog("%s: selector %q is variable of type %q: %q -> %q, package %q", location, selectorText, object.Type().String(), srcText, matchTexts, pkgText) - } else { - v.runConfig.DebugLog("%s: selector %q is variable with unsupported type %T", location, selectorText, object.Type()) - } - default: - // Something else? - v.runConfig.DebugLog("%s: selector %q is identifier with unsupported type %T", location, selectorText, object) - } - } else { - // No information about the identifier. Should - // not happen, but perhaps there were compile - // errors? - v.runConfig.DebugLog("%s: unknown selector identifier %q", location, selectorText) - } - default: - v.runConfig.DebugLog("%s: selector %q of unsupported type %T", location, selectorText, selector) - } - default: - v.runConfig.DebugLog("%s: unsupported type %T", location, node) - } - return matchTexts, pkgText -} - -// typeNameWithPackage tries to determine `.` and the full -// package path. This only needs to work for types of a selector in a selector -// expression. -func typeNameWithPackage(t types.Type) (typeName, packagePath string, ok bool) { - if ptr, ok := t.(*types.Pointer); ok { - t = ptr.Elem() - } - - switch t := t.(type) { - case *types.Named: - obj := t.Obj() - pkg := obj.Pkg() - if pkg == nil { - return "", "", false - } - return pkg.Name() + "." + obj.Name(), pkg.Path(), true - default: - return "", "", false - } -} - -func (v *visitor) permit(node ast.Node) bool { - if v.cfg.IgnorePermitDirectives { - return false - } - nodePos := v.runConfig.Fset.Position(node.Pos()) - nolint := regexp.MustCompile(fmt.Sprintf(`^//\s?permit:%s\b`, regexp.QuoteMeta(v.textFor(node)))) - for _, c := range v.comments { - commentPos := v.runConfig.Fset.Position(c.Pos()) - if commentPos.Line == nodePos.Line && len(c.List) > 0 && nolint.MatchString(c.List[0].Text) { - return true - } - } - return false -} diff --git a/vendor/github.com/ashanbrown/forbidigo/forbidigo/patterns.go b/vendor/github.com/ashanbrown/forbidigo/forbidigo/patterns.go deleted file mode 100644 index 2692dcd24..000000000 --- a/vendor/github.com/ashanbrown/forbidigo/forbidigo/patterns.go +++ /dev/null @@ -1,127 +0,0 @@ -package forbidigo - -import ( - "fmt" - "regexp" - "regexp/syntax" - "strings" - - "gopkg.in/yaml.v2" -) - -// pattern matches code that is not supposed to be used. -type pattern struct { - re, pkgRe *regexp.Regexp - - // Pattern is the regular expression string that is used for matching. - // It gets matched against the literal source code text or the expanded - // text, depending on the mode in which the analyzer runs. - Pattern string `yaml:"p"` - - // Package is a regular expression for the full package path of - // an imported item. Ignored unless the analyzer is configured to - // determine that information. - Package string `yaml:"pkg,omitempty"` - - // Msg gets printed in addition to the normal message if a match is - // found. - Msg string `yaml:"msg,omitempty"` -} - -// A yamlPattern pattern in a YAML string may be represented either by a string -// (the traditional regular expression syntax) or a struct (for more complex -// patterns). -type yamlPattern pattern - -func (p *yamlPattern) UnmarshalYAML(unmarshal func(interface{}) error) error { - // Try struct first. It's unlikely that a regular expression string - // is valid YAML for a struct. - var ptrn pattern - if err := unmarshal(&ptrn); err != nil { - errStr := err.Error() - // Didn't work, try plain string. - var ptrn string - if err := unmarshal(&ptrn); err != nil { - return fmt.Errorf("pattern is neither a regular expression string (%s) nor a Pattern struct (%s)", err.Error(), errStr) - } - p.Pattern = ptrn - } else { - *p = yamlPattern(ptrn) - } - return ((*pattern)(p)).validate() -} - -var _ yaml.Unmarshaler = &yamlPattern{} - -// parse accepts a regular expression or, if the string starts with { or contains a line break, a -// JSON or YAML representation of a Pattern. -func parse(ptrn string) (*pattern, error) { - pattern := &pattern{} - - if strings.HasPrefix(strings.TrimSpace(ptrn), "{") || - strings.Contains(ptrn, "\n") { - // Embedded JSON or YAML. We can decode both with the YAML decoder. - if err := yaml.UnmarshalStrict([]byte(ptrn), pattern); err != nil { - return nil, fmt.Errorf("parsing as JSON or YAML failed: %v", err) - } - } else { - pattern.Pattern = ptrn - } - - if err := pattern.validate(); err != nil { - return nil, err - } - return pattern, nil -} - -func (p *pattern) validate() error { - ptrnRe, err := regexp.Compile(p.Pattern) - if err != nil { - return fmt.Errorf("unable to compile source code pattern `%s`: %s", p.Pattern, err) - } - re, err := syntax.Parse(p.Pattern, syntax.Perl) - if err != nil { - return fmt.Errorf("unable to parse source code pattern `%s`: %s", p.Pattern, err) - } - msg := extractComment(re) - if msg != "" { - p.Msg = msg - } - p.re = ptrnRe - - if p.Package != "" { - pkgRe, err := regexp.Compile(p.Package) - if err != nil { - return fmt.Errorf("unable to compile package pattern `%s`: %s", p.Package, err) - } - p.pkgRe = pkgRe - } - - return nil -} - -func (p *pattern) matches(matchTexts []string) bool { - for _, text := range matchTexts { - if p.re.MatchString(text) { - return true - } - } - return false -} - -// Traverse the leaf submatches in the regex tree and extract a comment, if any -// is present. -func extractComment(re *syntax.Regexp) string { - for _, sub := range re.Sub { - subStr := sub.String() - if strings.HasPrefix(subStr, "#") { - return strings.TrimSpace(strings.TrimPrefix(sub.String(), "#")) - } - if len(sub.Sub) > 0 { - if comment := extractComment(sub); comment != "" { - return comment - } - } - } - return "" -} diff --git a/vendor/github.com/ashanbrown/makezero/LICENSE b/vendor/github.com/ashanbrown/makezero/LICENSE deleted file mode 100644 index dc1d47ad5..000000000 --- a/vendor/github.com/ashanbrown/makezero/LICENSE +++ /dev/null @@ -1,13 +0,0 @@ -Copyright 2019 Andrew Shannon Brown - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/vendor/github.com/ashanbrown/makezero/makezero/makezero.go b/vendor/github.com/ashanbrown/makezero/makezero/makezero.go deleted file mode 100644 index 18bcad3d0..000000000 --- a/vendor/github.com/ashanbrown/makezero/makezero/makezero.go +++ /dev/null @@ -1,230 +0,0 @@ -// Package makezero provides a linter for appends to slices initialized with non-zero length. -package makezero - -import ( - "bytes" - "fmt" - "go/ast" - "go/printer" - "go/token" - "go/types" - "log" - "regexp" -) - -// a decl might include multiple var, -// so var name with decl make final uniq obj. -type uniqDecl struct { - varName string - decl interface{} -} - -type Issue interface { - Details() string - Pos() token.Pos - Position() token.Position - String() string -} - -type AppendIssue struct { - name string - pos token.Pos - position token.Position -} - -func (a AppendIssue) Details() string { - return fmt.Sprintf("append to slice `%s` with non-zero initialized length", a.name) -} - -func (a AppendIssue) Pos() token.Pos { - return a.pos -} - -func (a AppendIssue) Position() token.Position { - return a.position -} - -func (a AppendIssue) String() string { return toString(a) } - -type MustHaveNonZeroInitLenIssue struct { - name string - pos token.Pos - position token.Position -} - -func (i MustHaveNonZeroInitLenIssue) Details() string { - return fmt.Sprintf("slice `%s` does not have non-zero initial length", i.name) -} - -func (i MustHaveNonZeroInitLenIssue) Pos() token.Pos { - return i.pos -} - -func (i MustHaveNonZeroInitLenIssue) Position() token.Position { - return i.position -} - -func (i MustHaveNonZeroInitLenIssue) String() string { return toString(i) } - -func toString(i Issue) string { - return fmt.Sprintf("%s at %s", i.Details(), i.Position()) -} - -type visitor struct { - initLenMustBeZero bool - - comments []*ast.CommentGroup // comments to apply during this visit - info *types.Info - - nonZeroLengthSliceDecls map[uniqDecl]struct{} - fset *token.FileSet - issues []Issue -} - -type Linter struct { - initLenMustBeZero bool -} - -func NewLinter(initialLengthMustBeZero bool) *Linter { - return &Linter{ - initLenMustBeZero: initialLengthMustBeZero, - } -} - -func (l Linter) Run(fset *token.FileSet, info *types.Info, nodes ...ast.Node) ([]Issue, error) { - var issues []Issue - for _, node := range nodes { - var comments []*ast.CommentGroup - if file, ok := node.(*ast.File); ok { - comments = file.Comments - } - visitor := visitor{ - nonZeroLengthSliceDecls: make(map[uniqDecl]struct{}), - initLenMustBeZero: l.initLenMustBeZero, - info: info, - fset: fset, - comments: comments, - } - ast.Walk(&visitor, node) - issues = append(issues, visitor.issues...) - } - return issues, nil -} - -func (v *visitor) Visit(node ast.Node) ast.Visitor { - switch node := node.(type) { - case *ast.CallExpr: - fun, ok := node.Fun.(*ast.Ident) - if !ok || fun.Name != "append" { - break - } - if sliceIdent, ok := node.Args[0].(*ast.Ident); ok && - v.hasNonZeroInitialLength(sliceIdent) && - !v.hasNoLintOnSameLine(fun) { - v.issues = append(v.issues, - AppendIssue{ - name: sliceIdent.Name, - pos: fun.Pos(), - position: v.fset.Position(fun.Pos()), - }) - } - case *ast.AssignStmt: - for i, right := range node.Rhs { - if right, ok := right.(*ast.CallExpr); ok { - fun, ok := right.Fun.(*ast.Ident) - if !ok || fun.Name != "make" { - continue - } - left := node.Lhs[i] - if len(right.Args) == 2 { - // ignore if not a slice or it has explicit zero length - if !v.isSlice(right.Args[0]) { - continue - } else if lit, ok := right.Args[1].(*ast.BasicLit); ok && lit.Kind == token.INT && lit.Value == "0" { - continue - } - if v.initLenMustBeZero && !v.hasNoLintOnSameLine(fun) { - v.issues = append(v.issues, MustHaveNonZeroInitLenIssue{ - name: v.textFor(left), - pos: node.Pos(), - position: v.fset.Position(node.Pos()), - }) - } - v.recordNonZeroLengthSlices(left) - } - } - } - } - return v -} - -func (v *visitor) textFor(node ast.Node) string { - typeBuf := new(bytes.Buffer) - if err := printer.Fprint(typeBuf, v.fset, node); err != nil { - log.Fatalf("ERROR: unable to print type: %s", err) - } - return typeBuf.String() -} - -func (v *visitor) hasNonZeroInitialLength(ident *ast.Ident) bool { - if ident.Obj == nil { - log.Printf("WARNING: could not determine with %q at %s is a slice (missing object type)", - ident.Name, v.fset.Position(ident.Pos()).String()) - return false - } - _, exists := v.nonZeroLengthSliceDecls[uniqDecl{ - varName: ident.Obj.Name, - decl: ident.Obj.Decl, - }] - return exists -} - -func (v *visitor) recordNonZeroLengthSlices(node ast.Node) { - ident, ok := node.(*ast.Ident) - if !ok { - return - } - if ident.Obj == nil { - return - } - v.nonZeroLengthSliceDecls[uniqDecl{ - varName: ident.Obj.Name, - decl: ident.Obj.Decl, - }] = struct{}{} -} - -func (v *visitor) isSlice(node ast.Node) bool { - // determine type if this is a user-defined type - if ident, ok := node.(*ast.Ident); ok { - obj := ident.Obj - if obj == nil { - if v.info != nil { - _, ok := v.info.ObjectOf(ident).Type().(*types.Slice) - return ok - } - return false - } - spec, ok := obj.Decl.(*ast.TypeSpec) - if !ok { - return false - } - node = spec.Type - } - - if node, ok := node.(*ast.ArrayType); ok { - return node.Len == nil // only slices have zero length - } - return false -} - -func (v *visitor) hasNoLintOnSameLine(node ast.Node) bool { - nolint := regexp.MustCompile(`^\s*nozero\b`) - nodePos := v.fset.Position(node.Pos()) - for _, c := range v.comments { - commentPos := v.fset.Position(c.Pos()) - if commentPos.Line == nodePos.Line && nolint.MatchString(c.Text()) { - return true - } - } - return false -} -- cgit mrf-deployment