116 lines
3.1 KiB
Go
116 lines
3.1 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"go/token"
|
|
"go/types"
|
|
"os"
|
|
"sort"
|
|
|
|
"golang.org/x/tools/go/analysis"
|
|
"golang.org/x/tools/go/analysis/passes/fieldalignment"
|
|
"golang.org/x/tools/go/analysis/passes/inspect"
|
|
"golang.org/x/tools/go/packages"
|
|
"google.golang.org/protobuf/compiler/protogen"
|
|
)
|
|
|
|
func (g *Generator) fieldAlign(plugin *protogen.Plugin) error {
|
|
if !g.fieldaligment {
|
|
return nil
|
|
}
|
|
|
|
fset := token.NewFileSet()
|
|
cfg := &packages.Config{
|
|
Mode: packages.NeedName | packages.NeedFiles | packages.NeedSyntax |
|
|
packages.NeedTypes | packages.NeedTypesInfo | packages.NeedTypesSizes,
|
|
Fset: fset,
|
|
Dir: g.tagPath,
|
|
}
|
|
|
|
pkgs, err := packages.Load(cfg, ".")
|
|
if err != nil {
|
|
return fmt.Errorf("fieldalignment load: %w", err)
|
|
}
|
|
|
|
for _, pkg := range pkgs {
|
|
if len(pkg.Errors) > 0 {
|
|
continue
|
|
}
|
|
|
|
makePass := func(a *analysis.Analyzer, resultOf map[*analysis.Analyzer]interface{}) *analysis.Pass {
|
|
return &analysis.Pass{
|
|
Analyzer: a,
|
|
Fset: fset,
|
|
Files: pkg.Syntax,
|
|
Pkg: pkg.Types,
|
|
TypesInfo: pkg.TypesInfo,
|
|
TypesSizes: pkg.TypesSizes,
|
|
ResultOf: resultOf,
|
|
Report: func(d analysis.Diagnostic) {},
|
|
ImportObjectFact: func(obj types.Object, fact analysis.Fact) bool { return false },
|
|
ExportObjectFact: func(obj types.Object, fact analysis.Fact) {},
|
|
ImportPackageFact: func(pkg *types.Package, fact analysis.Fact) bool { return false },
|
|
ExportPackageFact: func(fact analysis.Fact) {},
|
|
AllObjectFacts: func() []analysis.ObjectFact { return nil },
|
|
AllPackageFacts: func() []analysis.PackageFact { return nil },
|
|
}
|
|
}
|
|
|
|
inspectResult, err := inspect.Analyzer.Run(makePass(inspect.Analyzer, nil))
|
|
if err != nil {
|
|
return fmt.Errorf("inspect run: %w", err)
|
|
}
|
|
|
|
var fixes []analysis.SuggestedFix
|
|
alignPass := makePass(fieldalignment.Analyzer, map[*analysis.Analyzer]interface{}{
|
|
inspect.Analyzer: inspectResult,
|
|
})
|
|
alignPass.Report = func(d analysis.Diagnostic) {
|
|
fixes = append(fixes, d.SuggestedFixes...)
|
|
}
|
|
|
|
if _, err := fieldalignment.Analyzer.Run(alignPass); err != nil {
|
|
return fmt.Errorf("fieldalignment run: %w", err)
|
|
}
|
|
|
|
if err := applyTextEdits(fset, fixes); err != nil {
|
|
return fmt.Errorf("fieldalignment apply: %w", err)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func applyTextEdits(fset *token.FileSet, fixes []analysis.SuggestedFix) error {
|
|
byFile := make(map[string][]analysis.TextEdit)
|
|
for _, fix := range fixes {
|
|
for _, edit := range fix.TextEdits {
|
|
fname := fset.Position(edit.Pos).Filename
|
|
byFile[fname] = append(byFile[fname], edit)
|
|
}
|
|
}
|
|
|
|
for fname, edits := range byFile {
|
|
content, err := os.ReadFile(fname)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
sort.Slice(edits, func(i, j int) bool {
|
|
return edits[i].Pos > edits[j].Pos
|
|
})
|
|
|
|
for _, edit := range edits {
|
|
start := fset.Position(edit.Pos).Offset
|
|
end := fset.Position(edit.End).Offset
|
|
content = append(content[:start], append(edit.NewText, content[end:]...)...)
|
|
}
|
|
|
|
if err := os.WriteFile(fname, content, 0o644); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|