feat: allow the analysis of generated files (#4740)

This commit is contained in:
Ludovic Fernandez 2024-05-23 16:17:37 +02:00 committed by GitHub
parent 08deff4225
commit b99d5295f8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 62 additions and 39 deletions

View File

@ -2872,17 +2872,17 @@ issues:
- ".*\\.my\\.go$"
- lib/bad.go
# To follow strictly the Go generated file convention.
# Mode of the generated files analysis.
#
# If set to true, source files that have lines matching only the following regular expression will be excluded:
# `^// Code generated .* DO NOT EDIT\.$`
# This line must appear before the first non-comment, non-blank text in the file.
# https://go.dev/s/generatedcode
# - `strict`: sources are excluded by following strictly the Go generated file convention.
# Source files that have lines matching only the following regular expression will be excluded: `^// Code generated .* DO NOT EDIT\.$`
# This line must appear before the first non-comment, non-blank text in the file.
# https://go.dev/s/generatedcode
# - `lax`: sources are excluded if they contain lines `autogenerated file`, `code generated`, `do not edit`, etc.
# - `disable`: disable the generated files exclusion.
#
# By default, a lax pattern is applied:
# sources are excluded if they contain lines `autogenerated file`, `code generated`, `do not edit`, etc.
# Default: false
exclude-generated-strict: true
# Default: lax
exclude-generated: strict
# The list of ids of default excludes to include or disable.
# https://golangci-lint.run/usage/false-positives/#default-exclusions

View File

@ -3518,10 +3518,10 @@
"type": "boolean",
"default": false
},
"exclude-generated-strict": {
"description": "To follow strict Go generated file convention",
"type": "boolean",
"default": false
"exclude-generated": {
"description": "Mode of the generated files analysis.",
"enum": ["lax", "strict", "disable"],
"default": "lax"
},
"exclude-dirs": {
"description": "Which directories to exclude: issues from them won't be reported.",

View File

@ -103,6 +103,9 @@ func setupIssuesFlagSet(v *viper.Viper, fs *pflag.FlagSet) {
internal.AddFlagAndBind(v, fs, fs.Bool, "exclude-dirs-use-default", "issues.exclude-dirs-use-default", true,
getDefaultDirectoryExcludeHelp())
internal.AddFlagAndBind(v, fs, fs.String, "exclude-generated", "issues.exclude-generated", processors.AutogeneratedModeLax,
color.GreenString("Mode of the generated files analysis"))
const newDesc = "Show only new issues: if there are unstaged changes or untracked files, only those changes " +
"are analyzed, else only changes in HEAD~ are analyzed.\nIt's a super-useful option for integration " +
"of golangci-lint into existing large codebase.\nIt's not practical to fix all existing issues at " +

View File

@ -108,12 +108,14 @@ type Issues struct {
ExcludeCaseSensitive bool `mapstructure:"exclude-case-sensitive"`
ExcludePatterns []string `mapstructure:"exclude"`
ExcludeRules []ExcludeRule `mapstructure:"exclude-rules"`
ExcludeGeneratedStrict bool `mapstructure:"exclude-generated-strict"`
UseDefaultExcludes bool `mapstructure:"exclude-use-default"`
ExcludeFiles []string `mapstructure:"exclude-files"`
ExcludeDirs []string `mapstructure:"exclude-dirs"`
UseDefaultExcludeDirs bool `mapstructure:"exclude-dirs-use-default"`
ExcludeGenerated string `mapstructure:"exclude-generated"`
ExcludeFiles []string `mapstructure:"exclude-files"`
ExcludeDirs []string `mapstructure:"exclude-dirs"`
UseDefaultExcludeDirs bool `mapstructure:"exclude-dirs-use-default"`
MaxIssuesPerLinter int `mapstructure:"max-issues-per-linter"`
MaxSameIssues int `mapstructure:"max-same-issues"`
@ -124,6 +126,8 @@ type Issues struct {
Diff bool `mapstructure:"new"`
NeedFix bool `mapstructure:"fix"`
ExcludeGeneratedStrict bool `mapstructure:"exclude-generated-strict"` // Deprecated: use ExcludeGenerated instead.
}
func (i *Issues) Validate() error {

View File

@ -357,6 +357,12 @@ func (l *Loader) handleDeprecation() error {
}
}
// Deprecated since v1.59.0
if l.cfg.Issues.ExcludeGeneratedStrict {
l.log.Warnf("The configuration option `issues.exclude-generated-strict` is deprecated, please use `issues.exclude-generated`")
l.cfg.Issues.ExcludeGenerated = "strict" // Don't use the constants to avoid cyclic dependencies.
}
l.handleLinterOptionDeprecations()
return nil

View File

@ -75,7 +75,7 @@ func NewRunner(log logutils.Log, cfg *config.Config, args []string, goenv *gouti
skipFilesProcessor,
skipDirsProcessor, // must be after path prettifier
processors.NewAutogeneratedExclude(cfg.Issues.ExcludeGeneratedStrict),
processors.NewAutogeneratedExclude(cfg.Issues.ExcludeGenerated),
// Must be before exclude because users see already marked output and configure excluding by it.
processors.NewIdentifierMarker(),

View File

@ -12,6 +12,12 @@ import (
"github.com/golangci/golangci-lint/pkg/result"
)
const (
AutogeneratedModeLax = "lax"
AutogeneratedModeStrict = "strict"
AutogeneratedModeDisable = "disable"
)
const (
genCodeGenerated = "code generated"
genDoNotEdit = "do not edit"
@ -27,16 +33,16 @@ type fileSummary struct {
type AutogeneratedExclude struct {
debugf logutils.DebugFunc
strict bool
mode string
strictPattern *regexp.Regexp
fileSummaryCache map[string]*fileSummary
}
func NewAutogeneratedExclude(strict bool) *AutogeneratedExclude {
func NewAutogeneratedExclude(mode string) *AutogeneratedExclude {
return &AutogeneratedExclude{
debugf: logutils.Debug(logutils.DebugKeyAutogenExclude),
strict: strict,
mode: mode,
strictPattern: regexp.MustCompile(`^// Code generated .* DO NOT EDIT\.$`),
fileSummaryCache: map[string]*fileSummary{},
}
@ -47,6 +53,10 @@ func (*AutogeneratedExclude) Name() string {
}
func (p *AutogeneratedExclude) Process(issues []result.Issue) ([]result.Issue, error) {
if p.mode == AutogeneratedModeDisable {
return issues, nil
}
return filterIssuesErr(issues, p.shouldPassIssue)
}
@ -71,7 +81,7 @@ func (p *AutogeneratedExclude) shouldPassIssue(issue *result.Issue) (bool, error
fs = &fileSummary{}
p.fileSummaryCache[issue.FilePath()] = fs
if p.strict {
if p.mode == AutogeneratedModeStrict {
var err error
fs.generated, err = p.isGeneratedFileStrict(issue.FilePath())
if err != nil {

View File

@ -14,7 +14,7 @@ import (
)
func TestAutogeneratedExclude_isGeneratedFileLax_generated(t *testing.T) {
p := NewAutogeneratedExclude(false)
p := NewAutogeneratedExclude(AutogeneratedModeLax)
comments := []string{
` // generated by stringer -type Pill pill.go; DO NOT EDIT`,
@ -56,7 +56,7 @@ func TestAutogeneratedExclude_isGeneratedFileLax_generated(t *testing.T) {
}
func TestAutogeneratedExclude_isGeneratedFileLax_nonGenerated(t *testing.T) {
p := NewAutogeneratedExclude(false)
p := NewAutogeneratedExclude(AutogeneratedModeLax)
comments := []string{
"code not generated by",
@ -75,7 +75,7 @@ func TestAutogeneratedExclude_isGeneratedFileLax_nonGenerated(t *testing.T) {
}
func TestAutogeneratedExclude_isGeneratedFileStrict(t *testing.T) {
p := NewAutogeneratedExclude(true)
p := NewAutogeneratedExclude(AutogeneratedModeStrict)
testCases := []struct {
desc string
@ -154,21 +154,21 @@ func Test_getComments_fileWithLongLine(t *testing.T) {
func Test_shouldPassIssue(t *testing.T) {
testCases := []struct {
desc string
strict bool
mode string
issue *result.Issue
assert assert.BoolAssertionFunc
}{
{
desc: "typecheck issue",
strict: false,
desc: "typecheck issue",
mode: AutogeneratedModeLax,
issue: &result.Issue{
FromLinter: "typecheck",
},
assert: assert.True,
},
{
desc: "lax ",
strict: false,
desc: "lax ",
mode: AutogeneratedModeLax,
issue: &result.Issue{
FromLinter: "example",
Pos: token.Position{
@ -178,8 +178,8 @@ func Test_shouldPassIssue(t *testing.T) {
assert: assert.False,
},
{
desc: "strict ",
strict: true,
desc: "strict ",
mode: AutogeneratedModeStrict,
issue: &result.Issue{
FromLinter: "example",
Pos: token.Position{
@ -195,7 +195,7 @@ func Test_shouldPassIssue(t *testing.T) {
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
p := NewAutogeneratedExclude(test.strict)
p := NewAutogeneratedExclude(test.mode)
pass, err := p.shouldPassIssue(test.issue)
require.NoError(t, err)
@ -213,13 +213,13 @@ func Test_shouldPassIssue_error(t *testing.T) {
testCases := []struct {
desc string
strict bool
mode string
issue *result.Issue
expected string
}{
{
desc: "non-existing file (lax)",
strict: false,
desc: "non-existing file (lax)",
mode: AutogeneratedModeLax,
issue: &result.Issue{
FromLinter: "example",
Pos: token.Position{
@ -230,8 +230,8 @@ func Test_shouldPassIssue_error(t *testing.T) {
filepath.FromSlash("no-existing.go"), notFoundMsg),
},
{
desc: "non-existing file (strict)",
strict: true,
desc: "non-existing file (strict)",
mode: AutogeneratedModeStrict,
issue: &result.Issue{
FromLinter: "example",
Pos: token.Position{
@ -248,7 +248,7 @@ func Test_shouldPassIssue_error(t *testing.T) {
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
p := NewAutogeneratedExclude(test.strict)
p := NewAutogeneratedExclude(test.mode)
pass, err := p.shouldPassIssue(test.issue)