Skip to content

Commit f8bf84e

Browse files
committed
feat: add gomodreplace linter.
1 parent 507703b commit f8bf84e

File tree

7 files changed

+91
-1
lines changed

7 files changed

+91
-1
lines changed

.golangci.example.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,12 @@ linters-settings:
449449
servingv1: knative.dev/serving/pkg/apis/serving/v1
450450
# using `autoscalingv1alpha1` alias for `knative.dev/serving/pkg/apis/autoscaling/v1alpha1` package
451451
autoscalingv1alpha1: knative.dev/serving/pkg/apis/autoscaling/v1alpha1
452+
gomodreplace:
453+
# Allow local `replace` directives. Default is false.
454+
local: false
455+
# List of allowed `replace` directives. Default is empty.
456+
allow-list:
457+
- launchpad.net/gocheck
452458

453459
# The custom section can be used to define linter plugins to be loaded at runtime. See README doc
454460
# for more info.

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ require (
4141
github.com/kulti/thelper v0.4.0
4242
github.com/kunwardeep/paralleltest v1.0.2
4343
github.com/kyoh86/exportloopref v0.1.8
44+
github.com/ldez/gomodreplace v0.1.0
4445
github.com/maratori/testpackage v1.0.1
4546
github.com/matoous/godox v0.0.0-20210227103229-6504466cf951 // v1.0
4647
github.com/mattn/go-colorable v0.1.8

go.sum

Lines changed: 4 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/config/config.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,7 @@ type LintersSettings struct {
276276
Predeclared PredeclaredSettings
277277
Cyclop Cyclop
278278
ImportAs ImportAsSettings
279+
GoModReplace GoModReplaceSettings
279280

280281
Custom map[string]CustomLinterSettings
281282
}
@@ -464,6 +465,11 @@ type Cyclop struct {
464465

465466
type ImportAsSettings map[string]string
466467

468+
type GoModReplaceSettings struct {
469+
AllowList []string `mapstructure:"allow-list"`
470+
Local bool `mapstructure:"local"`
471+
}
472+
467473
var defaultLintersSettings = LintersSettings{
468474
Lll: LllSettings{
469475
LineLength: 120,

pkg/golinters/gomodreplace.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package golinters
2+
3+
import (
4+
"sync"
5+
6+
"github.com/golangci/golangci-lint/pkg/config"
7+
"github.com/ldez/gomodreplace"
8+
"golang.org/x/tools/go/analysis"
9+
10+
"github.com/golangci/golangci-lint/pkg/golinters/goanalysis"
11+
"github.com/golangci/golangci-lint/pkg/lint/linter"
12+
"github.com/golangci/golangci-lint/pkg/result"
13+
)
14+
15+
const goModReplaceName = "gomodreplace"
16+
17+
// NewGoModReplace returns a new gomodreplace linter.
18+
func NewGoModReplace(settings *config.GoModReplaceSettings) *goanalysis.Linter {
19+
var issues []goanalysis.Issue
20+
var mu sync.Mutex
21+
22+
var opts gomodreplace.Options
23+
if settings != nil {
24+
opts.AllowLocal = settings.Local
25+
opts.AllowList = settings.AllowList
26+
}
27+
28+
analyzer := &analysis.Analyzer{
29+
Name: goanalysis.TheOnlyAnalyzerName,
30+
Doc: goanalysis.TheOnlyanalyzerDoc,
31+
}
32+
33+
return goanalysis.NewLinter(
34+
goModReplaceName,
35+
"Manage the use of replace directives in go.mod.",
36+
[]*analysis.Analyzer{analyzer},
37+
nil,
38+
).WithContextSetter(func(lintCtx *linter.Context) {
39+
analyzer.Run = func(pass *analysis.Pass) (interface{}, error) {
40+
results, err := gomodreplace.Analyze(opts)
41+
if err != nil {
42+
lintCtx.Log.Warnf("running %s failed: %s: "+
43+
"if you are not using go modules it is suggested to disable this linter", goModReplaceName, err)
44+
return nil, nil
45+
}
46+
47+
mu.Lock()
48+
49+
for _, p := range results {
50+
issues = append(issues, goanalysis.NewIssue(&result.Issue{
51+
FromLinter: goModReplaceName,
52+
Pos: p.Start,
53+
Text: p.Reason,
54+
}, pass))
55+
}
56+
57+
mu.Unlock()
58+
59+
return nil, nil
60+
}
61+
}).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
62+
return issues
63+
}).WithLoadMode(goanalysis.LoadModeSyntax)
64+
}

pkg/lint/lintersdb/manager.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
100100
var reviveCfg *config.ReviveSettings
101101
var cyclopCfg *config.Cyclop
102102
var importAsCfg *config.ImportAsSettings
103+
var goModReplaceCfg *config.GoModReplaceSettings
103104
if m.cfg != nil {
104105
govetCfg = &m.cfg.LintersSettings.Govet
105106
testpackageCfg = &m.cfg.LintersSettings.Testpackage
@@ -112,6 +113,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
112113
reviveCfg = &m.cfg.LintersSettings.Revive
113114
cyclopCfg = &m.cfg.LintersSettings.Cyclop
114115
importAsCfg = &m.cfg.LintersSettings.ImportAs
116+
goModReplaceCfg = &m.cfg.LintersSettings.GoModReplace
115117
}
116118
const megacheckName = "megacheck"
117119
lcs := []*linter.Config{
@@ -394,6 +396,10 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
394396
WithPresets(linter.PresetStyle).
395397
WithLoadForGoAnalysis().
396398
WithURL("https://github.com/gostaticanalysis/forcetypeassert"),
399+
linter.NewConfig(golinters.NewGoModReplace(goModReplaceCfg)).
400+
WithPresets(linter.PresetStyle).
401+
WithLoadForGoAnalysis().
402+
WithURL("https://github.com/ldez/gomodreplace"),
397403

398404
// nolintlint must be last because it looks at the results of all the previous linters for unused nolint directives
399405
linter.NewConfig(golinters.NewNoLintLint()).

pkg/result/processors/autogenerated_exclude.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ func (p *AutogeneratedExclude) shouldPassIssue(i *result.Issue) (bool, error) {
5353
return true, nil
5454
}
5555

56+
if filepath.Base(i.FilePath()) == "go.mod" {
57+
return true, nil
58+
}
59+
5660
if isSpecialAutogeneratedFile(i.FilePath()) {
5761
return false, nil
5862
}

0 commit comments

Comments
 (0)