-
Notifications
You must be signed in to change notification settings - Fork 501
/
analysis.go
130 lines (119 loc) · 3.31 KB
/
analysis.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
package lib // import "go.opentelemetry.io/contrib/instrgen/lib"
import (
"fmt"
"go/ast"
"go/printer"
"go/token"
"os"
"golang.org/x/tools/go/ast/astutil"
"golang.org/x/tools/go/packages"
)
// PackageAnalysis analyze all package set accrding to passed
// pattern. It requires an information about path, pattern,
// root functions - entry points, function declarations,
// and so on.
type PackageAnalysis struct {
ProjectPath string
PackagePattern string
RootFunctions []FuncDescriptor
FuncDecls map[FuncDescriptor]bool
Callgraph map[FuncDescriptor][]FuncDescriptor
Interfaces map[string]bool
Debug bool
}
type importaction int
const (
// const that tells whether package should be imported.
Add importaction = iota
// or removed.
Remove
)
// Stores an information about operations on packages.
// Currently packages can be imported with an aliases
// or without.
type Import struct {
NamedPackage string
Package string
ImportAction importaction
}
// FileAnalysisPass executes an analysis for
// specific file node - translation unit.
type FileAnalysisPass interface {
Execute(node *ast.File,
analysis *PackageAnalysis,
pkg *packages.Package,
pkgs []*packages.Package) []Import
}
func createFile(name string) (*os.File, error) {
var out *os.File
out, err := os.Create(name)
if err != nil {
defer out.Close()
}
return out, err
}
func addImports(imports []Import, fset *token.FileSet, fileNode *ast.File) {
for _, imp := range imports {
if imp.ImportAction == Add {
if len(imp.NamedPackage) > 0 {
astutil.AddNamedImport(fset, fileNode, imp.NamedPackage, imp.Package)
} else {
astutil.AddImport(fset, fileNode, imp.Package)
}
} else {
if len(imp.NamedPackage) > 0 {
astutil.DeleteNamedImport(fset, fileNode, imp.NamedPackage, imp.Package)
} else {
astutil.DeleteImport(fset, fileNode, imp.Package)
}
}
}
}
// Execute function, main entry point to analysis process.
func (analysis *PackageAnalysis) Execute(pass FileAnalysisPass, fileSuffix string) ([]*ast.File, error) {
fset := token.NewFileSet()
cfg := &packages.Config{Fset: fset, Mode: LoadMode, Dir: analysis.ProjectPath}
pkgs, err := packages.Load(cfg, analysis.PackagePattern)
if err != nil {
return nil, err
}
var fileNodeSet []*ast.File
for _, pkg := range pkgs {
fmt.Println("\t", pkg)
// fileNode represents a translationUnit
var fileNode *ast.File
for _, fileNode = range pkg.Syntax {
fmt.Println("\t\t", fset.File(fileNode.Pos()).Name())
var out *os.File
out, err = createFile(fset.File(fileNode.Pos()).Name() + fileSuffix)
if err != nil {
return nil, err
}
if len(analysis.RootFunctions) == 0 {
e := printer.Fprint(out, fset, fileNode)
if e != nil {
return nil, e
}
continue
}
imports := pass.Execute(fileNode, analysis, pkg, pkgs)
addImports(imports, fset, fileNode)
e := printer.Fprint(out, fset, fileNode)
if e != nil {
return nil, e
}
if !analysis.Debug {
oldFileName := fset.File(fileNode.Pos()).Name() + fileSuffix
newFileName := fset.File(fileNode.Pos()).Name()
e = os.Rename(oldFileName, newFileName)
if e != nil {
return nil, e
}
}
fileNodeSet = append(fileNodeSet, fileNode)
}
}
return fileNodeSet, nil
}