-
Notifications
You must be signed in to change notification settings - Fork 10.6k
[stdlib] Fix Int(_:radix:) accepting unintentional cases (SR-187) #613
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 4 commits
0998e38
8d1261e
58bc25b
e6b08b9
4faa43a
17a5845
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -67,21 +67,20 @@ internal func _parseUnsignedAsciiAsUIntMax( | |
| /// non-negative number <= `maximum`, return that number. Otherwise, | ||
| /// return `nil`. | ||
| /// | ||
| /// - Note: If `text` begins with `"+"` or `"-"`, even if the rest of | ||
| /// the characters are `"0"`, the result is `nil`. | ||
| /// - Note: For text matching the regular expression "-0+", the result | ||
| /// is `0`, not `nil`. | ||
| internal func _parseAsciiAsUIntMax( | ||
| u16: String.UTF16View, _ radix: Int, _ maximum: UIntMax | ||
| utf16: String.UTF16View, _ radix: Int, _ maximum: UIntMax | ||
| ) -> UIntMax? { | ||
| if u16.isEmpty { return nil } | ||
| let c = u16.first | ||
| if _fastPath(c != _ascii16("-")) { | ||
| let unsignedText | ||
| = c == _ascii16("+") ? u16.dropFirst() : u16 | ||
| return _parseUnsignedAsciiAsUIntMax(unsignedText, radix, maximum) | ||
| } | ||
| else { | ||
| return _parseAsciiAsIntMax(u16, radix, 0) == 0 ? 0 : nil | ||
| } | ||
| if utf16.isEmpty { return nil } | ||
| // Parse (optional) sign | ||
| let (digitsUTF16, hasMinus) = _parseOptionalAsciiSign(utf16) | ||
| // Parse digits | ||
| guard let result = _parseUnsignedAsciiAsUIntMax(digitsUTF16, radix, maximum) else { return nil } | ||
| // Disallow < 0 | ||
| if hasMinus && result != 0 { return nil } | ||
| // Return | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The comments are intended as headings for code grouping (instead of newlines) so you can quickly skim the algorithm and it's structure/flow just from those headings. But I agree |
||
| return result | ||
| } | ||
|
|
||
| /// If text is an ASCII representation in the given `radix` of a | ||
|
|
@@ -91,23 +90,28 @@ internal func _parseAsciiAsUIntMax( | |
| /// - Note: For text matching the regular expression "-0+", the result | ||
| /// is `0`, not `nil`. | ||
| internal func _parseAsciiAsIntMax( | ||
| u16: String.UTF16View, _ radix: Int, _ maximum: IntMax | ||
| utf16: String.UTF16View, _ radix: Int, _ maximum: IntMax | ||
| ) -> IntMax? { | ||
| _sanityCheck(maximum >= 0, "maximum should be non-negative") | ||
| if utf16.isEmpty { return nil } | ||
| // Parse (optional) sign | ||
| let (digitsUTF16, hasMinus) = _parseOptionalAsciiSign(utf16) | ||
| // Parse digits | ||
| let absValueMax = UIntMax(bitPattern: maximum) + (hasMinus ? 1 : 0) // E.g. Int8's range is -128...127 | ||
| guard let absValue = _parseUnsignedAsciiAsUIntMax(digitsUTF16, radix, absValueMax) else { return nil } | ||
| // Return signed result | ||
| return IntMax(bitPattern: hasMinus ? 0 &- absValue : absValue) | ||
| } | ||
|
|
||
| if u16.isEmpty { return nil } | ||
|
|
||
| // Drop any leading "-" | ||
| let negative = u16.first == _ascii16("-") | ||
| let absResultText = negative ? u16.dropFirst() : u16 | ||
|
|
||
| let absResultMax = UIntMax(bitPattern: maximum) + (negative ? 1 : 0) | ||
|
|
||
| // Parse the result as unsigned | ||
| if let absResult = _parseAsciiAsUIntMax(absResultText, radix, absResultMax) { | ||
| return IntMax(bitPattern: negative ? 0 &- absResult : absResult) | ||
| /// Strip an optional single leading ASCII plus/minus sign from `utf16`. | ||
| private func _parseOptionalAsciiSign( | ||
| utf16: String.UTF16View | ||
| ) -> (digitsUTF16: String.UTF16View, isMinus: Bool) { | ||
| switch utf16.first { | ||
| case _ascii16("-")?: return (utf16.dropFirst(), true) | ||
| case _ascii16("+")?: return (utf16.dropFirst(), false) | ||
| default: return (utf16, false) | ||
| } | ||
| return nil | ||
| } | ||
|
|
||
| //===--- Loop over all integer types --------------------------------------===// | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We try to end inline comments with a period to remove the possible ambiguity between a complete comment and a comment that was left unfinished (for example, because the engineer was distracted).