Skip to content

feat: add errchkjson linter #2349

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .golangci.example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,13 @@ linters-settings:
# should ignore tests (default false)
skip-tests: false

errchkjson:
# with omit-safe set to true, errchkjson does not warn about errors from json encoding functions that are safe
# to be ignored, because they are not possible to happen (default false)
omit-safe: false
# if report-no-exported is true, encoding a struct without exported fields is reported as issue (default false)
report-no-exported: false

dogsled:
# checks assignments with too many blank identifiers; default is 2
max-blank-identifiers: 2
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ require (
github.com/blizzy78/varnamelen v0.4.0
github.com/bombsimon/wsl/v3 v3.3.0
github.com/breml/bidichk v0.2.1
github.com/breml/errchkjson v0.2.0
github.com/butuzov/ireturn v0.1.1
github.com/charithe/durationcheck v0.0.9
github.com/daixiang0/gci v0.2.9
Expand Down
2 changes: 2 additions & 0 deletions go.sum

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions pkg/config/linters_settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ type LintersSettings struct {
Dogsled DogsledSettings
Dupl DuplSettings
Errcheck ErrcheckSettings
ErrChkJSON ErrChkJSONSettings
ErrorLint ErrorLintSettings
Exhaustive ExhaustiveSettings
ExhaustiveStruct ExhaustiveStructSettings
Expand Down Expand Up @@ -165,6 +166,11 @@ type Cyclop struct {
SkipTests bool `mapstructure:"skip-tests"`
}

type ErrChkJSONSettings struct {
OmitSafe bool `mapstructure:"omit-safe"`
ReportNoExported bool `mapstructure:"report-no-exported"`
}

type DepGuardSettings struct {
ListType string `mapstructure:"list-type"`
Packages []string
Expand Down
80 changes: 80 additions & 0 deletions pkg/golinters/errchkjson.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package golinters

import (
"github.com/breml/errchkjson"
"golang.org/x/tools/go/analysis"

"github.com/golangci/golangci-lint/pkg/config"
"github.com/golangci/golangci-lint/pkg/golinters/goanalysis"
"github.com/golangci/golangci-lint/pkg/lint/linter"
)

func NewErrChkJSONFuncName(linters *config.Linters,
cfg *config.ErrChkJSONSettings,
errcheckCfg *config.ErrcheckSettings,
) *goanalysis.Linter {
a := errchkjson.NewAnalyzer()

var omitSafe bool
var reportNoExported bool
if cfg != nil {
omitSafe = cfg.OmitSafe
reportNoExported = cfg.ReportNoExported
}

// Modify errcheck config if this linter is enabled and OmitSafe is false
if isEnabled(linters, a.Name) && !omitSafe {
if errcheckCfg == nil {
errcheckCfg = &config.ErrcheckSettings{}
}
errcheckCfg.ExcludeFunctions = append(errcheckCfg.ExcludeFunctions,
"encoding/json.Marshal",
"encoding/json.MarshalIndent",
"(*encoding/json.Encoder).Encode",
)
}

Comment on lines +25 to +36
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I already said, a linter cannot interact with another linter.

The fact to create links between 2 linters means that 1 linter can be a part of the other linter.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ldez Thanks for having a look at my PR again, I really appreciate it.

As I already said, a linter cannot interact with another linter.

I do not exactly understand what you mean by "cannot". My understanding of "cannot" is, that it is technically not possible. But I don't think this is true, because the implementation of the PR does work as intended.
Or do you mean "cannot" in the sense of "MUST NOT" (or "SHALL NOT") as defined in https://datatracker.ietf.org/doc/html/rfc2119? That is: it is prohibited by guideline (even though, it could technically be possible).
If it is the later, I would like to know, if this guideline is written down somewhere and I would like to better understand the reasoning behind it. I understand, that in some cases the exclude patterns have been used to handle exactly such situations, e.g. for duplicates between gosec and errcheck (see EXC0008).

The fact to create links between 2 linters means that 1 linter can be a part of the other linter.
So what is your recommendation? I can see the following options (in no particular order):

  1. Add the functionality of errchkjson to errcheck. I do not know, if this special handling is in the scope of errcheck, because it defines its scope pretty general with "... checking for unchecked errors ..." and as far as I can tell, there is currently no special handling based on the input arguments in errcheck. Additionally, errchkjson also performs checks on the input types for the json encoding functions (can a type be encoded) and this is cleary out of scope for errcheck and would have to stay in this linter.
  2. Extend errchkjson to also include everything that is done by errcheck so the user is free to choose one linter or the other depending of the fact, if he wants to have the special handling of the json encoding functions. In my opinion, this does not make any sense.
  3. We add the necessary exceptions to the ExcludePatterns in pkg/config/issues.go, but I am not sure how exactly this would work given the fact, that the exclude pattern should only be added under special circumstances (both linters enabled and omitSafe not true).
  4. Add the errchkjson linter (without adding config settings to errcheck) and mention in the documentation / .golangci-lint.yaml, how the user can add the necessary excludes. This would work, but it is not really user friendly.
  5. Keep the current implementation.

cfgMap := map[string]map[string]interface{}{}
cfgMap[a.Name] = map[string]interface{}{
"omit-safe": omitSafe,
"report-no-exported": reportNoExported,
}

return goanalysis.NewLinter(
"errchkjson",
"Checks types passed to the json encoding functions. "+
"Reports unsupported types and reports occations, where the check for the returned error can be omitted.",
[]*analysis.Analyzer{a},
cfgMap,
).WithLoadMode(goanalysis.LoadModeTypesInfo)
}

func isEnabled(linters *config.Linters, linterName string) bool {
if linters != nil {
var enabled bool
for _, linter := range linters.Enable {
if linter == linterName {
enabled = true
break
}
}
var disabled bool
for _, linter := range linters.Disable {
if linter == linterName {
disabled = true
break
}
}
var presetEnabled bool
for _, preset := range linters.Presets {
if preset == linter.PresetBugs || preset == linter.PresetUnused {
presetEnabled = true
break
}
}
return enabled ||
linters.EnableAll && !disabled ||
presetEnabled && !disabled
}
return false
}
12 changes: 12 additions & 0 deletions pkg/lint/lintersdb/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,12 @@ func enableLinterConfigs(lcs []*linter.Config, isEnabled func(lc *linter.Config)

//nolint:funlen
func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
var linters *config.Linters

var bidichkCfg *config.BiDiChkSettings
var cyclopCfg *config.Cyclop
var errcheckCfg *config.ErrcheckSettings
var errchkjsonCfg *config.ErrChkJSONSettings
var errorlintCfg *config.ErrorLintSettings
var exhaustiveCfg *config.ExhaustiveSettings
var exhaustiveStructCfg *config.ExhaustiveStructSettings
Expand All @@ -127,8 +131,11 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
var nlreturnCfg *config.NlreturnSettings

if m.cfg != nil {
linters = &m.cfg.Linters
bidichkCfg = &m.cfg.LintersSettings.BiDiChk
cyclopCfg = &m.cfg.LintersSettings.Cyclop
errcheckCfg = &m.cfg.LintersSettings.Errcheck
errchkjsonCfg = &m.cfg.LintersSettings.ErrChkJSON
errorlintCfg = &m.cfg.LintersSettings.ErrorLint
exhaustiveCfg = &m.cfg.LintersSettings.Exhaustive
exhaustiveStructCfg = &m.cfg.LintersSettings.ExhaustiveStruct
Expand Down Expand Up @@ -173,6 +180,11 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
WithLoadForGoAnalysis().
WithPresets(linter.PresetPerformance, linter.PresetBugs).
WithURL("https://github.com/sonatard/noctx"),
linter.NewConfig(golinters.NewErrChkJSONFuncName(linters, errchkjsonCfg, errcheckCfg)).
WithSince("1.44.0").
WithPresets(linter.PresetBugs, linter.PresetUnused).
WithLoadForGoAnalysis().
WithURL("https://github.com/breml/errchkjson"),
linter.NewConfig(golinters.NewErrcheck()).
WithSince("v1.0.0").
WithLoadForGoAnalysis().
Expand Down
2 changes: 2 additions & 0 deletions test/testdata/configs/errchkjson.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
issues:
max-issues-per-linter: 100
3 changes: 3 additions & 0 deletions test/testdata/configs/errchkjson_no_exported.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
linters-settings:
errchkjson:
report-no-exported: true
5 changes: 5 additions & 0 deletions test/testdata/configs/errchkjson_omit_safe.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
issues:
max-issues-per-linter: 100
linters-settings:
errchkjson:
omit-safe: true
Loading