Merge pull request #3092 from rtfeldman/i3091

Turn on formatting tests for stdlib
This commit is contained in:
Ayaz 2022-05-18 21:41:56 -04:00 committed by GitHub
commit 1c43bbee47
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 101 additions and 98 deletions

View File

@ -10,6 +10,11 @@ test-gen-wasm = "test -p roc_gen_wasm -p test_gen --no-default-features --featur
rustflags = ["-Copt-level=s", "-Clto=fat"]
[env]
# Gives us the path of the workspace root for use in cargo tests without having
# to compute it per-package.
# https://github.com/rust-lang/cargo/issues/3946#issuecomment-973132993
ROC_WORKSPACE_DIR = { value = "", relative = true }
# Debug flags. Keep this up-to-date with compiler/debug_flags/src/lib.rs.
# Set = "1" to turn a debug flag on.
ROC_PRETTY_PRINT_ALIAS_CONTENTS = "0"

View File

@ -85,6 +85,7 @@ check-typos:
test-rust:
FROM +build-rust-test
ENV ROC_WORKSPACE_DIR=/earthbuild
ENV RUST_BACKTRACE=1
# for race condition problem with cli test
ENV ROC_NUM_WORKERS=1

View File

@ -1,6 +1,6 @@
interface Bool
exposes [ Bool, and, or, not, isEq, isNotEq ]
imports [ ]
imports []
Bool : [ True, False ]
@ -56,7 +56,6 @@ and : Bool, Bool -> Bool
## In Roc, this is not the case. See the performance notes for [Bool.and] for details.
or : Bool, Bool -> Bool
# xor : Bool, Bool -> Bool # currently unimplemented
## Returns `False` when given `True`, and vice versa.
not : Bool -> Bool

View File

@ -1,6 +1,6 @@
interface Box
exposes [ box, unbox ]
imports [ ]
imports []
box : a -> Box a
unbox : Box a -> a

View File

@ -17,7 +17,7 @@ interface Dict
]
imports
[
Bool.{ Bool }
Bool.{ Bool },
]
## A [dictionary](https://en.wikipedia.org/wiki/Associative_array) that lets you can associate keys with values.
@ -38,7 +38,7 @@ interface Dict
## |> Dict.insert "Delhi" 16_787_941
## |> Dict.insert "Amsterdam" 872_680
##
## ### Accessing keys or values
## ### Accessing keys or values
##
## We can use [Dict.keys] and [Dict.values] functions to get only the keys or only the values.
##
@ -68,8 +68,6 @@ interface Dict
## When comparing two dictionaries for equality, they are `==` only if their both their contents and their
## orderings match. This preserves the property that if `dict1 == dict2`, you should be able to rely on
## `fn dict1 == fn dict2` also being `True`, even if `fn` relies on the dictionary's ordering.
## An empty dictionary.
empty : Dict k v
single : k, v -> Dict k v

View File

@ -53,11 +53,10 @@ interface List
]
imports
[
Bool.{ Bool }
Bool.{ Bool },
]
## Types
## A sequential list of values.
##
## >>> [ 1, 2, 3 ] # a list of numbers
@ -193,10 +192,9 @@ interface List
## * Even when copying is faster, other list operations may still be slightly slower with persistent data structures. For example, even if it were a persistent data structure, [List.map], [List.walk], and [List.keepIf] would all need to traverse every element in the list and build up the result from scratch. These operations are all
## * Roc's compiler optimizes many list operations into in-place mutations behind the scenes, depending on how the list is being used. For example, [List.map], [List.keepIf], and [List.set] can all be optimized to perform in-place mutations.
## * If possible, it is usually best for performance to use large lists in a way where the optimizer can turn them into in-place mutations. If this is not possible, a persistent data structure might be faster - but this is a rare enough scenario that it would not be good for the average Roc program's performance if this were the way [List] worked by default. Instead, you can look outside Roc's standard modules for an implementation of a persistent data structure - likely built using [List] under the hood!
## Check if the list is empty.
##
## >>> List.isEmpty [ 1, 2, 3 ]
## >>> List.isEmpty [ 1, 2, 3 ]
##
## >>> List.isEmpty []
isEmpty : List a -> Bool
@ -321,7 +319,7 @@ walk : List elem, state, (state, elem -> state) -> state
## Note that in other languages, `walkBackwards` is sometimes called `reduceRight`,
## `fold`, `foldRight`, or `foldr`.
walkBackwards : List elem, state, (state, elem -> state) -> state
walkBackwards : List elem, state, (state, elem -> state) -> state
## Same as [List.walk], except you can stop walking early.
##
@ -406,7 +404,7 @@ keepOks : List before, (before -> Result after *) -> List after
## >>> fn = \str -> if Str.isEmpty str then Err StrWasEmpty else Ok (Str.len str)
## >>>
## >>> List.keepErrs [ "", "a", "bc", "", "d", "ef", "" ]
keepErrs: List before, (before -> Result * after) -> List after
keepErrs : List before, (before -> Result * after) -> List after
## Convert each element in the list to something new, by calling a conversion
## function on each of them. Then return a new list of the converted values.
@ -445,7 +443,7 @@ mapWithIndex : List a, (a, Nat -> b) -> List b
##
## >>> List.range 2 8
range : Int a, Int a -> List (Int a)
sortWith : List a, (a, a -> [ LT, EQ, GT ] ) -> List a
sortWith : List a, (a, a -> [ LT, EQ, GT ]) -> List a
## Sorts a list in ascending order (lowest to highest), using a function which
## specifies a way to represent each element as a number.
@ -541,7 +539,7 @@ drop : List elem, Nat -> List elem
## To replace the element at a given index, instead of dropping it, see [List.set].
dropAt : List elem, Nat -> List elem
min : List (Num a) -> Result (Num a) [ ListWasEmpty ]*
min : List (Num a) -> Result (Num a) [ ListWasEmpty ]*
min = \list ->
when List.first list is
Ok initial ->
@ -550,17 +548,15 @@ min = \list ->
Err ListWasEmpty ->
Err ListWasEmpty
minHelp : List (Num a), Num a -> Num a
minHelp : List (Num a), Num a -> Num a
minHelp = \list, initial ->
List.walk list initial \bestSoFar, current ->
if current < bestSoFar then
current
else
bestSoFar
max : List (Num a) -> Result (Num a) [ ListWasEmpty ]*
max : List (Num a) -> Result (Num a) [ ListWasEmpty ]*
max = \list ->
when List.first list is
Ok initial ->
@ -569,13 +565,11 @@ max = \list ->
Err ListWasEmpty ->
Err ListWasEmpty
maxHelp : List (Num a), Num a -> Num a
maxHelp : List (Num a), Num a -> Num a
maxHelp = \list, initial ->
List.walk list initial \bestSoFar, current ->
if current > bestSoFar then
current
else
bestSoFar
@ -616,4 +610,4 @@ intersperse : List elem, elem -> List elem
## than the given index, # and the `others` list will be all the others. (This
## means if you give an index of 0, the `before` list will be empty and the
## `others` list will have the same elements as the original list.)
split : List elem, Nat -> { before: List elem, others: List elem }
split : List elem, Nat -> { before : List elem, others : List elem }

View File

@ -4,46 +4,36 @@ interface Num
Num,
Int,
Frac,
Integer,
FloatingPoint,
I128,
I64,
I32,
I16,
I8,
U128,
U64,
U32,
U16,
U8,
Signed128,
Signed64,
Signed32,
Signed16,
Signed8,
Unsigned128,
Unsigned64,
Unsigned32,
Unsigned16,
Unsigned8,
Nat,
Dec,
F32,
F64,
Natural,
Decimal,
Binary32,
Binary64,
abs,
neg,
add,
@ -155,7 +145,7 @@ interface Num
]
imports
[
Bool.{ Bool }
Bool.{ Bool },
]
## Represents a number that could be either an [Int] or a [Frac].
@ -343,7 +333,6 @@ Num range := range
##
## As such, it's very important to design your code not to exceed these bounds!
## If you need to do math outside these bounds, consider using a larger numeric size.
Int range : Num (Integer range)
## A fixed-size number with a fractional component.
@ -501,7 +490,6 @@ F32 : Num (FloatingPoint Binary32)
Dec : Num (FloatingPoint Decimal)
# ------- Functions
## Convert a number to a [Str].
##
## This is the same as calling `Num.format {}` - so for more details on
@ -875,7 +863,6 @@ subChecked : Num a, Num a -> Result (Num a) [ Overflow ]*
mulWrap : Int range, Int range -> Int range
# mulSaturated : Num a, Num a -> Num a
## Multiply two numbers and check for overflow.
##
## This is the same as [Num.mul] except if the operation overflows, instead of
@ -1086,7 +1073,6 @@ minF64 = -1.7976931348623157e308
maxF64 : F64
maxF64 = 1.7976931348623157e308
## Converts an [Int] to an [I8]. If the given number can't be precisely represented in an [I8],
## the returned number may be different from the given number.
toI8 : Int * -> I8
@ -1142,9 +1128,7 @@ toNatChecked : Int * -> Result Nat [ OutOfBounds ]*
toF32Checked : Num * -> Result F32 [ OutOfBounds ]*
toF64Checked : Num * -> Result F64 [ OutOfBounds ]*
# Special Floating-Point operations
## When given a [F64] or [F32] value, returns `False` if that value is
## [*NaN*](Num.isNaN), ∞ or -∞, and `True` otherwise.
##
@ -1152,8 +1136,7 @@ toF64Checked : Num * -> Result F64 [ OutOfBounds ]*
##
## This is the opposite of [isInfinite], except when given [*NaN*](Num.isNaN). Both
## [isFinite] and [isInfinite] return `False` for [*NaN*](Num.isNaN).
#isFinite : Frac * -> Bool
# isFinite : Frac * -> Bool
## When given a [F64] or [F32] value, returns `True` if that value is either
## ∞ or -∞, and `False` otherwise.
##
@ -1161,8 +1144,7 @@ toF64Checked : Num * -> Result F64 [ OutOfBounds ]*
##
## This is the opposite of [isFinite], except when given [*NaN*](Num.isNaN). Both
## [isFinite] and [isInfinite] return `False` for [*NaN*](Num.isNaN).
#isInfinite : Frac * -> Bool
# isInfinite : Frac * -> Bool
## When given a [F64] or [F32] value, returns `True` if that value is
## *NaN* ([not a number](https://en.wikipedia.org/wiki/NaN)), and `False` otherwise.
##
@ -1185,21 +1167,17 @@ toF64Checked : Num * -> Result F64 [ OutOfBounds ]*
## Note that you should never put a *NaN* into a [Set], or use it as the key in
## a [Dict]. The result is entries that can never be removed from those
## collections! See the documentation for [Set.add] and [Dict.insert] for details.
#isNaN : Frac * -> Bool
# isNaN : Frac * -> Bool
## Returns the higher of two numbers.
##
## If either argument is [*NaN*](Num.isNaN), returns `False` no matter what. (*NaN*
## is [defined to be unordered](https://en.wikipedia.org/wiki/NaN#Comparison_with_NaN).)
#max : Num a, Num a -> Num a
# max : Num a, Num a -> Num a
## Returns the lower of two numbers.
##
## If either argument is [*NaN*](Num.isNaN), returns `False` no matter what. (*NaN*
## is [defined to be unordered](https://en.wikipedia.org/wiki/NaN#Comparison_with_NaN).)
#min : Num a, Num a -> Num a
# min : Num a, Num a -> Num a
# Branchless implementation that works for all numeric types:
#
# let is_lt = arg1 < arg2;
@ -1209,57 +1187,46 @@ toF64Checked : Num * -> Result F64 [ OutOfBounds ]*
# 1, 1 -> (0 - 1) + 1 == 0 # Eq
# 5, 1 -> (0 - 0) + 1 == 1 # Gt
# 1, 5 -> (1 - 0) + 1 == 2 # Lt
## Returns `Lt` if the first number is less than the second, `Gt` if
## the first is greater than the second, and `Eq` if they're equal.
##
## Although this can be passed to `List.sort`, you'll get better performance
## by using `List.sortAsc` or `List.sortDesc` instead.
#compare : Num a, Num a -> [ Lt, Eq, Gt ]
# compare : Num a, Num a -> [ Lt, Eq, Gt ]
## [Endianness](https://en.wikipedia.org/wiki/Endianness)
# Endi : [ Big, Little, Native ]
## The `Endi` argument does not matter for [U8] and [I8], since they have
## only one byte.
# toBytes : Num *, Endi -> List U8
## when Num.parseBytes bytes Big is
## Ok { val: f64, rest } -> ...
## Err (ExpectedNum (Frac Binary64)) -> ...
# parseBytes : List U8, Endi -> Result { val : Num a, rest : List U8 } [ ExpectedNum a ]*
## when Num.fromBytes bytes Big is
## Ok f64 -> ...
## Err (ExpectedNum (Frac Binary64)) -> ...
# fromBytes : List U8, Endi -> Result (Num a) [ ExpectedNum a ]*
# Bit shifts
## [Logical bit shift](https://en.wikipedia.org/wiki/Bitwise_operation#Logical_shift) left.
##
## `a << b` is shorthand for `Num.shl a b`.
#shl : Int a, Int a -> Int a
# shl : Int a, Int a -> Int a
## [Arithmetic bit shift](https://en.wikipedia.org/wiki/Bitwise_operation#Arithmetic_shift) left.
##
## This is called `shlWrap` because any bits shifted
## off the beginning of the number will be wrapped around to
## the end. (In contrast, [shl] replaces discarded bits with zeroes.)
#shlWrap : Int a, Int a -> Int a
# shlWrap : Int a, Int a -> Int a
## [Logical bit shift](https://en.wikipedia.org/wiki/Bitwise_operation#Logical_shift) right.
##
## `a >> b` is shorthand for `Num.shr a b`.
#shr : Int a, Int a -> Int a
# shr : Int a, Int a -> Int a
## [Arithmetic bit shift](https://en.wikipedia.org/wiki/Bitwise_operation#Arithmetic_shift) right.
##
## This is called `shrWrap` because any bits shifted
## off the end of the number will be wrapped around to
## the beginning. (In contrast, [shr] replaces discarded bits with zeroes.)
#shrWrap : Int a, Int a -> Int a
# shrWrap : Int a, Int a -> Int a
# ## Convert a number into a [Str], formatted with the given options.
# ##
# ## Default options:

View File

@ -12,8 +12,11 @@ Result ok err : [ Ok ok, Err err ]
isOk : Result ok err -> Bool
isOk = \result ->
when result is
Ok _ -> True
Err _ -> False
Ok _ ->
True
Err _ ->
False
## Return True if the result indicates a failure, else return False
##
@ -21,8 +24,11 @@ isOk = \result ->
isErr : Result ok err -> Bool
isErr = \result ->
when result is
Ok _ -> False
Err _ -> True
Ok _ ->
False
Err _ ->
True
## If the result is `Ok`, return the value it holds. Otherwise, return
## the given default value.
@ -33,8 +39,11 @@ isErr = \result ->
withDefault : Result ok err, ok -> ok
withDefault = \result, default ->
when result is
Ok value -> value
Err _ -> default
Ok value ->
value
Err _ ->
default
## If the result is `Ok`, transform the value it holds by running a conversion
## function on it. Then return a new `Ok` holding the transformed value.
@ -50,8 +59,11 @@ withDefault = \result, default ->
map : Result a err, (a -> b) -> Result b err
map = \result, transform ->
when result is
Ok v -> Ok (transform v)
Err e -> Err e
Ok v ->
Ok (transform v)
Err e ->
Err e
## If the result is `Err`, transform the value it holds by running a conversion
## function on it. Then return a new `Err` holding the transformed value.
@ -64,8 +76,11 @@ map = \result, transform ->
mapErr : Result ok a, (a -> b) -> Result ok b
mapErr = \result, transform ->
when result is
Ok v -> Ok v
Err e -> Err (transform e)
Ok v ->
Ok v
Err e ->
Err (transform e)
## If the result is `Ok`, transform the entire result by running a conversion
## function on the value the `Ok` holds. Then return that new result.
@ -78,5 +93,8 @@ mapErr = \result, transform ->
after : Result a err, (a -> Result b err) -> Result b err
after = \result, transform ->
when result is
Ok v -> transform v
Err e -> Err e
Ok v ->
transform v
Err e ->
Err e

View File

@ -1,6 +1,6 @@
interface Str
exposes
[
exposes
[
concat,
Utf8Problem,
Utf8ByteProblem,
@ -18,7 +18,6 @@ interface Str
trim,
trimLeft,
trimRight,
toDec,
toF64,
toF32,
@ -41,7 +40,6 @@ interface Str
## Dealing with text is a deep topic, so by design, Roc's `Str` module sticks
## to the basics.
##
## ### Unicode
##
## Unicode can represent text values which span multiple languages, symbols, and emoji.
@ -111,8 +109,6 @@ interface Str
## and you can use it as many times as you like inside a string. The name
## between the parentheses must refer to a `Str` value that is currently in
## scope, and it must be a name - it can't be an arbitrary expression like a function call.
Utf8ByteProblem :
[
InvalidStartByte,
@ -191,7 +187,6 @@ toUtf8 : Str -> List U8
# fromUtf8 : List U8 -> Result Str [ BadUtf8 Utf8Problem ]*
# fromUtf8Range : List U8 -> Result Str [ BadUtf8 Utf8Problem Nat, OutOfBounds ]*
fromUtf8 : List U8 -> Result Str [ BadUtf8 Utf8ByteProblem Nat ]*
fromUtf8Range : List U8, { start : Nat, count : Nat } -> Result Str [ BadUtf8 Utf8ByteProblem Nat, OutOfBounds ]*

View File

@ -159,10 +159,10 @@ where
fn fmt_docs<'buf>(buf: &mut Buf<'buf>, docs: &str) {
buf.push_str("##");
if !docs.starts_with(' ') {
if !docs.is_empty() {
buf.spaces(1);
}
buf.push_str(docs);
buf.push_str(docs.trim_end());
}
/// RemoveSpaces normalizes the ast to something that we _expect_ to be invariant under formatting.

View File

@ -14,7 +14,7 @@ mod test_fmt {
use roc_parse::module::{self, module_defs};
use roc_parse::parser::Parser;
use roc_parse::state::State;
use roc_test_utils::assert_multiline_str_eq;
use roc_test_utils::{assert_multiline_str_eq, workspace_root};
// Not intended to be used directly in tests; please use expr_formats_to or expr_formats_same
fn expr_formats_to(input: &str, expected: &str) {
@ -4633,13 +4633,7 @@ mod test_fmt {
/// `cargo run -- format $(find examples -name \*.roc)`
fn test_fmt_examples() {
let mut count = 0;
let mut root = std::env::current_dir()
.unwrap()
.parent()
.unwrap()
.parent()
.unwrap()
.to_owned();
let mut root = workspace_root();
root.push("examples");
for entry in walkdir::WalkDir::new(&root) {
let entry = entry.unwrap();
@ -4658,6 +4652,32 @@ mod test_fmt {
);
}
#[test]
/// Test that builtins are formatted correctly
/// If this test fails on your diff, it probably means you need to re-format a builtin.
/// Try this:
/// `cargo run -- format $(find compiler/builtins/roc -name \*.roc)`
fn test_fmt_builtins() {
let mut count = 0;
let mut root = workspace_root();
root.push("compiler/builtins/roc");
for entry in walkdir::WalkDir::new(&root) {
let entry = entry.unwrap();
let path = entry.path();
if path.extension() == Some(std::ffi::OsStr::new("roc")) {
count += 1;
let src = std::fs::read_to_string(path).unwrap();
println!("Now trying to format {}", path.display());
module_formats_same(&src);
}
}
assert!(
count > 0,
"Expecting to find at least 1 .roc file to format under {}",
root.display()
);
}
// this is a parse error atm
// #[test]
// fn multiline_apply() {

View File

@ -388,7 +388,6 @@ fn eat_line_comment<'a>(
}
b'\n' => {
state = state.advance_newline();
index += 1;
multiline = true;
comments_and_newlines.push(CommentOrNewline::Newline);
}
@ -424,7 +423,7 @@ fn eat_line_comment<'a>(
};
}
_ => false,
Some(_) => false,
}
} else {
false

View File

@ -1,3 +1,5 @@
use std::path::PathBuf;
#[doc(hidden)]
pub use pretty_assertions::assert_eq as _pretty_assert_eq;
@ -47,3 +49,8 @@ impl Drop for TmpDir {
let _ = remove_dir_all::remove_dir_all(&self.path);
}
}
pub fn workspace_root() -> PathBuf {
let root = std::env::var("ROC_WORKSPACE_DIR").expect("Can't find the ROC_WORKSPACE_DIR variable expected to be set in .cargo/config.toml. Are you running tests outside of cargo?");
PathBuf::from(root)
}