From cb207ae9d17701dfe62237bc31ad536e38119e2a Mon Sep 17 00:00:00 2001 From: Yoan Blanc Date: Tue, 21 Jan 2020 18:28:15 +0100 Subject: [PATCH] lint: factorize local definition Signed-off-by: Yoan Blanc --- .gitlab-ci.yml | 2 +- cmd/eclint/main.go | 15 ++++++ definition.go | 110 ++++++++++++++++++++++++++++++++++++++++ definition_test.go | 44 ++++++++++++++++ lint.go | 122 +++++++-------------------------------------- lint_test.go | 62 ++++++----------------- 6 files changed, 203 insertions(+), 152 deletions(-) create mode 100644 definition.go create mode 100644 definition_test.go diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c4a1caa..212e932 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -30,7 +30,7 @@ go test: eclint: stage: lint script: - - go build -o eclint cmd/eclint/main.go + - go build -o eclint gitlab.com/greut/eclint/cmd/eclint - ./eclint -exclude "testdata/**/*" golangci-lint: diff --git a/cmd/eclint/main.go b/cmd/eclint/main.go index eb7b474..59e1b6b 100644 --- a/cmd/eclint/main.go +++ b/cmd/eclint/main.go @@ -21,6 +21,10 @@ var ( version = "dev" ) +const ( + overridePrefix = "eclint_" +) + func main() { //nolint:funlen flagVersion := false forceColors := false @@ -73,6 +77,9 @@ func main() { //nolint:funlen if opt.Summary { opt.ShowAllErrors = true + } + + if opt.ShowAllErrors { opt.ShowErrorQuantity = 0 } @@ -136,6 +143,14 @@ func main() { //nolint:funlen break } + err = eclint.OverrideDefinitionUsingPrefix(def, overridePrefix) + if err != nil { + log.Error(err, "overriding the definition failed", "prefix", overridePrefix) + c++ + + break + } + errs := eclint.LintWithDefinition(def, filename, opt.Log.WithValues("filename", filename)) c += len(errs) diff --git a/definition.go b/definition.go new file mode 100644 index 0000000..c1c88d5 --- /dev/null +++ b/definition.go @@ -0,0 +1,110 @@ +package eclint + +import ( + "fmt" + "strconv" + "strings" + + "github.com/editorconfig/editorconfig-core-go/v2" +) + +// definition contains the fields that aren't native to EditorConfig.Definition +type definition struct { + editorconfig.Definition + BlockCommentStart []byte + BlockComment []byte + BlockCommentEnd []byte + MaxLength int + TabWidth int + IndentSize int + LastLine []byte + LastIndex int + InsideBlockComment bool +} + +func newDefinition(d *editorconfig.Definition) (*definition, error) { + def := &definition{ + Definition: *d, + TabWidth: d.TabWidth, + } + + if d.IndentSize != "" && d.IndentSize != UnsetValue { + is, err := strconv.Atoi(d.IndentSize) + if err != nil { + return nil, err + } + + def.IndentSize = is + } + + if def.IndentStyle != "" && def.IndentStyle != UnsetValue { + bs, ok := def.Raw["block_comment_start"] + if ok && bs != "" && bs != UnsetValue { + def.BlockCommentStart = []byte(bs) + bc, ok := def.Raw["block_comment"] + + if ok && bc != "" && bs != UnsetValue { + def.BlockComment = []byte(bc) + } + + be, ok := def.Raw["block_comment_end"] + if !ok || be == "" || be == UnsetValue { + return nil, fmt.Errorf(".editorconfig: block_comment_end was expected, none were found") + } + + def.BlockCommentEnd = []byte(be) + } + } + + if mll, ok := def.Raw["max_line_length"]; ok && mll != "off" && mll != UnsetValue { + ml, er := strconv.Atoi(mll) + if er != nil || ml < 0 { + return nil, fmt.Errorf(".editorconfig: max_line_length expected a non-negative number, got %q", mll) + } + + def.MaxLength = ml + + if def.TabWidth <= 0 { + def.TabWidth = DefaultTabWidth + } + } + + return def, nil +} + +// OverrideDefinitionUsingPrefix is an helper that takes the prefixed values. +// +// It replaces thoses values into the nominal ones. That way a tool could a +// different set of definition than the real editor would. +func OverrideDefinitionUsingPrefix(def *editorconfig.Definition, prefix string) error { + for k, v := range def.Raw { + if strings.HasPrefix(k, prefix) { + nk := k[len(prefix):] + def.Raw[nk] = v + + switch nk { + case "indent_style": + def.IndentStyle = v + case "indent_size": + def.IndentSize = v + case "charset": + def.Charset = v + case "end_of_line": + def.EndOfLine = v + case "tab_width": + i, err := strconv.Atoi(v) + if err != nil { + return fmt.Errorf("tab_width cannot be set. %w", err) + } + + def.TabWidth = i + case "trim_trailing_whitespace": + return fmt.Errorf("%v cannot be overridden yet, pr welcome", nk) + case "insert_final_newline": + return fmt.Errorf("%v cannot be overridden yet, pr welcome", nk) + } + } + } + + return nil +} diff --git a/definition_test.go b/definition_test.go new file mode 100644 index 0000000..c4df610 --- /dev/null +++ b/definition_test.go @@ -0,0 +1,44 @@ +package eclint_test + +import ( + "testing" + + "github.com/editorconfig/editorconfig-core-go/v2" + "gitlab.com/greut/eclint" +) + +func TestOverridingUsingPrefix(t *testing.T) { + def := &editorconfig.Definition{ + Charset: "utf-8 bom", + IndentStyle: "tab", + IndentSize: "3", + TabWidth: 3, + } + + raw := make(map[string]string) + raw["@_charset"] = "unset" + raw["@_indent_style"] = "space" + raw["@_indent_size"] = "4" + raw["@_tab_width"] = "4" + def.Raw = raw + + if err := eclint.OverrideDefinitionUsingPrefix(def, "@_"); err != nil { + t.Fatal(err) + } + + if def.Charset != "unset" { + t.Errorf("charset not changed, got %q", def.Charset) + } + + if def.IndentStyle != "space" { + t.Errorf("indent_style not changed, got %q", def.IndentStyle) + } + + if def.IndentSize != "4" { + t.Errorf("indent_size not changed, got %q", def.IndentSize) + } + + if def.TabWidth != 4 { + t.Errorf("tab_width not changed, got %d", def.TabWidth) + } +} diff --git a/lint.go b/lint.go index d878f2a..2f6c84b 100644 --- a/lint.go +++ b/lint.go @@ -6,8 +6,6 @@ import ( "fmt" "io" "os" - "strconv" - "strings" "github.com/editorconfig/editorconfig-core-go/v2" "github.com/go-logr/logr" @@ -31,64 +29,13 @@ const ( func validate( //nolint:funlen,gocyclo r io.Reader, charset string, log logr.Logger, - def *editorconfig.Definition, + def *definition, ) []error { - indentSize, _ := strconv.Atoi(def.IndentSize) - var lastLine []byte var lastIndex int - errs := make([]error, 0) - - var insideBlockComment bool - - var blockCommentStart []byte - - var blockComment []byte - - var blockCommentEnd []byte - - if def.IndentStyle != "" && def.IndentStyle != UnsetValue { - bs, ok := def.Raw["block_comment_start"] - if ok && bs != "" && bs != UnsetValue { - blockCommentStart = []byte(bs) - bc, ok := def.Raw["block_comment"] - - if ok && bc != "" && bs != UnsetValue { - blockComment = []byte(bc) - } - - be, ok := def.Raw["block_comment_end"] - if !ok || be == "" || be == UnsetValue { - errs = append(errs, fmt.Errorf("block_comment_end was expected, none were found")) - } - - blockCommentEnd = []byte(be) - } - } - - maxLength := 0 - tabWidth := def.TabWidth - - if mll, ok := def.Raw["max_line_length"]; ok && mll != "off" && mll != UnsetValue { - ml, err := strconv.Atoi(mll) - if err != nil || ml < 0 { - errs = append(errs, fmt.Errorf("max_line_length expected a non-negative number, got %q", mll)) - } - - maxLength = ml - - if tabWidth <= 0 { - tabWidth = DefaultTabWidth - } - } - - if len(errs) > 0 { - return errs - } - - errs = ReadLines(r, func(index int, data []byte) error { + errs := ReadLines(r, func(index int, data []byte) error { var err error // The last line may not have the expected ending. @@ -109,21 +56,21 @@ func validate( //nolint:funlen,gocyclo lastLine = data lastIndex = index - if def.IndentStyle != "" && def.IndentStyle != UnsetValue && def.IndentSize != UnsetValue { - if insideBlockComment && blockCommentEnd != nil { - insideBlockComment = !isBlockCommentEnd(blockCommentEnd, data) + if def.IndentStyle != "" && def.IndentStyle != UnsetValue && def.Definition.IndentSize != UnsetValue { + if def.InsideBlockComment && def.BlockCommentEnd != nil { + def.InsideBlockComment = !isBlockCommentEnd(def.BlockCommentEnd, data) } - err = indentStyle(def.IndentStyle, indentSize, data) - if err != nil && insideBlockComment && blockComment != nil { + err = indentStyle(def.IndentStyle, def.IndentSize, data) + if err != nil && def.InsideBlockComment && def.BlockComment != nil { // The indentation may fail within a block comment. if ve, ok := err.(ValidationError); ok { - err = checkBlockComment(ve.Position, blockComment, data) + err = checkBlockComment(ve.Position, def.BlockComment, data) } } - if err == nil && !insideBlockComment && blockCommentStart != nil { - insideBlockComment = isBlockCommentStart(blockCommentStart, data) + if err == nil && !def.InsideBlockComment && def.BlockCommentStart != nil { + def.InsideBlockComment = isBlockCommentStart(def.BlockCommentStart, data) } } @@ -131,7 +78,7 @@ func validate( //nolint:funlen,gocyclo err = trimTrailingWhitespace(data) } - if err == nil && maxLength > 0 { + if err == nil && def.MaxLength > 0 { // Remove any BOM from the first line. d := data if index == 0 && charset != "" { @@ -142,7 +89,7 @@ func validate( //nolint:funlen,gocyclo } } } - err = MaxLineLength(maxLength, tabWidth, d) + err = MaxLineLength(def.MaxLength, def.TabWidth, d) } // Enrich the error with the line number @@ -196,39 +143,6 @@ func validate( //nolint:funlen,gocyclo return errs } -func overrideUsingPrefix(def *editorconfig.Definition, prefix string) error { - for k, v := range def.Raw { - if strings.HasPrefix(k, prefix) { - nk := k[len(prefix):] - def.Raw[nk] = v - - switch nk { - case "indent_style": - def.IndentStyle = v - case "indent_size": - def.IndentSize = v - case "charset": - def.Charset = v - case "end_of_line": - def.EndOfLine = v - case "tab_width": - i, err := strconv.Atoi(v) - if err != nil { - return fmt.Errorf("tab_width cannot be set. %w", err) - } - - def.TabWidth = i - case "trim_trailing_whitespace": - return fmt.Errorf("%v cannot be overridden yet, pr welcome", nk) - case "insert_final_newline": - return fmt.Errorf("%v cannot be overridden yet, pr welcome", nk) - } - } - } - - return nil -} - // Lint does the hard work of validating the given file. func Lint(filename string, log logr.Logger) []error { def, err := editorconfig.GetDefinitionForFilename(filename) @@ -240,7 +154,12 @@ func Lint(filename string, log logr.Logger) []error { } // LintWithDefinition does the hard work of validating the given file. -func LintWithDefinition(def *editorconfig.Definition, filename string, log logr.Logger) []error { +func LintWithDefinition(d *editorconfig.Definition, filename string, log logr.Logger) []error { + def, err := newDefinition(d) + if err != nil { + return []error{err} + } + fp, err := os.Open(filename) if err != nil { return []error{fmt.Errorf("cannot open %s. %w", filename, err)} @@ -248,11 +167,6 @@ func LintWithDefinition(def *editorconfig.Definition, filename string, log logr. defer fp.Close() - err = overrideUsingPrefix(def, "eclint_") - if err != nil { - return []error{err} - } - r := bufio.NewReader(fp) ok, err := probeReadable(fp, r) diff --git a/lint_test.go b/lint_test.go index 8d02d08..da5a633 100644 --- a/lint_test.go +++ b/lint_test.go @@ -42,8 +42,11 @@ without a final newline.`), t.Run(tc.Name, func(t *testing.T) { t.Parallel() - def := &editorconfig.Definition{ + def, err := newDefinition(&editorconfig.Definition{ InsertFinalNewline: &tc.InsertFinalNewline, + }) + if err != nil { + t.Fatal(err) } r := bytes.NewReader(tc.File) @@ -59,8 +62,11 @@ without a final newline.`), t.Parallel() insertFinalNewline := !tc.InsertFinalNewline - def := &editorconfig.Definition{ + def, err := newDefinition(&editorconfig.Definition{ InsertFinalNewline: &insertFinalNewline, + }) + if err != nil { + t.Fatal(err) } r := bytes.NewReader(tc.File) @@ -154,9 +160,13 @@ func TestBlockComment(t *testing.T) { def.Raw["block_comment_start"] = tc.BlockCommentStart def.Raw["block_comment"] = tc.BlockComment def.Raw["block_comment_end"] = tc.BlockCommentEnd + d, err := newDefinition(def) + if err != nil { + t.Fatal(err) + } r := bytes.NewReader(tc.File) - for _, err := range validate(r, "utf-8", l, def) { + for _, err := range validate(r, "utf-8", l, d) { if err != nil { t.Errorf("no errors where expected, got %s", err) } @@ -182,8 +192,6 @@ func TestBlockCommentFailure(t *testing.T) { }, } - l := tlogr.TestLogger{} - for _, tc := range tests { tc := tc @@ -199,14 +207,10 @@ func TestBlockCommentFailure(t *testing.T) { def.Raw["block_comment"] = tc.BlockComment def.Raw["block_comment_end"] = tc.BlockCommentEnd - r := bytes.NewReader(tc.File) - errs := validate(r, "utf-8", l, def) - if len(errs) == 0 { + _, err := newDefinition(def) + if err == nil { t.Fatal("one error was expected, got none") } - if errs[0] == nil { - t.Errorf("no errors where expected, got %s", errs[0]) - } }) } } @@ -258,42 +262,6 @@ func TestLintImages(t *testing.T) { } } -func TestOverridingUsingPrefix(t *testing.T) { - def := &editorconfig.Definition{ - Charset: "utf-8 bom", - IndentStyle: "tab", - IndentSize: "3", - TabWidth: 3, - } - raw := make(map[string]string) - raw["@_charset"] = "unset" - raw["@_indent_style"] = "space" - raw["@_indent_size"] = "4" - raw["@_tab_width"] = "4" - def.Raw = raw - - err := overrideUsingPrefix(def, "@_") - if err != nil { - t.Fatal(err) - } - - if def.Charset != "unset" { - t.Errorf("charset not changed, got %q", def.Charset) - } - - if def.IndentStyle != "space" { - t.Errorf("indent_style not changed, got %q", def.IndentStyle) - } - - if def.IndentSize != "4" { - t.Errorf("indent_size not changed, got %q", def.IndentSize) - } - - if def.TabWidth != 4 { - t.Errorf("tab_width not changed, got %d", def.TabWidth) - } -} - func TestMaxLineLengthValidSpec(t *testing.T) { l := tlogr.TestLogger{}