Skip to content

Commit 35329ce

Browse files
committed
cmd/stringer: omit negative bounds check from String for unsigned integers
CL 707396 ("cmd/stringer: fix bounds checking for integer types spanning full range") introduced a universal lower bounds check for all integers, including unsigned ints. Unfortunately, performing a <0 bounds check on a uint gets flagged by static analysis tools like staticcheck. This commit only emits the bounds check if the type in question is either signed or has a non-zero minimum value, in which case the bounds check is necessary for unsigned types as well. Signed-off-by: Timo Beckers <[email protected]>
1 parent d2096d1 commit 35329ce

File tree

2 files changed

+75
-1
lines changed

2 files changed

+75
-1
lines changed

cmd/stringer/golden_test.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ var golden = []Golden{
3838
{"prefix", "Type", false, prefix_in, prefix_out},
3939
{"tokens", "", true, tokens_in, tokens_out},
4040
{"overflow8", "", false, overflow8_in, overflow8_out},
41+
{"ubounds0", "", false, ubounds0_in, ubounds0_out},
42+
{"ubounds1", "", false, ubounds1_in, ubounds1_out},
4143
}
4244

4345
// Each example starts with "type XXX [u]int", with a single space separating them.
@@ -988,6 +990,57 @@ func (i Overflow8) String() string {
988990
}
989991
`
990992

993+
const ubounds0_in = `type UBounds uint8
994+
const (
995+
B0 UBounds = 0
996+
)
997+
`
998+
999+
const ubounds0_out = `func _() {
1000+
// An "invalid array index" compiler error signifies that the constant values have changed.
1001+
// Re-run the stringer command to generate them again.
1002+
var x [1]struct{}
1003+
_ = x[B0-0]
1004+
}
1005+
1006+
const _UBounds_name = "B0"
1007+
1008+
var _UBounds_index = [...]uint8{0, 2}
1009+
1010+
func (i UBounds) String() string {
1011+
idx := int(i) - 0
1012+
if idx >= len(_UBounds_index)-1 {
1013+
return "UBounds(" + strconv.FormatInt(int64(i), 10) + ")"
1014+
}
1015+
return _UBounds_name[_UBounds_index[idx]:_UBounds_index[idx+1]]
1016+
}
1017+
`
1018+
1019+
const ubounds1_in = `type UBounds uint8
1020+
const (
1021+
B1 UBounds = 1
1022+
)
1023+
`
1024+
const ubounds1_out = `func _() {
1025+
// An "invalid array index" compiler error signifies that the constant values have changed.
1026+
// Re-run the stringer command to generate them again.
1027+
var x [1]struct{}
1028+
_ = x[B1-1]
1029+
}
1030+
1031+
const _UBounds_name = "B1"
1032+
1033+
var _UBounds_index = [...]uint8{0, 2}
1034+
1035+
func (i UBounds) String() string {
1036+
idx := int(i) - 1
1037+
if i < 1 || idx >= len(_UBounds_index)-1 {
1038+
return "UBounds(" + strconv.FormatInt(int64(i), 10) + ")"
1039+
}
1040+
return _UBounds_name[_UBounds_index[idx]:_UBounds_index[idx+1]]
1041+
}
1042+
`
1043+
9911044
func TestGolden(t *testing.T) {
9921045
testenv.NeedsTool(t, "go")
9931046

cmd/stringer/stringer.go

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -641,7 +641,12 @@ func (g *Generator) buildOneRun(runs [][]Value, typeName string) {
641641
values := runs[0]
642642
g.Printf("\n")
643643
g.declareIndexAndNameVar(values, typeName)
644-
g.Printf(stringOneRun, typeName, values[0].String())
644+
645+
if values[0].signed || values[0].String() != "0" {
646+
g.Printf(stringOneRun, typeName, values[0].String())
647+
} else {
648+
g.Printf(stringOneRunUnsigned, typeName, values[0].String())
649+
}
645650
}
646651

647652
// Arguments to format are:
@@ -657,6 +662,22 @@ const stringOneRun = `func (i %[1]s) String() string {
657662
}
658663
`
659664

665+
// Arguments to format are:
666+
//
667+
// [1]: type name
668+
// [2]: lowest defined value for type, as a string
669+
//
670+
// This is the unsigned version of [stringOneRun] with the lower bound check
671+
// removed.
672+
const stringOneRunUnsigned = `func (i %[1]s) String() string {
673+
idx := int(i) - %[2]s
674+
if idx >= len(_%[1]s_index)-1 {
675+
return "%[1]s(" + strconv.FormatInt(int64(i), 10) + ")"
676+
}
677+
return _%[1]s_name[_%[1]s_index[idx] : _%[1]s_index[idx+1]]
678+
}
679+
`
680+
660681
// buildMultipleRuns generates the variables and String method for multiple runs of contiguous values.
661682
// For this pattern, a single Printf format won't do.
662683
func (g *Generator) buildMultipleRuns(runs [][]Value, typeName string) {

0 commit comments

Comments
 (0)