Warn on the presence of unnecessary wildcards in output positions

This commit is contained in:
Ayaz Hafiz 2022-10-26 10:49:19 -05:00
parent d55dbbf0ae
commit cfe7c8e5ef
No known key found for this signature in database
GPG key ID: 0E2A37416A25EF58
17 changed files with 151 additions and 106 deletions

View file

@ -991,7 +991,7 @@ mod cli_run {
This #UserApp.main value is a: This #UserApp.main value is a:
Task.Task {} * [Write [Stdout]*]* ? Task.Task {} * [Write [Stdout]a]b ?
But the type annotation on main says it should be: But the type annotation on main says it should be:
@ -1012,7 +1012,7 @@ mod cli_run {
This #UserApp.main value is a: This #UserApp.main value is a:
Task.Task {} * [Write [Stdout]*]* ? Task.Task {} * [Write [Stdout]a]b ?
But toEffect needs its 1st argument to be: But toEffect needs its 1st argument to be:

View file

@ -1,7 +1,7 @@
interface Base64 exposes [fromBytes, fromStr, toBytes, toStr] imports [Base64.Decode, Base64.Encode] interface Base64 exposes [fromBytes, fromStr, toBytes, toStr] imports [Base64.Decode, Base64.Encode]
# base 64 encoding from a sequence of bytes # base 64 encoding from a sequence of bytes
fromBytes : List U8 -> Result Str [InvalidInput]* fromBytes : List U8 -> Result Str [InvalidInput]
fromBytes = \bytes -> fromBytes = \bytes ->
when Base64.Decode.fromBytes bytes is when Base64.Decode.fromBytes bytes is
Ok v -> Ok v ->
@ -11,16 +11,16 @@ fromBytes = \bytes ->
Err InvalidInput Err InvalidInput
# base 64 encoding from a string # base 64 encoding from a string
fromStr : Str -> Result Str [InvalidInput]* fromStr : Str -> Result Str [InvalidInput]
fromStr = \str -> fromStr = \str ->
fromBytes (Str.toUtf8 str) fromBytes (Str.toUtf8 str)
# base64-encode bytes to the original # base64-encode bytes to the original
toBytes : Str -> Result (List U8) [InvalidInput]* toBytes : Str -> Result (List U8) [InvalidInput]
toBytes = \str -> toBytes = \str ->
Ok (Base64.Encode.toBytes str) Ok (Base64.Encode.toBytes str)
toStr : Str -> Result Str [InvalidInput]* toStr : Str -> Result Str [InvalidInput]
toStr = \str -> toStr = \str ->
when toBytes str is when toBytes str is
Ok bytes -> Ok bytes ->

View file

@ -36,7 +36,7 @@ nestHelp = \{ s, f, m, x } ->
Expr : [Val I64, Var Str, Add Expr Expr, Mul Expr Expr, Pow Expr Expr, Ln Expr] Expr : [Val I64, Var Str, Add Expr Expr, Mul Expr Expr, Pow Expr Expr, Ln Expr]
divmod : I64, I64 -> Result { div : I64, mod : I64 } [DivByZero]* divmod : I64, I64 -> Result { div : I64, mod : I64 } [DivByZero]
divmod = \l, r -> divmod = \l, r ->
when Pair (Num.divTruncChecked l r) (Num.remChecked l r) is when Pair (Num.divTruncChecked l r) (Num.remChecked l r) is
Pair (Ok div) (Ok mod) -> Ok { div, mod } Pair (Ok div) (Ok mod) -> Ok { div, mod }

View file

@ -105,7 +105,7 @@ withCapacity = \n -> @Dict (List.withCapacity n)
## ##
## expect Dict.get dictionary 1 == Ok "Apple" ## expect Dict.get dictionary 1 == Ok "Apple"
## expect Dict.get dictionary 2000 == Err KeyNotFound ## expect Dict.get dictionary 2000 == Err KeyNotFound
get : Dict k v, k -> Result v [KeyNotFound]* | k has Eq get : Dict k v, k -> Result v [KeyNotFound] | k has Eq
get = \@Dict list, needle -> get = \@Dict list, needle ->
when List.findFirst list (\Pair key _ -> key == needle) is when List.findFirst list (\Pair key _ -> key == needle) is
Ok (Pair _ v) -> Ok (Pair _ v) ->

View file

@ -219,7 +219,7 @@ isEmpty = \list ->
# but will cause a reference count increment on the value it got out of the list # but will cause a reference count increment on the value it got out of the list
getUnsafe : List a, Nat -> a getUnsafe : List a, Nat -> a
get : List a, Nat -> Result a [OutOfBounds]* get : List a, Nat -> Result a [OutOfBounds]
get = \list, index -> get = \list, index ->
if index < List.len list then if index < List.len list then
Ok (List.getUnsafe list index) Ok (List.getUnsafe list index)
@ -298,7 +298,7 @@ reserve : List a, Nat -> List a
concat : List a, List a -> List a concat : List a, List a -> List a
## Returns the last element in the list, or `ListWasEmpty` if it was empty. ## Returns the last element in the list, or `ListWasEmpty` if it was empty.
last : List a -> Result a [ListWasEmpty]* last : List a -> Result a [ListWasEmpty]
last = \list -> last = \list ->
when List.get list (Num.subSaturated (List.len list) 1) is when List.get list (Num.subSaturated (List.len list) 1) is
Ok v -> Ok v Ok v -> Ok v
@ -683,7 +683,7 @@ sortDesc = \list -> List.sortWith list (\a, b -> Num.compare b a)
swap : List a, Nat, Nat -> List a swap : List a, Nat, Nat -> List a
## Returns the first element in the list, or `ListWasEmpty` if it was empty. ## Returns the first element in the list, or `ListWasEmpty` if it was empty.
first : List a -> Result a [ListWasEmpty]* first : List a -> Result a [ListWasEmpty]
first = \list -> first = \list ->
when List.get list 0 is when List.get list 0 is
Ok v -> Ok v Ok v -> Ok v
@ -776,7 +776,7 @@ drop = \list, n ->
## To replace the element at a given index, instead of dropping it, see [List.set]. ## To replace the element at a given index, instead of dropping it, see [List.set].
dropAt : List elem, Nat -> List elem dropAt : List elem, Nat -> List elem
min : List (Num a) -> Result (Num a) [ListWasEmpty]* min : List (Num a) -> Result (Num a) [ListWasEmpty]
min = \list -> min = \list ->
when List.first list is when List.first list is
Ok initial -> Ok initial ->
@ -793,7 +793,7 @@ minHelp = \list, initial ->
else else
bestSoFar bestSoFar
max : List (Num a) -> Result (Num a) [ListWasEmpty]* max : List (Num a) -> Result (Num a) [ListWasEmpty]
max = \list -> max = \list ->
when List.first list is when List.first list is
Ok initial -> Ok initial ->
@ -820,7 +820,7 @@ joinMap = \list, mapper ->
## Returns the first element of the list satisfying a predicate function. ## Returns the first element of the list satisfying a predicate function.
## If no satisfying element is found, an `Err NotFound` is returned. ## If no satisfying element is found, an `Err NotFound` is returned.
findFirst : List elem, (elem -> Bool) -> Result elem [NotFound]* findFirst : List elem, (elem -> Bool) -> Result elem [NotFound]
findFirst = \list, pred -> findFirst = \list, pred ->
callback = \_, elem -> callback = \_, elem ->
if pred elem then if pred elem then
@ -834,7 +834,7 @@ findFirst = \list, pred ->
## Returns the last element of the list satisfying a predicate function. ## Returns the last element of the list satisfying a predicate function.
## If no satisfying element is found, an `Err NotFound` is returned. ## If no satisfying element is found, an `Err NotFound` is returned.
findLast : List elem, (elem -> Bool) -> Result elem [NotFound]* findLast : List elem, (elem -> Bool) -> Result elem [NotFound]
findLast = \list, pred -> findLast = \list, pred ->
callback = \_, elem -> callback = \_, elem ->
if pred elem then if pred elem then
@ -849,7 +849,7 @@ findLast = \list, pred ->
## Returns the index at which the first element in the list ## Returns the index at which the first element in the list
## satisfying a predicate function can be found. ## satisfying a predicate function can be found.
## If no satisfying element is found, an `Err NotFound` is returned. ## If no satisfying element is found, an `Err NotFound` is returned.
findFirstIndex : List elem, (elem -> Bool) -> Result Nat [NotFound]* findFirstIndex : List elem, (elem -> Bool) -> Result Nat [NotFound]
findFirstIndex = \list, matcher -> findFirstIndex = \list, matcher ->
foundIndex = List.iterate list 0 \index, elem -> foundIndex = List.iterate list 0 \index, elem ->
if matcher elem then if matcher elem then
@ -864,7 +864,7 @@ findFirstIndex = \list, matcher ->
## Returns the last index at which the first element in the list ## Returns the last index at which the first element in the list
## satisfying a predicate function can be found. ## satisfying a predicate function can be found.
## If no satisfying element is found, an `Err NotFound` is returned. ## If no satisfying element is found, an `Err NotFound` is returned.
findLastIndex : List elem, (elem -> Bool) -> Result Nat [NotFound]* findLastIndex : List elem, (elem -> Bool) -> Result Nat [NotFound]
findLastIndex = \list, matches -> findLastIndex = \list, matches ->
foundIndex = List.iterateBackwards list (List.len list) \prevIndex, elem -> foundIndex = List.iterateBackwards list (List.len list) \prevIndex, elem ->
if matches elem then if matches elem then
@ -962,7 +962,7 @@ split = \elements, userSplitIndex ->
## remaining elements after that occurrence. If the delimiter is not found, returns `Err`. ## remaining elements after that occurrence. If the delimiter is not found, returns `Err`.
## ##
## List.splitFirst [Foo, Z, Bar, Z, Baz] Z == Ok { before: [Foo], after: [Bar, Baz] } ## List.splitFirst [Foo, Z, Bar, Z, Baz] Z == Ok { before: [Foo], after: [Bar, Baz] }
splitFirst : List elem, elem -> Result { before : List elem, after : List elem } [NotFound]* | elem has Eq splitFirst : List elem, elem -> Result { before : List elem, after : List elem } [NotFound] | elem has Eq
splitFirst = \list, delimiter -> splitFirst = \list, delimiter ->
when List.findFirstIndex list (\elem -> elem == delimiter) is when List.findFirstIndex list (\elem -> elem == delimiter) is
Ok index -> Ok index ->
@ -977,7 +977,7 @@ splitFirst = \list, delimiter ->
## remaining elements after that occurrence. If the delimiter is not found, returns `Err`. ## remaining elements after that occurrence. If the delimiter is not found, returns `Err`.
## ##
## List.splitLast [Foo, Z, Bar, Z, Baz] Z == Ok { before: [Foo, Bar], after: [Baz] } ## List.splitLast [Foo, Z, Bar, Z, Baz] Z == Ok { before: [Foo, Bar], after: [Baz] }
splitLast : List elem, elem -> Result { before : List elem, after : List elem } [NotFound]* | elem has Eq splitLast : List elem, elem -> Result { before : List elem, after : List elem } [NotFound] | elem has Eq
splitLast = \list, delimiter -> splitLast = \list, delimiter ->
when List.findLastIndex list (\elem -> elem == delimiter) is when List.findLastIndex list (\elem -> elem == delimiter) is
Ok index -> Ok index ->

View file

@ -743,7 +743,7 @@ atan : Frac a -> Frac a
## >>> Num.sqrt -4.0f64 ## >>> Num.sqrt -4.0f64
sqrt : Frac a -> Frac a sqrt : Frac a -> Frac a
sqrtChecked : Frac a -> Result (Frac a) [SqrtOfNegative]* sqrtChecked : Frac a -> Result (Frac a) [SqrtOfNegative]
sqrtChecked = \x -> sqrtChecked = \x ->
if x < 0.0 then if x < 0.0 then
Err SqrtOfNegative Err SqrtOfNegative
@ -752,7 +752,7 @@ sqrtChecked = \x ->
log : Frac a -> Frac a log : Frac a -> Frac a
logChecked : Frac a -> Result (Frac a) [LogNeedsPositive]* logChecked : Frac a -> Result (Frac a) [LogNeedsPositive]
logChecked = \x -> logChecked = \x ->
if x <= 0.0 then if x <= 0.0 then
Err LogNeedsPositive Err LogNeedsPositive
@ -791,7 +791,7 @@ logChecked = \x ->
## >>> |> Num.div 2.0 ## >>> |> Num.div 2.0
div : Frac a, Frac a -> Frac a div : Frac a, Frac a -> Frac a
divChecked : Frac a, Frac a -> Result (Frac a) [DivByZero]* divChecked : Frac a, Frac a -> Result (Frac a) [DivByZero]
divChecked = \a, b -> divChecked = \a, b ->
if Num.isZero b then if Num.isZero b then
Err DivByZero Err DivByZero
@ -800,7 +800,7 @@ divChecked = \a, b ->
divCeil : Int a, Int a -> Int a divCeil : Int a, Int a -> Int a
divCeilChecked : Int a, Int a -> Result (Int a) [DivByZero]* divCeilChecked : Int a, Int a -> Result (Int a) [DivByZero]
divCeilChecked = \a, b -> divCeilChecked = \a, b ->
if Num.isZero b then if Num.isZero b then
Err DivByZero Err DivByZero
@ -825,7 +825,7 @@ divCeilChecked = \a, b ->
## ##
divTrunc : Int a, Int a -> Int a divTrunc : Int a, Int a -> Int a
divTruncChecked : Int a, Int a -> Result (Int a) [DivByZero]* divTruncChecked : Int a, Int a -> Result (Int a) [DivByZero]
divTruncChecked = \a, b -> divTruncChecked = \a, b ->
if Num.isZero b then if Num.isZero b then
Err DivByZero Err DivByZero
@ -845,7 +845,7 @@ divTruncChecked = \a, b ->
## >>> Num.rem -8 -3 ## >>> Num.rem -8 -3
rem : Int a, Int a -> Int a rem : Int a, Int a -> Int a
remChecked : Int a, Int a -> Result (Int a) [DivByZero]* remChecked : Int a, Int a -> Result (Int a) [DivByZero]
remChecked = \a, b -> remChecked = \a, b ->
if Num.isZero b then if Num.isZero b then
Err DivByZero Err DivByZero
@ -944,7 +944,7 @@ addSaturated : Num a, Num a -> Num a
## ##
## This is the same as [Num.add] except if the operation overflows, instead of ## This is the same as [Num.add] except if the operation overflows, instead of
## panicking or returning ∞ or -∞, it will return `Err Overflow`. ## panicking or returning ∞ or -∞, it will return `Err Overflow`.
addChecked : Num a, Num a -> Result (Num a) [Overflow]* addChecked : Num a, Num a -> Result (Num a) [Overflow]
addChecked = \a, b -> addChecked = \a, b ->
result = addCheckedLowlevel a b result = addCheckedLowlevel a b
@ -970,7 +970,7 @@ subSaturated : Num a, Num a -> Num a
## ##
## This is the same as [Num.sub] except if the operation overflows, instead of ## This is the same as [Num.sub] except if the operation overflows, instead of
## panicking or returning ∞ or -∞, it will return `Err Overflow`. ## panicking or returning ∞ or -∞, it will return `Err Overflow`.
subChecked : Num a, Num a -> Result (Num a) [Overflow]* subChecked : Num a, Num a -> Result (Num a) [Overflow]
subChecked = \a, b -> subChecked = \a, b ->
result = subCheckedLowlevel a b result = subCheckedLowlevel a b
@ -994,7 +994,7 @@ mulSaturated : Num a, Num a -> Num a
## ##
## This is the same as [Num.mul] except if the operation overflows, instead of ## This is the same as [Num.mul] except if the operation overflows, instead of
## panicking or returning ∞ or -∞, it will return `Err Overflow`. ## panicking or returning ∞ or -∞, it will return `Err Overflow`.
mulChecked : Num a, Num a -> Result (Num a) [Overflow]* mulChecked : Num a, Num a -> Result (Num a) [Overflow]
mulChecked = \a, b -> mulChecked = \a, b ->
result = mulCheckedLowlevel a b result = mulCheckedLowlevel a b
@ -1250,19 +1250,19 @@ toF64 : Num * -> F64
## Converts a [Int] to an [I8]. ## Converts a [Int] to an [I8].
## If the given integer can't be precisely represented in an [I8], returns ## If the given integer can't be precisely represented in an [I8], returns
## `Err OutOfBounds`. ## `Err OutOfBounds`.
toI8Checked : Int * -> Result I8 [OutOfBounds]* toI8Checked : Int * -> Result I8 [OutOfBounds]
toI16Checked : Int * -> Result I16 [OutOfBounds]* toI16Checked : Int * -> Result I16 [OutOfBounds]
toI32Checked : Int * -> Result I32 [OutOfBounds]* toI32Checked : Int * -> Result I32 [OutOfBounds]
toI64Checked : Int * -> Result I64 [OutOfBounds]* toI64Checked : Int * -> Result I64 [OutOfBounds]
toI128Checked : Int * -> Result I128 [OutOfBounds]* toI128Checked : Int * -> Result I128 [OutOfBounds]
toU8Checked : Int * -> Result U8 [OutOfBounds]* toU8Checked : Int * -> Result U8 [OutOfBounds]
toU16Checked : Int * -> Result U16 [OutOfBounds]* toU16Checked : Int * -> Result U16 [OutOfBounds]
toU32Checked : Int * -> Result U32 [OutOfBounds]* toU32Checked : Int * -> Result U32 [OutOfBounds]
toU64Checked : Int * -> Result U64 [OutOfBounds]* toU64Checked : Int * -> Result U64 [OutOfBounds]
toU128Checked : Int * -> Result U128 [OutOfBounds]* toU128Checked : Int * -> Result U128 [OutOfBounds]
toNatChecked : Int * -> Result Nat [OutOfBounds]* toNatChecked : Int * -> Result Nat [OutOfBounds]
toF32Checked : Num * -> Result F32 [OutOfBounds]* toF32Checked : Num * -> Result F32 [OutOfBounds]
toF64Checked : Num * -> Result F64 [OutOfBounds]* toF64Checked : Num * -> Result F64 [OutOfBounds]
# Special Floating-Point operations # Special Floating-Point operations
## When given a [F64] or [F32] value, returns `Bool.false` if that value is ## When given a [F64] or [F32] value, returns `Bool.false` if that value is

View file

@ -220,7 +220,7 @@ toUtf8 : Str -> List U8
## ##
## expect Str.fromUtf8 [233, 185, 143] == Ok "鹏" ## expect Str.fromUtf8 [233, 185, 143] == Ok "鹏"
## expect Str.fromUtf8 [0xb0] == Err (BadUtf8 InvalidStartByte 0) ## expect Str.fromUtf8 [0xb0] == Err (BadUtf8 InvalidStartByte 0)
fromUtf8 : List U8 -> Result Str [BadUtf8 Utf8ByteProblem Nat]* fromUtf8 : List U8 -> Result Str [BadUtf8 Utf8ByteProblem Nat]
fromUtf8 = \bytes -> fromUtf8 = \bytes ->
result = fromUtf8RangeLowlevel bytes 0 (List.len bytes) result = fromUtf8RangeLowlevel bytes 0 (List.len bytes)
@ -233,7 +233,7 @@ fromUtf8 = \bytes ->
## into a [Str] ## into a [Str]
## ##
## expect Str.fromUtf8Range [72, 105, 80, 103] { start : 0, count : 2 } == Ok "Hi" ## expect Str.fromUtf8Range [72, 105, 80, 103] { start : 0, count : 2 } == Ok "Hi"
fromUtf8Range : List U8, { start : Nat, count : Nat } -> Result Str [BadUtf8 Utf8ByteProblem Nat, OutOfBounds]* fromUtf8Range : List U8, { start : Nat, count : Nat } -> Result Str [BadUtf8 Utf8ByteProblem Nat, OutOfBounds]
fromUtf8Range = \bytes, config -> fromUtf8Range = \bytes, config ->
if config.start + config.count <= List.len bytes then if config.start + config.count <= List.len bytes then
result = fromUtf8RangeLowlevel bytes config.start config.count result = fromUtf8RangeLowlevel bytes config.start config.count
@ -288,7 +288,7 @@ trimRight : Str -> Str
## expect Str.toDec "10" == Ok 10dec ## expect Str.toDec "10" == Ok 10dec
## expect Str.toDec "-0.25" == Ok -0.25dec ## expect Str.toDec "-0.25" == Ok -0.25dec
## expect Str.toDec "not a number" == Err InvalidNumStr ## expect Str.toDec "not a number" == Err InvalidNumStr
toDec : Str -> Result Dec [InvalidNumStr]* toDec : Str -> Result Dec [InvalidNumStr]
toDec = \string -> strToNumHelp string toDec = \string -> strToNumHelp string
## Encode a [Str] to a [F64]. A [F64] value is a 64-bit ## Encode a [Str] to a [F64]. A [F64] value is a 64-bit
@ -297,7 +297,7 @@ toDec = \string -> strToNumHelp string
## ##
## expect Str.toF64 "0.10" == Ok 0.10f64 ## expect Str.toF64 "0.10" == Ok 0.10f64
## expect Str.toF64 "not a number" == Err InvalidNumStr ## expect Str.toF64 "not a number" == Err InvalidNumStr
toF64 : Str -> Result F64 [InvalidNumStr]* toF64 : Str -> Result F64 [InvalidNumStr]
toF64 = \string -> strToNumHelp string toF64 = \string -> strToNumHelp string
## Encode a [Str] to a [F32].A [F32] value is a 32-bit ## Encode a [Str] to a [F32].A [F32] value is a 32-bit
@ -306,7 +306,7 @@ toF64 = \string -> strToNumHelp string
## ##
## expect Str.toF32 "0.10" == Ok 0.10f32 ## expect Str.toF32 "0.10" == Ok 0.10f32
## expect Str.toF32 "not a number" == Err InvalidNumStr ## expect Str.toF32 "not a number" == Err InvalidNumStr
toF32 : Str -> Result F32 [InvalidNumStr]* toF32 : Str -> Result F32 [InvalidNumStr]
toF32 = \string -> strToNumHelp string toF32 = \string -> strToNumHelp string
## Convert a [Str] to a [Nat]. If the given number doesn't fit in [Nat], it will be [truncated](https://www.ualberta.ca/computing-science/media-library/teaching-resources/java/truncation-rounding.html). ## Convert a [Str] to a [Nat]. If the given number doesn't fit in [Nat], it will be [truncated](https://www.ualberta.ca/computing-science/media-library/teaching-resources/java/truncation-rounding.html).
@ -324,7 +324,7 @@ toF32 = \string -> strToNumHelp string
## ##
## expect Str.toNat "9_000_000_000" == Ok 9000000000 ## expect Str.toNat "9_000_000_000" == Ok 9000000000
## expect Str.toNat "not a number" == Err InvalidNumStr ## expect Str.toNat "not a number" == Err InvalidNumStr
toNat : Str -> Result Nat [InvalidNumStr]* toNat : Str -> Result Nat [InvalidNumStr]
toNat = \string -> strToNumHelp string toNat = \string -> strToNumHelp string
## Encode a [Str] to an unsigned [U128] integer. A [U128] value can hold numbers ## Encode a [Str] to an unsigned [U128] integer. A [U128] value can hold numbers
@ -335,7 +335,7 @@ toNat = \string -> strToNumHelp string
## expect Str.toU128 "0.1" == Err InvalidNumStr ## expect Str.toU128 "0.1" == Err InvalidNumStr
## expect Str.toU128 "-1" == Err InvalidNumStr ## expect Str.toU128 "-1" == Err InvalidNumStr
## expect Str.toU128 "not a number" == Err InvalidNumStr ## expect Str.toU128 "not a number" == Err InvalidNumStr
toU128 : Str -> Result U128 [InvalidNumStr]* toU128 : Str -> Result U128 [InvalidNumStr]
toU128 = \string -> strToNumHelp string toU128 = \string -> strToNumHelp string
## Encode a [Str] to a signed [I128] integer. A [I128] value can hold numbers ## Encode a [Str] to a signed [I128] integer. A [I128] value can hold numbers
@ -347,7 +347,7 @@ toU128 = \string -> strToNumHelp string
## expect Str.toI128 "-1" == Ok -1i128 ## expect Str.toI128 "-1" == Ok -1i128
## expect Str.toI128 "0.1" == Err InvalidNumStr ## expect Str.toI128 "0.1" == Err InvalidNumStr
## expect Str.toI128 "not a number" == Err InvalidNumStr ## expect Str.toI128 "not a number" == Err InvalidNumStr
toI128 : Str -> Result I128 [InvalidNumStr]* toI128 : Str -> Result I128 [InvalidNumStr]
toI128 = \string -> strToNumHelp string toI128 = \string -> strToNumHelp string
## Encode a [Str] to an unsigned [U64] integer. A [U64] value can hold numbers ## Encode a [Str] to an unsigned [U64] integer. A [U64] value can hold numbers
@ -358,7 +358,7 @@ toI128 = \string -> strToNumHelp string
## expect Str.toU64 "0.1" == Err InvalidNumStr ## expect Str.toU64 "0.1" == Err InvalidNumStr
## expect Str.toU64 "-1" == Err InvalidNumStr ## expect Str.toU64 "-1" == Err InvalidNumStr
## expect Str.toU64 "not a number" == Err InvalidNumStr ## expect Str.toU64 "not a number" == Err InvalidNumStr
toU64 : Str -> Result U64 [InvalidNumStr]* toU64 : Str -> Result U64 [InvalidNumStr]
toU64 = \string -> strToNumHelp string toU64 = \string -> strToNumHelp string
## Encode a [Str] to a signed [I64] integer. A [I64] value can hold numbers ## Encode a [Str] to a signed [I64] integer. A [I64] value can hold numbers
@ -369,7 +369,7 @@ toU64 = \string -> strToNumHelp string
## expect Str.toI64 "-1" == Ok -1i64 ## expect Str.toI64 "-1" == Ok -1i64
## expect Str.toI64 "0.1" == Err InvalidNumStr ## expect Str.toI64 "0.1" == Err InvalidNumStr
## expect Str.toI64 "not a number" == Err InvalidNumStr ## expect Str.toI64 "not a number" == Err InvalidNumStr
toI64 : Str -> Result I64 [InvalidNumStr]* toI64 : Str -> Result I64 [InvalidNumStr]
toI64 = \string -> strToNumHelp string toI64 = \string -> strToNumHelp string
## Encode a [Str] to an unsigned [U32] integer. A [U32] value can hold numbers ## Encode a [Str] to an unsigned [U32] integer. A [U32] value can hold numbers
@ -380,7 +380,7 @@ toI64 = \string -> strToNumHelp string
## expect Str.toU32 "0.1" == Err InvalidNumStr ## expect Str.toU32 "0.1" == Err InvalidNumStr
## expect Str.toU32 "-1" == Err InvalidNumStr ## expect Str.toU32 "-1" == Err InvalidNumStr
## expect Str.toU32 "not a number" == Err InvalidNumStr ## expect Str.toU32 "not a number" == Err InvalidNumStr
toU32 : Str -> Result U32 [InvalidNumStr]* toU32 : Str -> Result U32 [InvalidNumStr]
toU32 = \string -> strToNumHelp string toU32 = \string -> strToNumHelp string
## Encode a [Str] to a signed [I32] integer. A [I32] value can hold numbers ## Encode a [Str] to a signed [I32] integer. A [I32] value can hold numbers
@ -391,7 +391,7 @@ toU32 = \string -> strToNumHelp string
## expect Str.toI32 "-1" == Ok -1i32 ## expect Str.toI32 "-1" == Ok -1i32
## expect Str.toI32 "0.1" == Err InvalidNumStr ## expect Str.toI32 "0.1" == Err InvalidNumStr
## expect Str.toI32 "not a number" == Err InvalidNumStr ## expect Str.toI32 "not a number" == Err InvalidNumStr
toI32 : Str -> Result I32 [InvalidNumStr]* toI32 : Str -> Result I32 [InvalidNumStr]
toI32 = \string -> strToNumHelp string toI32 = \string -> strToNumHelp string
## Encode a [Str] to an unsigned [U16] integer. A [U16] value can hold numbers ## Encode a [Str] to an unsigned [U16] integer. A [U16] value can hold numbers
@ -401,7 +401,7 @@ toI32 = \string -> strToNumHelp string
## expect Str.toU16 "0.1" == Err InvalidNumStr ## expect Str.toU16 "0.1" == Err InvalidNumStr
## expect Str.toU16 "-1" == Err InvalidNumStr ## expect Str.toU16 "-1" == Err InvalidNumStr
## expect Str.toU16 "not a number" == Err InvalidNumStr ## expect Str.toU16 "not a number" == Err InvalidNumStr
toU16 : Str -> Result U16 [InvalidNumStr]* toU16 : Str -> Result U16 [InvalidNumStr]
toU16 = \string -> strToNumHelp string toU16 = \string -> strToNumHelp string
## Encode a [Str] to a signed [I16] integer. A [I16] value can hold numbers ## Encode a [Str] to a signed [I16] integer. A [I16] value can hold numbers
@ -412,7 +412,7 @@ toU16 = \string -> strToNumHelp string
## expect Str.toI16 "-1" == Ok -1i16 ## expect Str.toI16 "-1" == Ok -1i16
## expect Str.toI16 "0.1" == Err InvalidNumStr ## expect Str.toI16 "0.1" == Err InvalidNumStr
## expect Str.toI16 "not a number" == Err InvalidNumStr ## expect Str.toI16 "not a number" == Err InvalidNumStr
toI16 : Str -> Result I16 [InvalidNumStr]* toI16 : Str -> Result I16 [InvalidNumStr]
toI16 = \string -> strToNumHelp string toI16 = \string -> strToNumHelp string
## Encode a [Str] to an unsigned [U8] integer. A [U8] value can hold numbers ## Encode a [Str] to an unsigned [U8] integer. A [U8] value can hold numbers
@ -422,7 +422,7 @@ toI16 = \string -> strToNumHelp string
## expect Str.toU8 "-0.1" == Err InvalidNumStr ## expect Str.toU8 "-0.1" == Err InvalidNumStr
## expect Str.toU8 "not a number" == Err InvalidNumStr ## expect Str.toU8 "not a number" == Err InvalidNumStr
## expect Str.toU8 "1500" == Err InvalidNumStr ## expect Str.toU8 "1500" == Err InvalidNumStr
toU8 : Str -> Result U8 [InvalidNumStr]* toU8 : Str -> Result U8 [InvalidNumStr]
toU8 = \string -> strToNumHelp string toU8 = \string -> strToNumHelp string
## Encode a [Str] to a signed [I8] integer. A [I8] value can hold numbers ## Encode a [Str] to a signed [I8] integer. A [I8] value can hold numbers
@ -432,7 +432,7 @@ toU8 = \string -> strToNumHelp string
## expect Str.toI8 "-15" == Ok -15i8 ## expect Str.toI8 "-15" == Ok -15i8
## expect Str.toI8 "150.00" == Err InvalidNumStr ## expect Str.toI8 "150.00" == Err InvalidNumStr
## expect Str.toI8 "not a number" == Err InvalidNumStr ## expect Str.toI8 "not a number" == Err InvalidNumStr
toI8 : Str -> Result I8 [InvalidNumStr]* toI8 : Str -> Result I8 [InvalidNumStr]
toI8 = \string -> strToNumHelp string toI8 = \string -> strToNumHelp string
## Get the byte at the given index, without performing a bounds check. ## Get the byte at the given index, without performing a bounds check.
@ -451,7 +451,7 @@ substringUnsafe : Str, Nat, Nat -> Str
## ##
## expect Str.replaceEach "foo/bar/baz" "/" "_" == Ok "foo_bar_baz" ## expect Str.replaceEach "foo/bar/baz" "/" "_" == Ok "foo_bar_baz"
## expect Str.replaceEach "not here" "/" "_" == Err NotFound ## expect Str.replaceEach "not here" "/" "_" == Err NotFound
replaceEach : Str, Str, Str -> Result Str [NotFound]* replaceEach : Str, Str, Str -> Result Str [NotFound]
replaceEach = \haystack, needle, flower -> replaceEach = \haystack, needle, flower ->
when splitFirst haystack needle is when splitFirst haystack needle is
Ok { before, after } -> Ok { before, after } ->
@ -483,7 +483,7 @@ expect Str.replaceEach "abXdeXghi" "X" "_" == Ok "ab_de_ghi"
## ##
## expect Str.replaceFirst "foo/bar/baz" "/" "_" == Ok "foo_bar/baz" ## expect Str.replaceFirst "foo/bar/baz" "/" "_" == Ok "foo_bar/baz"
## expect Str.replaceFirst "no slashes here" "/" "_" == Err NotFound ## expect Str.replaceFirst "no slashes here" "/" "_" == Err NotFound
replaceFirst : Str, Str, Str -> Result Str [NotFound]* replaceFirst : Str, Str, Str -> Result Str [NotFound]
replaceFirst = \haystack, needle, flower -> replaceFirst = \haystack, needle, flower ->
when splitFirst haystack needle is when splitFirst haystack needle is
Ok { before, after } -> Ok { before, after } ->
@ -498,7 +498,7 @@ expect Str.replaceFirst "abXdeXghi" "X" "_" == Ok "ab_deXghi"
## ##
## expect Str.replaceLast "foo/bar/baz" "/" "_" == Ok "foo/bar_baz" ## expect Str.replaceLast "foo/bar/baz" "/" "_" == Ok "foo/bar_baz"
## expect Str.replaceLast "no slashes here" "/" "_" == Err NotFound ## expect Str.replaceLast "no slashes here" "/" "_" == Err NotFound
replaceLast : Str, Str, Str -> Result Str [NotFound]* replaceLast : Str, Str, Str -> Result Str [NotFound]
replaceLast = \haystack, needle, flower -> replaceLast = \haystack, needle, flower ->
when splitLast haystack needle is when splitLast haystack needle is
Ok { before, after } -> Ok { before, after } ->
@ -514,7 +514,7 @@ expect Str.replaceLast "abXdeXghi" "X" "_" == Ok "abXde_ghi"
## ##
## expect Str.splitFirst "foo/bar/baz" "/" == Ok { before: "foo", after: "bar/baz" } ## expect Str.splitFirst "foo/bar/baz" "/" == Ok { before: "foo", after: "bar/baz" }
## expect Str.splitFirst "no slashes here" "/" == Err NotFound ## expect Str.splitFirst "no slashes here" "/" == Err NotFound
splitFirst : Str, Str -> Result { before : Str, after : Str } [NotFound]* splitFirst : Str, Str -> Result { before : Str, after : Str } [NotFound]
splitFirst = \haystack, needle -> splitFirst = \haystack, needle ->
when firstMatch haystack needle is when firstMatch haystack needle is
Some index -> Some index ->
@ -567,7 +567,7 @@ firstMatchHelp = \haystack, needle, index, lastPossible ->
## ##
## expect Str.splitLast "foo/bar/baz" "/" == Ok { before: "foo/bar", after: "baz" } ## expect Str.splitLast "foo/bar/baz" "/" == Ok { before: "foo/bar", after: "baz" }
## expect Str.splitLast "no slashes here" "/" == Err NotFound ## expect Str.splitLast "no slashes here" "/" == Err NotFound
splitLast : Str, Str -> Result { before : Str, after : Str } [NotFound]* splitLast : Str, Str -> Result { before : Str, after : Str } [NotFound]
splitLast = \haystack, needle -> splitLast = \haystack, needle ->
when lastMatch haystack needle is when lastMatch haystack needle is
Some index -> Some index ->
@ -684,7 +684,7 @@ appendScalarUnsafe : Str, U32 -> Str
## ##
## expect Str.appendScalar "H" 105 == Ok "Hi" ## expect Str.appendScalar "H" 105 == Ok "Hi"
## expect Str.appendScalar "😢" 0xabcdef == Err InvalidScalar ## expect Str.appendScalar "😢" 0xabcdef == Err InvalidScalar
appendScalar : Str, U32 -> Result Str [InvalidScalar]* appendScalar : Str, U32 -> Result Str [InvalidScalar]
appendScalar = \string, scalar -> appendScalar = \string, scalar ->
if isValidScalar scalar then if isValidScalar scalar then
Ok (appendScalarUnsafe string scalar) Ok (appendScalarUnsafe string scalar)
@ -749,7 +749,7 @@ walkScalarsUntilHelp = \string, state, step, index, length ->
strToNum : Str -> { berrorcode : U8, aresult : Num * } strToNum : Str -> { berrorcode : U8, aresult : Num * }
strToNumHelp : Str -> Result (Num a) [InvalidNumStr]* strToNumHelp : Str -> Result (Num a) [InvalidNumStr]
strToNumHelp = \string -> strToNumHelp = \string ->
result : { berrorcode : U8, aresult : Num a } result : { berrorcode : U8, aresult : Num a }
result = strToNum string result = strToNum string

View file

@ -334,7 +334,7 @@ pub(crate) fn canonicalize_annotation(
} }
} }
#[derive(Clone, Copy)] #[derive(Clone, Copy, PartialEq, Eq)]
enum CanPolarity { enum CanPolarity {
/// Polarity should be disregarded for now; relevant in aliaes + opaques. /// Polarity should be disregarded for now; relevant in aliaes + opaques.
Disregard, Disregard,
@ -1081,6 +1081,17 @@ fn can_extension_type<'a>(
references, references,
); );
if valid_extension_type(shallow_dealias_with_scope(scope, &ext_type)) { if valid_extension_type(shallow_dealias_with_scope(scope, &ext_type)) {
if matches!(loc_ann.extract_spaces().item, TypeAnnotation::Wildcard)
&& matches!(ext_problem_kind, ExtensionTypeKind::TagUnion)
&& pol == CanPolarity::Pos
{
// Wildcards are redundant in positive positions, since they will always be
// inferred as necessary there!
env.problem(roc_problem::can::Problem::UnnecessaryOutputWildcard {
region: loc_ann.region,
})
}
ext_type ext_type
} else { } else {
// Report an error but mark the extension variable to be inferred // Report an error but mark the extension variable to be inferred

View file

@ -182,6 +182,9 @@ pub enum Problem {
original_opaque: Symbol, original_opaque: Symbol,
ability_member: Symbol, ability_member: Symbol,
}, },
UnnecessaryOutputWildcard {
region: Region,
},
} }
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]

View file

@ -502,7 +502,7 @@ mod solve_expr {
Str.fromUtf8 Str.fromUtf8
"# "#
), ),
"List U8 -> Result Str [BadUtf8 Utf8ByteProblem Nat]*", "List U8 -> Result Str [BadUtf8 Utf8ByteProblem Nat]",
); );
} }
@ -3330,7 +3330,7 @@ mod solve_expr {
List.get ["a"] 0 List.get ["a"] 0
"# "#
), ),
"Result Str [OutOfBounds]*", "Result Str [OutOfBounds]",
); );
} }
@ -3432,7 +3432,7 @@ mod solve_expr {
List.get [10, 9, 8, 7] 1 List.get [10, 9, 8, 7] 1
"# "#
), ),
"Result (Num *) [OutOfBounds]*", "Result (Num *) [OutOfBounds]",
); );
infer_eq_without_problem( infer_eq_without_problem(
@ -3441,7 +3441,7 @@ mod solve_expr {
List.get List.get
"# "#
), ),
"List a, Nat -> Result a [OutOfBounds]*", "List a, Nat -> Result a [OutOfBounds]",
); );
} }
@ -3543,7 +3543,7 @@ mod solve_expr {
Num.divChecked Num.divChecked
"# "#
), ),
"Float a, Float a -> Result (Float a) [DivByZero]*", "Float a, Float a -> Result (Float a) [DivByZero]",
) )
} }
@ -3567,7 +3567,7 @@ mod solve_expr {
Num.divCeilChecked Num.divCeilChecked
"# "#
), ),
"Int a, Int a -> Result (Int a) [DivByZero]*", "Int a, Int a -> Result (Int a) [DivByZero]",
); );
} }
@ -3591,7 +3591,7 @@ mod solve_expr {
Num.divTruncChecked Num.divTruncChecked
"# "#
), ),
"Int a, Int a -> Result (Int a) [DivByZero]*", "Int a, Int a -> Result (Int a) [DivByZero]",
); );
} }
@ -3779,7 +3779,7 @@ mod solve_expr {
Model position : { openSet : Set position } Model position : { openSet : Set position }
cheapestOpen : Model position -> Result position [KeyNotFound]* | position has Eq cheapestOpen : Model position -> Result position [KeyNotFound] | position has Eq
cheapestOpen = \model -> cheapestOpen = \model ->
folder = \resSmallestSoFar, position -> folder = \resSmallestSoFar, position ->
@ -3794,14 +3794,14 @@ mod solve_expr {
Set.walk model.openSet (Ok { position: boom {}, cost: 0.0 }) folder Set.walk model.openSet (Ok { position: boom {}, cost: 0.0 }) folder
|> Result.map (\x -> x.position) |> Result.map (\x -> x.position)
astar : Model position -> Result position [KeyNotFound]* | position has Eq astar : Model position -> Result position [KeyNotFound] | position has Eq
astar = \model -> cheapestOpen model astar = \model -> cheapestOpen model
main = main =
astar astar
"# "#
), ),
"Model position -> Result position [KeyNotFound]* | position has Eq", "Model position -> Result position [KeyNotFound] | position has Eq",
); );
} }
@ -7498,7 +7498,7 @@ mod solve_expr {
r#" r#"
OList := [Nil, Cons {} OList] OList := [Nil, Cons {} OList]
lst : [Cons {} OList]* lst : [Cons {} OList]
olist : OList olist : OList
olist = (\l -> @OList l) lst olist = (\l -> @OList l) lst
@ -7517,7 +7517,7 @@ mod solve_expr {
r#" r#"
OList := [Nil, Cons {} OList] OList := [Nil, Cons {} OList]
lst : [Cons {} OList]* lst : [Cons {} OList]
olist : OList olist : OList
olist = @OList lst olist = @OList lst

View file

@ -1531,7 +1531,7 @@ fn polymorphic_tag() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
x : [Y U8]* x : [Y U8]
x = Y 3 x = Y 3
x x
"# "#
@ -1695,7 +1695,7 @@ fn instantiate_annotated_as_recursive_alias_toplevel() {
Value : [Nil, Array (List Value)] Value : [Nil, Array (List Value)]
foo : [Nil]* foo : [Nil]
foo = Nil foo = Nil
it : Value it : Value
@ -1723,7 +1723,7 @@ fn instantiate_annotated_as_recursive_alias_polymorphic_expr() {
main = main =
Value : [Nil, Array (List Value)] Value : [Nil, Array (List Value)]
foo : [Nil]* foo : [Nil]
foo = Nil foo = Nil
it : Value it : Value
@ -1750,7 +1750,7 @@ fn instantiate_annotated_as_recursive_alias_multiple_polymorphic_expr() {
main = main =
Value : [Nil, Array (List Value)] Value : [Nil, Array (List Value)]
foo : [Nil]* foo : [Nil]
foo = Nil foo = Nil
v1 : Value v1 : Value

View file

@ -70,7 +70,7 @@ fn num_floor_division() {
fn num_floor_checked_division_success() { fn num_floor_checked_division_success() {
expect_success( expect_success(
"Num.divTruncChecked 4 3", "Num.divTruncChecked 4 3",
"Ok 1 : Result (Int *) [DivByZero]*", "Ok 1 : Result (Int *) [DivByZero]",
); );
} }
@ -79,7 +79,7 @@ fn num_floor_checked_division_success() {
fn num_floor_checked_division_divby_zero() { fn num_floor_checked_division_divby_zero() {
expect_success( expect_success(
"Num.divTruncChecked 4 0", "Num.divTruncChecked 4 0",
"Err DivByZero : Result (Int *) [DivByZero]*", "Err DivByZero : Result (Int *) [DivByZero]",
); );
} }
@ -94,7 +94,7 @@ fn num_ceil_division() {
fn num_ceil_checked_division_success() { fn num_ceil_checked_division_success() {
expect_success( expect_success(
"Num.divCeilChecked 4 3", "Num.divCeilChecked 4 3",
"Ok 2 : Result (Int *) [DivByZero]*", "Ok 2 : Result (Int *) [DivByZero]",
) )
} }
@ -380,30 +380,30 @@ fn num_mul_saturated() {
#[cfg(not(feature = "wasm"))] #[cfg(not(feature = "wasm"))]
#[test] #[test]
fn num_add_checked() { fn num_add_checked() {
expect_success("Num.addChecked 1 1", "Ok 2 : Result (Num *) [Overflow]*"); expect_success("Num.addChecked 1 1", "Ok 2 : Result (Num *) [Overflow]");
expect_success( expect_success(
"Num.addChecked Num.maxI64 1", "Num.addChecked Num.maxI64 1",
"Err Overflow : Result I64 [Overflow]*", "Err Overflow : Result I64 [Overflow]",
); );
} }
#[cfg(not(feature = "wasm"))] #[cfg(not(feature = "wasm"))]
#[test] #[test]
fn num_sub_checked() { fn num_sub_checked() {
expect_success("Num.subChecked 1 1", "Ok 0 : Result (Num *) [Overflow]*"); expect_success("Num.subChecked 1 1", "Ok 0 : Result (Num *) [Overflow]");
expect_success( expect_success(
"Num.subChecked Num.minI64 1", "Num.subChecked Num.minI64 1",
"Err Overflow : Result I64 [Overflow]*", "Err Overflow : Result I64 [Overflow]",
); );
} }
#[cfg(not(feature = "wasm"))] #[cfg(not(feature = "wasm"))]
#[test] #[test]
fn num_mul_checked() { fn num_mul_checked() {
expect_success("Num.mulChecked 20 2", "Ok 40 : Result (Num *) [Overflow]*"); expect_success("Num.mulChecked 20 2", "Ok 40 : Result (Num *) [Overflow]");
expect_success( expect_success(
"Num.mulChecked Num.maxI64 2", "Num.mulChecked Num.maxI64 2",
"Err Overflow : Result I64 [Overflow]*", "Err Overflow : Result I64 [Overflow]",
); );
} }
@ -437,11 +437,11 @@ fn list_sum() {
fn list_first() { fn list_first() {
expect_success( expect_success(
"List.first [12, 9, 6, 3]", "List.first [12, 9, 6, 3]",
"Ok 12 : Result (Num *) [ListWasEmpty]*", "Ok 12 : Result (Num *) [ListWasEmpty]",
); );
expect_success( expect_success(
"List.first []", "List.first []",
"Err ListWasEmpty : Result a [ListWasEmpty]*", "Err ListWasEmpty : Result a [ListWasEmpty]",
); );
} }
@ -450,12 +450,12 @@ fn list_first() {
fn list_last() { fn list_last() {
expect_success( expect_success(
"List.last [12, 9, 6, 3]", "List.last [12, 9, 6, 3]",
"Ok 3 : Result (Num *) [ListWasEmpty]*", "Ok 3 : Result (Num *) [ListWasEmpty]",
); );
expect_success( expect_success(
"List.last []", "List.last []",
"Err ListWasEmpty : Result a [ListWasEmpty]*", "Err ListWasEmpty : Result a [ListWasEmpty]",
); );
} }
@ -654,18 +654,18 @@ fn type_problem() {
#[test] #[test]
fn issue_2149() { fn issue_2149() {
expect_success(r#"Str.toI8 "127""#, "Ok 127 : Result I8 [InvalidNumStr]*"); expect_success(r#"Str.toI8 "127""#, "Ok 127 : Result I8 [InvalidNumStr]");
expect_success( expect_success(
r#"Str.toI8 "128""#, r#"Str.toI8 "128""#,
"Err InvalidNumStr : Result I8 [InvalidNumStr]*", "Err InvalidNumStr : Result I8 [InvalidNumStr]",
); );
expect_success( expect_success(
r#"Str.toI16 "32767""#, r#"Str.toI16 "32767""#,
"Ok 32767 : Result I16 [InvalidNumStr]*", "Ok 32767 : Result I16 [InvalidNumStr]",
); );
expect_success( expect_success(
r#"Str.toI16 "32768""#, r#"Str.toI16 "32768""#,
"Err InvalidNumStr : Result I16 [InvalidNumStr]*", "Err InvalidNumStr : Result I16 [InvalidNumStr]",
); );
} }
@ -1155,7 +1155,7 @@ fn box_box_type_alias() {
fn issue_2582_specialize_result_value() { fn issue_2582_specialize_result_value() {
expect_success( expect_success(
r#"\x, list -> if x > 0 then List.first list else Ok """#, r#"\x, list -> if x > 0 then List.first list else Ok """#,
r"<function> : Num *, List Str -> Result Str [ListWasEmpty]*", r"<function> : Num *, List Str -> Result Str [ListWasEmpty]",
) )
} }
@ -1236,7 +1236,7 @@ fn dict_get_single() {
Dict.single 0 {a: 1, c: 2} |> Dict.get 0 Dict.single 0 {a: 1, c: 2} |> Dict.get 0
"# "#
), ),
r#"Ok { a: 1, c: 2 } : Result { a : Num *, c : Num * } [KeyNotFound]*"#, r#"Ok { a: 1, c: 2 } : Result { a : Num *, c : Num * } [KeyNotFound]"#,
) )
} }

View file

@ -1015,6 +1015,15 @@ pub fn can_problem<'b>(
title = "OVERLOADED SPECIALIZATION".to_string(); title = "OVERLOADED SPECIALIZATION".to_string();
severity = Severity::Warning; severity = Severity::Warning;
} }
Problem::UnnecessaryOutputWildcard { region } => {
doc = alloc.stack([
alloc.reflow("I see you annotated a wildcard in a place where it's not needed:"),
alloc.region(lines.convert_region(region)),
alloc.reflow("Tag unions that are constants, or the return values of functions, are always inferred to be open by default! You can remove this annotation safely."),
]);
title = "UNNECESSARY WILDCARD".to_string();
severity = Severity::Warning;
}
}; };
Report { Report {

View file

@ -11731,4 +11731,26 @@ All branches in an `if` must have the same type!
denoted with .. - is that what you meant? denoted with .. - is that what you meant?
"### "###
); );
test_report!(
unnecessary_extension_variable,
indoc!(
r#"
f : {} -> [A, B]*
f
"#
),
@r###"
UNNECESSARY WILDCARD /code/proj/Main.roc
I see you annotated a wildcard in a place where it's not needed:
4 f : {} -> [A, B]*
^
Tag unions that are constants, or the return values of functions, are
always inferred to be open by default! You can remove this annotation
safely.
"###
);
} }

View file

@ -147,7 +147,7 @@ toHelpHelper = \@Parser parser, configs ->
List.append configs (Positional config) List.append configs (Positional config)
|> Config |> Config
findOneArg : Str, Str, MarkedArgs -> Result { val : Str, newlyTaken : Taken } [NotFound]* findOneArg : Str, Str, MarkedArgs -> Result { val : Str, newlyTaken : Taken } [NotFound]
findOneArg = \long, short, { args, taken } -> findOneArg = \long, short, { args, taken } ->
argMatches = \{ index, found: _ }, arg -> argMatches = \{ index, found: _ }, arg ->
if Set.contains taken index || Set.contains taken (index + 1) then if Set.contains taken index || Set.contains taken (index + 1) then

View file

@ -2,13 +2,13 @@ interface Stdout
exposes [line, write] exposes [line, write]
imports [Effect, Task.{ Task }, InternalTask] imports [Effect, Task.{ Task }, InternalTask]
line : Str -> Task {} * [Write [Stdout]*]* line : Str -> Task {} * [Write [Stdout]]
line = \str -> line = \str ->
Effect.stdoutLine str Effect.stdoutLine str
|> Effect.map (\_ -> Ok {}) |> Effect.map (\_ -> Ok {})
|> InternalTask.fromEffect |> InternalTask.fromEffect
write : Str -> Task {} * [Write [Stdout]*]* write : Str -> Task {} * [Write [Stdout]]
write = \str -> write = \str ->
Effect.stdoutWrite str Effect.stdoutWrite str
|> Effect.map (\_ -> Ok {}) |> Effect.map (\_ -> Ok {})

View file

@ -19,7 +19,7 @@ pushStack = \ctx, data ->
# I think an open tag union should just work here. # I think an open tag union should just work here.
# Instead at a call sites, I need to match on the error and then return the same error. # Instead at a call sites, I need to match on the error and then return the same error.
# Otherwise it hits unreachable code in ir.rs # Otherwise it hits unreachable code in ir.rs
popStack : Context -> Result [T Context Data] [EmptyStack]* popStack : Context -> Result [T Context Data] [EmptyStack]
popStack = \ctx -> popStack = \ctx ->
when List.last ctx.stack is when List.last ctx.stack is
Ok val -> Ok val ->
@ -66,7 +66,7 @@ with = \path, callback ->
callback { scopes: [{ data: Some handle, index: 0, buf: [], whileInfo: None }], state: Executing, stack: [], vars: List.repeat (Number 0) Variable.totalCount } callback { scopes: [{ data: Some handle, index: 0, buf: [], whileInfo: None }], state: Executing, stack: [], vars: List.repeat (Number 0) Variable.totalCount }
# I am pretty sure there is a syntax to destructure and keep a reference to the whole, but Im not sure what it is. # I am pretty sure there is a syntax to destructure and keep a reference to the whole, but Im not sure what it is.
getChar : Context -> Task [T U8 Context] [EndOfData, NoScope]* getChar : Context -> Task [T U8 Context] [EndOfData, NoScope]
getChar = \ctx -> getChar = \ctx ->
when List.last ctx.scopes is when List.last ctx.scopes is
Ok scope -> Ok scope ->
@ -76,7 +76,7 @@ getChar = \ctx ->
Err ListWasEmpty -> Err ListWasEmpty ->
Task.fail NoScope Task.fail NoScope
getCharScope : Scope -> Task [T U8 Scope] [EndOfData, NoScope]* getCharScope : Scope -> Task [T U8 Scope] [EndOfData, NoScope]
getCharScope = \scope -> getCharScope = \scope ->
when List.get scope.buf scope.index is when List.get scope.buf scope.index is
Ok val -> Ok val ->