{"record":{"id":"af4a673610c8fb7f","repo":"stride3d/stride","slug":"cannot-use-chars-as-a-list-of-digit","errorCode":null,"errorMessage":"Cannot use {chars} as a list of digit","messagePattern":"Cannot use (.+?) as a list of digit","errorType":"exception","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"sources/shaders/Stride.Shaders.Parsers/Parsing/SDSL/Parsers/Terminals/Terminals.cs","lineNumber":155,"sourceCode":"    static string allChars = \"0123456789\";\n    public static DigitRange All { get; } = new(0..9);\n    public static DigitRange ExceptZero { get; } = new(1..9);\n    public static DigitRange OnlyZero { get; } = new(0);\n    public string Chars { get; set; }\n    public DigitRange(Range range)\n    {\n        var (o, l) = range.GetOffsetAndLength(allChars.Length);\n        Chars = allChars[o..Math.Min(allChars.Length, o + l + 1)];\n    }\n    public DigitRange(int digit)\n    {\n        Chars = $\"{(char)(digit + '0')}\";\n    }\n    public DigitRange(string chars)\n    {\n        foreach (var e in chars)\n            if (!char.IsDigit(e))\n                throw new ArgumentException($\"Cannot use {chars} as a list of digit\");\n        Chars = chars;\n    }\n\n    public static implicit operator DigitRange(Range range) => new(range);\n    public static implicit operator DigitRange(int number) => new(number);\n    public static implicit operator DigitRange(string numbers) => new(numbers);\n}\n\npublic record struct DigitTokenParser(DigitRange Mode) : IToken\n{\n    public readonly bool Match<TScanner>(ref TScanner scanner, bool advance)\n        where TScanner : struct, IScanner\n    {\n        bool found = false;\n        if (Mode.Chars.Contains((char)scanner.Peek()))\n            found = true;\n        if (advance && found)\n            scanner.Advance(1);","sourceCodeStart":137,"sourceCodeEnd":173,"githubUrl":"https://github.com/stride3d/stride/blob/96fad776d210c221682aac1ccdf4c79dc046fc38/sources/shaders/Stride.Shaders.Parsers/Parsing/SDSL/Parsers/Terminals/Terminals.cs#L137-L173","documentation":"The DigitRange(string) constructor validates that every character of the supplied string is an ASCII digit, since the string is used as the list of allowed digit characters for a terminal matcher. If any non-digit character is present it throws ArgumentException with 'Cannot use {chars} as a list of digit'.","triggerScenarios":"Constructing a DigitRange directly or via the implicit string conversion with a string containing letters, signs, spaces, or escapes — e.g. new DigitRange(\"0-9\") (range syntax unsupported), \"0x123456789abcdef\", or an empty/whitespace-containing string.","commonSituations":"Authors of custom SDSL terminals writing C-style ranges like \"0-9\" or \"a-f\" instead of enumerating digits; accidental copy of regex character-class syntax.","solutions":["Pass only digit characters, enumerating them explicitly: \"0123456789\"","Use the Range or int constructor overloads (implicit conversions) instead of a string with range syntax","Validate the string with char.IsDigit for each char before constructing"],"exampleFix":"// before\nvar digits = new DigitRange(\"0-9\");\n// after\nvar digits = new DigitRange(\"0123456789\");","handlingStrategy":"validation","validationCode":"bool IsDigitList(string s) => s.Length > 0 && s.All(char.IsDigit);\nif (!IsDigitList(chars)) throw new ArgumentException($\"Cannot use {chars} as a list of digit\");","typeGuard":"bool IsValidDigitRange(string s) => !string.IsNullOrEmpty(s) && s.All(char.IsDigit);","tryCatchPattern":"try { var range = new DigitRange(chars); }\ncatch (ArgumentException ex) { Log(ex.Message); range = new DigitRange(\"0123456789\"); }","preventionTips":["Never pass regex-style ranges like \"0-9\"; enumerate digits explicitly","Prefer the Range or int constructor overloads over raw strings","Reject/escape user-supplied character-set strings before constructing","Add a unit test asserting each char of every DigitRange constant is a digit"],"tags":["argument","validation","parser","sdsl"],"backgroundTag":"invalid-argument-format","analyzedSha":"96fad776d210c221682aac1ccdf4c79dc046fc38","analyzedAt":"2026-09-14T02:59:31.279Z","contentChangedAt":"2026-09-14T02:59:31.279Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}