feat: allow the analysis of generated files (#4740)
This commit is contained in:
		
							parent
							
								
									08deff4225
								
							
						
					
					
						commit
						b99d5295f8
					
				| @ -2872,17 +2872,17 @@ issues: | |||||||
|     - ".*\\.my\\.go$" |     - ".*\\.my\\.go$" | ||||||
|     - lib/bad.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: |   # - `strict`: sources are excluded by following strictly the Go generated file convention. | ||||||
|   #   `^// Code generated .* DO NOT EDIT\.$` |   #    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. |   #    This line must appear before the first non-comment, non-blank text in the file. | ||||||
|   # https://go.dev/s/generatedcode |   #    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: |   # Default: lax | ||||||
|   # sources are excluded if they contain lines `autogenerated file`, `code generated`, `do not edit`, etc. |   exclude-generated: strict | ||||||
|   # Default: false |  | ||||||
|   exclude-generated-strict: true |  | ||||||
| 
 | 
 | ||||||
|   # The list of ids of default excludes to include or disable. |   # The list of ids of default excludes to include or disable. | ||||||
|   # https://golangci-lint.run/usage/false-positives/#default-exclusions |   # https://golangci-lint.run/usage/false-positives/#default-exclusions | ||||||
|  | |||||||
| @ -3518,10 +3518,10 @@ | |||||||
|           "type": "boolean", |           "type": "boolean", | ||||||
|           "default": false |           "default": false | ||||||
|         }, |         }, | ||||||
|         "exclude-generated-strict": { |         "exclude-generated": { | ||||||
|           "description": "To follow strict Go generated file convention", |           "description": "Mode of the generated files analysis.", | ||||||
|           "type": "boolean", |           "enum": ["lax", "strict", "disable"], | ||||||
|           "default": false |           "default": "lax" | ||||||
|         }, |         }, | ||||||
|         "exclude-dirs": { |         "exclude-dirs": { | ||||||
|           "description": "Which directories to exclude: issues from them won't be reported.", |           "description": "Which directories to exclude: issues from them won't be reported.", | ||||||
|  | |||||||
| @ -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, | 	internal.AddFlagAndBind(v, fs, fs.Bool, "exclude-dirs-use-default", "issues.exclude-dirs-use-default", true, | ||||||
| 		getDefaultDirectoryExcludeHelp()) | 		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 " + | 	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 " + | 		"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 " + | 		"of golangci-lint into existing large codebase.\nIt's not practical to fix all existing issues at " + | ||||||
|  | |||||||
| @ -108,12 +108,14 @@ type Issues struct { | |||||||
| 	ExcludeCaseSensitive   bool          `mapstructure:"exclude-case-sensitive"` | 	ExcludeCaseSensitive   bool          `mapstructure:"exclude-case-sensitive"` | ||||||
| 	ExcludePatterns        []string      `mapstructure:"exclude"` | 	ExcludePatterns        []string      `mapstructure:"exclude"` | ||||||
| 	ExcludeRules           []ExcludeRule `mapstructure:"exclude-rules"` | 	ExcludeRules           []ExcludeRule `mapstructure:"exclude-rules"` | ||||||
| 	ExcludeGeneratedStrict bool          `mapstructure:"exclude-generated-strict"` |  | ||||||
| 	UseDefaultExcludes     bool          `mapstructure:"exclude-use-default"` | 	UseDefaultExcludes     bool          `mapstructure:"exclude-use-default"` | ||||||
| 
 | 
 | ||||||
| 	ExcludeFiles          []string `mapstructure:"exclude-files"` | 	ExcludeGenerated string `mapstructure:"exclude-generated"` | ||||||
| 	ExcludeDirs           []string `mapstructure:"exclude-dirs"` | 
 | ||||||
| 	UseDefaultExcludeDirs bool     `mapstructure:"exclude-dirs-use-default"` | 	ExcludeFiles []string `mapstructure:"exclude-files"` | ||||||
|  | 	ExcludeDirs  []string `mapstructure:"exclude-dirs"` | ||||||
|  | 
 | ||||||
|  | 	UseDefaultExcludeDirs bool `mapstructure:"exclude-dirs-use-default"` | ||||||
| 
 | 
 | ||||||
| 	MaxIssuesPerLinter int `mapstructure:"max-issues-per-linter"` | 	MaxIssuesPerLinter int `mapstructure:"max-issues-per-linter"` | ||||||
| 	MaxSameIssues      int `mapstructure:"max-same-issues"` | 	MaxSameIssues      int `mapstructure:"max-same-issues"` | ||||||
| @ -124,6 +126,8 @@ type Issues struct { | |||||||
| 	Diff              bool   `mapstructure:"new"` | 	Diff              bool   `mapstructure:"new"` | ||||||
| 
 | 
 | ||||||
| 	NeedFix bool `mapstructure:"fix"` | 	NeedFix bool `mapstructure:"fix"` | ||||||
|  | 
 | ||||||
|  | 	ExcludeGeneratedStrict bool `mapstructure:"exclude-generated-strict"` // Deprecated: use ExcludeGenerated instead. | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (i *Issues) Validate() error { | func (i *Issues) Validate() error { | ||||||
|  | |||||||
| @ -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() | 	l.handleLinterOptionDeprecations() | ||||||
| 
 | 
 | ||||||
| 	return nil | 	return nil | ||||||
|  | |||||||
| @ -75,7 +75,7 @@ func NewRunner(log logutils.Log, cfg *config.Config, args []string, goenv *gouti | |||||||
| 			skipFilesProcessor, | 			skipFilesProcessor, | ||||||
| 			skipDirsProcessor, // must be after path prettifier | 			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. | 			// Must be before exclude because users see already marked output and configure excluding by it. | ||||||
| 			processors.NewIdentifierMarker(), | 			processors.NewIdentifierMarker(), | ||||||
|  | |||||||
| @ -12,6 +12,12 @@ import ( | |||||||
| 	"github.com/golangci/golangci-lint/pkg/result" | 	"github.com/golangci/golangci-lint/pkg/result" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | const ( | ||||||
|  | 	AutogeneratedModeLax     = "lax" | ||||||
|  | 	AutogeneratedModeStrict  = "strict" | ||||||
|  | 	AutogeneratedModeDisable = "disable" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
| const ( | const ( | ||||||
| 	genCodeGenerated = "code generated" | 	genCodeGenerated = "code generated" | ||||||
| 	genDoNotEdit     = "do not edit" | 	genDoNotEdit     = "do not edit" | ||||||
| @ -27,16 +33,16 @@ type fileSummary struct { | |||||||
| type AutogeneratedExclude struct { | type AutogeneratedExclude struct { | ||||||
| 	debugf logutils.DebugFunc | 	debugf logutils.DebugFunc | ||||||
| 
 | 
 | ||||||
| 	strict        bool | 	mode          string | ||||||
| 	strictPattern *regexp.Regexp | 	strictPattern *regexp.Regexp | ||||||
| 
 | 
 | ||||||
| 	fileSummaryCache map[string]*fileSummary | 	fileSummaryCache map[string]*fileSummary | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func NewAutogeneratedExclude(strict bool) *AutogeneratedExclude { | func NewAutogeneratedExclude(mode string) *AutogeneratedExclude { | ||||||
| 	return &AutogeneratedExclude{ | 	return &AutogeneratedExclude{ | ||||||
| 		debugf:           logutils.Debug(logutils.DebugKeyAutogenExclude), | 		debugf:           logutils.Debug(logutils.DebugKeyAutogenExclude), | ||||||
| 		strict:           strict, | 		mode:             mode, | ||||||
| 		strictPattern:    regexp.MustCompile(`^// Code generated .* DO NOT EDIT\.$`), | 		strictPattern:    regexp.MustCompile(`^// Code generated .* DO NOT EDIT\.$`), | ||||||
| 		fileSummaryCache: map[string]*fileSummary{}, | 		fileSummaryCache: map[string]*fileSummary{}, | ||||||
| 	} | 	} | ||||||
| @ -47,6 +53,10 @@ func (*AutogeneratedExclude) Name() string { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (p *AutogeneratedExclude) Process(issues []result.Issue) ([]result.Issue, error) { | func (p *AutogeneratedExclude) Process(issues []result.Issue) ([]result.Issue, error) { | ||||||
|  | 	if p.mode == AutogeneratedModeDisable { | ||||||
|  | 		return issues, nil | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	return filterIssuesErr(issues, p.shouldPassIssue) | 	return filterIssuesErr(issues, p.shouldPassIssue) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -71,7 +81,7 @@ func (p *AutogeneratedExclude) shouldPassIssue(issue *result.Issue) (bool, error | |||||||
| 	fs = &fileSummary{} | 	fs = &fileSummary{} | ||||||
| 	p.fileSummaryCache[issue.FilePath()] = fs | 	p.fileSummaryCache[issue.FilePath()] = fs | ||||||
| 
 | 
 | ||||||
| 	if p.strict { | 	if p.mode == AutogeneratedModeStrict { | ||||||
| 		var err error | 		var err error | ||||||
| 		fs.generated, err = p.isGeneratedFileStrict(issue.FilePath()) | 		fs.generated, err = p.isGeneratedFileStrict(issue.FilePath()) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
|  | |||||||
| @ -14,7 +14,7 @@ import ( | |||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func TestAutogeneratedExclude_isGeneratedFileLax_generated(t *testing.T) { | func TestAutogeneratedExclude_isGeneratedFileLax_generated(t *testing.T) { | ||||||
| 	p := NewAutogeneratedExclude(false) | 	p := NewAutogeneratedExclude(AutogeneratedModeLax) | ||||||
| 
 | 
 | ||||||
| 	comments := []string{ | 	comments := []string{ | ||||||
| 		`	// generated by stringer -type Pill pill.go; DO NOT EDIT`, | 		`	// 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) { | func TestAutogeneratedExclude_isGeneratedFileLax_nonGenerated(t *testing.T) { | ||||||
| 	p := NewAutogeneratedExclude(false) | 	p := NewAutogeneratedExclude(AutogeneratedModeLax) | ||||||
| 
 | 
 | ||||||
| 	comments := []string{ | 	comments := []string{ | ||||||
| 		"code not generated by", | 		"code not generated by", | ||||||
| @ -75,7 +75,7 @@ func TestAutogeneratedExclude_isGeneratedFileLax_nonGenerated(t *testing.T) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestAutogeneratedExclude_isGeneratedFileStrict(t *testing.T) { | func TestAutogeneratedExclude_isGeneratedFileStrict(t *testing.T) { | ||||||
| 	p := NewAutogeneratedExclude(true) | 	p := NewAutogeneratedExclude(AutogeneratedModeStrict) | ||||||
| 
 | 
 | ||||||
| 	testCases := []struct { | 	testCases := []struct { | ||||||
| 		desc     string | 		desc     string | ||||||
| @ -154,21 +154,21 @@ func Test_getComments_fileWithLongLine(t *testing.T) { | |||||||
| func Test_shouldPassIssue(t *testing.T) { | func Test_shouldPassIssue(t *testing.T) { | ||||||
| 	testCases := []struct { | 	testCases := []struct { | ||||||
| 		desc   string | 		desc   string | ||||||
| 		strict bool | 		mode   string | ||||||
| 		issue  *result.Issue | 		issue  *result.Issue | ||||||
| 		assert assert.BoolAssertionFunc | 		assert assert.BoolAssertionFunc | ||||||
| 	}{ | 	}{ | ||||||
| 		{ | 		{ | ||||||
| 			desc:   "typecheck issue", | 			desc: "typecheck issue", | ||||||
| 			strict: false, | 			mode: AutogeneratedModeLax, | ||||||
| 			issue: &result.Issue{ | 			issue: &result.Issue{ | ||||||
| 				FromLinter: "typecheck", | 				FromLinter: "typecheck", | ||||||
| 			}, | 			}, | ||||||
| 			assert: assert.True, | 			assert: assert.True, | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			desc:   "lax ", | 			desc: "lax ", | ||||||
| 			strict: false, | 			mode: AutogeneratedModeLax, | ||||||
| 			issue: &result.Issue{ | 			issue: &result.Issue{ | ||||||
| 				FromLinter: "example", | 				FromLinter: "example", | ||||||
| 				Pos: token.Position{ | 				Pos: token.Position{ | ||||||
| @ -178,8 +178,8 @@ func Test_shouldPassIssue(t *testing.T) { | |||||||
| 			assert: assert.False, | 			assert: assert.False, | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			desc:   "strict ", | 			desc: "strict ", | ||||||
| 			strict: true, | 			mode: AutogeneratedModeStrict, | ||||||
| 			issue: &result.Issue{ | 			issue: &result.Issue{ | ||||||
| 				FromLinter: "example", | 				FromLinter: "example", | ||||||
| 				Pos: token.Position{ | 				Pos: token.Position{ | ||||||
| @ -195,7 +195,7 @@ func Test_shouldPassIssue(t *testing.T) { | |||||||
| 		t.Run(test.desc, func(t *testing.T) { | 		t.Run(test.desc, func(t *testing.T) { | ||||||
| 			t.Parallel() | 			t.Parallel() | ||||||
| 
 | 
 | ||||||
| 			p := NewAutogeneratedExclude(test.strict) | 			p := NewAutogeneratedExclude(test.mode) | ||||||
| 
 | 
 | ||||||
| 			pass, err := p.shouldPassIssue(test.issue) | 			pass, err := p.shouldPassIssue(test.issue) | ||||||
| 			require.NoError(t, err) | 			require.NoError(t, err) | ||||||
| @ -213,13 +213,13 @@ func Test_shouldPassIssue_error(t *testing.T) { | |||||||
| 
 | 
 | ||||||
| 	testCases := []struct { | 	testCases := []struct { | ||||||
| 		desc     string | 		desc     string | ||||||
| 		strict   bool | 		mode     string | ||||||
| 		issue    *result.Issue | 		issue    *result.Issue | ||||||
| 		expected string | 		expected string | ||||||
| 	}{ | 	}{ | ||||||
| 		{ | 		{ | ||||||
| 			desc:   "non-existing file (lax)", | 			desc: "non-existing file (lax)", | ||||||
| 			strict: false, | 			mode: AutogeneratedModeLax, | ||||||
| 			issue: &result.Issue{ | 			issue: &result.Issue{ | ||||||
| 				FromLinter: "example", | 				FromLinter: "example", | ||||||
| 				Pos: token.Position{ | 				Pos: token.Position{ | ||||||
| @ -230,8 +230,8 @@ func Test_shouldPassIssue_error(t *testing.T) { | |||||||
| 				filepath.FromSlash("no-existing.go"), notFoundMsg), | 				filepath.FromSlash("no-existing.go"), notFoundMsg), | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			desc:   "non-existing file (strict)", | 			desc: "non-existing file (strict)", | ||||||
| 			strict: true, | 			mode: AutogeneratedModeStrict, | ||||||
| 			issue: &result.Issue{ | 			issue: &result.Issue{ | ||||||
| 				FromLinter: "example", | 				FromLinter: "example", | ||||||
| 				Pos: token.Position{ | 				Pos: token.Position{ | ||||||
| @ -248,7 +248,7 @@ func Test_shouldPassIssue_error(t *testing.T) { | |||||||
| 		t.Run(test.desc, func(t *testing.T) { | 		t.Run(test.desc, func(t *testing.T) { | ||||||
| 			t.Parallel() | 			t.Parallel() | ||||||
| 
 | 
 | ||||||
| 			p := NewAutogeneratedExclude(test.strict) | 			p := NewAutogeneratedExclude(test.mode) | ||||||
| 
 | 
 | ||||||
| 			pass, err := p.shouldPassIssue(test.issue) | 			pass, err := p.shouldPassIssue(test.issue) | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Ludovic Fernandez
						Ludovic Fernandez