Skip to content

Commit 0f57128

Browse files
committed
feat: add the gosmopolitan linter
1 parent abe878d commit 0f57128

16 files changed

+225
-0
lines changed

.golangci.reference.yml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -940,6 +940,39 @@ linters-settings:
940940
# Default: "0600"
941941
G306: "0600"
942942

943+
gosmopolitan:
944+
# Allow and ignore `time.Local` usages.
945+
#
946+
# Default: false
947+
allow-time-local: true
948+
# List of fully qualified names in the `full/pkg/path.name` form, to act
949+
# as "i18n escape hatches".
950+
#
951+
# String literals inside call-like expressions to, or struct literals of
952+
# those names, are exempt from the writing system check.
953+
#
954+
# Default: []
955+
escape-hatches:
956+
- 'github.com/nicksnyder/go-i18n/v2/i18n.Message'
957+
- 'example.com/your/project/i18n/markers.Raw'
958+
- 'example.com/your/project/i18n/markers.OK'
959+
- 'example.com/your/project/i18n/markers.TODO'
960+
- 'command-line-arguments.Simple'
961+
# Ignore test files.
962+
#
963+
# Default: true
964+
ignore-tests: false
965+
# List of Unicode scripts to watch for any usage in string literals.
966+
# If empty the default of ["Han"] will be used.
967+
#
968+
# Default: ["Han"]
969+
watch-for-scripts:
970+
- Devanagari
971+
- Han
972+
- Hangul
973+
- Hiragana
974+
- Katakana
975+
943976
govet:
944977
# Report about shadowed variables.
945978
# Default: false
@@ -2042,6 +2075,7 @@ linters:
20422075
- goprintffuncname
20432076
- gosec
20442077
- gosimple
2078+
- gosmopolitan
20452079
- govet
20462080
- grouper
20472081
- ifshort
@@ -2152,6 +2186,7 @@ linters:
21522186
- goprintffuncname
21532187
- gosec
21542188
- gosimple
2189+
- gosmopolitan
21552190
- govet
21562191
- grouper
21572192
- ifshort

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ require (
107107
github.com/ultraware/whitespace v0.0.5
108108
github.com/uudashr/gocognit v1.0.6
109109
github.com/valyala/quicktemplate v1.7.0
110+
github.com/xen0n/gosmopolitan v1.2.1
110111
github.com/yagipy/maintidx v1.0.0
111112
github.com/yeya24/promlinter v0.2.0
112113
gitlab.com/bosi/decorder v0.2.3

go.sum

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/config/linters_settings.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,12 @@ var defaultLintersSettings = LintersSettings{
6161
Gosec: GoSecSettings{
6262
Concurrency: runtime.NumCPU(),
6363
},
64+
Gosmopolitan: GosmopolitanSettings{
65+
AllowTimeLocal: false,
66+
EscapeHatches: []string{},
67+
IgnoreTests: true,
68+
WatchForScripts: []string{"Han"},
69+
},
6470
Ifshort: IfshortSettings{
6571
MaxDeclLines: 1,
6672
MaxDeclChars: 30,
@@ -167,6 +173,7 @@ type LintersSettings struct {
167173
Gomodguard GoModGuardSettings
168174
Gosec GoSecSettings
169175
Gosimple StaticCheckSettings
176+
Gosmopolitan GosmopolitanSettings
170177
Govet GovetSettings
171178
Grouper GrouperSettings
172179
Ifshort IfshortSettings
@@ -447,6 +454,13 @@ type GoSecSettings struct {
447454
Concurrency int `mapstructure:"concurrency"`
448455
}
449456

457+
type GosmopolitanSettings struct {
458+
AllowTimeLocal bool `mapstructure:"allow-time-local"`
459+
EscapeHatches []string `mapstructure:"escape-hatches"`
460+
IgnoreTests bool `mapstructure:"ignore-tests"`
461+
WatchForScripts []string `mapstructure:"watch-for-scripts"`
462+
}
463+
450464
type GovetSettings struct {
451465
Go string `mapstructure:"-"`
452466
CheckShadowing bool `mapstructure:"check-shadowing"`

pkg/golinters/gosmopolitan.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package golinters
2+
3+
import (
4+
"strings"
5+
6+
"github.com/xen0n/gosmopolitan"
7+
"golang.org/x/tools/go/analysis"
8+
9+
"github.com/golangci/golangci-lint/pkg/config"
10+
"github.com/golangci/golangci-lint/pkg/golinters/goanalysis"
11+
)
12+
13+
func NewGosmopolitan(s *config.GosmopolitanSettings) *goanalysis.Linter {
14+
a := gosmopolitan.NewAnalyzer()
15+
16+
cfgMap := map[string]map[string]interface{}{}
17+
if s != nil {
18+
cfgMap[a.Name] = map[string]interface{}{
19+
"allowtimelocal": s.AllowTimeLocal,
20+
"escapehatches": strings.Join(s.EscapeHatches, ","),
21+
"lookattests": !s.IgnoreTests,
22+
"watchforscripts": strings.Join(s.WatchForScripts, ","),
23+
}
24+
}
25+
26+
return goanalysis.NewLinter(
27+
a.Name,
28+
a.Doc,
29+
[]*analysis.Analyzer{a},
30+
cfgMap,
31+
).WithLoadMode(goanalysis.LoadModeTypesInfo)
32+
}

pkg/lint/lintersdb/manager.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
135135
gomodguardCfg *config.GoModGuardSettings
136136
gosecCfg *config.GoSecSettings
137137
gosimpleCfg *config.StaticCheckSettings
138+
gosmopolitanCfg *config.GosmopolitanSettings
138139
govetCfg *config.GovetSettings
139140
grouperCfg *config.GrouperSettings
140141
ifshortCfg *config.IfshortSettings
@@ -213,6 +214,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
213214
gomodguardCfg = &m.cfg.LintersSettings.Gomodguard
214215
gosecCfg = &m.cfg.LintersSettings.Gosec
215216
gosimpleCfg = &m.cfg.LintersSettings.Gosimple
217+
gosmopolitanCfg = &m.cfg.LintersSettings.Gosmopolitan
216218
govetCfg = &m.cfg.LintersSettings.Govet
217219
grouperCfg = &m.cfg.LintersSettings.Grouper
218220
ifshortCfg = &m.cfg.LintersSettings.Ifshort
@@ -558,6 +560,12 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
558560
WithAlternativeNames(megacheckName).
559561
WithURL("https://github.com/dominikh/go-tools/tree/master/simple"),
560562

563+
linter.NewConfig(golinters.NewGosmopolitan(gosmopolitanCfg)).
564+
WithSince("v1.51.0").
565+
WithLoadForGoAnalysis().
566+
WithPresets(linter.PresetBugs).
567+
WithURL("https://github.com/xen0n/gosmopolitan"),
568+
561569
linter.NewConfig(golinters.NewGovet(govetCfg)).
562570
WithSince("v1.0.0").
563571
WithLoadForGoAnalysis().
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
linters-settings:
2+
gosmopolitan:
3+
allow-time-local: true
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
linters-settings:
2+
gosmopolitan:
3+
ignore-tests: false
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
linters-settings:
2+
gosmopolitan:
3+
escape-hatches:
4+
- 'command-line-arguments.A'
5+
- 'command-line-arguments.B'
6+
- 'command-line-arguments.C'
7+
- 'command-line-arguments.D'
8+
- 'fmt.Println'
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
linters-settings:
2+
gosmopolitan:
3+
watch-for-scripts:
4+
- Hiragana
5+
- Katakana
6+
- Latin
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
//golangcitest:args -Egosmopolitan
2+
//golangcitest:config_path testdata/configs/gosmopolitan_allow_time_local.yml
3+
//golangcitest:expected_exitcode 0
4+
package testdata
5+
6+
import (
7+
"time"
8+
)
9+
10+
func main() {
11+
_ = time.Local
12+
}

test/testdata/gosmopolitan_default.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
//golangcitest:args -Egosmopolitan
2+
package testdata
3+
4+
import (
5+
"fmt"
6+
"time"
7+
)
8+
9+
type col struct {
10+
// struct tag should not get reported
11+
Foo string `gorm:"column:bar;not null;comment:'不应该报告这一行'"`
12+
}
13+
14+
func main() {
15+
fmt.Println("hello world")
16+
fmt.Println("你好,世界") // want `string literal contains rune in Han script`
17+
fmt.Println("こんにちは、セカイ")
18+
19+
_ = col{Foo: "hello"}
20+
_ = col{Foo: "你好"} // want `string literal contains rune in Han script`
21+
22+
x := time.Local // want `usage of time.Local`
23+
_ = time.Now().In(x)
24+
_ = time.Date(2023, 1, 2, 3, 4, 5, 678901234, time.Local) // want `usage of time.Local`
25+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
//golangcitest:args -Egosmopolitan
2+
//golangcitest:config_path testdata/configs/gosmopolitan_dont_ignore_tests.yml
3+
package testdata
4+
5+
import (
6+
"time"
7+
)
8+
9+
func main() {
10+
_ = "开启检查测试文件" // want `string literal contains rune in Han script`
11+
_ = time.Local // want `usage of time.Local`
12+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
//golangcitest:args -Egosmopolitan
2+
//golangcitest:config_path testdata/configs/gosmopolitan_escape_hatches.yml
3+
package testdata
4+
5+
import (
6+
myAlias "fmt"
7+
)
8+
9+
type A string
10+
type B = string
11+
type C struct {
12+
foo string
13+
Bar string
14+
}
15+
16+
func D(fmt string) string {
17+
myAlias.Println(fmt, "测试")
18+
return myAlias.Sprintf("%s 测试", fmt) // want `string literal contains rune in Han script`
19+
}
20+
21+
type X struct {
22+
baz string
23+
}
24+
25+
func main() {
26+
_ = A("测试")
27+
_ = string(A(string("测试")))
28+
_ = B("测试")
29+
_ = C{
30+
foo: "测试",
31+
Bar: "测试",
32+
}
33+
_ = D("测试")
34+
35+
_ = &X{
36+
baz: "测试", // want `string literal contains rune in Han script`
37+
}
38+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
//golangcitest:args -Egosmopolitan
2+
//golangcitest:expected_exitcode 0
3+
package testdata
4+
5+
import (
6+
"time"
7+
)
8+
9+
func main() {
10+
_ = "默认不检查测试文件"
11+
_ = time.Local
12+
}

test/testdata/gosmopolitan_scripts.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
//golangcitest:args -Egosmopolitan
2+
//golangcitest:config_path testdata/configs/gosmopolitan_scripts.yml
3+
package testdata
4+
5+
import (
6+
"fmt"
7+
)
8+
9+
func main() {
10+
fmt.Println("hello world") // want `string literal contains rune in Latin script`
11+
fmt.Println("should not report this line") //nolint:gosmopolitan
12+
fmt.Println("你好,世界")
13+
fmt.Println("こんにちは、セカイ") // want `string literal contains rune in Hiragana script`
14+
}

0 commit comments

Comments
 (0)