4
4
"go/ast"
5
5
"go/parser"
6
6
"go/token"
7
+ "go/types"
7
8
"testing"
8
9
)
9
10
@@ -53,6 +54,20 @@ func example() {
53
54
},
54
55
expectedIssues : 1 ,
55
56
},
57
+ {
58
+ name : "duplicate computed consts" ,
59
+ code : `package example
60
+ const ConstA = "te"
61
+ const Test = "test"
62
+ func example() {
63
+ const ConstB = ConstA + "st"
64
+ }` ,
65
+ config : & Config {
66
+ FindDuplicates : true ,
67
+ EvalConstExpressions : true ,
68
+ },
69
+ expectedIssues : 1 ,
70
+ },
56
71
{
57
72
name : "string duplication with ignore" ,
58
73
code : `package example
@@ -164,7 +179,10 @@ func example() {
164
179
t .Fatalf ("Failed to parse test code: %v" , err )
165
180
}
166
181
167
- issues , err := Run ([]* ast.File {f }, fset , tt .config )
182
+ chkr , info := checker (fset )
183
+ _ = chkr .Files ([]* ast.File {f })
184
+
185
+ issues , err := Run ([]* ast.File {f }, fset , info , tt .config )
168
186
if err != nil {
169
187
t .Fatalf ("Run() error = %v" , err )
170
188
}
@@ -201,7 +219,10 @@ func example() {
201
219
MatchWithConstants : true ,
202
220
}
203
221
204
- issues , err := Run ([]* ast.File {f }, fset , config )
222
+ chkr , info := checker (fset )
223
+ _ = chkr .Files ([]* ast.File {f })
224
+
225
+ issues , err := Run ([]* ast.File {f }, fset , info , config )
205
226
if err != nil {
206
227
t .Fatalf ("Run() error = %v" , err )
207
228
}
@@ -256,16 +277,16 @@ func example2() {
256
277
expectedOccurrenceCount : 3 ,
257
278
},
258
279
{
259
- name : "duplicate consts in different packages " ,
260
- code1 : `package package1
280
+ name : "duplicate consts in different files " ,
281
+ code1 : `package example
261
282
const ConstA = "shared"
262
283
const ConstB = "shared"
263
284
` ,
264
- code2 : `package package2
285
+ code2 : `package example
265
286
const (
266
287
ConstC = "shared"
267
288
ConstD = "shared"
268
- ConstE= "unique"
289
+ ConstE = "unique"
269
290
)` ,
270
291
config : & Config {
271
292
FindDuplicates : true ,
@@ -290,7 +311,10 @@ const (
290
311
t .Fatalf ("Failed to parse test code: %v" , err )
291
312
}
292
313
293
- issues , err := Run ([]* ast.File {f1 , f2 }, fset , tt .config )
314
+ chkr , info := checker (fset )
315
+ _ = chkr .Files ([]* ast.File {f1 , f2 })
316
+
317
+ issues , err := Run ([]* ast.File {f1 , f2 }, fset , info , tt .config )
294
318
if err != nil {
295
319
t .Fatalf ("Run() error = %v" , err )
296
320
}
@@ -348,8 +372,10 @@ func allContexts(param string) string {
348
372
MinStringLength : 3 ,
349
373
MinOccurrences : 2 ,
350
374
}
375
+ chkr , info := checker (fset )
376
+ _ = chkr .Files ([]* ast.File {f })
351
377
352
- issues , err := Run ([]* ast.File {f }, fset , config )
378
+ issues , err := Run ([]* ast.File {f }, fset , info , config )
353
379
if err != nil {
354
380
t .Fatalf ("Run() error = %v" , err )
355
381
}
@@ -429,8 +455,10 @@ func multipleContexts() {
429
455
MinOccurrences : 2 ,
430
456
ExcludeTypes : tt .excludeTypes ,
431
457
}
458
+ chkr , info := checker (fset )
459
+ _ = chkr .Files ([]* ast.File {f })
432
460
433
- issues , err := Run ([]* ast.File {f }, fset , config )
461
+ issues , err := Run ([]* ast.File {f }, fset , info , config )
434
462
if err != nil {
435
463
t .Fatalf ("Run() error = %v" , err )
436
464
}
@@ -453,3 +481,84 @@ func multipleContexts() {
453
481
})
454
482
}
455
483
}
484
+
485
+ func TestConstExpressions (t * testing.T ) {
486
+ // Test detecting and matching string constants derived from expressions
487
+ code := `package example
488
+
489
+ const (
490
+ Prefix = "example.com/"
491
+ Label1 = Prefix + "some_label"
492
+ Label2 = Prefix + "another_label"
493
+ )
494
+
495
+ func example() {
496
+ // These should match the constants from expressions
497
+ a := "example.com/some_label"
498
+ b := "example.com/some_label"
499
+
500
+ // This should also match
501
+ web1 := "example.com/another_label"
502
+ web2 := "example.com/another_label"
503
+ }
504
+ `
505
+ fset := token .NewFileSet ()
506
+ f , err := parser .ParseFile (fset , "example.go" , code , 0 )
507
+ if err != nil {
508
+ t .Fatalf ("Failed to parse test code: %v" , err )
509
+ }
510
+
511
+ config := & Config {
512
+ MinStringLength : 3 ,
513
+ MinOccurrences : 2 ,
514
+ MatchWithConstants : true ,
515
+ EvalConstExpressions : true ,
516
+ }
517
+ chkr , info := checker (fset )
518
+ _ = chkr .Files ([]* ast.File {f })
519
+
520
+ issues , err := Run ([]* ast.File {f }, fset , info , config )
521
+ if err != nil {
522
+ t .Fatalf ("Run() error = %v" , err )
523
+ }
524
+
525
+ // We expect issues for both labels
526
+ expectedMatches := map [string ]string {
527
+ "example.com/some_label" : "Label1" ,
528
+ "example.com/another_label" : "Label2" ,
529
+ }
530
+
531
+ // Check that we have two issues
532
+ if len (issues ) != 2 {
533
+ t .Errorf ("Expected 2 issues, got %d" , len (issues ))
534
+ for _ , issue := range issues {
535
+ t .Logf ("Found issue: %q matches constant %q with %d occurrences" ,
536
+ issue .Str , issue .MatchingConst , issue .OccurrencesCount )
537
+ }
538
+ return
539
+ }
540
+
541
+ // Check that each string matches the expected constant
542
+ for _ , issue := range issues {
543
+ expectedConst , ok := expectedMatches [issue .Str ]
544
+ if ! ok {
545
+ t .Errorf ("Unexpected issue for string: %s" , issue .Str )
546
+ continue
547
+ }
548
+
549
+ if issue .MatchingConst != expectedConst {
550
+ t .Errorf ("For string %q: got matching const %q, want %q" ,
551
+ issue .Str , issue .MatchingConst , expectedConst )
552
+ }
553
+ }
554
+ }
555
+
556
+ func checker (fset * token.FileSet ) (* types.Checker , * types.Info ) {
557
+ cfg := & types.Config {
558
+ Error : func (err error ) {},
559
+ }
560
+ info := & types.Info {
561
+ Types : make (map [ast.Expr ]types.TypeAndValue ),
562
+ }
563
+ return types .NewChecker (cfg , fset , types .NewPackage ("" , "example" ), info ), info
564
+ }
0 commit comments