github.com/elek/golangci-lint@v1.42.2-0.20211208090441-c05b7fcb3a9a/pkg/golinters/gofumpt.go (about) 1 package golinters 2 3 import ( 4 "bytes" 5 "fmt" 6 "io/ioutil" 7 "sync" 8 9 "github.com/pkg/errors" 10 "github.com/shazow/go-diff/difflib" 11 "golang.org/x/tools/go/analysis" 12 "mvdan.cc/gofumpt/format" 13 14 "github.com/elek/golangci-lint/pkg/config" 15 "github.com/elek/golangci-lint/pkg/golinters/goanalysis" 16 "github.com/elek/golangci-lint/pkg/lint/linter" 17 ) 18 19 const gofumptName = "gofumpt" 20 21 func NewGofumpt() *goanalysis.Linter { 22 var mu sync.Mutex 23 var resIssues []goanalysis.Issue 24 differ := difflib.New() 25 26 analyzer := &analysis.Analyzer{ 27 Name: gofumptName, 28 Doc: goanalysis.TheOnlyanalyzerDoc, 29 } 30 return goanalysis.NewLinter( 31 gofumptName, 32 "Gofumpt checks whether code was gofumpt-ed.", 33 []*analysis.Analyzer{analyzer}, 34 nil, 35 ).WithContextSetter(func(lintCtx *linter.Context) { 36 settings := lintCtx.Settings().Gofumpt 37 38 options := format.Options{ 39 LangVersion: getLangVersion(settings), 40 ExtraRules: settings.ExtraRules, 41 } 42 43 analyzer.Run = func(pass *analysis.Pass) (interface{}, error) { 44 var fileNames []string 45 for _, f := range pass.Files { 46 pos := pass.Fset.PositionFor(f.Pos(), false) 47 fileNames = append(fileNames, pos.Filename) 48 } 49 50 var issues []goanalysis.Issue 51 52 for _, f := range fileNames { 53 input, err := ioutil.ReadFile(f) 54 if err != nil { 55 return nil, fmt.Errorf("unable to open file %s: %w", f, err) 56 } 57 58 output, err := format.Source(input, options) 59 if err != nil { 60 return nil, fmt.Errorf("error while running gofumpt: %w", err) 61 } 62 63 if !bytes.Equal(input, output) { 64 out := bytes.Buffer{} 65 _, err = out.WriteString(fmt.Sprintf("--- %[1]s\n+++ %[1]s\n", f)) 66 if err != nil { 67 return nil, fmt.Errorf("error while running gofumpt: %w", err) 68 } 69 70 err = differ.Diff(&out, bytes.NewReader(input), bytes.NewReader(output)) 71 if err != nil { 72 return nil, fmt.Errorf("error while running gofumpt: %w", err) 73 } 74 75 diff := out.String() 76 is, err := extractIssuesFromPatch(diff, lintCtx.Log, lintCtx, gofumptName) 77 if err != nil { 78 return nil, errors.Wrapf(err, "can't extract issues from gofumpt diff output %q", diff) 79 } 80 81 for i := range is { 82 issues = append(issues, goanalysis.NewIssue(&is[i], pass)) 83 } 84 } 85 } 86 87 if len(issues) == 0 { 88 return nil, nil 89 } 90 91 mu.Lock() 92 resIssues = append(resIssues, issues...) 93 mu.Unlock() 94 95 return nil, nil 96 } 97 }).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue { 98 return resIssues 99 }).WithLoadMode(goanalysis.LoadModeSyntax) 100 } 101 102 func getLangVersion(settings config.GofumptSettings) string { 103 if settings.LangVersion == "" { 104 // TODO: defaults to "1.15", in the future (v2) must be set by using build.Default.ReleaseTags like staticcheck. 105 return "1.15" 106 } 107 return settings.LangVersion 108 }