Skip to content

Commit 9ea8391

Browse files
committed
feat: remove examples/foo.go and add fixer.go for enhanced functionality
- **Why remove examples/foo.go?** The functions within were likely deemed unnecessary or have been refactored into a more comprehensive solution. - **Why add fixer.go?** Introduces a utility for modifying Go source files, possibly to automate code refactoring or corrections, enhancing the development workflow. refactor(main.go): streamline flag parsing and function removal logic feat(main.go): add JSON schema support for dead code analysis feat(schema.go): introduce JSON schema for deadcode output analysis feat(testdata): add test files and examples for dead code analysis feat(testdata/stringer.go): add new myString struct implementing fmt.Stringer This commit introduces a new file `stringer.go` within the `testdata` directory, which defines a `myString` struct. This struct implements the `fmt.Stringer` interface, ensuring that instances of `myString` can be easily converted to a string using the `String()` method. This addition aims to enhance the codebase by providing a clear and idiomatic way to convert `myString` instances to strings, following Go's convention of using the `Stringer` interface for string representations of types.
1 parent d484a9b commit 9ea8391

File tree

8 files changed

+198
-64
lines changed

8 files changed

+198
-64
lines changed

examples/foo.go

-5
This file was deleted.

fixer.go

+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package main
2+
3+
import (
4+
"bufio"
5+
"bytes"
6+
"fmt"
7+
"go/ast"
8+
"go/format"
9+
"go/parser"
10+
"go/token"
11+
"os"
12+
)
13+
14+
func fix(filename, functionName string) error {
15+
start, end, err := lookup(filename, functionName)
16+
if err != nil {
17+
return err
18+
}
19+
20+
if err := rewrite(filename, start, end); err != nil {
21+
return err
22+
}
23+
return nil
24+
}
25+
26+
func lookup(filename, functionName string) (int, int, error) {
27+
fset := token.NewFileSet()
28+
node, err := parser.ParseFile(fset, filename, nil, parser.ParseComments)
29+
if err != nil {
30+
return 0, 0, err
31+
}
32+
33+
var start, end int
34+
ast.Inspect(node, func(n ast.Node) bool {
35+
funcDecl, ok := n.(*ast.FuncDecl)
36+
if ok && funcDecl.Name.Name == functionName {
37+
start = fset.Position(funcDecl.Pos()).Line
38+
end = fset.Position(funcDecl.End()).Line
39+
return false
40+
}
41+
return true
42+
})
43+
if start == 0 || end == 0 {
44+
return 0, 0, fmt.Errorf("func %s not found in %s", functionName, filename)
45+
}
46+
return start, end, nil
47+
}
48+
49+
func rewrite(filename string, start, end int) error {
50+
file, err := os.Open(filename)
51+
if err != nil {
52+
return err
53+
}
54+
defer file.Close()
55+
56+
buf := new(bytes.Buffer)
57+
scanner := bufio.NewScanner(file)
58+
for l := 1; scanner.Scan(); {
59+
if l < start || l > end {
60+
if _, err := buf.Write(append(scanner.Bytes(), '\n')); err != nil {
61+
return err
62+
}
63+
}
64+
l++
65+
}
66+
if err := scanner.Err(); err != nil {
67+
return err
68+
}
69+
70+
// run gofmt
71+
b, err := format.Source(buf.Bytes())
72+
if err != nil {
73+
return err
74+
}
75+
// write to file
76+
info, err := file.Stat()
77+
if err != nil {
78+
return err
79+
}
80+
if err := os.WriteFile(filename, b, info.Mode()); err != nil {
81+
return err
82+
}
83+
return nil
84+
}

main.go

+37-59
Original file line numberDiff line numberDiff line change
@@ -1,81 +1,59 @@
11
package main
22

33
import (
4-
"bufio"
5-
"bytes"
4+
"encoding/json"
65
"flag"
7-
"go/ast"
8-
"go/format"
9-
"go/parser"
10-
"go/token"
6+
"io"
117
"log"
128
"os"
139
)
1410

11+
// flags
1512
var (
16-
fileName, functionName string
13+
jsonFlag = flag.String("json", "", "JSON file generated by deadcode")
14+
fileFlag = flag.String("file", "", "File to remove function from")
15+
functionFlag = flag.String("function", "", "Function to remove")
1716
)
1817

1918
func main() {
20-
flag.StringVar(&fileName, "f", "", "File to remove function from (shorthand)")
21-
flag.StringVar(&fileName, "file", "", "File to remove function from")
22-
flag.StringVar(&functionName, "fn", "", "File to remove function from (shorthand)")
23-
flag.StringVar(&functionName, "function", "", "Function to remove")
2419
flag.Parse()
2520

26-
if fileName == "" || functionName == "" {
27-
flag.Usage()
28-
os.Exit(1)
29-
}
30-
31-
fset := token.NewFileSet()
32-
node, err := parser.ParseFile(fset, fileName, nil, parser.ParseComments)
33-
if err != nil {
34-
log.Fatalf("Failed to parse file: %v", err)
35-
}
36-
37-
var start, end int
38-
ast.Inspect(node, func(n ast.Node) bool {
39-
fn, ok := n.(*ast.FuncDecl)
40-
if ok && fn.Name.Name == functionName {
41-
start = fset.Position(fn.Pos()).Line
42-
end = fset.Position(fn.End()).Line
43-
return false
21+
// run specific fixer
22+
if f, fn := *fileFlag, *functionFlag; f != "" && fn != "" {
23+
if err := fix(f, fn); err != nil {
24+
log.Fatal(err)
4425
}
45-
return true
46-
})
47-
48-
file, err := os.Open(fileName)
49-
if err != nil {
50-
log.Fatalf("Failed to open file: %v", err)
51-
}
52-
defer file.Close()
53-
54-
buf := new(bytes.Buffer)
55-
scanner := bufio.NewScanner(file)
56-
for l := 1; scanner.Scan(); {
57-
if l < start || l > end {
58-
if _, err := buf.Write(append(scanner.Bytes(), '\n')); err != nil {
59-
log.Fatalf("Failed to write to buffer: %v", err)
60-
}
26+
return
27+
}
28+
// run fixer by reading JSON
29+
var r io.Reader
30+
switch v := *jsonFlag; v {
31+
case "", "-":
32+
r = os.Stdin
33+
default:
34+
f, err := os.Open(v)
35+
if err != nil {
36+
log.Fatalf("failed to open file %s: %v", v, err)
6137
}
62-
l++
38+
defer f.Close()
39+
r = f
6340
}
64-
if err := scanner.Err(); err != nil {
65-
log.Fatalf("Failed to read file: %v", err)
41+
if err := run(r); err != nil {
42+
log.Fatal(err)
6643
}
44+
}
6745

68-
// run gofmt
69-
b, err := format.Source(buf.Bytes())
70-
if err != nil {
71-
log.Fatalf("Failed to format source: %v", err)
46+
func run(r io.Reader) error {
47+
packages := []Package{}
48+
if err := json.NewDecoder(r).Decode(&packages); err != nil {
49+
return err
7250
}
73-
// write to file
74-
info, err := file.Stat()
75-
if err != nil {
76-
log.Fatalf("Failed to get file info: %v", err)
77-
}
78-
if err := os.WriteFile(fileName, b, info.Mode()); err != nil {
79-
log.Fatalf("Failed to write to file: %v", err)
51+
for _, p := range packages {
52+
for _, f := range p.Funcs {
53+
if err := fix(f.Position.File, f.Name); err != nil {
54+
return err
55+
}
56+
}
8057
}
58+
return nil
8159
}

schema.go

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package main
2+
3+
// This file contains the JSON schema for the deadcode output.
4+
// https://pkg.go.dev/golang.org/x/[email protected]/cmd/deadcode#hdr-JSON_schema
5+
6+
type Package struct {
7+
Name string // declared name
8+
Path string // full import path
9+
Funcs []Function // list of dead functions within it
10+
}
11+
12+
type Function struct {
13+
Name string // name (sans package qualifier)
14+
Position Position // file/line/column of function declaration
15+
Generated bool // function is declared in a generated .go file
16+
}
17+
18+
type Edge struct {
19+
Initial string // initial entrypoint (main or init); first edge only
20+
Kind string // = static | dynamic
21+
Position Position // file/line/column of call site
22+
Callee string // target of the call
23+
}
24+
25+
type Position struct {
26+
File string // name of file
27+
Line, Col int // line and byte index, both 1-based
28+
}

testdata/main.go

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package main
2+
3+
func main() {
4+
Reachable()
5+
}
6+
7+
func init() {
8+
s := myString{Value: "hello"}
9+
s.Reachable()
10+
}

testdata/main_test.go

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package main
2+
3+
import "testing"
4+
5+
func TestReachableByTest(t *testing.T) {
6+
ReachableByTest()
7+
}

testdata/reachable.go

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package main
2+
3+
import "fmt"
4+
5+
func Reachable() {
6+
fmt.Println("reachable")
7+
}
8+
9+
func Unreachable() {
10+
fmt.Println("unreachable")
11+
}
12+
13+
func ReachableByTest() {
14+
fmt.Println("reachableByTest")
15+
}

testdata/stringer.go

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package main
2+
3+
import "fmt"
4+
5+
var _ fmt.Stringer = myString{}
6+
7+
type myString struct {
8+
Value string
9+
}
10+
11+
func (s myString) String() string {
12+
return s.Value
13+
}
14+
15+
func (s myString) Reachable() {
16+
return
17+
}

0 commit comments

Comments
 (0)