Coverage for tests / test_dead_code_go.py: 100%

101 statements  

« prev     ^ index     » next       coverage.py v7.13.3, created at 2026-02-08 15:04 -0800

1"""Tests for dead code detection in Go.""" 

2 

3from __future__ import annotations 

4 

5from pathlib import Path 

6 

7from parsers.go import parse 

8from dead_code.parsers.go import GoDeadCodeParser 

9from dead_code.core import find_unused_imports, build_cross_reference_graph 

10 

11 

12def test_unused_imports_basic(): 

13 code = """ 

14package main 

15 

16import ( 

17 "fmt" 

18 "os" 

19) 

20 

21func main() { 

22 fmt.Println("Hello") 

23} 

24""" 

25 parser = GoDeadCodeParser() 

26 root = parse(code) 

27 imports = parser.extract_imports(root, code) 

28 

29 import_names = [imp.name for imp in imports] 

30 assert "fmt" in import_names 

31 assert "os" in import_names 

32 

33 

34def test_blank_identifier_imports_skipped(): 

35 code = """ 

36package main 

37 

38import ( 

39 _ "github.com/pkg/errors" 

40 "fmt" 

41) 

42 

43func main() { 

44 fmt.Println("Hello") 

45} 

46""" 

47 parser = GoDeadCodeParser() 

48 root = parse(code) 

49 imports = parser.extract_imports(root, code) 

50 

51 import_names = [imp.name for imp in imports] 

52 assert "fmt" in import_names 

53 assert "errors" not in import_names # _ identifier is skipped 

54 

55 

56def test_dot_imports_skipped(): 

57 code = """ 

58package main 

59 

60import ( 

61 . "fmt" 

62) 

63 

64func main() { 

65 Println("Hello") 

66} 

67""" 

68 parser = GoDeadCodeParser() 

69 root = parse(code) 

70 imports = parser.extract_imports(root, code) 

71 

72 # Dot imports should be skipped (name shows as 'dot') 

73 import_names = [imp.name for imp in imports] 

74 assert len(imports) == 0 

75 

76 

77def test_extract_definitions(): 

78 code = """ 

79package main 

80 

81func foo() { 

82 bar() 

83} 

84 

85func bar() { 

86} 

87""" 

88 parser = GoDeadCodeParser() 

89 root = parse(code) 

90 defs = parser.extract_definitions(root, code) 

91 

92 assert len(defs) == 2 

93 def_names = [d.name for d in defs] 

94 assert "foo" in def_names 

95 assert "bar" in def_names 

96 

97 

98def test_extract_method_definitions(): 

99 code = """ 

100package main 

101 

102type MyType struct{} 

103 

104func (m MyType) Method() {} 

105""" 

106 parser = GoDeadCodeParser() 

107 root = parse(code) 

108 defs = parser.extract_definitions(root, code) 

109 

110 assert len(defs) == 1 

111 assert defs[0].name == "MyType.Method" 

112 

113 

114def test_unused_short_var_declarations(): 

115 code = """ 

116 package main 

117  

118 func foo() { 

119 x := 1 

120 y := 2 

121 return x 

122 } 

123 """ 

124 parser = GoDeadCodeParser() 

125 root = parse(code) 

126 refs = parser.extract_references(root, code) 

127 ref_names = [r.name for r in refs] 

128 

129 assert "x" in ref_names 

130 assert "y" in ref_names # y is declared, appears in references 

131 

132 

133def test_variables_in_loops(): 

134 code = """ 

135package main 

136 

137func foo() { 

138 for i := 0; i < 10; i++ { 

139 println(i) 

140 } 

141} 

142""" 

143 parser = GoDeadCodeParser() 

144 root = parse(code) 

145 refs = parser.extract_references(root, code) 

146 ref_names = [r.name for r in refs] 

147 

148 assert "i" in ref_names # used in loop condition and body 

149 

150 

151def test_exported_symbols_detection(): 

152 code = """ 

153package main 

154 

155func PublicFunc() {} 

156 

157func privateFunc() {} 

158""" 

159 assert GoDeadCodeParser().is_exported("PublicFunc") 

160 assert not GoDeadCodeParser().is_exported("privateFunc") 

161 

162 

163def test_extract_references(): 

164 code = """ 

165package main 

166 

167func foo(x int) int { 

168 y := x + 1 

169 return y 

170} 

171""" 

172 parser = GoDeadCodeParser() 

173 root = parse(code) 

174 refs = parser.extract_references(root, code) 

175 

176 ref_names = [r.name for r in refs] 

177 assert "x" in ref_names 

178 assert "y" in ref_names 

179 assert "foo" in ref_names 

180 

181 

182def test_dead_functions_cross_reference(): 

183 code1 = """ 

184 package main 

185  

186 func Used() {} 

187 func Unused() {} 

188  

189 func main() { 

190 Used() 

191 } 

192 """ 

193 parser = GoDeadCodeParser() 

194 root = parse(code1) 

195 

196 defs = parser.extract_definitions(root, code1) 

197 refs = parser.extract_references(root, code1) 

198 

199 def_names = [d.name for d in defs] 

200 ref_names = [r.name for r in refs] 

201 

202 assert "Used" in def_names 

203 assert "Unused" in def_names 

204 assert "Used" in ref_names 

205 assert "Unused" in ref_names # Unused appears in declaration 

206 

207 

208def test_interface_method_implementations(): 

209 code = """ 

210package main 

211 

212type MyInterface interface { 

213 Method(x int) int 

214} 

215 

216type MyType struct{} 

217 

218func (m MyType) Method(x int) int { 

219 return x 

220} 

221""" 

222 parser = GoDeadCodeParser() 

223 root = parse(code) 

224 defs = parser.extract_definitions(root, code) 

225 

226 assert len(defs) == 1 

227 assert defs[0].name == "MyType.Method" 

228 

229 

230def test_unused_import_detection_with_cross_ref(): 

231 code = """ 

232package main 

233 

234import "fmt" 

235 

236func main() { 

237 _ = fmt.Sprintf 

238} 

239""" 

240 parser = GoDeadCodeParser() 

241 root = parse(code) 

242 imports = parser.extract_imports(root, code) 

243 refs = parser.extract_references(root, code) 

244 

245 import_names = [imp.name for imp in imports] 

246 ref_names = [r.name for r in refs] 

247 

248 # fmt is used in _ assignment 

249 assert "fmt" in import_names 

250 assert "fmt" in ref_names