From 95dbd8ddb0ee7d75f6954c2f5bed9edda4024c84 Mon Sep 17 00:00:00 2001 From: Fernandez Ludovic Date: Mon, 19 Feb 2024 16:45:10 +0100 Subject: [PATCH 1/8] misspell: add extra-words --- .golangci.reference.yml | 8 ++++++++ pkg/config/linters_settings.go | 10 ++++++++-- pkg/golinters/misspell.go | 27 +++++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 2 deletions(-) diff --git a/.golangci.reference.yml b/.golangci.reference.yml index 1f6b8a26a768..8ae7b9a007bd 100644 --- a/.golangci.reference.yml +++ b/.golangci.reference.yml @@ -1364,6 +1364,14 @@ linters-settings: # Default: [] ignore-words: - someword + # Extra word corrections. + # `typo` and `correction` should only contain letters. + # Default: [] + extra-words: + - typo: "iff" + correction: "if" + - typo: "cancelation" + correction: "cancellation" # Mode of the analysis: # - default: checks all the file content. # - restricted: checks only comments. diff --git a/pkg/config/linters_settings.go b/pkg/config/linters_settings.go index db121883bd3d..2743f54e3893 100644 --- a/pkg/config/linters_settings.go +++ b/pkg/config/linters_settings.go @@ -663,12 +663,18 @@ type MalignedSettings struct { } type MisspellSettings struct { - Mode string `mapstructure:"mode"` - Locale string + Mode string `mapstructure:"mode"` + Locale string + ExtraWords []MisspellExtraWords `mapstructure:"extra-words"` // TODO(ldez): v2 the option must be renamed to `IgnoredRules`. IgnoreWords []string `mapstructure:"ignore-words"` } +type MisspellExtraWords struct { + Typo string + Correction string +} + type MustTagSettings struct { Functions []struct { Name string `mapstructure:"name"` diff --git a/pkg/golinters/misspell.go b/pkg/golinters/misspell.go index 0f69cdb8701e..67754912971d 100644 --- a/pkg/golinters/misspell.go +++ b/pkg/golinters/misspell.go @@ -5,6 +5,7 @@ import ( "go/token" "strings" "sync" + "unicode" "github.com/golangci/misspell" "golang.org/x/tools/go/analysis" @@ -95,6 +96,11 @@ func createMisspellReplacer(settings *config.MisspellSettings) (*misspell.Replac return nil, fmt.Errorf("unknown locale: %q", settings.Locale) } + err := appendExtraWords(replacer, settings.ExtraWords) + if err != nil { + return nil, err + } + if len(settings.IgnoreWords) != 0 { replacer.RemoveRule(settings.IgnoreWords) } @@ -153,3 +159,24 @@ func runMisspellOnFile(lintCtx *linter.Context, filename string, replacer *missp return res, nil } + +func appendExtraWords(replacer *misspell.Replacer, extraWords []config.MisspellExtraWords) error { + var extra []string + + for _, word := range extraWords { + if word.Typo == "" || strings.ContainsFunc(word.Typo, func(r rune) bool { return !unicode.IsLetter(r) }) { + return fmt.Errorf("the word in the 'typo' field should only contain letters") + } + if word.Correction == "" || strings.ContainsFunc(word.Correction, func(r rune) bool { return !unicode.IsLetter(r) }) { + return fmt.Errorf("the word in the 'correction' field should only contain letters") + } + + extra = append(extra, strings.ToLower(word.Typo), strings.ToLower(word.Correction)) + } + + if len(extra) > 0 { + replacer.AddRuleList(extra) + } + + return nil +} From e1baa5a03d8437ee032a4ddac04ad4f874e9fb59 Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Mon, 19 Feb 2024 19:54:11 +0100 Subject: [PATCH 2/8] Apply suggestions from code review Co-authored-by: Anton Telyshev --- pkg/config/linters_settings.go | 6 +++--- pkg/golinters/misspell.go | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/config/linters_settings.go b/pkg/config/linters_settings.go index 2743f54e3893..a60d522e0152 100644 --- a/pkg/config/linters_settings.go +++ b/pkg/config/linters_settings.go @@ -664,15 +664,15 @@ type MalignedSettings struct { type MisspellSettings struct { Mode string `mapstructure:"mode"` - Locale string + Locale string `mapstructure:"locale"` ExtraWords []MisspellExtraWords `mapstructure:"extra-words"` // TODO(ldez): v2 the option must be renamed to `IgnoredRules`. IgnoreWords []string `mapstructure:"ignore-words"` } type MisspellExtraWords struct { - Typo string - Correction string + Typo string `mapstructure:"typo"` + Correction string `mapstructure:"correction"` } type MustTagSettings struct { diff --git a/pkg/golinters/misspell.go b/pkg/golinters/misspell.go index 67754912971d..b6561f102b1a 100644 --- a/pkg/golinters/misspell.go +++ b/pkg/golinters/misspell.go @@ -98,7 +98,7 @@ func createMisspellReplacer(settings *config.MisspellSettings) (*misspell.Replac err := appendExtraWords(replacer, settings.ExtraWords) if err != nil { - return nil, err + return nil, fmt.Errorf("process extra words: %v": err) } if len(settings.IgnoreWords) != 0 { From 2ebe8a921cda4cd99b387ab52c63daf9a45ea210 Mon Sep 17 00:00:00 2001 From: Fernandez Ludovic Date: Mon, 19 Feb 2024 19:59:39 +0100 Subject: [PATCH 3/8] review: dedicated error for empty fields --- pkg/golinters/misspell.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/pkg/golinters/misspell.go b/pkg/golinters/misspell.go index b6561f102b1a..cdf6ee05b0e9 100644 --- a/pkg/golinters/misspell.go +++ b/pkg/golinters/misspell.go @@ -98,7 +98,7 @@ func createMisspellReplacer(settings *config.MisspellSettings) (*misspell.Replac err := appendExtraWords(replacer, settings.ExtraWords) if err != nil { - return nil, fmt.Errorf("process extra words: %v": err) + return nil, fmt.Errorf("process extra words: %w", err) } if len(settings.IgnoreWords) != 0 { @@ -164,10 +164,14 @@ func appendExtraWords(replacer *misspell.Replacer, extraWords []config.MisspellE var extra []string for _, word := range extraWords { - if word.Typo == "" || strings.ContainsFunc(word.Typo, func(r rune) bool { return !unicode.IsLetter(r) }) { + if word.Typo == "" || word.Correction == "" { + return fmt.Errorf("typo (%q) and correction (%q) fields should not be empty", word.Typo, word.Correction) + } + + if strings.ContainsFunc(word.Typo, func(r rune) bool { return !unicode.IsLetter(r) }) { return fmt.Errorf("the word in the 'typo' field should only contain letters") } - if word.Correction == "" || strings.ContainsFunc(word.Correction, func(r rune) bool { return !unicode.IsLetter(r) }) { + if strings.ContainsFunc(word.Correction, func(r rune) bool { return !unicode.IsLetter(r) }) { return fmt.Errorf("the word in the 'correction' field should only contain letters") } From 96eea0e543ab7fdd9959c57b6835a9f3abd20191 Mon Sep 17 00:00:00 2001 From: Fernandez Ludovic Date: Mon, 19 Feb 2024 20:12:16 +0100 Subject: [PATCH 4/8] review: unit tests --- pkg/config/linters_settings.go | 4 +- pkg/golinters/misspell_test.go | 94 ++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+), 2 deletions(-) create mode 100644 pkg/golinters/misspell_test.go diff --git a/pkg/config/linters_settings.go b/pkg/config/linters_settings.go index a60d522e0152..701beb2241b0 100644 --- a/pkg/config/linters_settings.go +++ b/pkg/config/linters_settings.go @@ -663,8 +663,8 @@ type MalignedSettings struct { } type MisspellSettings struct { - Mode string `mapstructure:"mode"` - Locale string `mapstructure:"locale"` + Mode string `mapstructure:"mode"` + Locale string `mapstructure:"locale"` ExtraWords []MisspellExtraWords `mapstructure:"extra-words"` // TODO(ldez): v2 the option must be renamed to `IgnoredRules`. IgnoreWords []string `mapstructure:"ignore-words"` diff --git a/pkg/golinters/misspell_test.go b/pkg/golinters/misspell_test.go new file mode 100644 index 000000000000..5b4f9138566e --- /dev/null +++ b/pkg/golinters/misspell_test.go @@ -0,0 +1,94 @@ +package golinters + +import ( + "testing" + + "github.com/golangci/misspell" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/golangci/golangci-lint/pkg/config" +) + +func Test_appendExtraWords(t *testing.T) { + extraWords := []config.MisspellExtraWords{ + { + Typo: "iff", + Correction: "if", + }, + { + Typo: "cancelation", + Correction: "cancellation", + }, + } + + replacer := &misspell.Replacer{} + + err := appendExtraWords(replacer, extraWords) + require.NoError(t, err) + + expected := []string{"iff", "if", "cancelation", "cancellation"} + + assert.Equal(t, replacer.Replacements, expected) +} + +func Test_appendExtraWords_error(t *testing.T) { + testCases := []struct { + desc string + extraWords []config.MisspellExtraWords + expected string + }{ + { + desc: "empty fields", + extraWords: []config.MisspellExtraWords{{ + Typo: "", + Correction: "", + }}, + expected: `typo ("") and correction ("") fields should not be empty`, + }, + { + desc: "empty typo", + extraWords: []config.MisspellExtraWords{{ + Typo: "", + Correction: "if", + }}, + expected: `typo ("") and correction ("if") fields should not be empty`, + }, + { + desc: "empty correction", + extraWords: []config.MisspellExtraWords{{ + Typo: "iff", + Correction: "", + }}, + expected: `typo ("iff") and correction ("") fields should not be empty`, + }, + { + desc: "invalid characters in typo", + extraWords: []config.MisspellExtraWords{{ + Typo: "i'ff", + Correction: "if", + }}, + expected: `the word in the 'typo' field should only contain letters`, + }, + { + desc: "invalid characters in correction", + extraWords: []config.MisspellExtraWords{{ + Typo: "iff", + Correction: "i'f", + }}, + expected: `the word in the 'correction' field should only contain letters`, + }, + } + + for _, test := range testCases { + test := test + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + replacer := &misspell.Replacer{} + + err := appendExtraWords(replacer, test.extraWords) + require.EqualError(t, err, test.expected) + }) + } +} From 73f18d72c5092134c7bba4c178918335ded19c1d Mon Sep 17 00:00:00 2001 From: Fernandez Ludovic Date: Mon, 19 Feb 2024 20:17:36 +0100 Subject: [PATCH 5/8] tests: more tests --- test/testdata/configs/misspell_custom.yml | 7 +++++++ test/testdata/misspell_custom.go | 10 ++++++++++ 2 files changed, 17 insertions(+) create mode 100644 test/testdata/configs/misspell_custom.yml create mode 100644 test/testdata/misspell_custom.go diff --git a/test/testdata/configs/misspell_custom.yml b/test/testdata/configs/misspell_custom.yml new file mode 100644 index 000000000000..c0f3a27cfddb --- /dev/null +++ b/test/testdata/configs/misspell_custom.yml @@ -0,0 +1,7 @@ +linters-settings: + misspell: + extra-words: + - typo: "iff" + correction: "if" + - typo: "cancelation" + correction: "cancellation" diff --git a/test/testdata/misspell_custom.go b/test/testdata/misspell_custom.go new file mode 100644 index 000000000000..32362fb636a1 --- /dev/null +++ b/test/testdata/misspell_custom.go @@ -0,0 +1,10 @@ +//golangcitest:args -Emisspell +//golangcitest:config_path testdata/configs/misspell_custom.yml +package testdata + +func Misspell() { + // comment with incorrect spelling: occured // want "`occured` is a misspelling of `occurred`" +} + +// the word iff should be reported here // want "\\`iff\\` is a misspelling of \\`if\\`" +// the word cancelation should be reported here // want "\\`cancelation\\` is a misspelling of \\`cancellation\\`" From 64b11b96de6b792caf625aa26cde547f4c34fa7f Mon Sep 17 00:00:00 2001 From: Fernandez Ludovic Date: Mon, 19 Feb 2024 20:21:30 +0100 Subject: [PATCH 6/8] feat: better error message --- pkg/golinters/misspell.go | 4 ++-- pkg/golinters/misspell_test.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/golinters/misspell.go b/pkg/golinters/misspell.go index cdf6ee05b0e9..874848cc267b 100644 --- a/pkg/golinters/misspell.go +++ b/pkg/golinters/misspell.go @@ -169,10 +169,10 @@ func appendExtraWords(replacer *misspell.Replacer, extraWords []config.MisspellE } if strings.ContainsFunc(word.Typo, func(r rune) bool { return !unicode.IsLetter(r) }) { - return fmt.Errorf("the word in the 'typo' field should only contain letters") + return fmt.Errorf("the word %q in the 'typo' field should only contain letters", word.Typo) } if strings.ContainsFunc(word.Correction, func(r rune) bool { return !unicode.IsLetter(r) }) { - return fmt.Errorf("the word in the 'correction' field should only contain letters") + return fmt.Errorf("the word %q in the 'correction' field should only contain letters", word.Correction) } extra = append(extra, strings.ToLower(word.Typo), strings.ToLower(word.Correction)) diff --git a/pkg/golinters/misspell_test.go b/pkg/golinters/misspell_test.go index 5b4f9138566e..0eeffe7fd5ba 100644 --- a/pkg/golinters/misspell_test.go +++ b/pkg/golinters/misspell_test.go @@ -68,7 +68,7 @@ func Test_appendExtraWords_error(t *testing.T) { Typo: "i'ff", Correction: "if", }}, - expected: `the word in the 'typo' field should only contain letters`, + expected: `the word "i'ff" in the 'typo' field should only contain letters`, }, { desc: "invalid characters in correction", @@ -76,7 +76,7 @@ func Test_appendExtraWords_error(t *testing.T) { Typo: "iff", Correction: "i'f", }}, - expected: `the word in the 'correction' field should only contain letters`, + expected: `the word "i'f" in the 'correction' field should only contain letters`, }, } From ca4bb500a5caeab69c758628800bc8be118a2c48 Mon Sep 17 00:00:00 2001 From: Fernandez Ludovic Date: Mon, 19 Feb 2024 20:30:18 +0100 Subject: [PATCH 7/8] feat: refactor --- pkg/golinters/misspell.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/pkg/golinters/misspell.go b/pkg/golinters/misspell.go index 874848cc267b..8a97534c58ff 100644 --- a/pkg/golinters/misspell.go +++ b/pkg/golinters/misspell.go @@ -161,7 +161,11 @@ func runMisspellOnFile(lintCtx *linter.Context, filename string, replacer *missp } func appendExtraWords(replacer *misspell.Replacer, extraWords []config.MisspellExtraWords) error { - var extra []string + if len(extraWords) == 0 { + return nil + } + + extra := make([]string, 0, len(extraWords)*2) for _, word := range extraWords { if word.Typo == "" || word.Correction == "" { @@ -178,9 +182,7 @@ func appendExtraWords(replacer *misspell.Replacer, extraWords []config.MisspellE extra = append(extra, strings.ToLower(word.Typo), strings.ToLower(word.Correction)) } - if len(extra) > 0 { - replacer.AddRuleList(extra) - } + replacer.AddRuleList(extra) return nil } From 5e4dffc402532eb3384bd5963e67df2c2aa5be8d Mon Sep 17 00:00:00 2001 From: Fernandez Ludovic Date: Tue, 20 Feb 2024 12:41:50 +0100 Subject: [PATCH 8/8] review --- .golangci.reference.yml | 3 +++ pkg/golinters/misspell_test.go | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.golangci.reference.yml b/.golangci.reference.yml index 8ae7b9a007bd..7766f2909782 100644 --- a/.golangci.reference.yml +++ b/.golangci.reference.yml @@ -1361,11 +1361,14 @@ linters-settings: # Setting locale to US will correct the British spelling of 'colour' to 'color'. # Default is to use a neutral variety of English. locale: US + # Typos to ignore. + # Should be in lower case. # Default: [] ignore-words: - someword # Extra word corrections. # `typo` and `correction` should only contain letters. + # The words are case-insensitive. # Default: [] extra-words: - typo: "iff" diff --git a/pkg/golinters/misspell_test.go b/pkg/golinters/misspell_test.go index 0eeffe7fd5ba..29e0862f9b83 100644 --- a/pkg/golinters/misspell_test.go +++ b/pkg/golinters/misspell_test.go @@ -17,8 +17,8 @@ func Test_appendExtraWords(t *testing.T) { Correction: "if", }, { - Typo: "cancelation", - Correction: "cancellation", + Typo: "canCELation", + Correction: "canceLLaTION", }, }