Skip to content

Commit

Permalink
feat: add the gosmopolitan linter (#3458)
Browse files Browse the repository at this point in the history
  • Loading branch information
xen0n authored Mar 25, 2023
1 parent b29a4f6 commit 553d7df
Show file tree
Hide file tree
Showing 16 changed files with 223 additions and 0 deletions.
33 changes: 33 additions & 0 deletions .golangci.reference.yml
Original file line number Diff line number Diff line change
Expand Up @@ -948,6 +948,37 @@ linters-settings:
# Default: "0600"
G306: "0600"

gosmopolitan:
# Allow and ignore `time.Local` usages.
#
# Default: false
allow-time-local: true
# List of fully qualified names in the `full/pkg/path.name` form, to act as "i18n escape hatches".
# String literals inside call-like expressions to, or struct literals of those names,
# are exempt from the writing system check.
#
# Default: []
escape-hatches:
- 'github.com/nicksnyder/go-i18n/v2/i18n.Message'
- 'example.com/your/project/i18n/markers.Raw'
- 'example.com/your/project/i18n/markers.OK'
- 'example.com/your/project/i18n/markers.TODO'
- 'command-line-arguments.Simple'
# Ignore test files.
#
# Default: true
ignore-tests: false
# List of Unicode scripts to watch for any usage in string literals.
# https://pkg.go.dev/unicode#pkg-variables
#
# Default: ["Han"]
watch-for-scripts:
- Devanagari
- Han
- Hangul
- Hiragana
- Katakana

govet:
# Report about shadowed variables.
# Default: false
Expand Down Expand Up @@ -2049,6 +2080,7 @@ linters:
- goprintffuncname
- gosec
- gosimple
- gosmopolitan
- govet
- grouper
- ifshort
Expand Down Expand Up @@ -2159,6 +2191,7 @@ linters:
- goprintffuncname
- gosec
- gosimple
- gosmopolitan
- govet
- grouper
- ifshort
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ require (
github.com/ultraware/whitespace v0.0.5
github.com/uudashr/gocognit v1.0.6
github.com/valyala/quicktemplate v1.7.0
github.com/xen0n/gosmopolitan v1.2.1
github.com/yagipy/maintidx v1.0.0
github.com/yeya24/promlinter v0.2.0
gitlab.com/bosi/decorder v0.2.3
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.

14 changes: 14 additions & 0 deletions pkg/config/linters_settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ var defaultLintersSettings = LintersSettings{
Gosec: GoSecSettings{
Concurrency: runtime.NumCPU(),
},
Gosmopolitan: GosmopolitanSettings{
AllowTimeLocal: false,
EscapeHatches: []string{},
IgnoreTests: true,
WatchForScripts: []string{"Han"},
},
Ifshort: IfshortSettings{
MaxDeclLines: 1,
MaxDeclChars: 30,
Expand Down Expand Up @@ -167,6 +173,7 @@ type LintersSettings struct {
Gomodguard GoModGuardSettings
Gosec GoSecSettings
Gosimple StaticCheckSettings
Gosmopolitan GosmopolitanSettings
Govet GovetSettings
Grouper GrouperSettings
Ifshort IfshortSettings
Expand Down Expand Up @@ -449,6 +456,13 @@ type GoSecSettings struct {
Concurrency int `mapstructure:"concurrency"`
}

type GosmopolitanSettings struct {
AllowTimeLocal bool `mapstructure:"allow-time-local"`
EscapeHatches []string `mapstructure:"escape-hatches"`
IgnoreTests bool `mapstructure:"ignore-tests"`
WatchForScripts []string `mapstructure:"watch-for-scripts"`
}

type GovetSettings struct {
Go string `mapstructure:"-"`
CheckShadowing bool `mapstructure:"check-shadowing"`
Expand Down
32 changes: 32 additions & 0 deletions pkg/golinters/gosmopolitan.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package golinters

import (
"strings"

"github.com/xen0n/gosmopolitan"
"golang.org/x/tools/go/analysis"

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

func NewGosmopolitan(s *config.GosmopolitanSettings) *goanalysis.Linter {
a := gosmopolitan.NewAnalyzer()

cfgMap := map[string]map[string]interface{}{}
if s != nil {
cfgMap[a.Name] = map[string]interface{}{
"allowtimelocal": s.AllowTimeLocal,
"escapehatches": strings.Join(s.EscapeHatches, ","),
"lookattests": !s.IgnoreTests,
"watchforscripts": strings.Join(s.WatchForScripts, ","),
}
}

return goanalysis.NewLinter(
a.Name,
a.Doc,
[]*analysis.Analyzer{a},
cfgMap,
).WithLoadMode(goanalysis.LoadModeTypesInfo)
}
8 changes: 8 additions & 0 deletions pkg/lint/lintersdb/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
gomodguardCfg *config.GoModGuardSettings
gosecCfg *config.GoSecSettings
gosimpleCfg *config.StaticCheckSettings
gosmopolitanCfg *config.GosmopolitanSettings
govetCfg *config.GovetSettings
grouperCfg *config.GrouperSettings
ifshortCfg *config.IfshortSettings
Expand Down Expand Up @@ -213,6 +214,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
gomodguardCfg = &m.cfg.LintersSettings.Gomodguard
gosecCfg = &m.cfg.LintersSettings.Gosec
gosimpleCfg = &m.cfg.LintersSettings.Gosimple
gosmopolitanCfg = &m.cfg.LintersSettings.Gosmopolitan
govetCfg = &m.cfg.LintersSettings.Govet
grouperCfg = &m.cfg.LintersSettings.Grouper
ifshortCfg = &m.cfg.LintersSettings.Ifshort
Expand Down Expand Up @@ -558,6 +560,12 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
WithAlternativeNames(megacheckName).
WithURL("https://github.com/dominikh/go-tools/tree/master/simple"),

linter.NewConfig(golinters.NewGosmopolitan(gosmopolitanCfg)).
WithSince("v1.53.0").
WithLoadForGoAnalysis().
WithPresets(linter.PresetBugs).
WithURL("https://github.com/xen0n/gosmopolitan"),

linter.NewConfig(golinters.NewGovet(govetCfg)).
WithSince("v1.0.0").
WithLoadForGoAnalysis().
Expand Down
3 changes: 3 additions & 0 deletions test/testdata/configs/gosmopolitan_allow_time_local.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
linters-settings:
gosmopolitan:
allow-time-local: true
3 changes: 3 additions & 0 deletions test/testdata/configs/gosmopolitan_dont_ignore_tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
linters-settings:
gosmopolitan:
ignore-tests: false
8 changes: 8 additions & 0 deletions test/testdata/configs/gosmopolitan_escape_hatches.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
linters-settings:
gosmopolitan:
escape-hatches:
- 'command-line-arguments.A'
- 'command-line-arguments.B'
- 'command-line-arguments.C'
- 'command-line-arguments.D'
- 'fmt.Println'
6 changes: 6 additions & 0 deletions test/testdata/configs/gosmopolitan_scripts.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
linters-settings:
gosmopolitan:
watch-for-scripts:
- Hiragana
- Katakana
- Latin
25 changes: 25 additions & 0 deletions test/testdata/gosmopolitan.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//golangcitest:args -Egosmopolitan
package testdata

import (
"fmt"
"time"
)

type col struct {
// struct tag should not get reported
Foo string `gorm:"column:bar;not null;comment:'不应该报告这一行'"`
}

func main() {
fmt.Println("hello world")
fmt.Println("你好,世界") // want `string literal contains rune in Han script`
fmt.Println("こんにちは、セカイ")

_ = col{Foo: "hello"}
_ = col{Foo: "你好"} // want `string literal contains rune in Han script`

x := time.Local // want `usage of time.Local`
_ = time.Now().In(x)
_ = time.Date(2023, 1, 2, 3, 4, 5, 678901234, time.Local) // want `usage of time.Local`
}
12 changes: 12 additions & 0 deletions test/testdata/gosmopolitan_allow_time_local.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//golangcitest:args -Egosmopolitan
//golangcitest:config_path testdata/configs/gosmopolitan_allow_time_local.yml
//golangcitest:expected_exitcode 0
package testdata

import (
"time"
)

func main() {
_ = time.Local
}
12 changes: 12 additions & 0 deletions test/testdata/gosmopolitan_dont_ignore_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//golangcitest:args -Egosmopolitan
//golangcitest:config_path testdata/configs/gosmopolitan_dont_ignore_tests.yml
package testdata

import (
"time"
)

func main() {
_ = "开启检查测试文件" // want `string literal contains rune in Han script`
_ = time.Local // want `usage of time.Local`
}
38 changes: 38 additions & 0 deletions test/testdata/gosmopolitan_escape_hatches.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//golangcitest:args -Egosmopolitan
//golangcitest:config_path testdata/configs/gosmopolitan_escape_hatches.yml
package testdata

import (
myAlias "fmt"
)

type A string
type B = string
type C struct {
foo string
Bar string
}

func D(fmt string) string {
myAlias.Println(fmt, "测试")
return myAlias.Sprintf("%s 测试", fmt) // want `string literal contains rune in Han script`
}

type X struct {
baz string
}

func main() {
_ = A("测试")
_ = string(A(string("测试")))
_ = B("测试")
_ = C{
foo: "测试",
Bar: "测试",
}
_ = D("测试")

_ = &X{
baz: "测试", // want `string literal contains rune in Han script`
}
}
12 changes: 12 additions & 0 deletions test/testdata/gosmopolitan_ignore_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//golangcitest:args -Egosmopolitan
//golangcitest:expected_exitcode 0
package testdata

import (
"time"
)

func main() {
_ = "默认不检查测试文件"
_ = time.Local
}
14 changes: 14 additions & 0 deletions test/testdata/gosmopolitan_scripts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//golangcitest:args -Egosmopolitan
//golangcitest:config_path testdata/configs/gosmopolitan_scripts.yml
package testdata

import (
"fmt"
)

func main() {
fmt.Println("hello world") // want `string literal contains rune in Latin script`
fmt.Println("should not report this line") //nolint:gosmopolitan
fmt.Println("你好,世界")
fmt.Println("こんにちは、セカイ") // want `string literal contains rune in Hiragana script`
}

0 comments on commit 553d7df

Please sign in to comment.