Skip to content

Conversation

@karrybit
Copy link

Problem

The runeToUpper() function in lang/util.go has a bug that corrupts characters that are already uppercase.

Symptoms

When generating documentation for Go code with generic types where methods are defined on inner struct types, example function names are corrupted in the output:

  • ExampleID_Equal()Example (%qual) (expected: Example (Equal))
  • ExampleID_String()Example (3tring) (expected: Example (String))
  • ExampleID_Value()Example (6alue) (expected: Example (Value))

Root Cause

The current implementation unconditionally subtracts the ASCII difference between lowercase and uppercase letters (32) from any rune:

const lowerToUpper = 'a' - 'A'  // = 32

func runeToUpper(r rune) rune {
    return r - lowerToUpper  // BUG: Subtracts 32 from ANY rune
}

When applied to an already uppercase letter, this produces incorrect results:

  • runeToUpper('E') = ASCII(69) - 32 = ASCII(37) = '%'
  • runeToUpper('S') = ASCII(83) - 32 = ASCII(51) = '3'
  • runeToUpper('V') = ASCII(86) - 32 = ASCII(54) = '6'

The bug accidentally works for lowercase letters:

  • runeToUpper('s') = ASCII(115) - 32 = ASCII(83) = 'S' ✓ (correct!)

Why This Bug Wasn't Caught Earlier

Most Go code follows the convention of starting method names with lowercase letters (e.g., structFields, myMethod). After gomarkdoc strips the type prefix, these names still start with lowercase letters, so runeToUpper() accidentally produces correct results.

The bug only manifests with code patterns like:

type ID[T comparable] struct{ inner[T] }

func (i *inner[T]) Equal(other T) bool { ... }
func (i *inner[T]) String() string { ... }
func (i *inner[T]) Value() T { ... }

Where example functions are named ExampleID_Equal, ExampleID_String, etc., and after stripping the type prefix ID_, we get Equal, String, Value (uppercase start).

Solution

Use the standard library's unicode.ToUpper() function, which:

  • Correctly handles both uppercase and lowercase characters
  • Supports non-ASCII Unicode characters
  • Follows Go idioms
func runeToUpper(r rune) rune {
    return unicode.ToUpper(r)
}

Testing

All existing tests pass except for two unrelated tests (TestFunc_textScannerInit and TestFunc_ioIoutilTempFile) that appear to have pre-existing failures related to Markdown link formatting.

The fix has been verified to correctly process example names for generic types with methods defined on inner structs.

Related

This fix addresses the same general category of issue as #47 and #51, which dealt with example name processing.

Replace the buggy runeToUpper() implementation with direct calls to
unicode.ToUpper(). The previous implementation unconditionally subtracted
32 from any rune, which corrupted uppercase characters.

This bug caused example names for generic types to be corrupted:
- ExampleID_Equal became "Example (%qual)" instead of "Example (Equal)"
- ExampleID_String became "Example (3tring)" instead of "Example (String)"
- ExampleID_Value became "Example (6alue)" instead of "Example (Value)"

The corruption occurred because runeToUpper('E') = ASCII(69) - 32 = '%'.

Changes:
- Remove buggy runeToUpper() function
- Remove unused lowerToUpper constant
- Call unicode.ToUpper() directly in splitCamel()
@karrybit karrybit force-pushed the fix/rune-to-upper-bug branch from 26da5e3 to def1e4f Compare December 11, 2025 05:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant