Go语言在代码规范之严格是有目共睹的,引用未使用的头文件会报“imported and not used”错误,定义未使用的变量会报“declared and not used”错误。因为golang编译器只有错误没有报警,所以代码中如果有此类问题,编译就会停止。
Golang不提供语法宽容的debug模式,也就是说,无论是什么时候,Go代码都必须保持干干净净的状态。
随手注释部分变量是编码阶段的一个常用手段,Golang的编译器每每跳出来,让你把变量的定义和相关头文件的引用也通通删掉。有时候临时注释一段代码会产生一堆未引用和未定义错误,而为了解决这些错误往往又会把代码被改得面目全非。对于debug阶段的程序员来讲,有一个管得太宽的编译器是一件痛苦的事情。
好在Golang是开源的,弄一个不太折腾的Golang编译器并不困难。
修改源码
首先下载golang代码:
$ git clone https://github.com/golang/go
拉到最新分支,现在最新的版本号是1.12.5:
$ cd go
$ git checkout -b debug-1.12.5 go1.12.5
未引用头文件和未定义变量错误相关的代码在下面4个文件中:
- src/cmd/compile/internal/gc/main.go
- src/cmd/compile/internal/gc/subr.go
- src/cmd/compile/internal/gc/swt.go
- src/cmd/compile/internal/gc/walk.go
用 if false {} 把相关代码注释掉。
具体内容可以看这个patch:
diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go
index 98ff2a3d27..2bb2e6fe37 100644
--- a/src/cmd/compile/internal/gc/main.go
+++ b/src/cmd/compile/internal/gc/main.go
@@ -1246,11 +1246,13 @@ func pkgnotused(lineno src.XPos, path string, name string) {
if i := strings.LastIndex(elem, "/"); i >= 0 {
elem = elem[i+1:]
}
+ if false {
if name == "" || elem == name {
yyerrorl(lineno, "imported and not used: %q", path)
} else {
yyerrorl(lineno, "imported and not used: %q as %s", path, name)
}
+ }
}
func mkpackage(pkgname string) {
diff --git a/src/cmd/compile/internal/gc/subr.go b/src/cmd/compile/internal/gc/subr.go
index 2a976dc4f0..be662c3df8 100644
--- a/src/cmd/compile/internal/gc/subr.go
+++ b/src/cmd/compile/internal/gc/subr.go
@@ -291,10 +291,12 @@ func importdot(opkg *types.Pkg, pack *Node) {
n++
}
+ if false {
if n == 0 {
// can't possibly be used - there were no symbols
yyerrorl(pack.Pos, "imported and not used: %q", opkg.Path)
}
+ }
}
func nod(op Op, nleft, nright *Node) *Node {
diff --git a/src/cmd/compile/internal/gc/swt.go b/src/cmd/compile/internal/gc/swt.go
index cc9a8f8b2c..6f27447bb0 100644
--- a/src/cmd/compile/internal/gc/swt.go
+++ b/src/cmd/compile/internal/gc/swt.go
@@ -70,12 +70,14 @@ func typecheckswitch(n *Node) {
if t != nil && !t.IsInterface() {
yyerrorl(n.Pos, "cannot type switch on non-interface value %L", n.Left.Right)
}
+ if false {
if v := n.Left.Left; v != nil && !v.isBlank() && n.List.Len() == 0 {
// We don't actually declare the type switch's guarded
// declaration itself. So if there are no cases, we
// won't notice that it went unused.
yyerrorl(v.Pos, "%v declared and not used", v.Sym)
}
+ }
} else {
// expression switch
top = ctxExpr
diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go
index 509579d21f..92ad512d37 100644
--- a/src/cmd/compile/internal/gc/walk.go
+++ b/src/cmd/compile/internal/gc/walk.go
@@ -41,6 +41,7 @@ func walk(fn *Node) {
}
}
+ if false {
for _, ln := range fn.Func.Dcl {
if ln.Op != ONAME || (ln.Class() != PAUTO && ln.Class() != PAUTOHEAP) || ln.Sym.Name[0] == '&' || ln.Name.Used() {
continue
@@ -55,6 +56,7 @@ func walk(fn *Node) {
yyerrorl(ln.Pos, "%v declared and not used", ln.Sym)
}
}
+ }
lineno = lno
if nerrors != 0 {
生成debug版编译器
编译debug版Golang:
$ cd src
$ GOOS=linux GOARCH=amd64 ./bootstrap.bash
../../目录下会生成两个文件:
$ ls ../../
go/ go-linux-amd64-bootstrap/ go-linux-amd64-bootstrap.tbz
其中go-linux-amd64-bootstrap就是debug版的Golang。
在/usr/bin下面创建一个可执行文件debuggo,文件中的GOROOT指向go-linux-amd64-bootstrap目录:
#!/bin/bash
GOROOT=/PATH-TO-GOLANG/go-linux-amd64-bootstrap
${GOROOT}/bin/go $@
好了,大功告成,debug的时候执行:
$ debuggo build
编译器果然就老实了很多。
不过要发布的时候记得用官方的编译器做语法检查:
$ go build -a