From bce3dfd4609bed9a2f694532c13430bd319d39f2 Mon Sep 17 00:00:00 2001 From: Fata Nugraha <4775076+fatanugraha@users.noreply.github.com> Date: Mon, 9 Oct 2023 23:45:29 +0800 Subject: [PATCH] unused: support passing in options (#4086) Co-authored-by: Fernandez Ludovic --- .golangci.reference.yml | 23 ++++++++++++++ pkg/config/linters_settings.go | 20 ++++++++++++ pkg/golinters/unused.go | 56 ++++++++++++++++++++++------------ pkg/lint/lintersdb/manager.go | 9 ++---- 4 files changed, 83 insertions(+), 25 deletions(-) diff --git a/.golangci.reference.yml b/.golangci.reference.yml index b8a8a98f..511491f6 100644 --- a/.golangci.reference.yml +++ b/.golangci.reference.yml @@ -2016,6 +2016,29 @@ linters-settings: # Default: false check-exported: true + unused: + # Mark all struct fields that have been written to as used. + # Default: true + field-writes-are-uses: false + # Treat IncDec statement (e.g. `i++` or `i--`) as both read and write operation instead of just write. + # Default: false + post-statements-are-reads: true + # Mark all exported identifiers as used. + # Default: true + exported-is-used: false + # Mark all exported fields as used. + # default: true + exported-fields-are-used: false + # Mark all function parameters as used. + # default: true + parameters-are-used: false + # Mark all local variables as used. + # default: true + local-variables-are-used: false + # Mark all identifiers inside generated files as used. + # Default: true + generated-is-used: false + varcheck: # Check usage of exported fields and variables. # Default: false diff --git a/pkg/config/linters_settings.go b/pkg/config/linters_settings.go index a70f37da..004b0525 100644 --- a/pkg/config/linters_settings.go +++ b/pkg/config/linters_settings.go @@ -126,6 +126,15 @@ var defaultLintersSettings = LintersSettings{ Unparam: UnparamSettings{ Algo: "cha", }, + Unused: UnusedSettings{ + FieldWritesAreUses: true, + PostStatementsAreReads: false, + ExportedIsUsed: true, + ExportedFieldsAreUsed: true, + ParametersAreUsed: true, + LocalVariablesAreUsed: true, + GeneratedIsUsed: true, + }, UseStdlibVars: UseStdlibVarsSettings{ HTTPMethod: true, HTTPStatusCode: true, @@ -223,6 +232,7 @@ type LintersSettings struct { Testpackage TestpackageSettings Thelper ThelperSettings Unparam UnparamSettings + Unused UnusedSettings UseStdlibVars UseStdlibVarsSettings Varcheck VarCheckSettings Varnamelen VarnamelenSettings @@ -794,6 +804,16 @@ type UnparamSettings struct { Algo string } +type UnusedSettings struct { + FieldWritesAreUses bool `mapstructure:"field-writes-are-uses"` + PostStatementsAreReads bool `mapstructure:"post-statements-are-reads"` + ExportedIsUsed bool `mapstructure:"exported-is-used"` + ExportedFieldsAreUsed bool `mapstructure:"exported-fields-are-used"` + ParametersAreUsed bool `mapstructure:"parameters-are-used"` + LocalVariablesAreUsed bool `mapstructure:"local-variables-are-used"` + GeneratedIsUsed bool `mapstructure:"generated-is-used"` +} + type VarCheckSettings struct { CheckExportedFields bool `mapstructure:"exported-fields"` } diff --git a/pkg/golinters/unused.go b/pkg/golinters/unused.go index aa9374d3..89ae7e98 100644 --- a/pkg/golinters/unused.go +++ b/pkg/golinters/unused.go @@ -5,6 +5,9 @@ import ( "sync" "golang.org/x/tools/go/analysis" + "honnef.co/go/tools/analysis/facts/directives" + "honnef.co/go/tools/analysis/facts/generated" + "honnef.co/go/tools/analysis/lint" "honnef.co/go/tools/unused" "github.com/golangci/golangci-lint/pkg/config" @@ -15,11 +18,7 @@ import ( const unusedName = "unused" -type UnusedSettings struct { - GoVersion string -} - -func NewUnused(settings *config.StaticCheckSettings) *goanalysis.Linter { +func NewUnused(settings *config.UnusedSettings, scSettings *config.StaticCheckSettings) *goanalysis.Linter { var mu sync.Mutex var resIssues []goanalysis.Issue @@ -28,11 +27,7 @@ func NewUnused(settings *config.StaticCheckSettings) *goanalysis.Linter { Doc: unused.Analyzer.Analyzer.Doc, Requires: unused.Analyzer.Analyzer.Requires, Run: func(pass *analysis.Pass) (any, error) { - issues, err := runUnused(pass) - if err != nil { - return nil, err - } - + issues := runUnused(pass, settings) if len(issues) == 0 { return nil, nil } @@ -45,7 +40,7 @@ func NewUnused(settings *config.StaticCheckSettings) *goanalysis.Linter { }, } - setAnalyzerGoVersion(analyzer, getGoVersion(settings)) + setAnalyzerGoVersion(analyzer, getGoVersion(scSettings)) return goanalysis.NewLinter( unusedName, @@ -57,21 +52,18 @@ func NewUnused(settings *config.StaticCheckSettings) *goanalysis.Linter { }).WithLoadMode(goanalysis.LoadModeTypesInfo) } -func runUnused(pass *analysis.Pass) ([]goanalysis.Issue, error) { - res, err := unused.Analyzer.Analyzer.Run(pass) - if err != nil { - return nil, err - } +func runUnused(pass *analysis.Pass, cfg *config.UnusedSettings) []goanalysis.Issue { + res := getUnusedResults(pass, cfg) used := make(map[string]bool) - for _, obj := range res.(unused.Result).Used { + for _, obj := range res.Used { used[fmt.Sprintf("%s %d %s", obj.Position.Filename, obj.Position.Line, obj.Name)] = true } var issues []goanalysis.Issue // Inspired by https://github.com/dominikh/go-tools/blob/d694aadcb1f50c2d8ac0a1dd06217ebb9f654764/lintcmd/lint.go#L177-L197 - for _, object := range res.(unused.Result).Unused { + for _, object := range res.Unused { if object.Kind == "type param" { continue } @@ -90,5 +82,31 @@ func runUnused(pass *analysis.Pass) ([]goanalysis.Issue, error) { issues = append(issues, issue) } - return issues, nil + return issues +} + +func getUnusedResults(pass *analysis.Pass, settings *config.UnusedSettings) unused.Result { + opts := unused.Options{ + FieldWritesAreUses: settings.FieldWritesAreUses, + PostStatementsAreReads: settings.PostStatementsAreReads, + ExportedIsUsed: settings.ExportedIsUsed, + ExportedFieldsAreUsed: settings.ExportedFieldsAreUsed, + ParametersAreUsed: settings.ParametersAreUsed, + LocalVariablesAreUsed: settings.LocalVariablesAreUsed, + GeneratedIsUsed: settings.GeneratedIsUsed, + } + + // ref: https://github.com/dominikh/go-tools/blob/4ec1f474ca6c0feb8e10a8fcca4ab95f5b5b9881/internal/cmd/unused/unused.go#L68 + nodes := unused.Graph(pass.Fset, + pass.Files, + pass.Pkg, + pass.TypesInfo, + pass.ResultOf[directives.Analyzer].([]lint.Directive), + pass.ResultOf[generated.Analyzer].(map[string]generated.Generator), + opts, + ) + + sg := unused.SerializedGraph{} + sg.Merge(nodes) + return sg.Results() } diff --git a/pkg/lint/lintersdb/manager.go b/pkg/lint/lintersdb/manager.go index 71dc3ceb..3eb60c28 100644 --- a/pkg/lint/lintersdb/manager.go +++ b/pkg/lint/lintersdb/manager.go @@ -137,7 +137,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config { testpackageCfg *config.TestpackageSettings thelperCfg *config.ThelperSettings unparamCfg *config.UnparamSettings - unusedCfg *config.StaticCheckSettings + unusedCfg *config.UnusedSettings usestdlibvars *config.UseStdlibVarsSettings varcheckCfg *config.VarCheckSettings varnamelenCfg *config.VarnamelenSettings @@ -218,7 +218,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config { testpackageCfg = &m.cfg.LintersSettings.Testpackage thelperCfg = &m.cfg.LintersSettings.Thelper unparamCfg = &m.cfg.LintersSettings.Unparam - unusedCfg = new(config.StaticCheckSettings) + unusedCfg = &m.cfg.LintersSettings.Unused usestdlibvars = &m.cfg.LintersSettings.UseStdlibVars varcheckCfg = &m.cfg.LintersSettings.Varcheck varnamelenCfg = &m.cfg.LintersSettings.Varnamelen @@ -247,9 +247,6 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config { if stylecheckCfg != nil && stylecheckCfg.GoVersion != "" { stylecheckCfg.GoVersion = trimGoVersion(m.cfg.Run.Go) } - if unusedCfg != nil && unusedCfg.GoVersion == "" { - unusedCfg.GoVersion = trimGoVersion(m.cfg.Run.Go) - } } const megacheckName = "megacheck" @@ -846,7 +843,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config { WithLoadForGoAnalysis(). WithURL("https://github.com/mvdan/unparam"), - linter.NewConfig(golinters.NewUnused(unusedCfg)). + linter.NewConfig(golinters.NewUnused(unusedCfg, staticcheckCfg)). WithEnabledByDefault(). WithSince("v1.20.0"). WithLoadForGoAnalysis().