diff --git a/.golangci.example.yml b/.golangci.example.yml index c2d24c33749c..ae080ad06e0e 100644 --- a/.golangci.example.yml +++ b/.golangci.example.yml @@ -204,6 +204,10 @@ linters-settings: dogsled: # checks assignments with too many blank identifiers; default is 2 max-blank-identifiers: 2 + nolintlint: + explain: true # require an explanation for nolint directives + machine: false # don't require machine-readable nolint directives (i.e. with no leading space) + specific: true # require nolint directives to be specific about which linter is being skipped whitespace: multi-if: false diff --git a/.golangci.yml b/.golangci.yml index 3b0148887afa..26e8d0bc1ef9 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -44,6 +44,10 @@ linters-settings: funlen: lines: 100 statements: 50 + nolintlint: + explain: false # don't require an explanation for nolint directives + machine: false # don't require machine-readable nolint directives (i.e. with no leading space) + specific: false # don't require nolint directives to be specific about which linter is being skipped linters: # inverted configuration with `enable-all` and `disable` is not scalable during updates of golangci-lint diff --git a/README.md b/README.md index ddf3402921c8..e667d413633b 100644 --- a/README.md +++ b/README.md @@ -213,6 +213,7 @@ lll: Reports long lines [fast: true, auto-fix: false] maligned: Tool to detect Go structs that would take less memory if their fields were sorted [fast: true, auto-fix: false] misspell: Finds commonly misspelled English words in comments [fast: true, auto-fix: true] nakedret: Finds naked returns in functions greater than a specified function length [fast: true, auto-fix: false] +nolintlint: Reports ill-formed or insufficient nolint directives [fast: true, auto-fix: false] prealloc: Finds slice declarations that could potentially be preallocated [fast: true, auto-fix: false] scopelint: Scopelint checks for unpinned variables in go programs [fast: true, auto-fix: false] stylecheck: Stylecheck is a replacement for golint [fast: false, auto-fix: false] @@ -465,6 +466,7 @@ golangci-lint help linters - [godox](https://github.com/matoous/godox) - Tool for detection of FIXME, TODO and other comment keywords - [funlen](https://github.com/ultraware/funlen) - Tool for detection of long functions - [whitespace](https://github.com/ultraware/whitespace) - Tool for detection of leading and trailing whitespace +- [nolintlint](https://github.com/ashanbrown/nolintlint) - Reports ill-formed or insufficient nolint directives ## Configuration @@ -789,6 +791,10 @@ linters-settings: dogsled: # checks assignments with too many blank identifiers; default is 2 max-blank-identifiers: 2 + nolintlint: + explain: true # require an explanation for nolint directives + machine: false # don't require machine-readable nolint directives (i.e. with no leading space) + specific: true # require nolint directives to be specific about which linter is being skipped whitespace: multi-if: false @@ -920,6 +926,10 @@ linters-settings: funlen: lines: 100 statements: 50 + nolintlint: + explain: false # don't require an explanation for nolint directives + machine: false # don't require machine-readable nolint directives (i.e. with no leading space) + specific: false # don't require nolint directives to be specific about which linter is being skipped linters: # inverted configuration with `enable-all` and `disable` is not scalable during updates of golangci-lint @@ -1115,6 +1125,7 @@ Thanks to developers and authors of used linters: - [leighmcculloch](https://github.com/leighmcculloch) - [matoous](https://github.com/matoous) - [ultraware](https://github.com/ultraware) +- [ashanbrown](https://github.com/ashanbrown) ## Changelog diff --git a/go.mod b/go.mod index 26cb4ffe6ab8..143a04e016a5 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.12 require ( github.com/OpenPeeDeeP/depguard v1.0.1 + github.com/ashanbrown/nolintlint/v2 v2.2.2 github.com/fatih/color v1.7.0 github.com/go-critic/go-critic v0.3.5-0.20190904082202-d79a9f0c64db github.com/go-lintpack/lintpack v0.5.2 @@ -38,7 +39,7 @@ require ( github.com/ultraware/funlen v0.0.2 github.com/ultraware/whitespace v0.0.3 github.com/valyala/quicktemplate v1.2.0 - golang.org/x/tools v0.0.0-20190912215617-3720d1ec3678 + golang.org/x/tools v0.0.0-20190917215024-905c8ffbfa41 gopkg.in/yaml.v2 v2.2.2 honnef.co/go/tools v0.0.1-2019.2.3 mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed diff --git a/go.sum b/go.sum index 5db88ed24c88..44a27e076e85 100644 --- a/go.sum +++ b/go.sum @@ -9,6 +9,8 @@ github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrU github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/ashanbrown/nolintlint/v2 v2.2.2 h1:vwqNclNDHOeE/KHK73fwNty3zVbmWSNk/SitRSzMG8A= +github.com/ashanbrown/nolintlint/v2 v2.2.2/go.mod h1:eFzROBu49LDYj99qhj4UUTtNH0PVFI8P71+UM+Q41wA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= @@ -28,6 +30,7 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/structtag v1.0.0/go.mod h1:IKitwq45uXL/yqi5mYghiD3w9H6eTOvI9vnk8tXMphA= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -140,6 +143,7 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/launchdarkly/go-options v1.0.0/go.mod h1:9l3OopYPepd4jRq/QlCAUez94s4i+MaOlo7ToKqSzoA= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= @@ -164,8 +168,10 @@ github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d h1:AREM5mwr4u1 github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo= github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= diff --git a/pkg/config/config.go b/pkg/config/config.go index f07037d947b2..0855b3f2c878 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -175,14 +175,15 @@ type LintersSettings struct { MultiIf bool `mapstructure:"multi-if"` } - Lll LllSettings - Unparam UnparamSettings - Nakedret NakedretSettings - Prealloc PreallocSettings - Errcheck ErrcheckSettings - Gocritic GocriticSettings - Godox GodoxSettings - Dogsled DogsledSettings + Lll LllSettings + Unparam UnparamSettings + Nakedret NakedretSettings + Prealloc PreallocSettings + Errcheck ErrcheckSettings + Gocritic GocriticSettings + Godox GodoxSettings + Dogsled DogsledSettings + NoLintLint NoLintLintSettings } type GovetSettings struct { @@ -243,6 +244,13 @@ type DogsledSettings struct { MaxBlankIdentifiers int `mapstructure:"max-blank-identifiers"` } +type NoLintLintSettings struct { + Explain bool + Machine bool + Specific bool + Exclude []string +} + var defaultLintersSettings = LintersSettings{ Lll: LllSettings{ LineLength: 120, @@ -268,6 +276,11 @@ var defaultLintersSettings = LintersSettings{ Dogsled: DogsledSettings{ MaxBlankIdentifiers: 2, }, + NoLintLint: NoLintLintSettings{ + Explain: false, + Machine: false, + Specific: false, + }, } type Linters struct { diff --git a/pkg/golinters/nolintlint.go b/pkg/golinters/nolintlint.go new file mode 100644 index 000000000000..c4557978954a --- /dev/null +++ b/pkg/golinters/nolintlint.go @@ -0,0 +1,65 @@ +package golinters + +import ( + "context" + "fmt" + "go/ast" + + "github.com/ashanbrown/nolintlint/v2/nolintlint" + + "github.com/golangci/golangci-lint/pkg/lint/linter" + "github.com/golangci/golangci-lint/pkg/result" +) + +type NoLintLint struct{} + +func (NoLintLint) Name() string { + return "nolintlint" +} + +func (NoLintLint) Desc() string { + return "Reports ill-formed or insufficient nolint directives" +} + +func (l NoLintLint) Run(ctx context.Context, lintCtx *linter.Context) (results []result.Issue, err error) { + var needs nolintlint.Needs + settings := lintCtx.Settings().NoLintLint + if settings.Explain { + needs |= nolintlint.NeedsExplanation + } + if settings.Machine { + needs |= nolintlint.NeedsMachine + } + if settings.Specific { + needs |= nolintlint.NeedsSpecific + } + lnt, err := nolintlint.NewLinter( + nolintlint.OptionNeeds(needs), + nolintlint.OptionExcludes(settings.Exclude), + ) + if err != nil { + return nil, err + } + for _, pkg := range lintCtx.Packages { + files, fset, err := getASTFilesForGoPkg(lintCtx, pkg) + if err != nil { + return nil, fmt.Errorf("could not load files: %s", err) + } + nodes := make([]ast.Node, 0, len(files)) + for _, n := range files { + nodes = append(nodes, n) + } + issues, err := lnt.Run(fset, nodes...) + if err != nil { + return nil, fmt.Errorf("linter failed to run: %s", err) + } + for _, i := range issues { + results = append(results, result.Issue{ + FromLinter: l.Name(), + Text: i.Details(), + Pos: i.Position(), + }) + } + } + return results, nil +} diff --git a/pkg/lint/lintersdb/manager.go b/pkg/lint/lintersdb/manager.go index 27862a7b0bc9..ecb21a19f828 100644 --- a/pkg/lint/lintersdb/manager.go +++ b/pkg/lint/lintersdb/manager.go @@ -257,6 +257,11 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config { WithSpeed(10). WithAutoFix(). WithURL("https://github.com/ultraware/whitespace"), + linter.NewConfig(golinters.NoLintLint{}). + WithLoadFiles(). + WithPresets(linter.PresetStyle). + WithSpeed(10). + WithURL("https://github.com/ashanbrown/nolintlint"), } isLocalRun := os.Getenv("GOLANGCI_COM_RUN") == "" diff --git a/pkg/result/processors/nolint.go b/pkg/result/processors/nolint.go index a2ad04643663..fbd9a14d8f54 100644 --- a/pkg/result/processors/nolint.go +++ b/pkg/result/processors/nolint.go @@ -126,6 +126,10 @@ func (p *Nolint) buildIgnoredRangesForFile(f *ast.File, fset *token.FileSet, fil } func (p *Nolint) shouldPassIssue(i *result.Issue) (bool, error) { + if i.FromLinter == "nolintlint" { // a nolint directive would otherwise prevent nolintlint from checking the line + return true, nil + } + fd, err := p.getOrCreateFileData(i) if err != nil { return false, err diff --git a/test/testdata/nolintlint.go b/test/testdata/nolintlint.go new file mode 100644 index 000000000000..2b7bc5dcfa20 --- /dev/null +++ b/test/testdata/nolintlint.go @@ -0,0 +1,15 @@ +//args: -Enolintlint +//config: linters-settings.nolintlint.explain=true +//config: linters-settings.nolintlint.specific=true +//config: linters-settings.nolintlint.machine=true +package testdata + +import "fmt" + +func Foo() { + fmt.Println("not specific") //nolint // ERROR "directive `.*` should mention specific linter such as `//nolint:my-linter`" + fmt.Println("not machine readable") // nolint // ERROR "directive `.*` should be written as `//nolint`" + fmt.Println("bad syntax") //nolint: deadcode // ERROR "directive `.*` should match `//nolint\[:\] \[// \]`" + fmt.Println("bad syntax") //nolint:deadcode lll // ERROR "directive `.*` should match `//nolint\[:\] \[// \]`" + fmt.Println("extra spaces") // nolint:deadcode // because // ERROR "directive `.*` should not have more than one leading space" +} diff --git a/vendor/github.com/ashanbrown/nolintlint/v2/LICENSE b/vendor/github.com/ashanbrown/nolintlint/v2/LICENSE new file mode 100644 index 000000000000..dc1d47ad542a --- /dev/null +++ b/vendor/github.com/ashanbrown/nolintlint/v2/LICENSE @@ -0,0 +1,13 @@ +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/nolintlint/v2/nolintlint/config_options.go b/vendor/github.com/ashanbrown/nolintlint/v2/nolintlint/config_options.go new file mode 100644 index 000000000000..b4d641981995 --- /dev/null +++ b/vendor/github.com/ashanbrown/nolintlint/v2/nolintlint/config_options.go @@ -0,0 +1,52 @@ +package nolintlint + +// Code generated by github.com/launchdarkly/go-options. DO NOT EDIT. + +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 { + for _, o := range options { + if err := o.apply(c); err != nil { + return err + } + } + return nil +} + +type Option interface { + apply(*config) error +} + +// OptionDirectives describes lint directives to check +func OptionDirectives(o []string) ApplyOptionFunc { + return func(c *config) error { + c.directives = o + return nil + } +} + +// OptionExcludes lists individual linters that don't require explanations +func OptionExcludes(o []string) ApplyOptionFunc { + return func(c *config) error { + c.excludes = o + return nil + } +} + +// OptionNeeds indicates which linter checks to perform +func OptionNeeds(o Needs) ApplyOptionFunc { + return func(c *config) error { + c.needs = o + return nil + } +} diff --git a/vendor/github.com/ashanbrown/nolintlint/v2/nolintlint/nolintlint.go b/vendor/github.com/ashanbrown/nolintlint/v2/nolintlint/nolintlint.go new file mode 100644 index 000000000000..c431331deab0 --- /dev/null +++ b/vendor/github.com/ashanbrown/nolintlint/v2/nolintlint/nolintlint.go @@ -0,0 +1,245 @@ +// nolintlint provides a linter for ensure that all //nolint directives are followed by explanations +package nolintlint + +import ( + "fmt" + "go/ast" + "go/token" + "regexp" + "strings" + "unicode" + + "github.com/pkg/errors" +) + +type BaseIssue struct { + fullDirective string + directiveWithOptionalLeadingSpace string + position token.Position +} + +func (b BaseIssue) Position() token.Position { + return b.position +} + +type ExtraLeadingSpace struct { + BaseIssue +} + +func (i ExtraLeadingSpace) Details() string { + return fmt.Sprintf("directive `%s` should not have more than one leading space", i.fullDirective) +} + +func (i ExtraLeadingSpace) String() string { return toString(i) } + +type NotMachine struct { + BaseIssue +} + +func (i NotMachine) Details() string { + expected := i.fullDirective[:2] + strings.TrimLeftFunc(i.fullDirective[2:], unicode.IsSpace) + return fmt.Sprintf("directive `%s` should be written without leading space as `%s`", + i.fullDirective, expected) +} + +func (i NotMachine) String() string { return toString(i) } + +type NotSpecific struct { + BaseIssue +} + +func (i NotSpecific) Details() string { + return fmt.Sprintf("directive `%s` should mention specific linter such as `//%s:my-linter`", + i.fullDirective, i.directiveWithOptionalLeadingSpace) +} + +func (i NotSpecific) String() string { return toString(i) } + +type ParseError struct { + BaseIssue +} + +func (i ParseError) Details() string { + return fmt.Sprintf("directive `%s` should match `//%s[:] [// ]`", + i.fullDirective, + i.directiveWithOptionalLeadingSpace) +} + +func (i ParseError) String() string { return toString(i) } + +type NoExplanation struct { + BaseIssue + fullDirectiveWithoutExplanation string +} + +func (i NoExplanation) Details() string { + return fmt.Sprintf("directive `%s` should provide explanation such as `%s // this is why`", + i.fullDirective, i.fullDirectiveWithoutExplanation) +} + +func (i NoExplanation) String() string { return toString(i) } + +func toString(i Issue) string { + return fmt.Sprintf("%s at %s", i.Details(), i.Position()) +} + +type Issue interface { + Details() string + Position() token.Position + String() string +} + +type Needs uint + +const ( + NeedsMachine Needs = 1 << iota + NeedsSpecific + NeedsExplanation + NeedsAll = NeedsMachine | NeedsSpecific | NeedsExplanation +) + +type DirectivePatterns struct { + directive *regexp.Regexp + full *regexp.Regexp +} + +//go:generate gobin -m -run github.com/launchdarkly/go-options config +type config struct { + directives []string // describes lint directives to check + excludes []string // lists individual linters that don't require explanations + needs Needs // indicates which linter checks to perform +} + +type Linter struct { + config + patterns []DirectivePatterns + excludeByLinter map[string]bool +} + +var defaultDirectives = []string{"nolint"} + +// NewLinter creates a linter that enforces that the provided directives fulfill the provided requirements +func NewLinter(options ...Option) (*Linter, error) { + config, err := newConfig(append([]Option{OptionDirectives(defaultDirectives)}, options...)...) + if err != nil { + return nil, errors.Wrap(err, "unable to parse options") + } + patterns := make([]DirectivePatterns, 0, len(config.directives)) + for _, d := range config.directives { + quoted := regexp.QuoteMeta(d) + directive, err := regexp.Compile(fmt.Sprintf(`^\s*(%s)(:\S+)?\b`, quoted)) + if err != nil { + return nil, errors.Wrapf(err, `unable to create directive pattern for "%s"`, d) + } + full, err := regexp.Compile(fmt.Sprintf(`^//\s*%s(:\S+)?\s*(//.*)?\s*\n?$`, quoted)) + if err != nil { + return nil, errors.Wrapf(err, `unable to specific create full pattern for "%s"`, d) + } + patterns = append(patterns, DirectivePatterns{ + directive: directive, + full: full, + }) + } + excludeByName := make(map[string]bool) + for _, e := range config.excludes { + excludeByName[e] = true + } + + return &Linter{ + config: config, + patterns: patterns, + excludeByLinter: excludeByName, + }, nil +} + +var leadingSpacePattern = regexp.MustCompile(`^//(\s*)`) +var trailingBlankExplanation = regexp.MustCompile(`\s*(//\s*)?$`) + +func (l Linter) Run(fset *token.FileSet, nodes ...ast.Node) ([]Issue, error) { + var issues []Issue + for _, node := range nodes { + if file, ok := node.(*ast.File); ok { + for _, c := range file.Comments { + for _, p := range l.patterns { + text := c.Text() + matches := p.directive.FindStringSubmatch(text) + if len(matches) == 0 { + continue + } + directive := matches[1] + + // check for a space between the "//" and the directive + leadingSpaceMatches := leadingSpacePattern.FindStringSubmatch(c.List[0].Text) // c.Text() doesn't have all leading space + if len(leadingSpaceMatches) == 0 { + continue + } + leadingSpace := leadingSpaceMatches[1] + + directiveWithOptionalLeadingSpace := directive + if len(leadingSpace) > 0 { + directiveWithOptionalLeadingSpace = " " + directive + } + + base := BaseIssue{ + fullDirective: c.List[0].Text, + directiveWithOptionalLeadingSpace: directiveWithOptionalLeadingSpace, + position: fset.Position(c.Pos()), + } + + // check for, report and eliminate leading spaces so we can check for other issues + if leadingSpace != "" && leadingSpace != " " { + issues = append(issues, ExtraLeadingSpace{ + BaseIssue: base, + }) + } + + if (l.needs&NeedsMachine) != 0 && strings.HasPrefix(directiveWithOptionalLeadingSpace, " ") { + issues = append(issues, NotMachine{BaseIssue: base}) + } + + fullMatches := p.full.FindStringSubmatch(c.List[0].Text) + if len(fullMatches) == 0 { + issues = append(issues, ParseError{BaseIssue: base}) + continue + } + lintersText, explanation := fullMatches[1], fullMatches[2] + var linters []string + if len(lintersText) > 0 { + lls := strings.Split(lintersText[1:], ",") + linters = make([]string, 0, len(lls)) + for _, ll := range lls { + if ll != "" { + linters = append(linters, ll) + break + } + } + } + if (l.needs & NeedsSpecific) != 0 { + if len(linters) == 0 { + issues = append(issues, NotSpecific{BaseIssue: base}) + } + } + + if (l.needs&NeedsExplanation) != 0 && (explanation == "" || strings.TrimSpace(explanation) == "//") { + needsExplanation := len(linters) == 0 // if no linters are mentioned, we must have explanation + // otherwise, check if we are excluding all of the mentioned linters + for _, ll := range linters { + if !l.excludeByLinter[ll] { // if a linter does require explanation + needsExplanation = true + break + } + } + if needsExplanation { + fullDirectiveWithoutExplanation := trailingBlankExplanation.ReplaceAllString(c.List[0].Text, "") + issues = append(issues, NoExplanation{ + BaseIssue: base, + fullDirectiveWithoutExplanation: fullDirectiveWithoutExplanation, + }) + } + } + } + } + } + } + return issues, nil +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 99689cca92f3..14af70f80fdd 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -4,6 +4,8 @@ github.com/BurntSushi/toml github.com/OpenPeeDeeP/depguard # github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 github.com/StackExchange/wmi +# github.com/ashanbrown/nolintlint/v2 v2.2.2 +github.com/ashanbrown/nolintlint/v2/nolintlint # github.com/davecgh/go-spew v1.1.1 github.com/davecgh/go-spew/spew # github.com/fatih/color v1.7.0 @@ -181,7 +183,7 @@ golang.org/x/sys/windows golang.org/x/text/transform golang.org/x/text/unicode/norm golang.org/x/text/width -# golang.org/x/tools v0.0.0-20190912215617-3720d1ec3678 => github.com/golangci/tools v0.0.0-20190915081525-6aa350649b1c +# golang.org/x/tools v0.0.0-20190917215024-905c8ffbfa41 => github.com/golangci/tools v0.0.0-20190915081525-6aa350649b1c golang.org/x/tools/go/analysis golang.org/x/tools/go/analysis/passes/asmdecl golang.org/x/tools/go/analysis/passes/assign