Files
protoc-gen-go-micro/fiealaligment.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
}