File: analysis.go

package info (click to toggle)
golang-opentelemetry-contrib 0.56.0-5
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 4,884 kB
  • sloc: makefile: 278; sh: 211; sed: 1
file content (130 lines) | stat: -rw-r--r-- 3,393 bytes parent folder | download
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 according 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
}