From c1ed068ac19ce7cc52585c825f87606e73d487e5 Mon Sep 17 00:00:00 2001 From: GeopJr Date: Mon, 28 Nov 2022 16:29:33 +0200 Subject: [PATCH 01/41] Handle triples without libc (#12594) Co-authored-by: Jason Frey --- spec/compiler/codegen/target_spec.cr | 1 + src/compiler/crystal/codegen/target.cr | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/spec/compiler/codegen/target_spec.cr b/spec/compiler/codegen/target_spec.cr index c5639d0bb217..db22ad7334bd 100644 --- a/spec/compiler/codegen/target_spec.cr +++ b/spec/compiler/codegen/target_spec.cr @@ -17,6 +17,7 @@ describe Crystal::Codegen::Target do Target.new("i686-unknown-linux-gnu").to_s.should eq("i386-unknown-linux-gnu") Target.new("amd64-unknown-openbsd").to_s.should eq("x86_64-unknown-openbsd") Target.new("arm64-apple-darwin20.2.0").to_s.should eq("aarch64-apple-darwin20.2.0") + Target.new("x86_64-suse-linux").to_s.should eq("x86_64-suse-linux-gnu") end it "parses freebsd version" do diff --git a/src/compiler/crystal/codegen/target.cr b/src/compiler/crystal/codegen/target.cr index c89880deabda..cb0c62d4f080 100644 --- a/src/compiler/crystal/codegen/target.cr +++ b/src/compiler/crystal/codegen/target.cr @@ -32,6 +32,17 @@ class Crystal::Codegen::Target else # no need to tweak the architecture end + + if linux? && environment_parts.size == 1 + case @vendor + when "suse", "redhat", "slackware", "amazon", "unknown", "montavista", "mti" + # Build string instead of setting it as "linux-gnu" + # since "linux6E" & "linuxspe" are available. + @environment = "#{@environment}-gnu" + else + # no need to tweak the environment + end + end end def environment_parts From d06e5a374c20ccfb3421491096bef7a3af27a3fa Mon Sep 17 00:00:00 2001 From: Guilherme Bernal Date: Mon, 28 Nov 2022 06:30:33 -0800 Subject: [PATCH 02/41] Add WebAssembly specs (#12571) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Johannes Müller Co-authored-by: Sijawusz Pur Rahnama Co-authored-by: Ary Borenszweig Co-authored-by: Beta Ziliani Co-authored-by: Quinton Miller Co-authored-by: George Dietrich Co-authored-by: Dmitri Goutnik Co-authored-by: Gabriel Holodak Co-authored-by: Caspian Baska Co-authored-by: David Keller --- .github/workflows/smoke.yml | 1 - .github/workflows/wasm32.yml | 45 ++++++ spec/generate_wasm32_spec.sh | 61 ++++++++ spec/wasm32_std_spec.cr | 268 +++++++++++++++++++++++++++++++++++ src/crystal/scheduler.cr | 5 +- src/spec/expectations.cr | 74 +++++----- 6 files changed, 418 insertions(+), 36 deletions(-) create mode 100644 .github/workflows/wasm32.yml create mode 100755 spec/generate_wasm32_spec.sh create mode 100644 spec/wasm32_std_spec.cr diff --git a/.github/workflows/smoke.yml b/.github/workflows/smoke.yml index 0e65788721e3..9fd272badbb3 100644 --- a/.github/workflows/smoke.yml +++ b/.github/workflows/smoke.yml @@ -47,7 +47,6 @@ jobs: - arm-linux-gnueabihf - i386-linux-gnu - i386-linux-musl - - wasm32-unknown-wasi - x86_64-dragonfly - x86_64-freebsd - x86_64-netbsd diff --git a/.github/workflows/wasm32.yml b/.github/workflows/wasm32.yml new file mode 100644 index 000000000000..e2b0fbf49bbe --- /dev/null +++ b/.github/workflows/wasm32.yml @@ -0,0 +1,45 @@ +name: WebAssembly CI + +on: [push, pull_request] + +env: + SPEC_SPLIT_DOTS: 160 + +jobs: + wasm32-test: + runs-on: ubuntu-latest + container: crystallang/crystal:1.6.1-build + steps: + - name: Download Crystal source + uses: actions/checkout@v2 + + - name: Install wasmtime + uses: mwilliamson/setup-wasmtime-action@v1 + with: + wasmtime-version: "2.0.0" + + - name: Install LLVM 13 + run: | + apt-get update + apt-get install -y curl lsb-release wget software-properties-common gnupg + curl -O https://apt.llvm.org/llvm.sh + chmod +x llvm.sh + ./llvm.sh 13 + ln -s $(which wasm-ld-13) /usr/bin/wasm-ld + + - name: Download wasm32 libs + run: | + mkdir wasm32-wasi-libs + curl -LO https://github.com/lbguilherme/wasm-libs/releases/download/0.0.2/wasm32-wasi-libs.tar.gz + echo "114dd08b776c92e15b4ec83178fa486dc436e24b7f662c3241a8cdf2506fe426 wasm32-wasi-libs.tar.gz" | sha256sum -c - + tar -f wasm32-wasi-libs.tar.gz -C wasm32-wasi-libs -xz + rm wasm32-wasi-libs.tar.gz + + - name: Build spec/wasm32_std_spec.cr + run: bin/crystal build spec/wasm32_std_spec.cr -o wasm32_std_spec.wasm --target wasm32-wasi + env: + CRYSTAL_LIBRARY_PATH: ${{ github.workspace }}/wasm32-wasi-libs + + - name: Run wasm32_std_spec.wasm + run: | + wasmtime run wasm32_std_spec.wasm diff --git a/spec/generate_wasm32_spec.sh b/spec/generate_wasm32_spec.sh new file mode 100755 index 000000000000..a6388e0cc8e4 --- /dev/null +++ b/spec/generate_wasm32_spec.sh @@ -0,0 +1,61 @@ +#! /usr/bin/env bash +set +x + +# This script iterates through each spec file and tries to build and run it. +# +# * `failed codegen` annotates specs that error in the compiler. +# This is mostly caused by some API not being ported to wasm32 (either the spec +# target itself or some tools used by the spec). +# * `failed linking` annotates specs that compile but don't link. +# Most failures are caused by missing libraries (libxml2, libyaml, libgmp, +# libllvm, libz, libssl). +# * `failed to run` annotates specs that compile and link but don't properly +# execute. +# +# PREREQUISITES: +# +# - wasmtime (https://wasmtime.dev/) +# +# Note: Libs are downloaded from https://github.com/lbguilherme/wasm-libs +# +# USAGE: +# +# $ spec/generate_wasm32_spec.sh > spec/wasm32_std_spec.cr + +WORK_DIR=$(mktemp -d) +function cleanup { + rm -rf $WORK_DIR +} +trap cleanup EXIT + +mkdir "$WORK_DIR"/wasm32-wasi-libs +curl -L https://github.com/lbguilherme/wasm-libs/releases/download/0.0.2/wasm32-wasi-libs.tar.gz | tar -C "$WORK_DIR"/wasm32-wasi-libs -xz +export CRYSTAL_LIBRARY_PATH="$WORK_DIR"/wasm32-wasi-libs + +command="$0 $*" +echo "# This file is autogenerated by \`${command% }\`" +echo "# $(date --rfc-3339 seconds)" +echo + +for spec in $(find "spec/std" -type f -iname "*_spec.cr" | LC_ALL=C sort); do + require="require \"./${spec##spec/}\"" + target=$WORK_DIR"/"$spec".wasm" + mkdir -p $(dirname "$target") + + if ! output=$(bin/crystal build "$spec" -o "$target" --target wasm32-wasi 2>&1); then + if [[ "$output" =~ "execution of command failed" ]]; then + echo "# $require (failed linking)" + else + echo "# $require (failed codegen)" + fi + continue + fi + + wasmtime run "$target" > /dev/null; exit=$? + + if [ $exit -eq 0 ]; then + echo "$require" + else + echo "# $require (failed to run)" + fi +done diff --git a/spec/wasm32_std_spec.cr b/spec/wasm32_std_spec.cr new file mode 100644 index 000000000000..4e8e9cf7a0fd --- /dev/null +++ b/spec/wasm32_std_spec.cr @@ -0,0 +1,268 @@ +# This file is autogenerated by `spec/generate_wasm32_spec.sh` +# 2022-10-06 08:12:57-03:00 + +require "./std/array_spec.cr" +require "./std/atomic_spec.cr" +require "./std/base64_spec.cr" +# require "./std/benchmark_spec.cr" (failed to run) +# require "./std/big/big_decimal_spec.cr" (failed linking) +# require "./std/big/big_float_spec.cr" (failed linking) +# require "./std/big/big_int_spec.cr" (failed linking) +# require "./std/big/big_rational_spec.cr" (failed linking) +require "./std/big/number_spec.cr" +require "./std/bit_array_spec.cr" +require "./std/bool_spec.cr" +require "./std/box_spec.cr" +# require "./std/channel_spec.cr" (failed to run) +require "./std/char/reader_spec.cr" +# require "./std/char_spec.cr" (failed to run) +require "./std/class_spec.cr" +require "./std/colorize_spec.cr" +require "./std/comparable_spec.cr" +require "./std/complex_spec.cr" +# require "./std/compress/deflate/deflate_spec.cr" (failed linking) +# require "./std/compress/gzip/gzip_spec.cr" (failed linking) +# require "./std/compress/zip/zip_file_spec.cr" (failed linking) +# require "./std/compress/zip/zip_spec.cr" (failed linking) +# require "./std/compress/zlib/reader_spec.cr" (failed linking) +# require "./std/compress/zlib/stress_spec.cr" (failed linking) +# require "./std/compress/zlib/writer_spec.cr" (failed linking) +# require "./std/concurrent/select_spec.cr" (failed to run) +# require "./std/concurrent_spec.cr" (failed to run) +require "./std/crypto/bcrypt/base64_spec.cr" +require "./std/crypto/bcrypt/password_spec.cr" +require "./std/crypto/bcrypt_spec.cr" +require "./std/crypto/blowfish_spec.cr" +require "./std/crypto/subtle_spec.cr" +require "./std/crystal/compiler_rt/ashlti3_spec.cr" +require "./std/crystal/compiler_rt/ashrti3_spec.cr" +require "./std/crystal/compiler_rt/divmod128_spec.cr" +require "./std/crystal/compiler_rt/fixint_spec.cr" +require "./std/crystal/compiler_rt/float_spec.cr" +require "./std/crystal/compiler_rt/lshrti3_spec.cr" +require "./std/crystal/compiler_rt/mulodi4_spec.cr" +require "./std/crystal/compiler_rt/mulosi4_spec.cr" +require "./std/crystal/compiler_rt/muloti4_spec.cr" +require "./std/crystal/compiler_rt/multi3_spec.cr" +require "./std/crystal/compiler_rt/powidf2_spec.cr" +require "./std/crystal/compiler_rt/powisf2_spec.cr" +# require "./std/crystal/digest/md5_spec.cr" (failed to run) +# require "./std/crystal/digest/sha1_spec.cr" (failed to run) +# require "./std/crystal/hasher_spec.cr" (failed linking) +require "./std/crystal/pointer_linked_list_spec.cr" +# require "./std/crystal/syntax_highlighter/colorize_spec.cr" (failed to run) +# require "./std/crystal/syntax_highlighter/html_spec.cr" (failed linking) +require "./std/csv/csv_build_spec.cr" +require "./std/csv/csv_lex_spec.cr" +require "./std/csv/csv_parse_spec.cr" +require "./std/csv/csv_spec.cr" +require "./std/deque_spec.cr" +# require "./std/digest/adler32_spec.cr" (failed linking) +# require "./std/digest/crc32_spec.cr" (failed linking) +# require "./std/digest/io_digest_spec.cr" (failed linking) +# require "./std/digest/md5_spec.cr" (failed linking) +# require "./std/digest/sha1_spec.cr" (failed linking) +# require "./std/digest/sha256_spec.cr" (failed linking) +# require "./std/digest/sha512_spec.cr" (failed linking) +# require "./std/dir_spec.cr" (failed to run) +require "./std/double_spec.cr" +require "./std/ecr/ecr_lexer_spec.cr" +# require "./std/ecr/ecr_spec.cr" (failed linking) +require "./std/enum_spec.cr" +require "./std/enumerable_spec.cr" +# require "./std/env_spec.cr" (failed to run) +# require "./std/errno_spec.cr" (failed to run) +# require "./std/exception/call_stack_spec.cr" (failed to run) +# require "./std/exception_spec.cr" (failed codegen) +# require "./std/file/tempfile_spec.cr" (failed to run) +# require "./std/file_spec.cr" (failed linking) +# require "./std/file_utils_spec.cr" (failed linking) +require "./std/float_printer/diy_fp_spec.cr" +require "./std/float_printer/grisu3_spec.cr" +require "./std/float_printer/ieee_spec.cr" +# require "./std/float_printer_spec.cr" (failed to run) +require "./std/float_spec.cr" +require "./std/gc_spec.cr" +require "./std/hash_spec.cr" +require "./std/html_spec.cr" +# require "./std/http/chunked_content_spec.cr" (failed linking) +# require "./std/http/client/client_spec.cr" (failed linking) +# require "./std/http/client/response_spec.cr" (failed linking) +# require "./std/http/cookie_spec.cr" (failed linking) +# require "./std/http/formdata/builder_spec.cr" (failed linking) +# require "./std/http/formdata/parser_spec.cr" (failed linking) +# require "./std/http/formdata_spec.cr" (failed linking) +require "./std/http/headers_spec.cr" +# require "./std/http/http_spec.cr" (failed linking) +require "./std/http/params_spec.cr" +# require "./std/http/request_spec.cr" (failed linking) +# require "./std/http/server/handlers/compress_handler_spec.cr" (failed linking) +# require "./std/http/server/handlers/error_handler_spec.cr" (failed linking) +# require "./std/http/server/handlers/handler_spec.cr" (failed linking) +# require "./std/http/server/handlers/log_handler_spec.cr" (failed linking) +# require "./std/http/server/handlers/static_file_handler_spec.cr" (failed linking) +# require "./std/http/server/handlers/websocket_handler_spec.cr" (failed linking) +# require "./std/http/server/request_processor_spec.cr" (failed linking) +# require "./std/http/server/response_spec.cr" (failed linking) +# require "./std/http/server/server_spec.cr" (failed linking) +# require "./std/http/status_spec.cr" (failed linking) +# require "./std/http/web_socket_spec.cr" (failed linking) +# require "./std/humanize_spec.cr" (failed to run) +require "./std/indexable/mutable_spec.cr" +require "./std/indexable_spec.cr" +# require "./std/ini_spec.cr" (failed to run) +# require "./std/int_spec.cr" (failed linking) +# require "./std/io/argf_spec.cr" (failed to run) +# require "./std/io/buffered_spec.cr" (failed to run) +require "./std/io/byte_format_spec.cr" +require "./std/io/delimited_spec.cr" +# require "./std/io/file_descriptor_spec.cr" (failed to run) +# require "./std/io/hexdump_spec.cr" (failed to run) +# require "./std/io/io_spec.cr" (failed linking) +# require "./std/io/memory_spec.cr" (failed to run) +# require "./std/io/multi_writer_spec.cr" (failed to run) +require "./std/io/sized_spec.cr" +# require "./std/io/stapled_spec.cr" (failed to run) +require "./std/iterator_spec.cr" +# require "./std/json/any_spec.cr" (failed linking) +require "./std/json/builder_spec.cr" +require "./std/json/lexer_spec.cr" +require "./std/json/parser_spec.cr" +# require "./std/json/pull_parser_spec.cr" (failed to run) +# require "./std/json/serializable_spec.cr" (failed codegen) +# require "./std/json/serialization_spec.cr" (failed codegen) +# require "./std/kernel_spec.cr" (failed to run) +require "./std/levenshtein_spec.cr" +# require "./std/llvm/aarch64_spec.cr" (failed linking) +# require "./std/llvm/arm_abi_spec.cr" (failed linking) +# require "./std/llvm/llvm_spec.cr" (failed linking) +# require "./std/llvm/type_spec.cr" (failed linking) +# require "./std/llvm/x86_64_abi_spec.cr" (failed linking) +# require "./std/llvm/x86_abi_spec.cr" (failed linking) +# require "./std/log/broadcast_backend_spec.cr" (failed to run) +# require "./std/log/builder_spec.cr" (failed to run) +# require "./std/log/context_spec.cr" (failed to run) +# require "./std/log/dispatch_spec.cr" (failed to run) +require "./std/log/env_config_spec.cr" +# require "./std/log/format_spec.cr" (failed codegen) +# require "./std/log/io_backend_spec.cr" (failed to run) +# require "./std/log/log_spec.cr" (failed to run) +require "./std/log/main_spec.cr" +# require "./std/log/metadata_spec.cr" (failed to run) +# require "./std/log/spec_spec.cr" (failed to run) +require "./std/match_data_spec.cr" +# require "./std/math_spec.cr" (failed to run) +# require "./std/mime/media_type_spec.cr" (failed linking) +# require "./std/mime/multipart/builder_spec.cr" (failed linking) +# require "./std/mime/multipart/parser_spec.cr" (failed linking) +# require "./std/mime/multipart_spec.cr" (failed linking) +# require "./std/mime_spec.cr" (failed to run) +# require "./std/mutex_spec.cr" (failed to run) +require "./std/named_tuple_spec.cr" +# require "./std/number_spec.cr" (failed linking) +# require "./std/oauth/access_token_spec.cr" (failed linking) +# require "./std/oauth/authorization_header_spec.cr" (failed linking) +# require "./std/oauth/consumer_spec.cr" (failed linking) +# require "./std/oauth/params_spec.cr" (failed linking) +# require "./std/oauth/request_token_spec.cr" (failed linking) +# require "./std/oauth/signature_spec.cr" (failed linking) +# require "./std/oauth2/access_token_spec.cr" (failed linking) +# require "./std/oauth2/client_spec.cr" (failed linking) +# require "./std/oauth2/session_spec.cr" (failed linking) +# require "./std/object_spec.cr" (failed to run) +# require "./std/openssl/cipher_spec.cr" (failed linking) +# require "./std/openssl/digest_spec.cr" (failed linking) +# require "./std/openssl/hmac_spec.cr" (failed linking) +# require "./std/openssl/pkcs5_spec.cr" (failed linking) +# require "./std/openssl/ssl/context_spec.cr" (failed linking) +# require "./std/openssl/ssl/hostname_validation_spec.cr" (failed linking) +# require "./std/openssl/ssl/server_spec.cr" (failed linking) +# require "./std/openssl/ssl/socket_spec.cr" (failed linking) +# require "./std/openssl/x509/certificate_spec.cr" (failed linking) +# require "./std/openssl/x509/name_spec.cr" (failed linking) +require "./std/option_parser_spec.cr" +# require "./std/path_spec.cr" (failed to run) +require "./std/pointer_spec.cr" +require "./std/pp_spec.cr" +require "./std/pretty_print_spec.cr" +require "./std/proc_spec.cr" +# require "./std/process/find_executable_spec.cr" (failed to run) +require "./std/process_spec.cr" +# require "./std/raise_spec.cr" (failed codegen) +require "./std/random/isaac_spec.cr" +require "./std/random/pcg32_spec.cr" +require "./std/random/secure_spec.cr" +# require "./std/random_spec.cr" (failed linking) +# require "./std/range_spec.cr" (failed linking) +require "./std/record_spec.cr" +# require "./std/reference_spec.cr" (failed to run) +# require "./std/regex_spec.cr" (failed to run) +require "./std/semantic_version_spec.cr" +require "./std/set_spec.cr" +require "./std/signal_spec.cr" +require "./std/slice_spec.cr" +# require "./std/socket/address_spec.cr" (failed to run) +# require "./std/socket/addrinfo_spec.cr" (failed to run) +# require "./std/socket/socket_spec.cr" (failed to run) +require "./std/socket/tcp_server_spec.cr" +require "./std/socket/tcp_socket_spec.cr" +# require "./std/socket/udp_socket_spec.cr" (failed to run) +# require "./std/socket/unix_server_spec.cr" (failed to run) +# require "./std/socket/unix_socket_spec.cr" (failed to run) +# require "./std/spec/context_spec.cr" (failed to run) +require "./std/spec/expectations_spec.cr" +# require "./std/spec/filters_spec.cr" (failed to run) +require "./std/spec/helpers/iterate_spec.cr" +# require "./std/spec/hooks_spec.cr" (failed to run) +# require "./std/spec/junit_formatter_spec.cr" (failed linking) +require "./std/spec/tap_formatter_spec.cr" +# require "./std/spec_spec.cr" (failed codegen) +# require "./std/sprintf_spec.cr" (failed linking) +require "./std/static_array_spec.cr" +require "./std/string/grapheme_break_spec.cr" +require "./std/string/grapheme_spec.cr" +require "./std/string/utf16_spec.cr" +require "./std/string_builder_spec.cr" +require "./std/string_pool_spec.cr" +require "./std/string_scanner_spec.cr" +# require "./std/string_spec.cr" (failed to run) +# require "./std/struct_spec.cr" (failed linking) +require "./std/symbol_spec.cr" +require "./std/syscall_spec.cr" +# require "./std/system/group_spec.cr" (failed to run) +# require "./std/system/user_spec.cr" (failed to run) +# require "./std/system_error_spec.cr" (failed to run) +# require "./std/system_spec.cr" (failed to run) +# require "./std/thread/condition_variable_spec.cr" (failed to run) +# require "./std/thread/mutex_spec.cr" (failed to run) +# require "./std/thread_spec.cr" (failed to run) +require "./std/time/custom_formats_spec.cr" +# require "./std/time/format_spec.cr" (failed to run) +# require "./std/time/location_spec.cr" (failed to run) +require "./std/time/span_spec.cr" +# require "./std/time/time_spec.cr" (failed to run) +require "./std/tuple_spec.cr" +require "./std/uint_spec.cr" +require "./std/uri/params_spec.cr" +require "./std/uri/punycode_spec.cr" +# require "./std/uri_spec.cr" (failed linking) +require "./std/uuid/json_spec.cr" +# require "./std/uuid/yaml_spec.cr" (failed linking) +require "./std/uuid_spec.cr" +# require "./std/va_list_spec.cr" (failed to run) +# require "./std/weak_ref_spec.cr" (failed to run) +require "./std/winerror_spec.cr" +# require "./std/xml/builder_spec.cr" (failed linking) +# require "./std/xml/html_spec.cr" (failed linking) +# require "./std/xml/reader_spec.cr" (failed linking) +# require "./std/xml/xml_spec.cr" (failed linking) +# require "./std/xml/xpath_spec.cr" (failed linking) +# require "./std/yaml/any_spec.cr" (failed linking) +# require "./std/yaml/builder_spec.cr" (failed codegen) +# require "./std/yaml/nodes/builder_spec.cr" (failed codegen) +# require "./std/yaml/schema/core_spec.cr" (failed codegen) +# require "./std/yaml/schema/fail_safe_spec.cr" (failed codegen) +# require "./std/yaml/serializable_spec.cr" (failed codegen) +# require "./std/yaml/serialization_spec.cr" (failed codegen) +# require "./std/yaml/yaml_pull_parser_spec.cr" (failed codegen) +# require "./std/yaml/yaml_spec.cr" (failed codegen) diff --git a/src/crystal/scheduler.cr b/src/crystal/scheduler.cr index 2344dbc5f2f7..61df03294875 100644 --- a/src/crystal/scheduler.cr +++ b/src/crystal/scheduler.cr @@ -175,7 +175,10 @@ class Crystal::Scheduler end protected def yield : Nil - sleep(0.seconds) + # TODO: Fiber switching and libevent for wasm32 + {% unless flag?(:wasm32) %} + sleep(0.seconds) + {% end %} end protected def yield(fiber : Fiber) : Nil diff --git a/src/spec/expectations.cr b/src/spec/expectations.cr index 399f5be6f253..fa37ca4def5c 100644 --- a/src/spec/expectations.cr +++ b/src/spec/expectations.cr @@ -382,45 +382,51 @@ module Spec # If *message* is a regular expression, it is used to match the error message. # # It returns the rescued exception. - def expect_raises(klass : T.class, message : String | Regex | Nil = nil, file = __FILE__, line = __LINE__) forall T - yield - rescue ex : T - # We usually bubble Spec::AssertionFailed, unless this is the expected exception - if ex.is_a?(Spec::AssertionFailed) && klass != Spec::AssertionFailed - raise ex - end - - # `NestingSpecError` is treated as the same above. - if ex.is_a?(Spec::NestingSpecError) && klass != Spec::NestingSpecError - raise ex + {% if flag?(:wasm32) %} + def expect_raises(klass : T.class, message : String | Regex | Nil = nil, file = __FILE__, line = __LINE__, &) forall T + # TODO: Enable "expect_raises" for wasm32 after exceptions are working. end + {% else %} + def expect_raises(klass : T.class, message : String | Regex | Nil = nil, file = __FILE__, line = __LINE__) forall T + yield + rescue ex : T + # We usually bubble Spec::AssertionFailed, unless this is the expected exception + if ex.is_a?(Spec::AssertionFailed) && klass != Spec::AssertionFailed + raise ex + end - ex_to_s = ex.to_s - case message - when Regex - unless (ex_to_s =~ message) - backtrace = ex.backtrace.join('\n') { |f| " # #{f}" } - fail "Expected #{klass} with message matching #{message.inspect}, " \ - "got #<#{ex.class}: #{ex_to_s}> with backtrace:\n#{backtrace}", file, line + # `NestingSpecError` is treated as the same above. + if ex.is_a?(Spec::NestingSpecError) && klass != Spec::NestingSpecError + raise ex end - when String - unless ex_to_s.includes?(message) - backtrace = ex.backtrace.join('\n') { |f| " # #{f}" } - fail "Expected #{klass} with #{message.inspect}, got #<#{ex.class}: " \ - "#{ex_to_s}> with backtrace:\n#{backtrace}", file, line + + ex_to_s = ex.to_s + case message + when Regex + unless (ex_to_s =~ message) + backtrace = ex.backtrace.join('\n') { |f| " # #{f}" } + fail "Expected #{klass} with message matching #{message.inspect}, " \ + "got #<#{ex.class}: #{ex_to_s}> with backtrace:\n#{backtrace}", file, line + end + when String + unless ex_to_s.includes?(message) + backtrace = ex.backtrace.join('\n') { |f| " # #{f}" } + fail "Expected #{klass} with #{message.inspect}, got #<#{ex.class}: " \ + "#{ex_to_s}> with backtrace:\n#{backtrace}", file, line + end + when Nil + # No need to check the message end - when Nil - # No need to check the message - end - ex - rescue ex - backtrace = ex.backtrace.join('\n') { |f| " # #{f}" } - fail "Expected #{klass}, got #<#{ex.class}: #{ex}> with backtrace:\n" \ - "#{backtrace}", file, line - else - fail "Expected #{klass} but nothing was raised", file, line - end + ex + rescue ex + backtrace = ex.backtrace.join('\n') { |f| " # #{f}" } + fail "Expected #{klass}, got #<#{ex.class}: #{ex}> with backtrace:\n" \ + "#{backtrace}", file, line + else + fail "Expected #{klass} but nothing was raised", file, line + end + {% end %} end module ObjectExtensions From c18b00e3e4933318a2ffea9c72abf037fef0d28a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20M=C3=BCller?= Date: Mon, 28 Nov 2022 15:32:06 +0100 Subject: [PATCH 03/41] Implement `flock_*` for Win32 (#12766) --- spec/std/file_spec.cr | 11 +++-- src/crystal/system/win32/file.cr | 45 +++++++++++++++++-- src/lib_c/x86_64-windows-msvc/c/fileapi.cr | 15 +++++++ src/lib_c/x86_64-windows-msvc/c/minwinbase.cr | 3 ++ 4 files changed, 65 insertions(+), 9 deletions(-) diff --git a/spec/std/file_spec.cr b/spec/std/file_spec.cr index a393049f596a..e9270a48b483 100644 --- a/spec/std/file_spec.cr +++ b/spec/std/file_spec.cr @@ -933,22 +933,21 @@ describe "File" do end end - # TODO: implement flock on windows describe "flock" do - pending_win32 "exclusively locks a file" do + it "#flock_exclusive" do File.open(datapath("test_file.txt")) do |file1| File.open(datapath("test_file.txt")) do |file2| file1.flock_exclusive do exc = expect_raises(IO::Error, "Error applying file lock: file is already locked") do file2.flock_exclusive(blocking: false) { } end - exc.os_error.should eq Errno::EWOULDBLOCK + exc.os_error.should eq({% if flag?(:win32) %}WinError::ERROR_LOCK_VIOLATION{% else %}Errno::EWOULDBLOCK{% end %}) end end end end - pending_win32 "shared locks a file" do + it "#flock_shared" do File.open(datapath("test_file.txt")) do |file1| File.open(datapath("test_file.txt")) do |file2| file1.flock_shared do @@ -958,7 +957,7 @@ describe "File" do end end - pending_win32 "#flock_shared soft blocking fiber" do + it "#flock_shared soft blocking fiber" do File.open(datapath("test_file.txt")) do |file1| File.open(datapath("test_file.txt")) do |file2| done = Channel(Nil).new @@ -975,7 +974,7 @@ describe "File" do end end - pending_win32 "#flock_exclusive soft blocking fiber" do + it "#flock_exclusive soft blocking fiber" do File.open(datapath("test_file.txt")) do |file1| File.open(datapath("test_file.txt")) do |file2| done = Channel(Nil).new diff --git a/src/crystal/system/win32/file.cr b/src/crystal/system/win32/file.cr index 0df1926cc047..bda41cde8b69 100644 --- a/src/crystal/system/win32/file.cr +++ b/src/crystal/system/win32/file.cr @@ -258,15 +258,54 @@ module Crystal::System::File end private def system_flock_shared(blocking : Bool) : Nil - raise NotImplementedError.new("File#flock_shared") + flock(false, blocking) end private def system_flock_exclusive(blocking : Bool) : Nil - raise NotImplementedError.new("File#flock_exclusive") + flock(true, blocking) end private def system_flock_unlock : Nil - raise NotImplementedError.new("File#flock_unlock") + unlock_file(windows_handle) + end + + private def flock(exclusive, retry) + flags = LibC::LOCKFILE_FAIL_IMMEDIATELY + flags |= LibC::LOCKFILE_EXCLUSIVE_LOCK if exclusive + + handle = windows_handle + if retry + until lock_file(handle, flags) + ::Fiber.yield + end + else + lock_file(handle, flags) || raise IO::Error.from_winerror("Error applying file lock: file is already locked") + end + end + + private def lock_file(handle, flags) + # lpOverlapped must be provided despite the synchronous use of this method. + overlapped = LibC::OVERLAPPED.new + # lock the entire file with offset 0 in overlapped and number of bytes set to max value + if 0 != LibC.LockFileEx(handle, flags, 0, 0xFFFF_FFFF, 0xFFFF_FFFF, pointerof(overlapped)) + true + else + winerror = WinError.value + if winerror == WinError::ERROR_LOCK_VIOLATION + false + else + raise IO::Error.from_winerror("LockFileEx", winerror) + end + end + end + + private def unlock_file(handle) + # lpOverlapped must be provided despite the synchronous use of this method. + overlapped = LibC::OVERLAPPED.new + # unlock the entire file with offset 0 in overlapped and number of bytes set to max value + if 0 == LibC.UnlockFileEx(handle, 0, 0xFFFF_FFFF, 0xFFFF_FFFF, pointerof(overlapped)) + raise IO::Error.from_winerror("UnLockFileEx") + end end private def system_fsync(flush_metadata = true) : Nil diff --git a/src/lib_c/x86_64-windows-msvc/c/fileapi.cr b/src/lib_c/x86_64-windows-msvc/c/fileapi.cr index 42533d369411..701a9590ca22 100644 --- a/src/lib_c/x86_64-windows-msvc/c/fileapi.cr +++ b/src/lib_c/x86_64-windows-msvc/c/fileapi.cr @@ -75,6 +75,21 @@ lib LibC fun FindNextFileW(hFindFile : HANDLE, lpFindFileData : WIN32_FIND_DATAW*) : BOOL fun FindClose(hFindFile : HANDLE) : BOOL + fun LockFileEx( + hFile : HANDLE, + dwFlags : DWORD, + dwReserved : DWORD, + nNumberOfBytesToLockLow : DWORD, + nNumberOfBytesToLockHigh : DWORD, + lpOverlapped : OVERLAPPED* + ) : BOOL + fun UnlockFileEx( + hFile : HANDLE, + dwReserved : DWORD, + nNumberOfBytesToUnlockLow : DWORD, + nNumberOfBytesToUnlockHigh : DWORD, + lpOverlapped : OVERLAPPED* + ) : BOOL fun SetFileTime(hFile : HANDLE, lpCreationTime : FILETIME*, lpLastAccessTime : FILETIME*, lpLastWriteTime : FILETIME*) : BOOL end diff --git a/src/lib_c/x86_64-windows-msvc/c/minwinbase.cr b/src/lib_c/x86_64-windows-msvc/c/minwinbase.cr index d01b5232e387..cb56e3269de2 100644 --- a/src/lib_c/x86_64-windows-msvc/c/minwinbase.cr +++ b/src/lib_c/x86_64-windows-msvc/c/minwinbase.cr @@ -45,6 +45,9 @@ lib LibC GetFileExMaxInfoLevel end + LOCKFILE_FAIL_IMMEDIATELY = DWORD.new(0x00000001) + LOCKFILE_EXCLUSIVE_LOCK = DWORD.new(0x00000002) + STATUS_PENDING = 0x103 STILL_ACTIVE = STATUS_PENDING end From 424df1efcec25da22054704756622a2d5bc9b861 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20M=C3=BCller?= Date: Mon, 28 Nov 2022 21:55:49 +0100 Subject: [PATCH 04/41] Add docs to `ENV#has_key?` (#12781) Co-authored-by: Jack Thorne --- src/env.cr | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/env.cr b/src/env.cr index 6e5b47104288..b28e4014ea22 100644 --- a/src/env.cr +++ b/src/env.cr @@ -40,8 +40,12 @@ module ENV value end - # Returns `true` if the environment variable named *key* exists and `false` - # if it doesn't. + # Returns `true` if the environment variable named *key* exists and `false` if it doesn't. + # + # ``` + # ENV.has_key?("NOT_A_REAL_KEY") # => false + # ENV.has_key?("PATH") # => true + # ``` def self.has_key?(key : String) : Bool Crystal::System::Env.has_key?(key) end From 37eaa39a35e2d9ce2957adb2bdb6c0e1d5b8064e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20M=C3=BCller?= Date: Tue, 29 Nov 2022 11:50:46 +0100 Subject: [PATCH 05/41] Improve specs by removing absolute path references (#12776) --- spec/std/file_spec.cr | 55 +++++++++++++--------------------------- spec/std/process_spec.cr | 26 ++++++++++--------- 2 files changed, 31 insertions(+), 50 deletions(-) diff --git a/spec/std/file_spec.cr b/spec/std/file_spec.cr index e9270a48b483..ea71a9e2497f 100644 --- a/spec/std/file_spec.cr +++ b/spec/std/file_spec.cr @@ -1,24 +1,5 @@ require "./spec_helper" -private def base - Dir.current -end - -private def tmpdir - "/tmp" -end - -private def rootdir - "/" -end - -private def home - home = ENV["HOME"] - return home if home == "/" - - home.chomp('/') -end - private def it_raises_on_null_byte(operation, file = __FILE__, line = __LINE__, end_line = __END_LINE__, &block) it "errors on #{operation}", file, line, end_line do expect_raises(ArgumentError, "String contains null byte") do @@ -354,8 +335,8 @@ describe "File" do it "chown" do # changing owners requires special privileges, so we test that method calls do compile - typeof(File.chown("/tmp/test")) - typeof(File.chown("/tmp/test", uid: 1001, gid: 100, follow_symlinks: true)) + typeof(File.chown(".")) + typeof(File.chown(".", uid: 1001, gid: 100, follow_symlinks: true)) File.open(File::NULL, "w") do |file| typeof(file.chown) @@ -596,30 +577,27 @@ describe "File" do end end - describe "real_path" do + describe "#realpath" do it "expands paths for normal files" do - {% if flag?(:win32) %} - File.real_path("C:\\Windows").should eq(File.real_path("C:\\Windows")) - File.real_path("C:\\Windows\\..").should eq(File.real_path("C:\\")) - {% else %} - File.real_path("/usr/share").should eq(File.real_path("/usr/share")) - File.real_path("/usr/share/..").should eq(File.real_path("/usr")) - {% end %} + path = File.join(File.realpath("."), datapath("dir")) + File.realpath(path).should eq(path) + File.realpath(File.join(path, "..")).should eq(File.dirname(path)) end it "raises if file doesn't exist" do - expect_raises(File::NotFoundError, "Error resolving real path: '/usr/share/foo/bar'") do - File.real_path("/usr/share/foo/bar") + path = datapath("doesnotexist") + expect_raises(File::NotFoundError, "Error resolving real path: '#{path.inspect_unquoted}'") do + File.realpath(path) end end - # TODO: see Crystal::System::File.real_path TODO + # TODO: see Crystal::System::File.realpath TODO pending_win32 "expands paths of symlinks" do file_path = File.expand_path(datapath("test_file.txt")) with_tempfile("symlink.txt") do |symlink_path| File.symlink(file_path, symlink_path) - real_symlink_path = File.real_path(symlink_path) - real_file_path = File.real_path(file_path) + real_symlink_path = File.realpath(symlink_path) + real_file_path = File.realpath(file_path) real_symlink_path.should eq(real_file_path) end end @@ -1318,10 +1296,11 @@ describe "File" do end end - # TODO: there is no file which is reliably nonwriteable on windows - pending_win32 "raises if file cannot be accessed" do - expect_raises(File::Error, "Error setting time on file: '/bin/ls'") do - File.touch("/bin/ls") + it "raises if file cannot be accessed" do + # This path is invalid because it represents a file path as a directory path + path = File.join(datapath("test_file.txt"), "doesnotexist") + expect_raises(File::Error, path.inspect_unquoted) do + File.touch(path) end end end diff --git a/spec/std/process_spec.cr b/spec/std/process_spec.cr index 2d40623f4738..3655dad0a3f7 100644 --- a/spec/std/process_spec.cr +++ b/spec/std/process_spec.cr @@ -166,19 +166,21 @@ describe Process do $?.exit_code.should eq(0) end - pending_win32 "chroot raises when unprivileged" do - status, output, _ = compile_and_run_source <<-'CODE' - begin - Process.chroot("/usr") - puts "FAIL" - rescue ex - puts ex.inspect - end - CODE + {% if flag?(:unix) %} + it "chroot raises when unprivileged" do + status, output, _ = compile_and_run_source <<-'CODE' + begin + Process.chroot(".") + puts "FAIL" + rescue ex + puts ex.inspect + end + CODE - status.success?.should be_true - output.should eq("#\n") - end + status.success?.should be_true + output.should eq("#\n") + end + {% end %} it "sets working directory" do parent = File.dirname(Dir.current) From 1a2d6801807be44f1377255c99b55bc59c0e382e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20M=C3=BCller?= Date: Tue, 29 Nov 2022 17:41:44 +0100 Subject: [PATCH 06/41] Re-organize and enhance specs for `Regex` (#12788) --- spec/std/regex_spec.cr | 452 ++++++++++++++++++++++++++++++----------- 1 file changed, 329 insertions(+), 123 deletions(-) diff --git a/spec/std/regex_spec.cr b/spec/std/regex_spec.cr index 918222241f5a..24ac68b35f9a 100644 --- a/spec/std/regex_spec.cr +++ b/spec/std/regex_spec.cr @@ -1,116 +1,200 @@ require "./spec_helper" describe "Regex" do - it "compare to other instances" do - Regex.new("foo").should eq(Regex.new("foo")) - Regex.new("foo").should_not eq(Regex.new("bar")) + describe ".new" do + it "doesn't crash when PCRE tries to free some memory (#771)" do + expect_raises(ArgumentError) { Regex.new("foo)") } + end + + it "raises exception with invalid regex" do + expect_raises(ArgumentError) { Regex.new("+") } + end end - it "does =~" do - (/foo/ =~ "bar foo baz").should eq(4) - $~.group_size.should eq(0) + it "#options" do + /cat/.options.ignore_case?.should be_false + /cat/i.options.ignore_case?.should be_true + /cat/.options.multiline?.should be_false + /cat/m.options.multiline?.should be_true + /cat/.options.extended?.should be_false + /cat/x.options.extended?.should be_true + /cat/mx.options.multiline?.should be_true + /cat/mx.options.extended?.should be_true + /cat/mx.options.ignore_case?.should be_false + /cat/xi.options.ignore_case?.should be_true + /cat/xi.options.extended?.should be_true + /cat/xi.options.multiline?.should be_false end - it "does inspect" do - /foo/.inspect.should eq("/foo/") - /foo/.inspect.should eq("/foo/") - /foo/imx.inspect.should eq("/foo/imx") + it "#source" do + /foo/.source.should eq "foo" + /(foo|bar)*/.source.should eq "(foo|bar)*" + /foo\x96/.source.should eq "foo\\x96" + Regex.new("").source.should eq "" end - it "does to_s" do - /foo/.to_s.should eq("(?-imsx:foo)") - /foo/im.to_s.should eq("(?ims-x:foo)") - /foo/imx.to_s.should eq("(?imsx-:foo)") + describe "#match" do + it "returns matchdata" do + md = /(?.)(?.)/.match("Crystal").should_not be_nil + md[0].should eq "Cr" + md.captures.should eq [] of String + md.named_captures.should eq({"bar" => "C", "foo" => "r"}) + end - "Crystal".match(/(?C)#{/(?R)/i}/).should be_truthy - "Crystal".match(/(?C)#{/(?R)/}/i).should be_falsey + it "assigns captures" do + matchdata = /foo/.match("foo") + $~.should eq(matchdata) - md = "Crystal".match(/(?.)#{/(?.)/}/).not_nil! - md[0].should eq("Cr") - md["bar"].should eq("C") - md["foo"].should eq("r") - end + /foo/.match("bar") + expect_raises(NilAssertionError) { $~ } + end - it "does inspect with slash" do - %r(/).inspect.should eq("/\\//") - %r(\/).inspect.should eq("/\\//") - end + it "returns nil on non-match" do + /Crystal/.match("foo").should be_nil + end - it "does to_s with slash" do - %r(/).to_s.should eq("(?-imsx:\\/)") - %r(\/).to_s.should eq("(?-imsx:\\/)") - end + describe "with pos" do + it "positive" do + /foo/.match("foo", 0).should_not be_nil + /foo/.match("foo", 1).should be_nil + /foo/.match(".foo", 1).should_not be_nil + /foo/.match("..foo", 1).should_not be_nil + + /foo/.match("bar", 0).should be_nil + /foo/.match("bar", 1).should be_nil + end + + it "char index" do + /foo/.match("öfoo", 1).should_not be_nil + end + + pending "negative" do + /foo/.match("..foo", -3).should_not be_nil + /foo/.match("..foo", -2).should be_nil + end + end - it "doesn't crash when PCRE tries to free some memory (#771)" do - expect_raises(ArgumentError) { Regex.new("foo)") } + it "with options" do + /foo/.match(".foo", options: Regex::Options::ANCHORED).should be_nil + end end - it "checks if Char need to be escaped" do - Regex.needs_escape?('*').should be_true - Regex.needs_escape?('|').should be_true - Regex.needs_escape?('@').should be_false - end + describe "#match_at_byte_index" do + it "assigns captures" do + matchdata = /foo/.match_at_byte_index("..foo", 1) + $~.should eq(matchdata) - it "checks if String need to be escaped" do - Regex.needs_escape?("10$").should be_true - Regex.needs_escape?("foo").should be_false - end + /foo/.match_at_byte_index("foo", 1) + expect_raises(NilAssertionError) { $~ } - it "escapes" do - Regex.escape(" .\\+*?[^]$(){}=!<>|:-hello").should eq("\\ \\.\\\\\\+\\*\\?\\[\\^\\]\\$\\(\\)\\{\\}\\=\\!\\<\\>\\|\\:\\-hello") - end + /foo/.match("foo") # make sure $~ is assigned + $~.should_not be_nil - it "matches ignore case" do - ("HeLlO" =~ /hello/).should be_nil - ("HeLlO" =~ /hello/i).should eq(0) - end + /foo/.match_at_byte_index("foo", 5) + expect_raises(NilAssertionError) { $~ } + end - it "matches lines beginnings on ^ in multiline mode" do - ("foo\nbar" =~ /^bar/).should be_nil - ("foo\nbar" =~ /^bar/m).should eq(4) - end + it "positive index" do + md = /foo/.match_at_byte_index("foo", 0).should_not be_nil + md.begin.should eq 0 + /foo/.match_at_byte_index("foo", 1).should be_nil + md = /foo/.match_at_byte_index(".foo", 1).should_not be_nil + md.begin.should eq 1 + md = /foo/.match_at_byte_index("..foo", 1).should_not be_nil + md.begin.should eq 2 + /foo/.match_at_byte_index("foo", 5).should be_nil + + /foo/.match_at_byte_index("bar", 0).should be_nil + /foo/.match_at_byte_index("bar", 1).should be_nil + end - it "matches multiline" do - ("foo\nbaz" =~ //).should be_nil - ("foo\nbaz" =~ //m).should eq(4) - end + it "multibyte index" do + md = /foo/.match_at_byte_index("öfoo", 1).should_not be_nil + md.begin.should eq 1 + md.byte_begin.should eq 2 - it "matches unicode char against [[:print:]] (#11262)" do - ("\n☃" =~ /[[:print:]]/).should eq(1) - end + md = /foo/.match_at_byte_index("öfoo", 2).should_not be_nil + md.begin.should eq 1 + md.byte_begin.should eq 2 + end - it "matches unicode char against [[:alnum:]] (#4704)" do - /[[:alnum:]]/x.match("à").should_not be_nil - end + pending "negative" do + md = /foo/.match_at_byte_index("..foo", -3).should_not be_nil + md.begin.should eq 0 + /foo/.match_at_byte_index("..foo", -2).should be_nil + end - it "matches with =~ and captures" do - ("fooba" =~ /f(o+)(bar?)/).should eq(0) - $~.group_size.should eq(2) - $1.should eq("oo") - $2.should eq("ba") + it "with options" do + /foo/.match_at_byte_index("..foo", 1, options: Regex::Options::ANCHORED).should be_nil + end end - it "matches with =~ and gets utf-8 codepoint index" do - index = "こんに" =~ /ん/ - index.should eq(1) - end + describe "#matches?" do + it "basic" do + /foo/.matches?("foo").should be_true + expect_raises(NilAssertionError) { $~ } + /foo/.matches?("bar").should be_false + expect_raises(NilAssertionError) { $~ } + end - it "matches with === and captures" do - "foo" =~ /foo/ - (/f(o+)(bar?)/ === "fooba").should be_true - $~.group_size.should eq(2) - $1.should eq("oo") - $2.should eq("ba") - end + describe "options" do + it "ignore case" do + /hello/.matches?("HeLlO").should be_false + /hello/i.matches?("HeLlO").should be_true + end + + describe "multiline" do + it "anchor" do + /^bar/.matches?("foo\nbar").should be_false + /^bar/m.matches?("foo\nbar").should be_true + end + + it "span" do + //.matches?("foo\nbaz").should be_false + //m.matches?("foo\nbaz").should be_true + end + end + + describe "extended" do + it "ignores white space" do + /foo bar/.matches?("foobar").should be_false + /foo bar/x.matches?("foobar").should be_true + end + + it "ignores comments" do + /foo#comment\nbar/.matches?("foobar").should be_false + /foo#comment\nbar/x.matches?("foobar").should be_true + end + end + + it "anchored" do + Regex.new("foo", Regex::Options::ANCHORED).matches?("foo").should be_true + Regex.new("foo", Regex::Options::ANCHORED).matches?(".foo").should be_false + end + end - describe "#matches?" do - it "matches but create no MatchData" do - /f(o+)(bar?)/.matches?("fooba").should be_true - /f(o+)(bar?)/.matches?("barfo").should be_false + describe "unicode" do + it "unicode support" do + /ん/.matches?("こんに").should be_true + end + + it "matches unicode char against [[:alnum:]] (#4704)" do + /[[:alnum:]]/.matches?("à").should be_true + end + + it "matches unicode char against [[:print:]] (#11262)" do + /[[:print:]]/.matches?("\n☃").should be_true + end + + it "invalid codepoint" do + /foo/.matches?("f\x96o").should be_false + /f\x96o/.matches?("f\x96o").should be_false + /f.o/.matches?("f\x96o").should be_true + end end - it "can specify initial position of matching" do - /f(o+)(bar?)/.matches?("fooba", 1).should be_false + it "with options" do + /foo/.matches?(".foo", options: Regex::Options::ANCHORED).should be_false end it "matches a large single line string" do @@ -122,33 +206,172 @@ describe "Regex" do end end - describe "name_table" do + describe "#matches_at_byte_index?" do + it "positive index" do + /foo/.matches_at_byte_index?("foo", 0).should be_true + /foo/.matches_at_byte_index?("foo", 1).should be_false + /foo/.matches_at_byte_index?(".foo", 1).should be_true + /foo/.matches_at_byte_index?("..foo", 1).should be_true + /foo/.matches_at_byte_index?("foo", 5).should be_false + + /foo/.matches_at_byte_index?("bar", 0).should be_false + /foo/.matches_at_byte_index?("bar", 1).should be_false + end + + it "multibyte index" do + /foo/.matches_at_byte_index?("öfoo", 1).should be_true + end + + pending "negative" do + /foo/.matches_at_byte_index?("..foo", -3).should be_true + /foo/.matches_at_byte_index?("..foo", -2).should be_false + end + + it "with options" do + /foo/.matches_at_byte_index?("..foo", 1, options: Regex::Options::ANCHORED).should be_false + end + end + + describe "#===" do + it "basic" do + (/f(o+)(bar?)/ === "fooba").should be_true + (/f(o+)(bar?)/ === "pooba").should be_false + end + + it "assigns captures" do + /f(o+)(bar?)/ === "fooba" + $~.group_size.should eq(2) + $1.should eq("oo") + $2.should eq("ba") + + /f(o+)(bar?)/ === "pooba" + expect_raises(NilAssertionError) { $~ } + end + end + + describe "#=~" do + it "returns match index or nil" do + (/foo/ =~ "bar foo baz").should eq(4) + (/foo/ =~ "bar boo baz").should be_nil + end + + it "assigns captures" do + "fooba" =~ /f(o+)(bar?)/ + $~.group_size.should eq(2) + $1.should eq("oo") + $2.should eq("ba") + + /foo/ =~ "bar boo baz" + expect_raises(NilAssertionError) { $~ } + end + + it "accepts any type" do + (/foo/ =~ nil).should be_nil + (/foo/ =~ 1).should be_nil + (/foo/ =~ [1, 2]).should be_nil + (/foo/ =~ true).should be_nil + end + end + + describe "#name_table" do it "is a map of capture group number to name" do - table = (/(? (?(\d\d)?\d\d) - (?\d\d) - (?\d\d) )/x).name_table - table[1].should eq("date") - table[2].should eq("year") - table[3]?.should be_nil - table[4].should eq("month") - table[5].should eq("day") + (/(? (?(\d\d)?\d\d) - (?\d\d) - (?\d\d) )/x).name_table.should eq({ + 1 => "date", + 2 => "year", + 4 => "month", + 5 => "day", + }) end + + it "alpanumeric" do + /(?)/.name_table.should eq({1 => "f1"}) + end + + it "duplicate name" do + /(?)(?)/.name_table.should eq({1 => "foo", 2 => "foo"}) + end + end + + it "#capture_count" do + /(?:.)/x.capture_count.should eq(0) + /(?.+)/.capture_count.should eq(1) + /(.)?/x.capture_count.should eq(1) + /(.)|(.)/x.capture_count.should eq(2) end - describe "capture_count" do - it "returns the number of (named & non-named) capture groups" do - /(?:.)/x.capture_count.should eq(0) - /(?.+)/.capture_count.should eq(1) - /(.)?/x.capture_count.should eq(1) - /(.)|(.)/x.capture_count.should eq(2) + describe "#inspect" do + it "with options" do + /foo/.inspect.should eq("/foo/") + /foo/im.inspect.should eq("/foo/im") + /foo/imx.inspect.should eq("/foo/imx") + end + + it "escapes" do + %r(/).inspect.should eq("/\\//") + %r(\/).inspect.should eq("/\\//") end end - it "raises exception with invalid regex" do - expect_raises(ArgumentError) { Regex.new("+") } + describe "#to_s" do + it "with options" do + /foo/.to_s.should eq("(?-imsx:foo)") + /foo/im.to_s.should eq("(?ims-x:foo)") + /foo/imx.to_s.should eq("(?imsx-:foo)") + end + + it "with slash" do + %r(/).to_s.should eq("(?-imsx:\\/)") + %r(\/).to_s.should eq("(?-imsx:\\/)") + end + + it "interpolation" do + regex = /(?R)/i + /(?C)#{regex}/.should eq /(?C)(?i-msx:(?R))/ + /(?C)#{regex}/i.should eq /(?C)(?i-msx:(?R))/i + end + end + + it "#==" do + regex = Regex.new("foo", Regex::Options::IGNORE_CASE) + (regex == Regex.new("foo", Regex::Options::IGNORE_CASE)).should be_true + (regex == Regex.new("foo")).should be_false + (regex == Regex.new("bar", Regex::Options::IGNORE_CASE)).should be_false + (regex == Regex.new("bar")).should be_false + end + + it "#hash" do + hash = Regex.new("foo", Regex::Options::IGNORE_CASE).hash + hash.should eq(Regex.new("foo", Regex::Options::IGNORE_CASE).hash) + hash.should_not eq(Regex.new("foo").hash) + hash.should_not eq(Regex.new("bar", Regex::Options::IGNORE_CASE).hash) + hash.should_not eq(Regex.new("bar").hash) + end + + it "#dup" do + regex = /foo/ + regex.dup.should be(regex) + end + + it "#clone" do + regex = /foo/ + regex.clone.should be(regex) end - it "raises if outside match range with []" do - "foo" =~ /foo/ - expect_raises(IndexError) { $1 } + describe ".needs_escape?" do + it "Char" do + Regex.needs_escape?('*').should be_true + Regex.needs_escape?('|').should be_true + Regex.needs_escape?('@').should be_false + end + + it "String" do + Regex.needs_escape?("10$").should be_true + Regex.needs_escape?("foo").should be_false + end + end + + it ".escape" do + Regex.escape(" .\\+*?[^]$(){}=!<>|:-hello").should eq("\\ \\.\\\\\\+\\*\\?\\[\\^\\]\\$\\(\\)\\{\\}\\=\\!\\<\\>\\|\\:\\-hello") end describe ".union" do @@ -189,29 +412,12 @@ describe "Regex" do end end - it "dups" do - regex = /foo/ - regex.dup.should be(regex) + it "#+" do + (/dogs/ + /cats/i).should eq /(?-imsx:dogs)|(?i-msx:cats)/ end - it "clones" do - regex = /foo/ - regex.clone.should be(regex) - end - - it "checks equality by ==" do - regex = Regex.new("foo", Regex::Options::IGNORE_CASE) - (regex == Regex.new("foo", Regex::Options::IGNORE_CASE)).should be_true - (regex == Regex.new("foo")).should be_false - (regex == Regex.new("bar", Regex::Options::IGNORE_CASE)).should be_false - (regex == Regex.new("bar")).should be_false - end - - it "hashes" do - hash = Regex.new("foo", Regex::Options::IGNORE_CASE).hash - hash.should eq(Regex.new("foo", Regex::Options::IGNORE_CASE).hash) - hash.should_not eq(Regex.new("foo").hash) - hash.should_not eq(Regex.new("bar", Regex::Options::IGNORE_CASE).hash) - hash.should_not eq(Regex.new("bar").hash) + it ".error?" do + Regex.error?("(foo|bar)").should be_nil + Regex.error?("(foo|bar").should eq "missing ) at 8" end end From 5cc9efffff4a74b9d4d1a636a6d0196ce7264506 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20M=C3=BCller?= Date: Tue, 29 Nov 2022 17:42:19 +0100 Subject: [PATCH 07/41] Re-organize and enhance specs for `Regex::MatchData` (#12789) --- spec/interpreter_std_spec.cr | 2 +- spec/std/match_data_spec.cr | 303 ---------------------- spec/std/regex/match_data_spec.cr | 402 ++++++++++++++++++++++++++++++ spec/wasm32_std_spec.cr | 2 +- 4 files changed, 404 insertions(+), 305 deletions(-) delete mode 100644 spec/std/match_data_spec.cr create mode 100644 spec/std/regex/match_data_spec.cr diff --git a/spec/interpreter_std_spec.cr b/spec/interpreter_std_spec.cr index 44c7a0ae1d78..cb47c8dc1027 100644 --- a/spec/interpreter_std_spec.cr +++ b/spec/interpreter_std_spec.cr @@ -146,7 +146,7 @@ require "./std/log/log_spec.cr" require "./std/log/main_spec.cr" require "./std/log/metadata_spec.cr" require "./std/log/spec_spec.cr" -require "./std/match_data_spec.cr" +require "./std/regex/match_data_spec.cr" require "./std/math_spec.cr" require "./std/mime/media_type_spec.cr" require "./std/mime/multipart/builder_spec.cr" diff --git a/spec/std/match_data_spec.cr b/spec/std/match_data_spec.cr deleted file mode 100644 index 367eb76dee89..000000000000 --- a/spec/std/match_data_spec.cr +++ /dev/null @@ -1,303 +0,0 @@ -require "spec" - -describe "Regex::MatchData" do - it "does inspect" do - /f(o)(x)/.match("the fox").inspect.should eq(%(Regex::MatchData("fox" 1:"o" 2:"x"))) - /f(o)(x)?/.match("the fort").inspect.should eq(%(Regex::MatchData("fo" 1:"o" 2:nil))) - /fox/.match("the fox").inspect.should eq(%(Regex::MatchData("fox"))) - end - - it "does to_s" do - /f(o)(x)/.match("the fox").to_s.should eq(%(Regex::MatchData("fox" 1:"o" 2:"x"))) - /f(?o)(?x)/.match("the fox").to_s.should eq(%(Regex::MatchData("fox" lettero:"o" letterx:"x"))) - /fox/.match("the fox").to_s.should eq(%(Regex::MatchData("fox"))) - end - - it "does pretty_print" do - /f(o)(x)?/.match("the fo").pretty_inspect.should eq(%(Regex::MatchData("fo" 1:"o" 2:nil))) - - expected = <<-REGEX - Regex::MatchData("foooo" - first:"f" - second:"oooo" - third:"ooo" - fourth:"oo" - fifth:"o") - REGEX - - /(?f)(?o(?o(?o(?o))))/.match("fooooo").pretty_inspect.should eq(expected) - end - - it "does size" do - "Crystal".match(/[p-s]/).not_nil!.size.should eq(1) - "Crystal".match(/r(ys)/).not_nil!.size.should eq(2) - "Crystal".match(/r(ys)(?ta)/).not_nil!.size.should eq(3) - end - - describe "#[]" do - it "captures empty group" do - ("foo" =~ /(?z?)foo/).should eq(0) - $~[1].should eq("") - $~["g1"].should eq("") - end - - it "capture named group" do - ("fooba" =~ /f(?o+)(?bar?)/).should eq(0) - $~["g1"].should eq("oo") - $~["g2"].should eq("ba") - end - - it "captures duplicated named group" do - re = /(?:(?foo)|(?bar))*/ - - ("foo" =~ re).should eq(0) - $~["g1"].should eq("foo") - - ("bar" =~ re).should eq(0) - $~["g1"].should eq("bar") - - ("foobar" =~ re).should eq(0) - $~["g1"].should eq("bar") - - ("barfoo" =~ re).should eq(0) - $~["g1"].should eq("foo") - end - - it "can use negative index" do - "foo" =~ /(f)(oo)/ - $~[-1].should eq("oo") - $~[-2].should eq("f") - $~[-3].should eq("foo") - expect_raises(IndexError, "Invalid capture group index: -4") { $~[-4] } - end - - it "raises exception when named group doesn't exist" do - ("foo" =~ /foo/).should eq(0) - expect_raises(KeyError, "Capture group 'group' does not exist") { $~["group"] } - end - - it "raises exception on optional empty group" do - ("foo" =~ /(?z)?foo/).should eq(0) - expect_raises(IndexError, "Capture group 1 was not matched") { $~[1] } - expect_raises(KeyError, "Capture group 'g1' was not matched") { $~["g1"] } - end - - it "raises if outside match range with []" do - "foo" =~ /foo/ - expect_raises(IndexError, "Invalid capture group index: 1") { $~[1] } - end - - it "raises if special variable accessed on invalid capture group" do - "spice" =~ /spice(s)?/ - expect_raises(IndexError, "Capture group 1 was not matched") { $1 } - expect_raises(IndexError, "Invalid capture group index: 3") { $3 } - end - - it "can use range" do - "ab" =~ /(a)(b)/ - $~[1..2].should eq(["a", "b"]) - $~[1..].should eq(["a", "b"]) - $~[..].should eq(["ab", "a", "b"]) - expect_raises(IndexError) { $~[4..] } - end - - it "can use start and count" do - "ab" =~ /(a)(b)/ - $~[1, 2].should eq(["a", "b"]) - expect_raises(IndexError) { $~[4, 1] } - end - end - - describe "#[]?" do - it "capture empty group" do - ("foo" =~ /(?z?)foo/).should eq(0) - $~[1]?.should eq("") - $~["g1"]?.should eq("") - end - - it "capture optional empty group" do - ("foo" =~ /(?z)?foo/).should eq(0) - $~[1]?.should be_nil - $~["g1"]?.should be_nil - end - - it "capture named group" do - ("fooba" =~ /f(?o+)(?bar?)/).should eq(0) - $~["g1"]?.should eq("oo") - $~["g2"]?.should eq("ba") - end - - it "captures duplicated named group" do - re = /(?:(?foo)|(?bar))*/ - - ("foo" =~ re).should eq(0) - $~["g1"]?.should eq("foo") - - ("bar" =~ re).should eq(0) - $~["g1"]?.should eq("bar") - - ("foobar" =~ re).should eq(0) - $~["g1"]?.should eq("bar") - - ("barfoo" =~ re).should eq(0) - $~["g1"]?.should eq("foo") - end - - it "can use negative index" do - "foo" =~ /(b)?(f)(oo)/ - $~[-1]?.should eq("oo") - $~[-2]?.should eq("f") - $~[-3]?.should be_nil - $~[-4]?.should eq("foo") - end - - it "returns nil exception when named group doesn't exist" do - ("foo" =~ /foo/).should eq(0) - $~["group"]?.should be_nil - end - - it "returns nil if outside match range with []" do - "foo" =~ /foo/ - $~[1]?.should be_nil - end - - it "can use range" do - "ab" =~ /(a)(b)/ - $~[1..2]?.should eq(["a", "b"]) - $~[1..]?.should eq(["a", "b"]) - $~[..]?.should eq(["ab", "a", "b"]) - $~[4..]?.should be_nil - end - - it "can use start and count" do - "ab" =~ /(a)(b)/ - $~[1, 2]?.should eq(["a", "b"]) - $~[4, 1]?.should be_nil - end - end - - describe "#post_match" do - it "returns an empty string when there's nothing after" do - "Crystal".match(/ystal/).not_nil!.post_match.should eq "" - end - - it "returns the part of the string after the match" do - "Crystal".match(/yst/).not_nil!.post_match.should eq "al" - end - - it "works with unicode" do - "há日本語".match(/本/).not_nil!.post_match.should eq "語" - end - end - - describe "#pre_match" do - it "returns an empty string when there's nothing before" do - "Crystal".match(/Cryst/).not_nil!.pre_match.should eq "" - end - - it "returns the part of the string before the match" do - "Crystal".match(/yst/).not_nil!.pre_match.should eq "Cr" - end - - it "works with unicode" do - "há日本語".match(/本/).not_nil!.pre_match.should eq "há日" - end - end - - describe "#captures" do - it "gets an array of unnamed captures" do - "Crystal".match(/(Cr)y/).not_nil!.captures.should eq(["Cr"]) - "Crystal".match(/(Cr)(?y)(st)(?al)/).not_nil!.captures.should eq(["Cr", "st"]) - end - - it "gets an array of unnamed captures with optional" do - "Crystal".match(/(Cr)(s)?/).not_nil!.captures.should eq(["Cr", nil]) - "Crystal".match(/(Cr)(?s)?(tal)?/).not_nil!.captures.should eq(["Cr", nil]) - end - end - - describe "#named_captures" do - it "gets a hash of named captures" do - "Crystal".match(/(?Cr)y/).not_nil!.named_captures.should eq({"name1" => "Cr"}) - "Crystal".match(/(Cr)(?y)(st)(?al)/).not_nil!.named_captures.should eq({"name1" => "y", "name2" => "al"}) - end - - it "gets a hash of named captures with optional" do - "Crystal".match(/(?Cr)(?s)?/).not_nil!.named_captures.should eq({"name1" => "Cr", "name2" => nil}) - "Crystal".match(/(Cr)(?s)?(t)?(?al)?/).not_nil!.named_captures.should eq({"name1" => nil, "name2" => nil}) - end - - it "gets a hash of named captures with duplicated name" do - "Crystal".match(/(?Cr)y(?s)/).not_nil!.named_captures.should eq({"name" => "s"}) - end - end - - describe "#to_a" do - it "converts into an array" do - "Crystal".match(/(?Cr)(y)/).not_nil!.to_a.should eq(["Cry", "Cr", "y"]) - "Crystal".match(/(Cr)(?y)(st)(?al)/).not_nil!.to_a.should eq(["Crystal", "Cr", "y", "st", "al"]) - end - - it "converts into an array having nil" do - "Crystal".match(/(?Cr)(s)?/).not_nil!.to_a.should eq(["Cr", "Cr", nil]) - "Crystal".match(/(Cr)(?s)?(yst)?(?al)?/).not_nil!.to_a.should eq(["Crystal", "Cr", nil, "yst", "al"]) - end - end - - describe "#to_h" do - it "converts into a hash" do - "Crystal".match(/(?Cr)(y)/).not_nil!.to_h.should eq({ - 0 => "Cry", - "name1" => "Cr", - 2 => "y", - }) - "Crystal".match(/(Cr)(?y)(st)(?al)/).not_nil!.to_h.should eq({ - 0 => "Crystal", - 1 => "Cr", - "name1" => "y", - 3 => "st", - "name2" => "al", - }) - end - - it "converts into a hash having nil" do - "Crystal".match(/(?Cr)(s)?/).not_nil!.to_h.should eq({ - 0 => "Cr", - "name1" => "Cr", - 2 => nil, - }) - "Crystal".match(/(Cr)(?s)?(yst)?(?al)?/).not_nil!.to_h.should eq({ - 0 => "Crystal", - 1 => "Cr", - "name1" => nil, - 3 => "yst", - "name2" => "al", - }) - end - - it "converts into a hash with duplicated names" do - "Crystal".match(/(Cr)(?s)?(yst)?(?al)?/).not_nil!.to_h.should eq({ - 0 => "Crystal", - 1 => "Cr", - "name" => "al", - 3 => "yst", - }) - end - end - - it "can check equality" do - re = /((?he)llo)/ - m1 = re.match("hello") - m2 = re.match("hello") - m1.should be_truthy - m2.should be_truthy - m1.should eq(m2) - end - - it "hashes" do - re = /(a|b)/ - hash = re.match("a").hash - hash.should eq(re.match("a").hash) - hash.should_not eq(re.match("b").hash) - end -end diff --git a/spec/std/regex/match_data_spec.cr b/spec/std/regex/match_data_spec.cr new file mode 100644 index 000000000000..1079dff145e1 --- /dev/null +++ b/spec/std/regex/match_data_spec.cr @@ -0,0 +1,402 @@ +require "spec" + +private def matchdata(re, string) + re.match(string).should_not be_nil +end + +describe "Regex::MatchData" do + it "#regex" do + regex = /foo/ + matchdata(regex, "foo").regex.should be(regex) + end + + it "#string" do + string = "foo" + matchdata(/foo/, string).string.should be(string) + end + + it "#inspect" do + matchdata(/f(o)(x)/, "the fox").inspect.should eq(%(Regex::MatchData("fox" 1:"o" 2:"x"))) + matchdata(/f(o)(x)?/, "the fort").inspect.should eq(%(Regex::MatchData("fo" 1:"o" 2:nil))) + matchdata(/fox/, "the fox").inspect.should eq(%(Regex::MatchData("fox"))) + end + + it "#to_s" do + matchdata(/f(o)(x)/, "the fox").to_s.should eq(%(Regex::MatchData("fox" 1:"o" 2:"x"))) + matchdata(/f(?o)(?x)/, "the fox").to_s.should eq(%(Regex::MatchData("fox" lettero:"o" letterx:"x"))) + matchdata(/fox/, "the fox").to_s.should eq(%(Regex::MatchData("fox"))) + end + + it "#pretty_print" do + matchdata(/f(o)(x)?/, "the fo").pretty_inspect.should eq(%(Regex::MatchData("fo" 1:"o" 2:nil))) + + expected = <<-REGEX + Regex::MatchData("foooo" + first:"f" + second:"oooo" + third:"ooo" + fourth:"oo" + fifth:"o") + REGEX + + matchdata(/(?f)(?o(?o(?o(?o))))/, "fooooo").pretty_inspect.should eq(expected) + end + + it "#size" do + matchdata(/[p-s]/, "Crystal").size.should eq(1) + matchdata(/r(ys)/, "Crystal").size.should eq(2) + matchdata(/r(ys)(?ta)/, "Crystal").size.should eq(3) + matchdata(/foo(bar)?/, "foo").size.should eq(2) + matchdata(/foo(bar)?/, "foobar").size.should eq(2) + end + + describe "#begin" do + it "no captures" do + matchdata(/foo/, "foo").begin.should eq 0 + matchdata(/foo/, "foo").begin(-1).should eq 0 + matchdata(/foo/, ".foo.").begin.should eq 1 + matchdata(/foo/, ".foo.").begin(-1).should eq 1 + end + + it "out of range" do + expect_raises(IndexError) do + matchdata(/foo/, "foo").begin(1) + end + end + + it "with capture" do + matchdata(/f(o)o/, "foo").begin.should eq 0 + matchdata(/f(o)o/, "foo").begin(1).should eq 1 + matchdata(/f(o)o/, "foo").begin(-1).should eq 1 + matchdata(/f(o)o/, ".foo.").begin.should eq 1 + matchdata(/f(o)o/, ".foo.").begin(1).should eq 2 + matchdata(/f(o)o/, ".foo.").begin(-1).should eq 2 + end + + it "char index" do + matchdata(/foo/, "öfoo").begin.should eq 1 + end + end + + describe "#byte_begin" do + it "char index" do + matchdata(/foo/, "öfoo").byte_begin.should eq 2 + end + end + + describe "#end" do + it "no captures" do + matchdata(/foo/, "foo").end.should eq 3 + matchdata(/foo/, "foo").end(-1).should eq 3 + matchdata(/foo/, ".foo.").end.should eq 4 + matchdata(/foo/, ".foo.").end(-1).should eq 4 + end + + it "out of range" do + expect_raises(IndexError) do + matchdata(/foo/, "foo").end(1) + end + end + + it "with capture" do + matchdata(/f(o)o/, "foo").end.should eq 3 + matchdata(/f(o)o/, "foo").end(1).should eq 2 + matchdata(/f(o)o/, "foo").end(-1).should eq 2 + matchdata(/f(o)o/, ".foo.").end.should eq 4 + matchdata(/f(o)o/, ".foo.").end(1).should eq 3 + matchdata(/f(o)o/, ".foo.").end(-1).should eq 3 + end + + it "char index" do + matchdata(/foo/, "öfoo").end.should eq 4 + end + end + + describe "#byte_end" do + it "char index" do + matchdata(/foo/, "öfoo").byte_end.should eq 5 + end + end + + describe "#[]" do + describe "String" do + it "capture named group" do + md = matchdata(/f(?o+)(?bar?)/, "fooba") + md["g1"].should eq("oo") + md["g2"].should eq("ba") + end + + it "captures duplicated named group" do + re = /(?:(?foo)|(?bar))*/ + + matchdata(re, "foo")["g1"].should eq("foo") + matchdata(re, "bar")["g1"].should eq("bar") + matchdata(re, "foobar")["g1"].should eq("bar") + matchdata(re, "barfoo")["g1"].should eq("foo") + end + + it "raises exception when named group doesn't exist" do + md = matchdata(/foo/, "foo") + expect_raises(KeyError, "Capture group 'group' does not exist") { md["group"] } + end + + it "captures empty group" do + matchdata(/(?z?)foo/, "foo")["g1"].should eq("") + end + + it "raises exception on optional empty group" do + md = matchdata(/(?z)?foo/, "foo") + expect_raises(KeyError, "Capture group 'g1' was not matched") { md["g1"] } + end + end + + describe "Int" do + it "can use negative index" do + md = matchdata(/(f)(oo)/, "foo") + md[-1].should eq("oo") + md[-2].should eq("f") + md[-3].should eq("foo") + expect_raises(IndexError, "Invalid capture group index: -4") { md[-4] } + end + + it "raises if outside match range with []" do + md = matchdata(/foo/, "foo") + expect_raises(IndexError, "Invalid capture group index: 1") { md[1] } + end + + it "raises if special variable accessed on invalid capture group" do + md = matchdata(/spice(s)?/, "spice") + expect_raises(IndexError, "Capture group 1 was not matched") { md[1] } + expect_raises(IndexError, "Invalid capture group index: 3") { md[3] } + end + + it "captures empty group" do + matchdata(/(?z?)foo/, "foo")[1].should eq("") + end + + it "raises exception on optional empty group" do + md = matchdata(/(?z)?foo/, "foo") + expect_raises(IndexError, "Capture group 1 was not matched") { md[1] } + end + end + + describe "Range" do + it "can use range" do + md = matchdata(/(a)(b)/, "ab") + md[1..2].should eq(["a", "b"]) + md[1..].should eq(["a", "b"]) + md[..].should eq(["ab", "a", "b"]) + expect_raises(IndexError) { md[4..] } + end + + it "can use start and count" do + md = matchdata(/(a)(b)/, "ab") + md[1, 2].should eq(["a", "b"]) + expect_raises(IndexError) { md[4, 1] } + end + end + end + + describe "#[]?" do + describe "String" do + it "capture named group" do + md = matchdata(/f(?o+)(?bar?)/, "fooba") + md["g1"]?.should eq("oo") + md["g2"]?.should eq("ba") + end + + it "captures duplicated named group" do + re = /(?:(?foo)|(?bar))*/ + + md = matchdata(re, "foo") + md["g1"]?.should eq("foo") + + md = matchdata(re, "bar") + md["g1"]?.should eq("bar") + + md = matchdata(re, "foobar") + md["g1"]?.should eq("bar") + + md = matchdata(re, "barfoo") + md["g1"]?.should eq("foo") + end + + it "returns nil exception when named group doesn't exist" do + md = matchdata(/foo/, "foo") + md["group"]?.should be_nil + end + + it "capture empty group" do + matchdata(/(?z?)foo/, "foo")["g1"]?.should eq("") + end + + it "capture optional empty group" do + matchdata(/(?z)?foo/, "foo")["g1"]?.should be_nil + end + end + + describe "Int" do + it "can use negative index" do + md = matchdata(/(b)?(f)(oo)/, "foo") + md[-1]?.should eq("oo") + md[-2]?.should eq("f") + md[-3]?.should be_nil + md[-4]?.should eq("foo") + end + + it "returns nil if outside match range with []" do + md = matchdata(/foo/, "foo") + md[1]?.should be_nil + end + + it "capture empty group" do + matchdata(/(?z?)foo/, "foo")[1]?.should eq("") + end + + it "capture optional empty group" do + matchdata(/(?z)?foo/, "foo")[1]?.should be_nil + end + end + + describe "Range" do + it "can use range" do + md = matchdata(/(a)(b)/, "ab") + md[1..2]?.should eq(["a", "b"]) + md[1..]?.should eq(["a", "b"]) + md[..]?.should eq(["ab", "a", "b"]) + md[4..]?.should be_nil + end + + it "can use start and count" do + md = matchdata(/(a)(b)/, "ab") + md[1, 2]?.should eq(["a", "b"]) + md[4, 1]?.should be_nil + end + end + end + + describe "#post_match" do + it "returns an empty string when there's nothing after" do + matchdata(/ystal/, "Crystal").post_match.should eq "" + end + + it "returns the part of the string after the match" do + matchdata(/yst/, "Crystal").post_match.should eq "al" + end + + it "works with unicode" do + matchdata(/本/, "há日本語").post_match.should eq "語" + end + end + + describe "#pre_match" do + it "returns an empty string when there's nothing before" do + matchdata(/Cryst/, "Crystal").pre_match.should eq "" + end + + it "returns the part of the string before the match" do + matchdata(/yst/, "Crystal").pre_match.should eq "Cr" + end + + it "works with unicode" do + matchdata(/本/, "há日本語").pre_match.should eq "há日" + end + end + + describe "#captures" do + it "gets an array of unnamed captures" do + matchdata(/(Cr)y/, "Crystal").captures.should eq(["Cr"]) + matchdata(/(Cr)(?y)(st)(?al)/, "Crystal").captures.should eq(["Cr", "st"]) + end + + it "gets an array of unnamed captures with optional" do + matchdata(/(Cr)(s)?/, "Crystal").captures.should eq(["Cr", nil]) + matchdata(/(Cr)(?s)?(tal)?/, "Crystal").captures.should eq(["Cr", nil]) + end + end + + describe "#named_captures" do + it "gets a hash of named captures" do + matchdata(/(?Cr)y/, "Crystal").named_captures.should eq({"name1" => "Cr"}) + matchdata(/(Cr)(?y)(st)(?al)/, "Crystal").named_captures.should eq({"name1" => "y", "name2" => "al"}) + end + + it "gets a hash of named captures with optional" do + matchdata(/(?Cr)(?s)?/, "Crystal").named_captures.should eq({"name1" => "Cr", "name2" => nil}) + matchdata(/(Cr)(?s)?(t)?(?al)?/, "Crystal").named_captures.should eq({"name1" => nil, "name2" => nil}) + end + + it "gets a hash of named captures with duplicated name" do + matchdata(/(?Cr)y(?s)/, "Crystal").named_captures.should eq({"name" => "s"}) + end + end + + describe "#to_a" do + it "converts into an array" do + matchdata(/(?Cr)(y)/, "Crystal").to_a.should eq(["Cry", "Cr", "y"]) + matchdata(/(Cr)(?y)(st)(?al)/, "Crystal").to_a.should eq(["Crystal", "Cr", "y", "st", "al"]) + end + + it "converts into an array having nil" do + matchdata(/(?Cr)(s)?/, "Crystal").to_a.should eq(["Cr", "Cr", nil]) + matchdata(/(Cr)(?s)?(yst)?(?al)?/, "Crystal").to_a.should eq(["Crystal", "Cr", nil, "yst", "al"]) + end + end + + describe "#to_h" do + it "converts into a hash" do + matchdata(/(?Cr)(y)/, "Crystal").to_h.should eq({ + 0 => "Cry", + "name1" => "Cr", + 2 => "y", + }) + matchdata(/(Cr)(?y)(st)(?al)/, "Crystal").to_h.should eq({ + 0 => "Crystal", + 1 => "Cr", + "name1" => "y", + 3 => "st", + "name2" => "al", + }) + end + + it "converts into a hash having nil" do + matchdata(/(?Cr)(s)?/, "Crystal").to_h.should eq({ + 0 => "Cr", + "name1" => "Cr", + 2 => nil, + }) + matchdata(/(Cr)(?s)?(yst)?(?al)?/, "Crystal").to_h.should eq({ + 0 => "Crystal", + 1 => "Cr", + "name1" => nil, + 3 => "yst", + "name2" => "al", + }) + end + + it "converts into a hash with duplicated names" do + matchdata(/(Cr)(?s)?(yst)?(?al)?/, "Crystal").to_h.should eq({ + 0 => "Crystal", + 1 => "Cr", + "name" => "al", + 3 => "yst", + }) + end + end + + it "#==" do + re = /((?he)llo)/ + m1 = re.match("hello") + m2 = re.match("hello") + m1.should be_truthy + m2.should be_truthy + m1.should eq(m2) + end + + it "#hash" do + re = /(a|b)/ + hash = re.match("a").hash + hash.should eq(re.match("a").hash) + hash.should_not eq(re.match("b").hash) + end +end diff --git a/spec/wasm32_std_spec.cr b/spec/wasm32_std_spec.cr index 4e8e9cf7a0fd..cd0e0c1507f1 100644 --- a/spec/wasm32_std_spec.cr +++ b/spec/wasm32_std_spec.cr @@ -150,7 +150,7 @@ require "./std/log/env_config_spec.cr" require "./std/log/main_spec.cr" # require "./std/log/metadata_spec.cr" (failed to run) # require "./std/log/spec_spec.cr" (failed to run) -require "./std/match_data_spec.cr" +require "./std/regex/match_data_spec.cr" # require "./std/math_spec.cr" (failed to run) # require "./std/mime/media_type_spec.cr" (failed linking) # require "./std/mime/multipart/builder_spec.cr" (failed linking) From a83537ef5f5f0448a7d7bba4bdb10a2aa6f084c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20M=C3=BCller?= Date: Wed, 30 Nov 2022 13:11:52 +0100 Subject: [PATCH 08/41] Fix `XML::Node#errors` to return `nil` when empty (#12795) --- spec/std/xml/xml_spec.cr | 3 +++ src/xml/node.cr | 6 ++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/spec/std/xml/xml_spec.cr b/spec/std/xml/xml_spec.cr index 4c5e18c97249..3c695f517497 100644 --- a/spec/std/xml/xml_spec.cr +++ b/spec/std/xml/xml_spec.cr @@ -162,6 +162,9 @@ describe XML do xml = XML.parse(%()) xml.root.not_nil!.name.should eq("people") xml.errors.try(&.map(&.to_s)).should eq ["Opening and ending tag mismatch: people line 1 and foo"] + + xml = XML.parse(%()) + xml.errors.should be_nil end describe "#namespace" do diff --git a/src/xml/node.cr b/src/xml/node.cr index 218897b5d025..89451839dc85 100644 --- a/src/xml/node.cr +++ b/src/xml/node.cr @@ -7,7 +7,7 @@ class XML::Node end # :ditto: - def initialize(node : LibXML::Doc*, @errors = nil) + def initialize(node : LibXML::Doc*, @errors : Array(XML::Error)? = nil) initialize(node.as(LibXML::Node*)) end @@ -578,7 +578,9 @@ class XML::Node # Returns the list of `XML::Error` found when parsing this document. # Returns `nil` if no errors were found. - getter errors : Array(XML::Error)? + def errors : Array(XML::Error)? + return @errors unless @errors.try &.empty? + end private def check_no_null_byte(string) if string.includes? Char::ZERO From 1b932187607adf7abb41968c8a36e62e2c2dbebd Mon Sep 17 00:00:00 2001 From: Gabriel Holodak Date: Wed, 30 Nov 2022 07:12:14 -0500 Subject: [PATCH 09/41] Fix crash when using `sizeof`, `instance_sizeof`, or `offsetof` as a type arg (#12577) --- spec/compiler/semantic/static_array_spec.cr | 24 ++++++++++++++++++++ src/compiler/crystal/semantic/type_lookup.cr | 4 ++++ src/compiler/crystal/syntax/parser.cr | 6 +++-- 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/spec/compiler/semantic/static_array_spec.cr b/spec/compiler/semantic/static_array_spec.cr index cd2db6e88159..0112584753c7 100644 --- a/spec/compiler/semantic/static_array_spec.cr +++ b/spec/compiler/semantic/static_array_spec.cr @@ -155,4 +155,28 @@ describe "Semantic: static array" do ), "expected argument #1 to 'fn' to be StaticArray(Int32, 11), not StaticArray(Int32, 10)" end + + it "doesn't crash on sizeof (#8858)" do + assert_error %( + alias BadArray = Int32[sizeof(Int32)] + ), + "can't use sizeof(Int32) as a generic type argument" + end + + it "doesn't crash on instance_sizeof (#8858)" do + assert_error %( + alias BadArray = Int32[instance_sizeof(String)] + ), + "can't use instance_sizeof(String) as a generic type argument" + end + + it "doesn't crash on offsetof (#8858)" do + assert_error %( + class Foo + @foo : Int32 = 0 + end + alias BadArray = Int32[offsetof(Foo, @foo)] + ), + "can't use offsetof(Foo, @foo) as a generic type argument" + end end diff --git a/src/compiler/crystal/semantic/type_lookup.cr b/src/compiler/crystal/semantic/type_lookup.cr index 7a98450cc72b..64c3b319cf6e 100644 --- a/src/compiler/crystal/semantic/type_lookup.cr +++ b/src/compiler/crystal/semantic/type_lookup.cr @@ -247,6 +247,10 @@ class Crystal::Type type_var.raise "can only splat tuple type, not #{splat_type}" end next + when SizeOf, InstanceSizeOf, OffsetOf + next unless @raise + + type_var.raise "can't use #{type_var} as a generic type argument" end # Check the case of T resolving to a number diff --git a/src/compiler/crystal/syntax/parser.cr b/src/compiler/crystal/syntax/parser.cr index adcbcabeece5..e554f500b738 100644 --- a/src/compiler/crystal/syntax/parser.cr +++ b/src/compiler/crystal/syntax/parser.cr @@ -5771,6 +5771,7 @@ module Crystal end def parse_sizeof(klass) + sizeof_location = @token.location next_token_skip_space check :OP_LPAREN @@ -5785,10 +5786,11 @@ module Crystal check :OP_RPAREN next_token_skip_space - klass.new(exp).at_end(end_location) + klass.new(exp).at(sizeof_location).at_end(end_location) end def parse_offsetof + offsetof_location = @token.location next_token_skip_space check :OP_LPAREN @@ -5817,7 +5819,7 @@ module Crystal check :OP_RPAREN next_token_skip_space - OffsetOf.new(type, offset).at_end(end_location) + OffsetOf.new(type, offset).at(offsetof_location).at_end(end_location) end def parse_type_def From 312e369f74edc3f2785a3959595892dbc2396092 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20M=C3=BCller?= Date: Thu, 1 Dec 2022 00:30:47 +0100 Subject: [PATCH 10/41] Add missing positive spec for `Regex#match` with option (#12804) --- spec/std/regex_spec.cr | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/spec/std/regex_spec.cr b/spec/std/regex_spec.cr index 24ac68b35f9a..c38194bc8785 100644 --- a/spec/std/regex_spec.cr +++ b/spec/std/regex_spec.cr @@ -76,6 +76,7 @@ describe "Regex" do it "with options" do /foo/.match(".foo", options: Regex::Options::ANCHORED).should be_nil + /foo/.match("foo", options: Regex::Options::ANCHORED).should_not be_nil end end @@ -126,6 +127,7 @@ describe "Regex" do it "with options" do /foo/.match_at_byte_index("..foo", 1, options: Regex::Options::ANCHORED).should be_nil + /foo/.match_at_byte_index(".foo", 1, options: Regex::Options::ANCHORED).should_not be_nil end end @@ -195,6 +197,7 @@ describe "Regex" do it "with options" do /foo/.matches?(".foo", options: Regex::Options::ANCHORED).should be_false + /foo/.matches?("foo", options: Regex::Options::ANCHORED).should be_true end it "matches a large single line string" do @@ -229,6 +232,7 @@ describe "Regex" do it "with options" do /foo/.matches_at_byte_index?("..foo", 1, options: Regex::Options::ANCHORED).should be_false + /foo/.matches_at_byte_index?(".foo", 1, options: Regex::Options::ANCHORED).should be_true end end From 926e89cc025c7ee1ffe4ee1179526188119e1ae0 Mon Sep 17 00:00:00 2001 From: Quinton Miller Date: Thu, 1 Dec 2022 19:57:35 +0800 Subject: [PATCH 11/41] Redefine defs when constant and number in generic arguments are equal (#12785) --- spec/compiler/semantic/restrictions_spec.cr | 77 +++++++++++++++++++ src/compiler/crystal/semantic/restrictions.cr | 48 ++++++++++++ 2 files changed, 125 insertions(+) diff --git a/spec/compiler/semantic/restrictions_spec.cr b/spec/compiler/semantic/restrictions_spec.cr index 139f881cc447..4336b142b8ca 100644 --- a/spec/compiler/semantic/restrictions_spec.cr +++ b/spec/compiler/semantic/restrictions_spec.cr @@ -548,6 +548,46 @@ describe "Restrictions" do end end + describe "Path vs NumberLiteral" do + it "inserts constant before number literal of same value with generic arguments" do + assert_type(<<-CR) { bool } + X = 1 + + class Foo(N) + end + + def foo(a : Foo(1)) + 'a' + end + + def foo(a : Foo(X)) + true + end + + foo(Foo(1).new) + CR + end + + it "inserts number literal before constant of same value with generic arguments" do + assert_type(<<-CR) { bool } + X = 1 + + class Foo(N) + end + + def foo(a : Foo(X)) + 'a' + end + + def foo(a : Foo(1)) + true + end + + foo(Foo(1).new) + CR + end + end + describe "free variables" do it "inserts path before free variable with same name" do assert_type(<<-CR) { tuple_of([char, bool]) } @@ -577,6 +617,43 @@ describe "Restrictions" do CR end + # TODO: enable in #12784 + pending "inserts constant before free variable with same name" do + assert_type(<<-CR) { tuple_of([char, bool]) } + class Foo(T); end + + X = 1 + + def foo(x : Foo(X)) forall X + true + end + + def foo(x : Foo(X)) + 'a' + end + + {foo(Foo(1).new), foo(Foo(2).new)} + CR + end + + pending "keeps constant before free variable with same name" do + assert_type(<<-CR) { tuple_of([char, bool]) } + class Foo(T); end + + X = 1 + + def foo(x : Foo(X)) + 'a' + end + + def foo(x : Foo(X)) forall X + true + end + + {foo(Foo(1).new), foo(Foo(2).new)} + CR + end + it "inserts path before free variable even if free var resolves to a more specialized type" do assert_type(<<-CR) { tuple_of([int32, int32, bool]) } class Foo diff --git a/src/compiler/crystal/semantic/restrictions.cr b/src/compiler/crystal/semantic/restrictions.cr index 5e157e3ef200..523030444d2e 100644 --- a/src/compiler/crystal/semantic/restrictions.cr +++ b/src/compiler/crystal/semantic/restrictions.cr @@ -605,11 +605,59 @@ module Crystal false end + def restriction_of?(other : NumberLiteral, owner, self_free_vars = nil, other_free_vars = nil) + return false if self_free_vars && self.single_name?.try { |name| self_free_vars.includes?(name) } + + # this happens when `self` and `other` are generic arguments: + # + # ``` + # X = 1 + # + # def foo(param : StaticArray(Int32, X)) + # end + # + # def foo(param : StaticArray(Int32, 1)) + # end + # ``` + case self_type = owner.lookup_path(self) + when Const + self_type.value == other + when NumberLiteral + self_type == other + else + false + end + end + def restriction_of?(other, owner, self_free_vars = nil, other_free_vars = nil) false end end + class NumberLiteral + def restriction_of?(other : Path, owner, self_free_vars = nil, other_free_vars = nil) + # this happens when `self` and `other` are generic arguments: + # + # ``` + # X = 1 + # + # def foo(param : StaticArray(Int32, 1)) + # end + # + # def foo(param : StaticArray(Int32, X)) + # end + # ``` + case other_type = owner.lookup_path(other) + when Const + other_type.value == self + when NumberLiteral + other_type == self + else + false + end + end + end + class Union def restriction_of?(other, owner, self_free_vars = nil, other_free_vars = nil) # For a union to be considered before another restriction, From d40b44b66df1bea01e8deb4289a65d7aea362501 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 1 Dec 2022 12:57:50 +0100 Subject: [PATCH 12/41] Update actions/checkout action to v3 (#12805) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/wasm32.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/wasm32.yml b/.github/workflows/wasm32.yml index e2b0fbf49bbe..835c2ddbcccd 100644 --- a/.github/workflows/wasm32.yml +++ b/.github/workflows/wasm32.yml @@ -11,7 +11,7 @@ jobs: container: crystallang/crystal:1.6.1-build steps: - name: Download Crystal source - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Install wasmtime uses: mwilliamson/setup-wasmtime-action@v1 From 251bd55afd9db5547945808e2e9bcd45a4e73e11 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Fri, 2 Dec 2022 02:25:49 +0800 Subject: [PATCH 13/41] Use qualified type reference `YAML::Any` (#12688) --- src/json/any.cr | 14 +++++++------- src/yaml/any.cr | 20 ++++++++++---------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/json/any.cr b/src/json/any.cr index 5a9a96c78265..59a11b852c5d 100644 --- a/src/json/any.cr +++ b/src/json/any.cr @@ -18,7 +18,7 @@ # which return `nil` when the underlying value type won't match. struct JSON::Any # All possible JSON types. - alias Type = Nil | Bool | Int64 | Float64 | String | Array(Any) | Hash(String, Any) + alias Type = Nil | Bool | Int64 | Float64 | String | Array(JSON::Any) | Hash(String, JSON::Any) # Reads a `JSON::Any` value from the given pull parser. def self.new(pull : JSON::PullParser) @@ -224,25 +224,25 @@ struct JSON::Any # Checks that the underlying value is `Array`, and returns its value. # Raises otherwise. - def as_a : Array(Any) + def as_a : Array(JSON::Any) @raw.as(Array) end # Checks that the underlying value is `Array`, and returns its value. # Returns `nil` otherwise. - def as_a? : Array(Any)? + def as_a? : Array(JSON::Any)? as_a if @raw.is_a?(Array) end # Checks that the underlying value is `Hash`, and returns its value. # Raises otherwise. - def as_h : Hash(String, Any) + def as_h : Hash(String, JSON::Any) @raw.as(Hash) end # Checks that the underlying value is `Hash`, and returns its value. # Returns `nil` otherwise. - def as_h? : Hash(String, Any)? + def as_h? : Hash(String, JSON::Any)? as_h if @raw.is_a?(Hash) end @@ -283,12 +283,12 @@ struct JSON::Any # Returns a new JSON::Any instance with the `raw` value `dup`ed. def dup - Any.new(raw.dup) + JSON::Any.new(raw.dup) end # Returns a new JSON::Any instance with the `raw` value `clone`ed. def clone - Any.new(raw.clone) + JSON::Any.new(raw.clone) end end diff --git a/src/yaml/any.cr b/src/yaml/any.cr index 284e6ad0e33d..f0a7978369b9 100644 --- a/src/yaml/any.cr +++ b/src/yaml/any.cr @@ -26,14 +26,14 @@ # which return `nil` when the underlying value type won't match. struct YAML::Any # All valid YAML core schema types. - alias Type = Nil | Bool | Int64 | Float64 | String | Time | Bytes | Array(Any) | Hash(Any, Any) | Set(Any) + alias Type = Nil | Bool | Int64 | Float64 | String | Time | Bytes | Array(YAML::Any) | Hash(YAML::Any, YAML::Any) | Set(YAML::Any) def self.new(ctx : YAML::ParseContext, node : YAML::Nodes::Node) case node when YAML::Nodes::Scalar new YAML::Schema::Core.parse_scalar(node.value) when YAML::Nodes::Sequence - ary = [] of Any + ary = [] of YAML::Any node.each do |value| ary << new(ctx, value) @@ -41,7 +41,7 @@ struct YAML::Any new ary when YAML::Nodes::Mapping - hash = {} of Any => Any + hash = {} of YAML::Any => YAML::Any node.each do |key, value| hash[new(ctx, key)] = new(ctx, value) @@ -62,7 +62,7 @@ struct YAML::Any # Returns the raw underlying value, a `Type`. getter raw : Type - # Creates a `Any` that wraps the given `Type`. + # Creates a `YAML::Any` that wraps the given `Type`. def initialize(@raw : Type) end @@ -236,25 +236,25 @@ struct YAML::Any # Checks that the underlying value is `Array`, and returns its value. # Raises otherwise. - def as_a : Array(Any) + def as_a : Array(YAML::Any) @raw.as(Array) end # Checks that the underlying value is `Array`, and returns its value. # Returns `nil` otherwise. - def as_a? : Array(Any)? + def as_a? : Array(YAML::Any)? @raw.as?(Array) end # Checks that the underlying value is `Hash`, and returns its value. # Raises otherwise. - def as_h : Hash(Any, Any) + def as_h : Hash(YAML::Any, YAML::Any) @raw.as(Hash) end # Checks that the underlying value is `Hash`, and returns its value. # Returns `nil` otherwise. - def as_h? : Hash(Any, Any)? + def as_h? : Hash(YAML::Any, YAML::Any)? @raw.as?(Hash) end @@ -311,12 +311,12 @@ struct YAML::Any # Returns a new YAML::Any instance with the `raw` value `dup`ed. def dup - Any.new(raw.dup) + YAML::Any.new(raw.dup) end # Returns a new YAML::Any instance with the `raw` value `clone`ed. def clone - Any.new(raw.clone) + YAML::Any.new(raw.clone) end # Forwards `to_json_object_key` to `raw` if it responds to that method, From 49edc0bdf73f9c60b83c349f1930f33ffdee563e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20M=C3=BCller?= Date: Fri, 2 Dec 2022 11:17:55 +0100 Subject: [PATCH 14/41] Raise `IndexError` on unmatched subpattern for `MatchData#begin` and `#end` (#12810) Resolves https://github.com/crystal-lang/crystal/issues/12806 --- spec/std/regex/match_data_spec.cr | 102 ++++++++++++++++++++++++++---- src/regex/match_data.cr | 28 +++++++- 2 files changed, 116 insertions(+), 14 deletions(-) diff --git a/spec/std/regex/match_data_spec.cr b/spec/std/regex/match_data_spec.cr index 1079dff145e1..7fc9c3219faa 100644 --- a/spec/std/regex/match_data_spec.cr +++ b/spec/std/regex/match_data_spec.cr @@ -65,12 +65,33 @@ describe "Regex::MatchData" do end it "with capture" do - matchdata(/f(o)o/, "foo").begin.should eq 0 - matchdata(/f(o)o/, "foo").begin(1).should eq 1 - matchdata(/f(o)o/, "foo").begin(-1).should eq 1 - matchdata(/f(o)o/, ".foo.").begin.should eq 1 - matchdata(/f(o)o/, ".foo.").begin(1).should eq 2 - matchdata(/f(o)o/, ".foo.").begin(-1).should eq 2 + md = matchdata(/f(o)o/, "foo") + md.begin.should eq 0 + md.begin(1).should eq 1 + md.begin(-1).should eq 1 + + md = matchdata(/f(o)o/, ".foo.") + md.begin.should eq 1 + md.begin(1).should eq 2 + md.begin(-1).should eq 2 + end + + it "with unmatched capture" do + md = matchdata(/f(x)?o/, "foo") + expect_raises(IndexError, "Capture group 1 was not matched") do + md.begin(1) + end + expect_raises(IndexError, "Capture group 1 was not matched") do + md.begin(-1) + end + + md = matchdata(/f(x)?o/, ".foo.") + expect_raises(IndexError, "Capture group 1 was not matched") do + md.begin(1) + end + expect_raises(IndexError, "Capture group 1 was not matched") do + md.begin(-1) + end end it "char index" do @@ -82,6 +103,24 @@ describe "Regex::MatchData" do it "char index" do matchdata(/foo/, "öfoo").byte_begin.should eq 2 end + + it "with unmatched capture" do + md = matchdata(/f(x)?o/, "foo") + expect_raises(IndexError, "Capture group 1 was not matched") do + md.byte_begin(1) + end + expect_raises(IndexError, "Capture group 1 was not matched") do + md.byte_begin(-1) + end + + md = matchdata(/f(x)?o/, ".foo.") + expect_raises(IndexError, "Capture group 1 was not matched") do + md.byte_begin(1) + end + expect_raises(IndexError, "Capture group 1 was not matched") do + md.byte_begin(-1) + end + end end describe "#end" do @@ -99,12 +138,33 @@ describe "Regex::MatchData" do end it "with capture" do - matchdata(/f(o)o/, "foo").end.should eq 3 - matchdata(/f(o)o/, "foo").end(1).should eq 2 - matchdata(/f(o)o/, "foo").end(-1).should eq 2 - matchdata(/f(o)o/, ".foo.").end.should eq 4 - matchdata(/f(o)o/, ".foo.").end(1).should eq 3 - matchdata(/f(o)o/, ".foo.").end(-1).should eq 3 + md = matchdata(/f(o)o/, "foo") + md.end.should eq 3 + md.end(1).should eq 2 + md.end(-1).should eq 2 + + md = matchdata(/f(o)o/, ".foo.") + md.end.should eq 4 + md.end(1).should eq 3 + md.end(-1).should eq 3 + end + + it "with unmatched capture" do + md = matchdata(/f(x)?o/, "foo") + expect_raises(IndexError, "Capture group 1 was not matched") do + md.end(1) + end + expect_raises(IndexError, "Capture group 1 was not matched") do + md.end(-1) + end + + md = matchdata(/f(x)?o/, ".foo.") + expect_raises(IndexError, "Capture group 1 was not matched") do + md.end(1) + end + expect_raises(IndexError, "Capture group 1 was not matched") do + md.end(-1) + end end it "char index" do @@ -116,6 +176,24 @@ describe "Regex::MatchData" do it "char index" do matchdata(/foo/, "öfoo").byte_end.should eq 5 end + + it "with unmatched capture" do + md = matchdata(/f(x)?o/, "foo") + expect_raises(IndexError, "Capture group 1 was not matched") do + md.byte_end(1) + end + expect_raises(IndexError, "Capture group 1 was not matched") do + md.byte_end(-1) + end + + md = matchdata(/f(x)?o/, ".foo.") + expect_raises(IndexError, "Capture group 1 was not matched") do + md.byte_end(1) + end + expect_raises(IndexError, "Capture group 1 was not matched") do + md.byte_end(-1) + end + end end describe "#[]" do diff --git a/src/regex/match_data.cr b/src/regex/match_data.cr index 89e12204986e..085c829073e8 100644 --- a/src/regex/match_data.cr +++ b/src/regex/match_data.cr @@ -59,10 +59,15 @@ class Regex # When *n* is `0` or not given, uses the match of the entire `Regex`. # Otherwise, uses the match of the *n*th capture group. # + # Raises `IndexError` if the index is out of range or the respective + # subpattern is unused. + # # ``` # "Crystal".match(/r/).not_nil!.begin(0) # => 1 # "Crystal".match(/r(ys)/).not_nil!.begin(1) # => 2 # "クリスタル".match(/リ(ス)/).not_nil!.begin(0) # => 1 + # "Crystal".match(/r/).not_nil!.begin(1) # IndexError: Invalid capture group index: 1 + # "Crystal".match(/r(x)?/).not_nil!.begin(1) # IndexError: Capture group 1 was not matched # ``` def begin(n = 0) : Int32 @string.byte_index_to_char_index(byte_begin(n)).not_nil! @@ -73,10 +78,15 @@ class Regex # When *n* is `0` or not given, uses the match of the entire `Regex`. # Otherwise, uses the match of the *n*th capture group. # + # Raises `IndexError` if the index is out of range or the respective + # subpattern is unused. + # # ``` # "Crystal".match(/r/).not_nil!.end(0) # => 2 # "Crystal".match(/r(ys)/).not_nil!.end(1) # => 4 # "クリスタル".match(/リ(ス)/).not_nil!.end(0) # => 3 + # "Crystal".match(/r/).not_nil!.end(1) # IndexError: Invalid capture group index: 1 + # "Crystal".match(/r(x)?/).not_nil!.end(1) # IndexError: Capture group 1 was not matched # ``` def end(n = 0) : Int32 @string.byte_index_to_char_index(byte_end(n)).not_nil! @@ -87,15 +97,22 @@ class Regex # When *n* is `0` or not given, uses the match of the entire `Regex`. # Otherwise, uses the match of the *n*th capture group. # + # Raises `IndexError` if the index is out of range or the respective + # subpattern is unused. + # # ``` # "Crystal".match(/r/).not_nil!.byte_begin(0) # => 1 # "Crystal".match(/r(ys)/).not_nil!.byte_begin(1) # => 2 # "クリスタル".match(/リ(ス)/).not_nil!.byte_begin(0) # => 3 + # "Crystal".match(/r/).not_nil!.byte_begin(1) # IndexError: Invalid capture group index: 1 + # "Crystal".match(/r(x)?/).not_nil!.byte_begin(1) # IndexError: Capture group 1 was not matched # ``` def byte_begin(n = 0) : Int32 check_index_out_of_bounds n n += size if n < 0 - @ovector[n * 2] + value = @ovector[n * 2] + raise_capture_group_was_not_matched(n) if value < 0 + value end # Returns the position of the next byte after the match. @@ -103,15 +120,22 @@ class Regex # When *n* is `0` or not given, uses the match of the entire `Regex`. # Otherwise, uses the match of the *n*th capture group. # + # Raises `IndexError` if the index is out of range or the respective + # subpattern is unused. + # # ``` # "Crystal".match(/r/).not_nil!.byte_end(0) # => 2 # "Crystal".match(/r(ys)/).not_nil!.byte_end(1) # => 4 # "クリスタル".match(/リ(ス)/).not_nil!.byte_end(0) # => 9 + # "Crystal".match(/r/).not_nil!.byte_end(1) # IndexError: Invalid capture group index: 1 + # "Crystal".match(/r(x)?/).not_nil!.byte_end(1) # IndexError: Capture group 1 was not matched # ``` def byte_end(n = 0) : Int32 check_index_out_of_bounds n n += size if n < 0 - @ovector[n * 2 + 1] + value = @ovector[n * 2 + 1] + raise_capture_group_was_not_matched(n) if value < 0 + value end # Returns the match of the *n*th capture group, or `nil` if there isn't From 49f34536ad9c9f40aa4d900f5169c3850298cbbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20M=C3=BCller?= Date: Sun, 4 Dec 2022 12:38:23 +0100 Subject: [PATCH 15/41] Use context-specific heredoc deliminators (#12816) --- scripts/generate_grapheme_break_specs.cr | 4 +- scripts/generate_ssl_server_defaults.cr | 8 +- spec/compiler/codegen/cast_spec.cr | 4 +- spec/compiler/codegen/is_a_spec.cr | 4 +- spec/compiler/codegen/multi_assign_spec.cr | 92 ++-- spec/compiler/codegen/pointer_spec.cr | 4 +- spec/compiler/codegen/primitives_spec.cr | 12 +- spec/compiler/codegen/private_spec.cr | 16 +- spec/compiler/codegen/proc_spec.cr | 4 +- .../compiler/crystal/tools/doc/method_spec.cr | 8 +- spec/compiler/crystal/tools/doc/type_spec.cr | 44 +- spec/compiler/crystal/tools/doc_spec.cr | 4 +- spec/compiler/crystal/tools/expand_spec.cr | 140 +++--- spec/compiler/crystal/tools/hierarchy_spec.cr | 8 +- spec/compiler/crystal/tools/init_spec.cr | 24 +- .../compiler/crystal/tools/playground_spec.cr | 94 ++-- spec/compiler/formatter/formatter_spec.cr | 326 +++++++------- spec/compiler/interpreter/autocast_spec.cr | 36 +- spec/compiler/interpreter/blocks_spec.cr | 144 +++--- spec/compiler/interpreter/bugs_spec.cr | 36 +- spec/compiler/interpreter/calls_spec.cr | 152 +++---- spec/compiler/interpreter/casts_spec.cr | 124 +++--- spec/compiler/interpreter/class_vars_spec.cr | 28 +- spec/compiler/interpreter/classes_spec.cr | 36 +- spec/compiler/interpreter/closures_spec.cr | 112 ++--- spec/compiler/interpreter/constants_spec.cr | 24 +- .../compiler/interpreter/control_flow_spec.cr | 56 +-- spec/compiler/interpreter/enum_spec.cr | 8 +- spec/compiler/interpreter/exceptions_spec.cr | 64 +-- spec/compiler/interpreter/extern_spec.cr | 28 +- spec/compiler/interpreter/integration_spec.cr | 56 +-- spec/compiler/interpreter/is_a_spec.cr | 40 +- spec/compiler/interpreter/lib_spec.cr | 16 +- .../interpreter/multidispatch_spec.cr | 76 ++-- spec/compiler/interpreter/named_tuple_spec.cr | 12 +- spec/compiler/interpreter/pointers_spec.cr | 104 ++--- spec/compiler/interpreter/primitives_spec.cr | 88 ++-- spec/compiler/interpreter/procs_spec.cr | 32 +- spec/compiler/interpreter/responds_to_spec.cr | 8 +- .../compiler/interpreter/special_vars_spec.cr | 24 +- spec/compiler/interpreter/structs_spec.cr | 60 +-- spec/compiler/interpreter/symbol_spec.cr | 12 +- spec/compiler/interpreter/tuple_spec.cr | 48 +- spec/compiler/interpreter/typeof_spec.cr | 4 +- spec/compiler/interpreter/types_spec.cr | 28 +- spec/compiler/interpreter/unions_spec.cr | 32 +- spec/compiler/macro/macro_methods_spec.cr | 12 +- spec/compiler/normalize/array_literal_spec.cr | 36 +- spec/compiler/normalize/hash_literal_spec.cr | 20 +- spec/compiler/normalize/multi_assign_spec.cr | 96 ++-- spec/compiler/normalize/proc_pointer_spec.cr | 32 +- spec/compiler/normalize/select_spec.cr | 20 +- spec/compiler/parser/parser_spec.cr | 24 +- spec/compiler/semantic/abstract_def_spec.cr | 168 +++---- spec/compiler/semantic/annotation_spec.cr | 8 +- spec/compiler/semantic/automatic_cast_spec.cr | 4 +- spec/compiler/semantic/block_spec.cr | 24 +- spec/compiler/semantic/case_spec.cr | 8 +- spec/compiler/semantic/class_spec.cr | 4 +- spec/compiler/semantic/closure_spec.cr | 4 +- spec/compiler/semantic/const_spec.cr | 12 +- spec/compiler/semantic/def_overload_spec.cr | 24 +- spec/compiler/semantic/def_spec.cr | 4 +- spec/compiler/semantic/did_you_mean_spec.cr | 4 +- spec/compiler/semantic/doc_spec.cr | 4 +- spec/compiler/semantic/enum_spec.cr | 8 +- spec/compiler/semantic/exception_spec.cr | 4 +- spec/compiler/semantic/generic_class_spec.cr | 4 +- spec/compiler/semantic/instance_var_spec.cr | 40 +- spec/compiler/semantic/lib_spec.cr | 8 +- spec/compiler/semantic/macro_spec.cr | 412 +++++++++--------- spec/compiler/semantic/metaclass_spec.cr | 8 +- spec/compiler/semantic/multi_assign_spec.cr | 20 +- spec/compiler/semantic/pointer_spec.cr | 16 +- spec/compiler/semantic/private_spec.cr | 4 +- spec/compiler/semantic/proc_spec.cr | 28 +- .../semantic/recursive_struct_check_spec.cr | 4 +- .../semantic/restrictions_augmenter_spec.cr | 120 ++--- spec/compiler/semantic/restrictions_spec.cr | 68 +-- spec/compiler/semantic/return_spec.cr | 12 +- spec/compiler/semantic/sizeof_spec.cr | 4 +- spec/compiler/semantic/splat_spec.cr | 4 +- .../semantic/virtual_metaclass_spec.cr | 4 +- spec/compiler/semantic/virtual_spec.cr | 4 +- .../semantic/visibility_modifiers_spec.cr | 4 +- spec/compiler/semantic/warnings_spec.cr | 184 ++++---- .../syntax_highlighter/colorize_spec.cr | 12 +- .../crystal/syntax_highlighter/html_spec.cr | 12 +- spec/std/exception/call_stack_spec.cr | 4 +- spec/std/http/client/client_spec.cr | 4 +- spec/std/http/request_spec.cr | 8 +- .../std/http/server/request_processor_spec.cr | 52 +-- spec/std/http/server/server_spec.cr | 8 +- spec/std/kernel_spec.cr | 56 +-- spec/std/process_spec.cr | 4 +- spec/std/spec/hooks_spec.cr | 4 +- spec/std/string/grapheme_break_spec.cr | 134 +++--- spec/std/yaml/yaml_spec.cr | 4 +- src/compiler/crystal/tools/doc/templates.cr | 8 +- .../crystal/tools/playground/server.cr | 8 +- src/yaml.cr | 4 +- src/yaml/any.cr | 4 +- 102 files changed, 2038 insertions(+), 2040 deletions(-) diff --git a/scripts/generate_grapheme_break_specs.cr b/scripts/generate_grapheme_break_specs.cr index 7e800a15a349..e31c602cb38f 100644 --- a/scripts/generate_grapheme_break_specs.cr +++ b/scripts/generate_grapheme_break_specs.cr @@ -22,7 +22,7 @@ def string_or_char(string) end File.open(path, "w") do |file| - file.puts <<-CR + file.puts <<-CRYSTAL # This file was automatically generated by running: # # scripts/generate_grapheme_break_spec.cr @@ -33,7 +33,7 @@ File.open(path, "w") do |file| require "./spec_helper" describe "String#each_grapheme" do - CR + CRYSTAL HTTP::Client.get(url).body.each_line do |line| next if line.starts_with?('#') diff --git a/scripts/generate_ssl_server_defaults.cr b/scripts/generate_ssl_server_defaults.cr index a0a87647d788..bab12ad2bb0e 100755 --- a/scripts/generate_ssl_server_defaults.cr +++ b/scripts/generate_ssl_server_defaults.cr @@ -13,12 +13,12 @@ DEFAULTS_FILE = "src/openssl/ssl/defaults.cr" json = JSON.parse(HTTP::Client.get(url).body) File.open(DEFAULTS_FILE, "w") do |file| - file.print <<-CR + file.print <<-CRYSTAL # THIS FILE WAS AUTOMATICALLY GENERATED BY script/ssl_server_defaults.cr # on #{Time.utc}. abstract class OpenSSL::SSL::Context - CR + CRYSTAL configuration = json["configurations"].as_h.each do |level, configuration| clients = configuration["oldest_clients"].as_a @@ -27,7 +27,7 @@ File.open(DEFAULTS_FILE, "w") do |file| disabled_ciphers = %w(!RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS) all_ciphers = ciphersuites + ciphers + disabled_ciphers - file.puts <<-CR + file.puts <<-CRYSTAL # The list of secure ciphers on **#{level}** compatibility level as per Mozilla # recommendations. @@ -52,7 +52,7 @@ File.open(DEFAULTS_FILE, "w") do |file| # # See https://wiki.mozilla.org/Security/Server_Side_TLS for details. CIPHER_SUITES_#{level.upcase} = "#{ciphersuites.join(":")}" - CR + CRYSTAL end file.puts "end" end diff --git a/spec/compiler/codegen/cast_spec.cr b/spec/compiler/codegen/cast_spec.cr index 1260ab2ffe43..b4ad3d4bdc3e 100644 --- a/spec/compiler/codegen/cast_spec.cr +++ b/spec/compiler/codegen/cast_spec.cr @@ -425,7 +425,7 @@ describe "Code gen: cast" do end it "cast virtual metaclass type to nilable virtual instance type (#12628)" do - run(<<-CR).to_b.should be_true + run(<<-CRYSTAL).to_b.should be_true abstract struct Base end @@ -433,6 +433,6 @@ describe "Code gen: cast" do end Base.as(Base | Base.class).as?(Base | Impl).nil? - CR + CRYSTAL end end diff --git a/spec/compiler/codegen/is_a_spec.cr b/spec/compiler/codegen/is_a_spec.cr index 2d751bc81c15..97d72378be42 100644 --- a/spec/compiler/codegen/is_a_spec.cr +++ b/spec/compiler/codegen/is_a_spec.cr @@ -924,7 +924,7 @@ describe "Codegen: is_a?" do end it "virtual metaclass type is not virtual instance type (#12628)" do - run(<<-CR).to_b.should be_false + run(<<-CRYSTAL).to_b.should be_false abstract struct Base end @@ -932,6 +932,6 @@ describe "Codegen: is_a?" do end Base.as(Base | Base.class).is_a?(Base | Impl) - CR + CRYSTAL end end diff --git a/spec/compiler/codegen/multi_assign_spec.cr b/spec/compiler/codegen/multi_assign_spec.cr index faffb8fb34f5..e9075e8c16bd 100644 --- a/spec/compiler/codegen/multi_assign_spec.cr +++ b/spec/compiler/codegen/multi_assign_spec.cr @@ -2,15 +2,15 @@ require "../../spec_helper" describe "Code gen: multi assign" do it "supports n to n assignment" do - run(<<-CR).to_i.should eq(123) + run(<<-CRYSTAL).to_i.should eq(123) a, b, c = 1, 2, 3 a &* 100 &+ b &* 10 &+ c - CR + CRYSTAL end context "without strict_multi_assign" do it "supports 1 to n assignment" do - run(<<-CR).to_i.should eq(123) + run(<<-CRYSTAL).to_i.should eq(123) class Foo def [](index) index &+ 1 @@ -19,11 +19,11 @@ describe "Code gen: multi assign" do a, b, c = Foo.new a &* 100 &+ b &* 10 &+ c - CR + CRYSTAL end it "doesn't raise if value size in 1 to n assignment doesn't match target count" do - run(<<-CR).to_i.should eq(4) + run(<<-CRYSTAL).to_i.should eq(4) require "prelude" begin @@ -33,13 +33,13 @@ describe "Code gen: multi assign" do raise ex unless ex.message == "Multiple assignment count mismatch" 5 end - CR + CRYSTAL end end context "strict_multi_assign" do it "supports 1 to n assignment" do - run(<<-CR, flags: %w(strict_multi_assign)).to_i.should eq(123) + run(<<-CRYSTAL, flags: %w(strict_multi_assign)).to_i.should eq(123) require "prelude" class Foo @@ -56,11 +56,11 @@ describe "Code gen: multi assign" do a, b, c = Foo.new a &* 100 &+ b &* 10 &+ c - CR + CRYSTAL end it "raises if value size in 1 to n assignment doesn't match target count" do - run(<<-CR, flags: %w(strict_multi_assign)).to_i.should eq(5) + run(<<-CRYSTAL, flags: %w(strict_multi_assign)).to_i.should eq(5) require "prelude" begin @@ -70,126 +70,126 @@ describe "Code gen: multi assign" do raise ex unless ex.message == "Multiple assignment count mismatch" 5 end - CR + CRYSTAL end end it "supports m to n assignment, with splat on left-hand side (1)" do - run(<<-CR).to_i.should eq(12345) + run(<<-CRYSTAL).to_i.should eq(12345) #{tuple_new} a, *b, c = 1, 2, 3, 4, 5 a &* 10000 &+ b[0] &* 1000 &+ b[1] &* 100 &+ b[2] &* 10 &+ c - CR + CRYSTAL end it "supports m to n assignment, with splat on left-hand side (2)" do - run(<<-CR).to_i.should eq(12345) + run(<<-CRYSTAL).to_i.should eq(12345) #{tuple_new} *a, b, c = 1, 2, 3, 4, 5 a[0] &* 10000 &+ a[1] &* 1000 &+ a[2] &* 100 &+ b &* 10 &+ c - CR + CRYSTAL end it "supports m to n assignment, with splat on left-hand side (3)" do - run(<<-CR).to_i.should eq(12345) + run(<<-CRYSTAL).to_i.should eq(12345) #{tuple_new} a, b, *c = 1, 2, 3, 4, 5 a &* 10000 &+ b &* 1000 &+ c[0] &* 100 &+ c[1] &* 10 &+ c[2] - CR + CRYSTAL end it "supports m to n assignment, splat is empty tuple (1)" do - run(<<-CR).to_b.should be_true + run(<<-CRYSTAL).to_b.should be_true #{tuple_new} _, *x, _ = 1, 2 x.is_a?(Tuple()) - CR + CRYSTAL end it "supports m to n assignment, splat is empty tuple (2)" do - run(<<-CR).to_b.should be_true + run(<<-CRYSTAL).to_b.should be_true #{tuple_new} *x, _, _ = 1, 2 x.is_a?(Tuple()) - CR + CRYSTAL end it "supports m to n assignment, splat is empty tuple (3)" do - run(<<-CR).to_b.should be_true + run(<<-CRYSTAL).to_b.should be_true #{tuple_new} _, _, *x = 1, 2 x.is_a?(Tuple()) - CR + CRYSTAL end it "supports 1 to n assignment, with splat on left-hand side (1)" do - run(<<-CR).to_i.should eq(12345) + run(<<-CRYSTAL).to_i.should eq(12345) require "prelude" a, *b, c = {1, 2, 3, 4, 5} a &* 10000 &+ b[0] &* 1000 &+ b[1] &* 100 &+ b[2] &* 10 &+ c - CR + CRYSTAL end it "supports 1 to n assignment, with splat on left-hand side (2)" do - run(<<-CR).to_i.should eq(12345) + run(<<-CRYSTAL).to_i.should eq(12345) #{range_new} #{include_indexable} *a, b, c = {1, 2, 3, 4, 5} a[0] &* 10000 &+ a[1] &* 1000 &+ a[2] &* 100 &+ b &* 10 &+ c - CR + CRYSTAL end it "supports 1 to n assignment, with splat on left-hand side (3)" do - run(<<-CR).to_i.should eq(12345) + run(<<-CRYSTAL).to_i.should eq(12345) #{range_new} #{include_indexable} a, b, *c = {1, 2, 3, 4, 5} a &* 10000 &+ b &* 1000 &+ c[0] &* 100 &+ c[1] &* 10 &+ c[2] - CR + CRYSTAL end it "supports 1 to n assignment, splat is empty (1)" do - run(<<-CR).to_b.should be_true + run(<<-CRYSTAL).to_b.should be_true require "prelude" _, *x, _ = {1, 2} x.is_a?(Tuple()) - CR + CRYSTAL end it "supports 1 to n assignment, splat is empty (2)" do - run(<<-CR).to_b.should be_true + run(<<-CRYSTAL).to_b.should be_true #{tuple_new} #{range_new} #{include_indexable} *x, _, _ = {1, 2} x.is_a?(Tuple()) - CR + CRYSTAL end it "supports 1 to n assignment, splat is empty (3)" do - run(<<-CR).to_b.should be_true + run(<<-CRYSTAL).to_b.should be_true #{tuple_new} #{range_new} #{include_indexable} _, _, *x = {1, 2} x.is_a?(Tuple()) - CR + CRYSTAL end it "supports 1 to n assignment, raises if too short" do - run(<<-CR).to_b.should be_true + run(<<-CRYSTAL).to_b.should be_true require "prelude" begin @@ -198,11 +198,11 @@ describe "Code gen: multi assign" do rescue ex : IndexError ex.message == "Multiple assignment count mismatch" end - CR + CRYSTAL end it "supports 1 to n assignment, raises if out of bounds (1)" do - run(<<-CR).to_b.should be_true + run(<<-CRYSTAL).to_b.should be_true require "prelude" begin @@ -211,11 +211,11 @@ describe "Code gen: multi assign" do rescue ex : IndexError true end - CR + CRYSTAL end it "supports 1 to n assignment, raises if out of bounds (2)" do - run(<<-CR).to_b.should be_true + run(<<-CRYSTAL).to_b.should be_true require "prelude" begin @@ -224,33 +224,33 @@ describe "Code gen: multi assign" do rescue ex : IndexError true end - CR + CRYSTAL end end private def tuple_new - <<-CR + <<-CRYSTAL struct Tuple def self.new(*args) args end end - CR + CRYSTAL end private def range_new - <<-CR + <<-CRYSTAL struct Range(B, E) def initialize(@begin : B, @end : E, @exclusive : Bool = false) end end - CR + CRYSTAL end private def include_indexable - <<-CR + <<-CRYSTAL struct Tuple(*T) include Indexable(Union(*T)) end - CR + CRYSTAL end diff --git a/spec/compiler/codegen/pointer_spec.cr b/spec/compiler/codegen/pointer_spec.cr index 592420ec2248..8e5cd488fd52 100644 --- a/spec/compiler/codegen/pointer_spec.cr +++ b/spec/compiler/codegen/pointer_spec.cr @@ -439,7 +439,7 @@ describe "Code gen: pointer" do end it "passes arguments correctly for typedef metaclass (#8544)" do - run <<-CR + run <<-CRYSTAL lib LibFoo type Foo = Void* end @@ -453,7 +453,7 @@ describe "Code gen: pointer" do x = 1 LibFoo::Foo.foo(x) Pointer(Void).foo(x) - CR + CRYSTAL end it "generates correct code for Pointer.malloc(0) (#2905)" do diff --git a/spec/compiler/codegen/primitives_spec.cr b/spec/compiler/codegen/primitives_spec.cr index 442228eabfa7..cbe5695beb4f 100644 --- a/spec/compiler/codegen/primitives_spec.cr +++ b/spec/compiler/codegen/primitives_spec.cr @@ -312,7 +312,7 @@ describe "Code gen: primitives" do describe "atomicrmw" do it "codegens atomicrmw with enums" do - run(<<-CR).to_i.should eq(3) + run(<<-CRYSTAL).to_i.should eq(3) enum RMWBinOp Add = 1 end @@ -328,11 +328,11 @@ describe "Code gen: primitives" do x = 1 atomicrmw(:add, pointerof(x), 2, :sequentially_consistent, false) x - CR + CRYSTAL end it "codegens atomicrmw with enums" do - run(<<-CR).to_i.should eq(3) + run(<<-CRYSTAL).to_i.should eq(3) enum RMWBinOp Add = 1 end @@ -348,12 +348,12 @@ describe "Code gen: primitives" do x = 1 atomicrmw(RMWBinOp::Add, pointerof(x), 2, Ordering::SequentiallyConsistent, false) x - CR + CRYSTAL end # TODO: remove once support for 1.4 is dropped it "codegens atomicrmw with symbols" do - run(<<-CR).to_i.should eq(3) + run(<<-CRYSTAL).to_i.should eq(3) @[Primitive(:atomicrmw)] def atomicrmw(op : Symbol, ptr : Int32*, val : Int32, ordering : Symbol, singlethread : Bool) : Int32 end @@ -361,7 +361,7 @@ describe "Code gen: primitives" do x = 1 atomicrmw(:add, pointerof(x), 2, :sequentially_consistent, false) x - CR + CRYSTAL end end diff --git a/spec/compiler/codegen/private_spec.cr b/spec/compiler/codegen/private_spec.cr index 1a5ebc1c8ea2..b2e3a0e11b75 100644 --- a/spec/compiler/codegen/private_spec.cr +++ b/spec/compiler/codegen/private_spec.cr @@ -43,17 +43,17 @@ describe "Codegen: private" do end it "codegens class var of private type with same name as public type (#11620)" do - src1 = Compiler::Source.new("foo.cr", <<-CR) + src1 = Compiler::Source.new("foo.cr", <<-CRYSTAL) module Foo @@x = true end - CR + CRYSTAL - src2 = Compiler::Source.new("foo_private.cr", <<-CR) + src2 = Compiler::Source.new("foo_private.cr", <<-CRYSTAL) private module Foo @@x = 1 end - CR + CRYSTAL compiler = create_spec_compiler compiler.prelude = "empty" @@ -63,17 +63,17 @@ describe "Codegen: private" do end it "codegens class vars of private types with same name (#11620)" do - src1 = Compiler::Source.new("foo1.cr", <<-CR) + src1 = Compiler::Source.new("foo1.cr", <<-CRYSTAL) private module Foo @@x = true end - CR + CRYSTAL - src2 = Compiler::Source.new("foo2.cr", <<-CR) + src2 = Compiler::Source.new("foo2.cr", <<-CRYSTAL) private module Foo @@x = 1 end - CR + CRYSTAL compiler = create_spec_compiler compiler.prelude = "empty" diff --git a/spec/compiler/codegen/proc_spec.cr b/spec/compiler/codegen/proc_spec.cr index 05f9650a7e42..217f2b8ba9a5 100644 --- a/spec/compiler/codegen/proc_spec.cr +++ b/spec/compiler/codegen/proc_spec.cr @@ -10,11 +10,11 @@ describe "Code gen: proc" do end it "call proc literal with return type" do - run(<<-CR).to_b.should be_true + run(<<-CRYSTAL).to_b.should be_true f = -> : Int32 | Float64 { 1 } x = f.call x.is_a?(Int32) && x == 1 - CR + CRYSTAL end it "call proc pointer" do diff --git a/spec/compiler/crystal/tools/doc/method_spec.cr b/spec/compiler/crystal/tools/doc/method_spec.cr index 52069555d6e2..7a605c4c0ef2 100644 --- a/spec/compiler/crystal/tools/doc/method_spec.cr +++ b/spec/compiler/crystal/tools/doc/method_spec.cr @@ -158,23 +158,23 @@ describe Doc::Method do end it "trailing comment is not a doc comment" do - program = semantic(<<-CR, inject_primitives: false, wants_doc: true).program + program = semantic(<<-CRYSTAL, inject_primitives: false, wants_doc: true).program nil # trailing comment def foo end - CR + CRYSTAL generator = Doc::Generator.new program, [""] method = generator.type(program).lookup_class_method("foo").not_nil! method.doc.should be_nil end it "trailing comment is not part of a doc comment" do - program = semantic(<<-CR, inject_primitives: false, wants_doc: true).program + program = semantic(<<-CRYSTAL, inject_primitives: false, wants_doc: true).program nil # trailing comment # doc comment def foo end - CR + CRYSTAL generator = Doc::Generator.new program, [""] method = generator.type(program).lookup_class_method("foo").not_nil! method.doc.should eq("doc comment") diff --git a/spec/compiler/crystal/tools/doc/type_spec.cr b/spec/compiler/crystal/tools/doc/type_spec.cr index 5f2dfca77fa0..73184dff873b 100644 --- a/spec/compiler/crystal/tools/doc/type_spec.cr +++ b/spec/compiler/crystal/tools/doc/type_spec.cr @@ -47,12 +47,12 @@ describe Doc::Type do describe "#node_to_html" do it "shows relative path" do - program = semantic(<<-CODE).program + program = semantic(<<-CRYSTAL).program class Foo class Bar end end - CODE + CRYSTAL generator = Doc::Generator.new program, [""] foo = generator.type(program.types["Foo"]) @@ -60,12 +60,12 @@ describe Doc::Type do end it "shows relative generic" do - program = semantic(<<-CODE).program + program = semantic(<<-CRYSTAL).program class Foo class Bar(T) end end - CODE + CRYSTAL generator = Doc::Generator.new program, [""] foo = generator.type(program.types["Foo"]) @@ -73,12 +73,12 @@ describe Doc::Type do end it "shows generic path with necessary colons" do - program = semantic(<<-CODE).program + program = semantic(<<-CRYSTAL).program class Foo class Foo end end - CODE + CRYSTAL generator = Doc::Generator.new program, [""] foo = generator.type(program.types["Foo"]) @@ -86,12 +86,12 @@ describe Doc::Type do end it "shows generic path with unnecessary colons" do - program = semantic(<<-CODE).program + program = semantic(<<-CRYSTAL).program class Foo class Bar end end - CODE + CRYSTAL generator = Doc::Generator.new program, [""] foo = generator.type(program.types["Foo"]) @@ -99,13 +99,13 @@ describe Doc::Type do end it "shows tuples" do - program = semantic(<<-CODE).program + program = semantic(<<-CRYSTAL).program class Foo end class Bar end - CODE + CRYSTAL generator = Doc::Generator.new program, [""] foo = generator.type(program.types["Foo"]) @@ -114,13 +114,13 @@ describe Doc::Type do end it "shows named tuples" do - program = semantic(<<-CODE).program + program = semantic(<<-CRYSTAL).program class Foo end class Bar end - CODE + CRYSTAL generator = Doc::Generator.new program, [""] foo = generator.type(program.types["Foo"]) @@ -130,7 +130,7 @@ describe Doc::Type do end it "ASTNode has no superclass" do - program = semantic(<<-CODE).program + program = semantic(<<-CRYSTAL).program module Crystal module Macros class ASTNode @@ -139,7 +139,7 @@ describe Doc::Type do end end end - CODE + CRYSTAL generator = Doc::Generator.new program, [""] macros_module = program.types["Crystal"].types["Macros"] @@ -150,7 +150,7 @@ describe Doc::Type do end it "ASTNode has no ancestors" do - program = semantic(<<-CODE).program + program = semantic(<<-CRYSTAL).program module Crystal module Macros class ASTNode @@ -159,7 +159,7 @@ describe Doc::Type do end end end - CODE + CRYSTAL generator = Doc::Generator.new program, [""] macros_module = program.types["Crystal"].types["Macros"] @@ -171,13 +171,13 @@ describe Doc::Type do describe "#instance_methods" do it "sorts operators first" do - program = semantic(<<-CODE).program + program = semantic(<<-CRYSTAL).program class Foo def foo; end def ~; end def +; end end - CODE + CRYSTAL generator = Doc::Generator.new program, [""] type = generator.type(program.types["Foo"]) @@ -187,13 +187,13 @@ describe Doc::Type do describe "#class_methods" do it "sorts operators first" do - program = semantic(<<-CODE).program + program = semantic(<<-CRYSTAL).program class Foo def self.foo; end def self.~; end def self.+; end end - CODE + CRYSTAL generator = Doc::Generator.new program, [""] type = generator.type(program.types["Foo"]) @@ -203,13 +203,13 @@ describe Doc::Type do describe "#macros" do it "sorts operators first" do - program = semantic(<<-CODE).program + program = semantic(<<-CRYSTAL).program class Foo macro foo; end macro ~; end macro +; end end - CODE + CRYSTAL generator = Doc::Generator.new program, [""] type = generator.type(program.types["Foo"]) diff --git a/spec/compiler/crystal/tools/doc_spec.cr b/spec/compiler/crystal/tools/doc_spec.cr index d6acc34b9f29..541a6867d97b 100644 --- a/spec/compiler/crystal/tools/doc_spec.cr +++ b/spec/compiler/crystal/tools/doc_spec.cr @@ -4,13 +4,13 @@ describe Crystal::Doc::Generator do describe ".anchor_link" do it "generates the correct anchor link" do Crystal::Doc.anchor_link("anchor").should eq( - <<-ANCHOR + <<-HTML - ANCHOR + HTML ) end end diff --git a/spec/compiler/crystal/tools/expand_spec.cr b/spec/compiler/crystal/tools/expand_spec.cr index 9d29afc1911a..a4eccdea49d4 100644 --- a/spec/compiler/crystal/tools/expand_spec.cr +++ b/spec/compiler/crystal/tools/expand_spec.cr @@ -102,109 +102,109 @@ describe "expand" do end it "expands macro control {% if %}" do - code = <<-CODE + code = <<-CRYSTAL {%‸ if 1 == 1 %} true {% end %} - CODE + CRYSTAL assert_expand_simple code, "true" end it "expands macro control {% if %} with cursor inside it" do - code = <<-CODE + code = <<-CRYSTAL {% if 1 == 1 %} tr‸ue {% end %} - CODE + CRYSTAL assert_expand_simple code, "true" end it "expands macro control {% if %} with cursor at end of it" do - code = <<-CODE + code = <<-CRYSTAL {% if 1 == 1 %} true {% end ‸%} - CODE + CRYSTAL assert_expand_simple code, "true" end it "expands macro control {% if %} with indent" do - code = <<-CODE + code = <<-CRYSTAL begin {% if 1 == 1 %} t‸rue {% end %} end - CODE + CRYSTAL - original = <<-CODE + original = <<-CRYSTAL {% if 1 == 1 %} true {% end %} - CODE + CRYSTAL assert_expand_simple code, original: original, expanded: "true" end it "expands macro control {% for %}" do - code = <<-CODE + code = <<-CRYSTAL {% f‸or x in 1..3 %} {{ x }} {% end %} - CODE + CRYSTAL assert_expand_simple code, "1\n2\n3\n" end it "expands macro control {% for %} with cursor inside it" do - code = <<-CODE + code = <<-CRYSTAL {% for x in 1..3 %} ‸ {{ x }} {% end %} - CODE + CRYSTAL assert_expand_simple code, "1\n2\n3\n" end it "expands macro control {% for %} with cursor at end of it" do - code = <<-CODE + code = <<-CRYSTAL {% for x in 1..3 %} {{ x }} ‸{% end %} - CODE + CRYSTAL assert_expand_simple code, "1\n2\n3\n" end it "expands macro control {% for %} with indent" do - code = <<-CODE + code = <<-CRYSTAL begin {% f‸or x in 1..3 %} {{ x }} {% end %} end - CODE + CRYSTAL - original = <<-CODE + original = <<-CRYSTAL {% for x in 1..3 %} {{ x }} {% end %} - CODE + CRYSTAL assert_expand_simple code, original: original, expanded: "1\n2\n3\n" end it "expands simple macro" do - code = <<-CODE + code = <<-CRYSTAL macro foo 1 end ‸foo - CODE + CRYSTAL assert_expand_simple code, original: "foo", expanded: "1" do |expansion| expansion.expanded_macros.size.should eq(1) @@ -220,31 +220,31 @@ describe "expand" do end it "expands simple macro with cursor inside it" do - code = <<-CODE + code = <<-CRYSTAL macro foo 1 end f‸oo - CODE + CRYSTAL assert_expand_simple code, original: "foo", expanded: "1" end it "expands simple macro with cursor at end of it" do - code = <<-CODE + code = <<-CRYSTAL macro foo 1 end fo‸o - CODE + CRYSTAL assert_expand_simple code, original: "foo", expanded: "1" end it "expands complex macro" do - code = <<-CODE + code = <<-CRYSTAL macro foo {% if true %} "if true" @@ -255,13 +255,13 @@ describe "expand" do end ‸foo - CODE + CRYSTAL assert_expand_simple code, original: "foo", expanded: %("if true"\n"1"\n"2"\n"3"\n) end it "expands macros with 2 level" do - code = <<-CODE + code = <<-CRYSTAL macro foo :foo end @@ -272,7 +272,7 @@ describe "expand" do end b‸ar - CODE + CRYSTAL assert_expand code, [["bar", "foo\n:bar\n", ":foo\n:bar\n"]] do |result| expansion = result.expansions.not_nil![0] @@ -297,7 +297,7 @@ describe "expand" do end it "expands macros with 3 level" do - code = <<-CODE + code = <<-CRYSTAL macro foo :foo end @@ -314,7 +314,7 @@ describe "expand" do end ba‸z - CODE + CRYSTAL assert_expand code, [["baz", "foo\nbar\n:baz\n", ":foo\nfoo\n:bar\n:baz\n", ":foo\n:foo\n:bar\n:baz\n"]] do |result| expansion = result.expansions.not_nil![0] @@ -352,7 +352,7 @@ describe "expand" do end it "expands macro of module" do - code = <<-CODE + code = <<-CRYSTAL module Foo macro foo :Foo @@ -361,7 +361,7 @@ describe "expand" do end Foo.f‸oo - CODE + CRYSTAL assert_expand_simple code, original: "Foo.foo", expanded: ":Foo\n:foo\n" do |expansion| expansion.expanded_macros.size.should eq(1) @@ -377,7 +377,7 @@ describe "expand" do end it "expands macro of module with cursor at module name" do - code = <<-CODE + code = <<-CRYSTAL module Foo macro foo :Foo @@ -386,13 +386,13 @@ describe "expand" do end F‸oo.foo - CODE + CRYSTAL assert_expand_simple code, original: "Foo.foo", expanded: ":Foo\n:foo\n" end it "expands macro of module with cursor at dot" do - code = <<-CODE + code = <<-CRYSTAL module Foo macro foo :Foo @@ -401,13 +401,13 @@ describe "expand" do end Foo‸.foo - CODE + CRYSTAL assert_expand_simple code, original: "Foo.foo", expanded: ":Foo\n:foo\n" end it "expands macro of module inside module" do - code = <<-CODE + code = <<-CRYSTAL module Foo macro foo :Foo @@ -416,35 +416,35 @@ describe "expand" do f‸oo end - CODE + CRYSTAL assert_expand_simple code, original: "foo", expanded: ":Foo\n:foo\n" end %w(module class struct enum lib).each do |keyword| it "expands macro expression inside #{keyword}" do - code = <<-CODE + code = <<-CRYSTAL #{keyword} Foo ‸{{ "Foo = 1".id }} end - CODE + CRYSTAL assert_expand_simple code, original: %({{ "Foo = 1".id }}), expanded: "Foo = 1" end it "expands macro expression inside private #{keyword}" do - code = <<-CODE + code = <<-CRYSTAL private #{keyword} Foo ‸{{ "Foo = 1".id }} end - CODE + CRYSTAL assert_expand_simple code, original: %({{ "Foo = 1".id }}), expanded: "Foo = 1" end unless keyword == "lib" it "expands macro expression inside def of private #{keyword}" do - code = <<-CODE + code = <<-CRYSTAL private #{keyword} Foo Foo = 1 def self.foo @@ -453,7 +453,7 @@ describe "expand" do end Foo.foo - CODE + CRYSTAL assert_expand_simple code, original: "{{ :foo }}", expanded: ":foo" end @@ -462,25 +462,25 @@ describe "expand" do %w(struct union).each do |keyword| it "expands macro expression inside C #{keyword}" do - code = <<-CODE + code = <<-CRYSTAL lib Foo #{keyword} Foo ‸{{ "x : Int32".id }} end end - CODE + CRYSTAL assert_expand_simple code, original: %({{ "x : Int32".id }}), expanded: "x : Int32" end it "expands macro expression inside C #{keyword} of private lib" do - code = <<-CODE + code = <<-CRYSTAL private lib Foo #{keyword} Foo ‸{{ "x : Int32".id }} end end - CODE + CRYSTAL assert_expand_simple code, original: %({{ "x : Int32".id }}), expanded: "x : Int32" end @@ -488,14 +488,14 @@ describe "expand" do ["", "private "].each do |prefix| it "expands macro expression inside #{prefix}def" do - code = <<-CODE + code = <<-CRYSTAL #{prefix}def foo(x : T) forall T ‸{{ T }} end foo 1 foo "bar" - CODE + CRYSTAL assert_expand code, [ ["{{ T }}", "Int32"], @@ -504,7 +504,7 @@ describe "expand" do end it "expands macro expression inside def of #{prefix}module" do - code = <<-CODE + code = <<-CRYSTAL #{prefix}module Foo(T) def self.foo {{ ‸T }} @@ -514,7 +514,7 @@ describe "expand" do Foo(Int32).foo Foo(String).foo Foo(1).foo - CODE + CRYSTAL assert_expand code, [ ["{{ T }}", "Int32"], @@ -524,7 +524,7 @@ describe "expand" do end it "expands macro expression inside def of nested #{prefix}module" do - code = <<-CODE + code = <<-CRYSTAL #{prefix}module Foo #{prefix}module Bar(T) def self.foo @@ -536,7 +536,7 @@ describe "expand" do Bar(String).foo Bar(1).foo end - CODE + CRYSTAL assert_expand code, [ ["{{ T }}", "Int32"], @@ -547,54 +547,54 @@ describe "expand" do end it "expands macro expression inside fun" do - code = <<-CODE + code = <<-CRYSTAL fun foo {{ :foo‸ }} end - CODE + CRYSTAL assert_expand_simple code, original: "{{ :foo }}", expanded: ":foo" end it "doesn't expand macro expression" do - code = <<-CODE + code = <<-CRYSTAL {{ 1 + 2 }} ‸ - CODE + CRYSTAL assert_expand_fail code end it "doesn't expand macro expression with cursor out of end" do - code = <<-CODE + code = <<-CRYSTAL {{ 1 + 2 }}‸ - CODE + CRYSTAL assert_expand_fail code end it "doesn't expand macro expression" do - code = <<-CODE + code = <<-CRYSTAL ‸ {{ 1 + 2 }} - CODE + CRYSTAL assert_expand_fail code end it "doesn't expand normal call" do - code = <<-CODE + code = <<-CRYSTAL def foo 1 end ‸foo - CODE + CRYSTAL assert_expand_fail code, "no expansion found: foo may not be a macro" end it "expands macro with doc" do - code = <<-CODE + code = <<-CRYSTAL macro foo(x) # string of {{ x }} def {{ x }}_str @@ -607,9 +607,9 @@ describe "expand" do end ‸foo(hello) - CODE + CRYSTAL - expanded = <<-CODE + expanded = <<-CRYSTAL # string of hello def hello_str "hello" @@ -618,7 +618,7 @@ describe "expand" do def hello_sym :hello end - CODE + CRYSTAL assert_expand_simple code, original: "foo(hello)", expanded: expanded + '\n' end diff --git a/spec/compiler/crystal/tools/hierarchy_spec.cr b/spec/compiler/crystal/tools/hierarchy_spec.cr index b2c80fa6e018..9e01acded7ec 100644 --- a/spec/compiler/crystal/tools/hierarchy_spec.cr +++ b/spec/compiler/crystal/tools/hierarchy_spec.cr @@ -2,13 +2,13 @@ require "../../../spec_helper" describe Crystal::TextHierarchyPrinter do it "works" do - program = semantic(<<-CR).program + program = semantic(<<-CRYSTAL).program class Foo end class Bar < Foo end - CR + CRYSTAL output = String.build { |io| Crystal.print_hierarchy(program, io, "ar$", "text") } output.should eq(<<-EOS) @@ -25,13 +25,13 @@ end describe Crystal::JSONHierarchyPrinter do it "works" do - program = semantic(<<-CR).program + program = semantic(<<-CRYSTAL).program class Foo end class Bar < Foo end - CR + CRYSTAL output = String.build { |io| Crystal.print_hierarchy(program, io, "ar$", "json") } JSON.parse(output).should eq(JSON.parse(<<-EOS)) diff --git a/spec/compiler/crystal/tools/init_spec.cr b/spec/compiler/crystal/tools/init_spec.cr index 05e52ff10da7..1408b0d4e0af 100644 --- a/spec/compiler/crystal/tools/init_spec.cr +++ b/spec/compiler/crystal/tools/init_spec.cr @@ -45,11 +45,11 @@ module Crystal describe Init::InitProject do it "correctly uses git config" do within_temporary_directory do - File.write(".gitconfig", <<-CONTENT) + File.write(".gitconfig", <<-INI) [user] email = dorian@dorianmarie.fr name = Dorian Marié - CONTENT + INI with_env("GIT_CONFIG": "#{FileUtils.pwd}/.gitconfig") do exec_init("example", "example", "app") @@ -118,7 +118,7 @@ module Crystal readme.should contain("# example") readme.should contain(%{1. Add the dependency to your `shard.yml`:}) - readme.should contain(<<-EOF + readme.should contain(<<-MARKDOWN ```yaml dependencies: @@ -126,7 +126,7 @@ module Crystal github: jsmith/example ``` - EOF + MARKDOWN ) readme.should contain(%{2. Run `shards install`}) readme.should contain(%{TODO: Write a description here}) @@ -142,7 +142,7 @@ module Crystal readme.should contain(%{TODO: Write a description here}) readme.should_not contain(%{1. Add the dependency to your `shard.yml`:}) - readme.should_not contain(<<-EOF + readme.should_not contain(<<-MARKDOWN ```yaml dependencies: @@ -150,7 +150,7 @@ module Crystal github: jsmith/example ``` - EOF + MARKDOWN ) readme.should_not contain(%{2. Run `shards install`}) readme.should contain(%{TODO: Write installation instructions here}) @@ -175,7 +175,7 @@ module Crystal end with_file "example/src/example.cr" do |example| - example.should eq(<<-EOF + example.should eq(<<-CRYSTAL # TODO: Write documentation for `Example` module Example VERSION = "0.1.0" @@ -183,21 +183,21 @@ module Crystal # TODO: Put your code here end - EOF + CRYSTAL ) end with_file "example/spec/spec_helper.cr" do |example| - example.should eq(<<-EOF + example.should eq(<<-CRYSTAL require "spec" require "../src/example" - EOF + CRYSTAL ) end with_file "example/spec/example_spec.cr" do |example| - example.should eq(<<-EOF + example.should eq(<<-CRYSTAL require "./spec_helper" describe Example do @@ -208,7 +208,7 @@ module Crystal end end - EOF + CRYSTAL ) end diff --git a/spec/compiler/crystal/tools/playground_spec.cr b/spec/compiler/crystal/tools/playground_spec.cr index ae784db3335b..2fe1a9b722f7 100644 --- a/spec/compiler/crystal/tools/playground_spec.cr +++ b/spec/compiler/crystal/tools/playground_spec.cr @@ -141,22 +141,22 @@ describe Playground::AgentInstrumentorTransformer do assert_agent %( def foo 4 - end), <<-CR + end), <<-CRYSTAL def foo _p.i(3) { 4 } end - CR + CRYSTAL end it "instrument single statement var def" do assert_agent %( def foo(x) x - end), <<-CR + end), <<-CRYSTAL def foo(x) _p.i(3) { x } end - CR + CRYSTAL end it "instrument multi statement def" do @@ -164,23 +164,23 @@ describe Playground::AgentInstrumentorTransformer do def foo 2 6 - end), <<-CR + end), <<-CRYSTAL def foo _p.i(3) { 2 } _p.i(4) { 6 } end - CR + CRYSTAL end it "instrument returns inside def" do assert_agent %( def foo return 4 - end), <<-CR + end), <<-CRYSTAL def foo return _p.i(3) { 4 } end - CR + CRYSTAL end it "instrument class defs" do @@ -196,7 +196,7 @@ describe Playground::AgentInstrumentorTransformer do def self.bar(x, y) x+y end - end), <<-CR + end), <<-CRYSTAL class Foo def initialize @x = _p.i(4) { 3 }.as(typeof(3)) @@ -209,7 +209,7 @@ describe Playground::AgentInstrumentorTransformer do _p.i(11) { x + y } end end - CR + CRYSTAL end it "instrument instance variable and class variables reads and writes" do @@ -225,7 +225,7 @@ describe Playground::AgentInstrumentorTransformer do def self.bar @@x end - end), <<-CR + end), <<-CRYSTAL class Foo def initialize @x = _p.i(4) { 3 }.as(typeof(3)) @@ -238,7 +238,7 @@ describe Playground::AgentInstrumentorTransformer do _p.i(11) { @@x } end end - CR + CRYSTAL end it "do not instrument class initializing arguments" do @@ -248,7 +248,7 @@ describe Playground::AgentInstrumentorTransformer do @z = @x + @y end end - ), <<-CR + ), <<-CRYSTAL class Foo def initialize(x, y) @x = x @@ -256,7 +256,7 @@ describe Playground::AgentInstrumentorTransformer do @z = _p.i(4) { @x + @y }.as(typeof(@x + @y)) end end - CR + CRYSTAL end it "allow visibility modifiers" do @@ -268,7 +268,7 @@ describe Playground::AgentInstrumentorTransformer do protected def self.bar 2 end - end), <<-CR + end), <<-CRYSTAL class Foo private def bar _p.i(4) { 1 } @@ -277,18 +277,18 @@ describe Playground::AgentInstrumentorTransformer do _p.i(7) { 2 } end end - CR + CRYSTAL end it "do not instrument macro calls in class" do assert_agent %( class Foo property foo - end), <<-CR + end), <<-CRYSTAL class Foo property foo end - CR + CRYSTAL end it "instrument nested class defs" do @@ -299,7 +299,7 @@ describe Playground::AgentInstrumentorTransformer do @x = 3 end end - end), <<-CR + end), <<-CRYSTAL class Bar class Foo def initialize @@ -307,19 +307,19 @@ describe Playground::AgentInstrumentorTransformer do end end end - CR + CRYSTAL end it "do not instrument records class" do assert_agent %( record Foo, x, y - ), <<-CR + ), <<-CRYSTAL record Foo, x, y - CR + CRYSTAL end it "do not instrument top level macro calls" do - assert_agent(<<-FROM, <<-TO + assert_agent(<<-CRYSTAL, <<-CRYSTAL) macro bar def foo 4 @@ -327,7 +327,7 @@ describe Playground::AgentInstrumentorTransformer do end bar foo - FROM + CRYSTAL macro bar def foo 4 @@ -335,12 +335,11 @@ describe Playground::AgentInstrumentorTransformer do end bar _p.i(7) { foo } - TO - ) + CRYSTAL end it "do not instrument class/module declared macro" do - assert_agent(<<-FROM, <<-TO + assert_agent(<<-CRYSTAL, <<-CRYSTAL) module Bar macro bar 4 @@ -354,7 +353,7 @@ describe Playground::AgentInstrumentorTransformer do 8 end end - FROM + CRYSTAL module Bar macro bar 4 @@ -368,8 +367,7 @@ describe Playground::AgentInstrumentorTransformer do _p.i(11) { 8 } end end - TO - ) + CRYSTAL end it "instrument inside modules" do @@ -382,7 +380,7 @@ describe Playground::AgentInstrumentorTransformer do end end end - end), <<-CR + end), <<-CRYSTAL module Bar class Baz class Foo @@ -392,7 +390,7 @@ describe Playground::AgentInstrumentorTransformer do end end end - CR + CRYSTAL end it "instrument if statement" do @@ -402,13 +400,13 @@ describe Playground::AgentInstrumentorTransformer do else c end - ), <<-CR + ), <<-CRYSTAL if a _p.i(3) { b } else _p.i(5) { c } end - CR + CRYSTAL end it "instrument unless statement" do @@ -418,13 +416,13 @@ describe Playground::AgentInstrumentorTransformer do else c end - ), <<-CR + ), <<-CRYSTAL unless a _p.i(3) { b } else _p.i(5) { c } end - CR + CRYSTAL end it "instrument while statement" do @@ -433,12 +431,12 @@ describe Playground::AgentInstrumentorTransformer do b c end - ), <<-CR + ), <<-CRYSTAL while a _p.i(3) { b } _p.i(4) { c } end - CR + CRYSTAL end it "instrument case statement" do @@ -452,7 +450,7 @@ describe Playground::AgentInstrumentorTransformer do else d end - ), <<-CR + ), <<-CRYSTAL case a when 0 _p.i(4) { b } @@ -461,7 +459,7 @@ describe Playground::AgentInstrumentorTransformer do else _p.i(8) { d } end - CR + CRYSTAL end it "instrument blocks and single yields" do @@ -472,7 +470,7 @@ describe Playground::AgentInstrumentorTransformer do foo do |a| a end - ), <<-CR + ), <<-CRYSTAL def foo(x) yield _p.i(3) { x } end @@ -481,7 +479,7 @@ describe Playground::AgentInstrumentorTransformer do _p.i(6) { a } end end - CR + CRYSTAL end it "instrument blocks and but non multi yields" do @@ -492,7 +490,7 @@ describe Playground::AgentInstrumentorTransformer do foo do |a, i| a end - ), <<-CR + ), <<-CRYSTAL def foo(x) yield x, 1 end @@ -501,7 +499,7 @@ describe Playground::AgentInstrumentorTransformer do _p.i(6) { a } end end - CR + CRYSTAL end it "instrument nested blocks unless in same line" do @@ -513,7 +511,7 @@ describe Playground::AgentInstrumentorTransformer do end baz { 'c' } end - ), <<-CR + ), <<-CRYSTAL a = _p.i(2) do foo do _p.i(3) { 'a' } @@ -529,7 +527,7 @@ describe Playground::AgentInstrumentorTransformer do end end end - CR + CRYSTAL end it "instrument typeof" do @@ -554,7 +552,7 @@ describe Playground::AgentInstrumentorTransformer do rescue 0 end - ), <<-CR + ), <<-CRYSTAL begin raise(_p.i(3) { "The exception" }) rescue ex : String @@ -573,7 +571,7 @@ describe Playground::AgentInstrumentorTransformer do _p.i(16) { 0 } end end - CR + CRYSTAL end end diff --git a/spec/compiler/formatter/formatter_spec.cr b/spec/compiler/formatter/formatter_spec.cr index f8f8d5fddd0e..5731cbe68815 100644 --- a/spec/compiler/formatter/formatter_spec.cr +++ b/spec/compiler/formatter/formatter_spec.cr @@ -308,15 +308,15 @@ describe Crystal::Formatter do ); end CRYSTAL - assert_format <<-BEFORE, <<-AFTER + assert_format <<-CRYSTAL, <<-CRYSTAL def foo( @[MyAnn] bar ); end - BEFORE + CRYSTAL def foo( @[MyAnn] bar ); end - AFTER + CRYSTAL assert_format <<-CRYSTAL def foo( @@ -352,31 +352,31 @@ describe Crystal::Formatter do ); end CRYSTAL - assert_format <<-BEFORE, <<-AFTER + assert_format <<-CRYSTAL, <<-CRYSTAL def foo( @[MyAnn] bar ); end - BEFORE + CRYSTAL def foo( @[MyAnn] bar ); end - AFTER + CRYSTAL - assert_format <<-BEFORE, <<-AFTER + assert_format <<-CRYSTAL, <<-CRYSTAL def foo( - @[MyAnn] + @[MyAnn] bar ); end - BEFORE + CRYSTAL def foo( @[MyAnn] bar ); end - AFTER + CRYSTAL - assert_format <<-BEFORE + assert_format <<-CRYSTAL def foo( @[MyAnn] @[MyAnn] @@ -386,9 +386,9 @@ describe Crystal::Formatter do @[MyAnn] biz ); end - BEFORE + CRYSTAL - assert_format <<-BEFORE + assert_format <<-CRYSTAL def foo( @[MyAnn] @[MyAnn] @@ -400,12 +400,12 @@ describe Crystal::Formatter do @[MyAnn] biz ); end - BEFORE + CRYSTAL - assert_format <<-BEFORE, <<-AFTER + assert_format <<-CRYSTAL, <<-CRYSTAL def foo( - @[MyAnn] - @[MyAnn] + @[MyAnn] + @[MyAnn] bar, @[MyAnn] @[MyAnn] baz, @@ -416,7 +416,7 @@ describe Crystal::Formatter do biz ); end - BEFORE + CRYSTAL def foo( @[MyAnn] @[MyAnn] @@ -428,7 +428,7 @@ describe Crystal::Formatter do @[MyAnn] biz ); end - AFTER + CRYSTAL assert_format "loop do\n 1\nrescue\n 2\nend" assert_format "loop do\n 1\n loop do\n 2\n rescue\n 3\n end\n 4\nend" @@ -770,12 +770,12 @@ describe Crystal::Formatter do assert_format "case 1 \n in Int32 \n 3 \n end", "case 1\nin Int32\n 3\nend" - assert_format <<-CODE + assert_format <<-CRYSTAL case 0 when 0 then 1; 2 # Comments end - CODE + CRYSTAL assert_format "select \n when foo \n 2 \n end", "select\nwhen foo\n 2\nend" assert_format "select \n when foo \n 2 \n when bar \n 3 \n end", "select\nwhen foo\n 2\nwhen bar\n 3\nend" @@ -916,12 +916,12 @@ describe Crystal::Formatter do assert_format "alias Foo::Bar=Baz", "alias Foo::Bar = Baz" assert_format "alias Foo::Bar= Baz", "alias Foo::Bar = Baz" assert_format "alias Foo::Bar =Baz", "alias Foo::Bar = Baz" - assert_format <<-BEFORE, <<-AFTER + assert_format <<-CRYSTAL, <<-CRYSTAL alias Foo= Bar - BEFORE + CRYSTAL alias Foo = Bar - AFTER + CRYSTAL assert_format "lib Foo\nend" assert_format "lib Foo\ntype Foo = Bar\nend", "lib Foo\n type Foo = Bar\nend" assert_format "lib Foo\nfun foo\nend", "lib Foo\n fun foo\nend" @@ -938,81 +938,81 @@ describe Crystal::Formatter do assert_format "lib Foo\n fun foo(Int32) : Int32\nend" assert_format "fun foo(x : Int32) : Int32\n 1\nend" assert_format "fun foo(\n x : Int32,\n ...\n) : Int32\n 1\nend" - assert_format <<-CODE + assert_format <<-CRYSTAL lib Foo fun foo = bar(Int32) : Int32 end - CODE - assert_format <<-CODE + CRYSTAL + assert_format <<-CRYSTAL lib Foo fun foo = bar : Void end - CODE - assert_format <<-BEFORE, <<-AFTER + CRYSTAL + assert_format <<-CRYSTAL, <<-CRYSTAL lib Foo fun foo = bar : Void end - BEFORE + CRYSTAL lib Foo fun foo = bar : Void end - AFTER - assert_format <<-CODE + CRYSTAL + assert_format <<-CRYSTAL lib Foo fun foo = bar(Int32) : Int32 end - CODE - assert_format <<-BEFORE, <<-AFTER + CRYSTAL + assert_format <<-CRYSTAL, <<-CRYSTAL lib Foo fun foo = bar(Int32) : Int32 end - BEFORE + CRYSTAL lib Foo fun foo = bar(Int32) : Int32 end - AFTER - assert_format <<-BEFORE, <<-AFTER + CRYSTAL + assert_format <<-CRYSTAL, <<-CRYSTAL lib Foo fun foo = bar(Int32, Int32) : Int32 end - BEFORE + CRYSTAL lib Foo fun foo = bar(Int32, Int32) : Int32 end - AFTER + CRYSTAL assert_format "lib Foo\n fun foo = bar(Int32) : Int32\nend" - assert_format <<-CODE + assert_format <<-CRYSTAL lib Foo fun foo = "bar"(Int32) : Int32 end - CODE - assert_format <<-CODE + CRYSTAL + assert_format <<-CRYSTAL lib Foo fun foo = "bar"(Int32) : Int32 end - CODE - assert_format <<-CODE + CRYSTAL + assert_format <<-CRYSTAL lib Foo fun foo = "bar"(Int32) : Int32 # comment end - CODE + CRYSTAL assert_format "lib Foo\n $foo : Int32 \nend", "lib Foo\n $foo : Int32\nend" assert_format "lib Foo\n $foo = hello : Int32 \nend", "lib Foo\n $foo = hello : Int32\nend" assert_format "lib Foo\nalias Foo = Bar -> \n$a : Int32\nend", "lib Foo\n alias Foo = Bar ->\n $a : Int32\nend" @@ -1642,13 +1642,13 @@ describe Crystal::Formatter do assert_format "{%\n if 1\n 2\n end\n%}" - assert_format <<-CODE + assert_format <<-CRYSTAL # ```text # 1 + 2 # ``` - CODE + CRYSTAL - assert_format <<-CODE + assert_format <<-CRYSTAL # ```text # 1 + 2 # ``` @@ -1656,19 +1656,19 @@ describe Crystal::Formatter do # ``` # 3 + 4 # ``` - CODE + CRYSTAL - assert_format <<-CODE + assert_format <<-CRYSTAL X(typeof(begin e.is_a?(Y) ? 1 : 2 end)) - CODE + CRYSTAL - assert_format <<-CODE + assert_format <<-CRYSTAL X(typeof(begin e.is_a?(Y) end)) - CODE + CRYSTAL # Keep trailing spaces in macros. assert_format( @@ -1762,7 +1762,7 @@ describe Crystal::Formatter do assert_format "foo\n .bar\n .baz(\n 1\n )" assert_format "foo.bar\n .baz(\n 1\n )" - assert_format <<-BEFORE, + assert_format <<-CRYSTAL, def foo {% if flag?(:foo) %} foo + bar @@ -1770,8 +1770,8 @@ describe Crystal::Formatter do baz + qux {% end %} end - BEFORE - <<-AFTER + CRYSTAL + <<-CRYSTAL def foo {% if flag?(:foo) %} foo + bar @@ -1779,39 +1779,39 @@ describe Crystal::Formatter do baz + qux {% end %} end - AFTER + CRYSTAL - assert_format <<-BEFORE, + assert_format <<-CRYSTAL, def foo {% for x in y %} foo + bar {% end %} end - BEFORE - <<-AFTER + CRYSTAL + <<-CRYSTAL def foo {% for x in y %} foo + bar {% end %} end - AFTER + CRYSTAL - assert_format <<-BEFORE, + assert_format <<-CRYSTAL, x = {% if flag?(:foo) %} foo + bar {% else %} baz + qux {% end %} - BEFORE - <<-AFTER + CRYSTAL + <<-CRYSTAL x = {% if flag?(:foo) %} foo + bar {% else %} baz + qux {% end %} - AFTER + CRYSTAL - assert_format <<-CODE + assert_format <<-CRYSTAL {% if flag?(:freebsd) %} 1 + 2 {% end %} @@ -1820,9 +1820,9 @@ describe Crystal::Formatter do when 1234 then 1 else x end - CODE + CRYSTAL - assert_format <<-CODE + assert_format <<-CRYSTAL {% if z %} 1 {% end %} @@ -1834,17 +1834,17 @@ describe Crystal::Formatter do 1 end - CODE + CRYSTAL - assert_format <<-CODE + assert_format <<-CRYSTAL lib LibFoo {% begin %} fun foo : Int32 {% end %} end - CODE + CRYSTAL - assert_format <<-CODE + assert_format <<-CRYSTAL lib LibFoo struct Bar {% begin %} @@ -1852,9 +1852,9 @@ describe Crystal::Formatter do {% end %} end end - CODE + CRYSTAL - assert_format <<-CODE + assert_format <<-CRYSTAL enum Foo {% begin %} A @@ -1862,9 +1862,9 @@ describe Crystal::Formatter do C {% end %} end - CODE + CRYSTAL - assert_format <<-CODE + assert_format <<-CRYSTAL a = 1 b, c = 2, 3 {% begin %} @@ -1872,50 +1872,50 @@ describe Crystal::Formatter do b |= 2 c |= 3 {% end %} - CODE + CRYSTAL - assert_format <<-CODE + assert_format <<-CRYSTAL lib LibFoo {% begin %} fun x = y(Int32) {% end %} end - CODE + CRYSTAL - assert_format <<-CODE + assert_format <<-CRYSTAL {% begin %} " foo" {% end %} - CODE + CRYSTAL - assert_format <<-BEFORE, + assert_format <<-CRYSTAL, {% if z %} class Foo end {% end %} - BEFORE - <<-AFTER + CRYSTAL + <<-CRYSTAL {% if z %} class Foo end {% end %} - AFTER + CRYSTAL - assert_format <<-CODE + assert_format <<-CRYSTAL {% if true %} # x {% end %} - CODE + CRYSTAL - assert_format <<-CODE + assert_format <<-CRYSTAL {% if true %} # x # y {% end %} - CODE + CRYSTAL - assert_format <<-CODE + assert_format <<-CRYSTAL {% if true %} # x # @@ -1924,17 +1924,17 @@ describe Crystal::Formatter do # ``` # x # ``` - CODE + CRYSTAL - assert_format <<-CODE + assert_format <<-CRYSTAL def foo(x) {% if true %} x = x + 2 {% end %} end - CODE + CRYSTAL - assert_format <<-CODE + assert_format <<-CRYSTAL def foo(x) {% if true %} # comment @@ -1942,9 +1942,9 @@ describe Crystal::Formatter do B = 2 {% end %} end - CODE + CRYSTAL - assert_format <<-CODE + assert_format <<-CRYSTAL def foo(x) {% if true %} \\{% if true %} @@ -1959,17 +1959,17 @@ describe Crystal::Formatter do \\{% x %} {% end %} end - CODE + CRYSTAL it "gives proper line number in syntax error inside macro" do - source = <<-CODE + source = <<-CRYSTAL a = 1 b = 2 {% begin %} c |= 3 {% end %} - CODE + CRYSTAL ex = expect_raises(Crystal::SyntaxException) do Crystal.format(source) @@ -1978,52 +1978,52 @@ describe Crystal::Formatter do end # #8197 - assert_format <<-CODE + assert_format <<-CRYSTAL foo .foo1(bar .bar1 .bar2) - CODE + CRYSTAL - assert_format <<-CODE + assert_format <<-CRYSTAL foo.foo1( bar .bar1 .bar2) - CODE + CRYSTAL assert_format "[] of (Array(T))" assert_format "[] of (((Array(T))))" - assert_format <<-CODE + assert_format <<-CRYSTAL macro foo # bar baz end - CODE + CRYSTAL assert_format "a.!" assert_format "a &.!" assert_format "a &.a.!" assert_format "a &.!.!" - assert_format <<-CODE + assert_format <<-CRYSTAL ->{ # first comment puts "hi" # second comment } - CODE + CRYSTAL # #9014 - assert_format <<-CODE + assert_format <<-CRYSTAL {% unless true 1 end %} - CODE + CRYSTAL - assert_format <<-CODE + assert_format <<-CRYSTAL {% unless true 1 @@ -2031,9 +2031,9 @@ describe Crystal::Formatter do 2 end %} - CODE + CRYSTAL - assert_format <<-CODE + assert_format <<-CRYSTAL {% if true 1 @@ -2041,48 +2041,48 @@ describe Crystal::Formatter do 2 end %} - CODE + CRYSTAL # #4626 - assert_format <<-CODE + assert_format <<-CRYSTAL 1 # foo / 1 / - CODE + CRYSTAL - assert_format <<-CODE + assert_format <<-CRYSTAL 1 # foo / #{1} / - CODE + CRYSTAL - assert_format <<-BEFORE, + assert_format <<-CRYSTAL, def foo # Comment end - BEFORE - <<-AFTER + CRYSTAL + <<-CRYSTAL def foo # Comment end - AFTER + CRYSTAL - assert_format <<-BEFORE, + assert_format <<-CRYSTAL, def foo 1 # Comment end - BEFORE - <<-AFTER + CRYSTAL + <<-CRYSTAL def foo 1 # Comment end - AFTER + CRYSTAL - assert_format <<-CODE + assert_format <<-CRYSTAL def foo 1 end @@ -2092,142 +2092,142 @@ describe Crystal::Formatter do def bar 2 end - CODE + CRYSTAL - assert_format <<-CODE + assert_format <<-CRYSTAL require "foo" @x : Int32 class Bar end - CODE + CRYSTAL - assert_format <<-CODE + assert_format <<-CRYSTAL x = <<-FOO hello FOO def bar end - CODE + CRYSTAL - assert_format <<-BEFORE, <<-AFTER + assert_format <<-CRYSTAL, <<-CRYSTAL begin 1 # Comment end - BEFORE + CRYSTAL begin 1 # Comment end - AFTER + CRYSTAL - assert_format <<-BEFORE, <<-AFTER + assert_format <<-CRYSTAL, <<-CRYSTAL begin # Comment end - BEFORE + CRYSTAL begin # Comment end - AFTER + CRYSTAL - assert_format <<-CODE + assert_format <<-CRYSTAL foo 1, # comment do end - CODE + CRYSTAL - assert_format <<-CODE + assert_format <<-CRYSTAL foo 1, # comment # bar do end - CODE + CRYSTAL # #10190 - assert_format <<-CODE + assert_format <<-CRYSTAL foo( 1, ) do 2 end - CODE + CRYSTAL - assert_format <<-CODE + assert_format <<-CRYSTAL foo( 1, ) { 2 } - CODE + CRYSTAL # #11079 - assert_format <<-CODE + assert_format <<-CRYSTAL foo = [1, [2, 3], 4] - CODE + CRYSTAL - assert_format <<-CODE + assert_format <<-CRYSTAL foo = {1, {2, 3}, 4} - CODE + CRYSTAL # #10817 - assert_format <<-CODE + assert_format <<-CRYSTAL def func # comment (1 + 2) / 3 end - CODE + CRYSTAL # #10943 - assert_format <<-CODE + assert_format <<-CRYSTAL foo do # a # b bar end - CODE + CRYSTAL # #10499 - assert_format <<-CODE + assert_format <<-CRYSTAL case nil else nil; nil # comment end - CODE + CRYSTAL - assert_format <<-CODE + assert_format <<-CRYSTAL case nil else nil; nil # comment end - CODE + CRYSTAL # #12493 - assert_format <<-CODE + assert_format <<-CRYSTAL select # when foo when bar break end - CODE + CRYSTAL - assert_format <<-CODE + assert_format <<-CRYSTAL select # some comment when bar break end - CODE + CRYSTAL # #12378 - assert_format <<-CODE + assert_format <<-CRYSTAL macro foo macro bar \\{% begin %} @@ -2235,5 +2235,5 @@ describe Crystal::Formatter do \\{% end %} end end - CODE + CRYSTAL end diff --git a/spec/compiler/interpreter/autocast_spec.cr b/spec/compiler/interpreter/autocast_spec.cr index 0aa9e93c250c..701ceeefd0bd 100644 --- a/spec/compiler/interpreter/autocast_spec.cr +++ b/spec/compiler/interpreter/autocast_spec.cr @@ -4,7 +4,7 @@ require "./spec_helper" describe Crystal::Repl::Interpreter do context "autocast" do it "autocasts symbol to enum" do - interpret(<<-CODE).should eq(1) + interpret(<<-CRYSTAL).should eq(1) enum Color Red Green @@ -17,31 +17,31 @@ describe Crystal::Repl::Interpreter do c = foo :green c.value - CODE + CRYSTAL end it "autocasts number literal to integer" do - interpret(<<-CODE).should eq(12) + interpret(<<-CRYSTAL).should eq(12) def foo(x : UInt8) x end foo(12) - CODE + CRYSTAL end it "autocasts number literal to float" do - interpret(<<-CODE).should eq(12.0) + interpret(<<-CRYSTAL).should eq(12.0) def foo(x : Float64) x end foo(12) - CODE + CRYSTAL end it "autocasts symbol to enum in multidispatch (#11782)" do - interpret(<<-CODE).should eq(1) + interpret(<<-CRYSTAL).should eq(1) enum Color Red Green @@ -61,11 +61,11 @@ describe Crystal::Repl::Interpreter do end (Foo.new || Bar.new).foo(:green).value - CODE + CRYSTAL end it "autocasts int in multidispatch" do - interpret(<<-CODE).should eq(1) + interpret(<<-CRYSTAL).should eq(1) class Foo def foo(x : Int64) x @@ -79,11 +79,11 @@ describe Crystal::Repl::Interpreter do end (Foo.new || Bar.new).foo(1) - CODE + CRYSTAL end it "autocasts symbol to enum in ivar initializer (#12216)" do - interpret(<<-CODE).should eq(2) + interpret(<<-CRYSTAL).should eq(2) enum Color Red Green @@ -100,40 +100,40 @@ describe Crystal::Repl::Interpreter do foo = Foo.new foo.color.value - CODE + CRYSTAL end it "autocasts integer var to integer (#12560)" do - interpret(<<-CODE).should eq(1) + interpret(<<-CRYSTAL).should eq(1) def foo(x : Int64) x end x = 1_i32 foo(x) - CODE + CRYSTAL end it "autocasts integer var to float (#12560)" do - interpret(<<-CODE).should eq(1) + interpret(<<-CRYSTAL).should eq(1) def foo(x : Float64) x end x = 1_i32 foo(x) - CODE + CRYSTAL end it "autocasts float32 var to float64 (#12560)" do - interpret(<<-CODE).should eq(1) + interpret(<<-CRYSTAL).should eq(1) def foo(x : Float64) x end x = 1.0_f32 foo(x) - CODE + CRYSTAL end end end diff --git a/spec/compiler/interpreter/blocks_spec.cr b/spec/compiler/interpreter/blocks_spec.cr index 4261242d03a6..227979288a67 100644 --- a/spec/compiler/interpreter/blocks_spec.cr +++ b/spec/compiler/interpreter/blocks_spec.cr @@ -4,7 +4,7 @@ require "./spec_helper" describe Crystal::Repl::Interpreter do context "blocks" do it "interprets simplest block" do - interpret(<<-CODE).should eq(1) + interpret(<<-CRYSTAL).should eq(1) def foo yield end @@ -14,11 +14,11 @@ describe Crystal::Repl::Interpreter do a += 1 end a - CODE + CRYSTAL end it "interprets block with multiple yields" do - interpret(<<-CODE).should eq(2) + interpret(<<-CRYSTAL).should eq(2) def foo yield yield @@ -29,11 +29,11 @@ describe Crystal::Repl::Interpreter do a += 1 end a - CODE + CRYSTAL end it "interprets yield return value" do - interpret(<<-CODE).should eq(1) + interpret(<<-CRYSTAL).should eq(1) def foo yield end @@ -42,11 +42,11 @@ describe Crystal::Repl::Interpreter do 1 end z - CODE + CRYSTAL end it "interprets yield inside another block" do - interpret(<<-CODE).should eq(1) + interpret(<<-CRYSTAL).should eq(1) def foo bar do yield @@ -62,11 +62,11 @@ describe Crystal::Repl::Interpreter do a += 1 end a - CODE + CRYSTAL end it "interprets yield inside def with arguments" do - interpret(<<-CODE).should eq(18) + interpret(<<-CRYSTAL).should eq(18) def foo(x) a = yield a + x @@ -76,11 +76,11 @@ describe Crystal::Repl::Interpreter do 8 end a - CODE + CRYSTAL end it "interprets yield expression" do - interpret(<<-CODE).should eq(2) + interpret(<<-CRYSTAL).should eq(2) def foo yield 1 end @@ -90,11 +90,11 @@ describe Crystal::Repl::Interpreter do a += x end a - CODE + CRYSTAL end it "interprets yield expressions" do - interpret(<<-CODE).should eq(2 + 2*3 + 4*5) + interpret(<<-CRYSTAL).should eq(2 + 2*3 + 4*5) def foo yield 3, 4, 5 end @@ -104,11 +104,11 @@ describe Crystal::Repl::Interpreter do a += a * x + y * z end a - CODE + CRYSTAL end it "discards yield expression" do - interpret(<<-CODE).should eq(3) + interpret(<<-CRYSTAL).should eq(3) def foo yield 1 end @@ -118,11 +118,11 @@ describe Crystal::Repl::Interpreter do a = 3 end a - CODE + CRYSTAL end it "yields different values to form a union" do - interpret(<<-CODE).should eq(5) + interpret(<<-CRYSTAL).should eq(5) def foo yield 1 yield 'a' @@ -139,11 +139,11 @@ describe Crystal::Repl::Interpreter do end end a - CODE + CRYSTAL end it "returns from block" do - interpret(<<-CODE).should eq(42) + interpret(<<-CRYSTAL).should eq(42) def foo baz do yield @@ -165,11 +165,11 @@ describe Crystal::Repl::Interpreter do end bar - CODE + CRYSTAL end it "interprets next inside block" do - interpret(<<-CODE).should eq(10) + interpret(<<-CRYSTAL).should eq(10) def foo yield end @@ -181,11 +181,11 @@ describe Crystal::Repl::Interpreter do end 20 end - CODE + CRYSTAL end it "interprets next inside block (union, through next)" do - interpret(<<-CODE).should eq(10) + interpret(<<-CRYSTAL).should eq(10) def foo yield end @@ -203,11 +203,11 @@ describe Crystal::Repl::Interpreter do else 20 end - CODE + CRYSTAL end it "interprets next inside block (union, through normal exit)" do - interpret(<<-CODE).should eq('a') + interpret(<<-CRYSTAL).should eq('a') def foo yield end @@ -225,11 +225,11 @@ describe Crystal::Repl::Interpreter do else 'b' end - CODE + CRYSTAL end it "interprets break inside block" do - interpret(<<-CODE).should eq(20) + interpret(<<-CRYSTAL).should eq(20) def baz yield end @@ -248,11 +248,11 @@ describe Crystal::Repl::Interpreter do end 20 end - CODE + CRYSTAL end it "interprets break inside block (union, through break)" do - interpret(<<-CODE).should eq(20) + interpret(<<-CRYSTAL).should eq(20) def foo yield 'a' @@ -270,11 +270,11 @@ describe Crystal::Repl::Interpreter do else 30 end - CODE + CRYSTAL end it "interprets break inside block (union, through normal flow)" do - interpret(<<-CODE).should eq('a') + interpret(<<-CRYSTAL).should eq('a') def foo yield 'a' @@ -292,11 +292,11 @@ describe Crystal::Repl::Interpreter do else 'b' end - CODE + CRYSTAL end it "interprets break inside block (union, through return)" do - interpret(<<-CODE).should eq('a') + interpret(<<-CRYSTAL).should eq('a') def foo yield return 'a' @@ -314,11 +314,11 @@ describe Crystal::Repl::Interpreter do else 'b' end - CODE + CRYSTAL end it "interprets block with args that conflict with a local var" do - interpret(<<-CODE).should eq(201) + interpret(<<-CRYSTAL).should eq(201) def foo yield 1 end @@ -331,11 +331,11 @@ describe Crystal::Repl::Interpreter do end x + a - CODE + CRYSTAL end it "interprets block with args that conflict with a local var" do - interpret(<<-CODE).should eq(216) + interpret(<<-CRYSTAL).should eq(216) def foo yield 1 end @@ -375,11 +375,11 @@ describe Crystal::Repl::Interpreter do x += a end x + a - CODE + CRYSTAL end it "clears block local variables when calling block" do - interpret(<<-CODE).should eq(20) + interpret(<<-CRYSTAL).should eq(20) def foo yield 1 end @@ -406,11 +406,11 @@ describe Crystal::Repl::Interpreter do else z end - CODE + CRYSTAL end it "clears block local variables when calling block (2)" do - interpret(<<-CODE).should eq(20) + interpret(<<-CRYSTAL).should eq(20) def foo yield end @@ -433,11 +433,11 @@ describe Crystal::Repl::Interpreter do else 20 end - CODE + CRYSTAL end it "captures non-closure block" do - interpret(<<-CODE).should eq(42) + interpret(<<-CRYSTAL).should eq(42) def capture(&block : Int32 -> Int32) block end @@ -447,11 +447,11 @@ describe Crystal::Repl::Interpreter do a = 100 b = capture { |x| x + 1 } b.call(41) - CODE + CRYSTAL end it "casts yield expression to block var type (not block arg type)" do - interpret(<<-CODE).should eq(42) + interpret(<<-CRYSTAL).should eq(42) def foo yield 42 end @@ -466,11 +466,11 @@ describe Crystal::Repl::Interpreter do a = 0 bar { |z| a = z } a - CODE + CRYSTAL end it "interprets with ... yield" do - interpret(<<-CODE).should eq(31) + interpret(<<-CRYSTAL).should eq(31) struct Int32 def plus(x : Int32) self + x @@ -484,11 +484,11 @@ describe Crystal::Repl::Interpreter do foo do |x| 1 + (plus x) end - CODE + CRYSTAL end it "interprets with ... yield with struct" do - interpret(<<-CODE).should eq(2) + interpret(<<-CRYSTAL).should eq(2) struct Foo def initialize @x = 1 @@ -511,11 +511,11 @@ describe Crystal::Repl::Interpreter do inc x end - CODE + CRYSTAL end it "interprets with ... yield with extra arguments (#12296)" do - interpret(<<-CODE).should eq(1) + interpret(<<-CRYSTAL).should eq(1) class Object def itself self @@ -529,11 +529,11 @@ describe Crystal::Repl::Interpreter do build do |t| itself end - CODE + CRYSTAL end it "counts with ... yield scope in block args bytesize (#12316)" do - interpret(<<-CODE).should eq(42) + interpret(<<-CRYSTAL).should eq(42) class Object def itself self @@ -551,11 +551,11 @@ describe Crystal::Repl::Interpreter do foo do |x| itself &- x end - CODE + CRYSTAL end it "interprets yield with splat (1)" do - interpret(<<-CODE).should eq((2 - 3) * 4) + interpret(<<-CRYSTAL).should eq((2 - 3) * 4) def foo t = {2, 3, 4} yield *t @@ -566,11 +566,11 @@ describe Crystal::Repl::Interpreter do a = (x1 - x2) * x3 end a - CODE + CRYSTAL end it "interprets yield with splat (2)" do - interpret(<<-CODE).should eq((((1 - 2) * 3) - 4) * 5) + interpret(<<-CRYSTAL).should eq((((1 - 2) * 3) - 4) * 5) def foo t = {2, 3, 4} yield 1, *t, 5 @@ -581,11 +581,11 @@ describe Crystal::Repl::Interpreter do a = (((x1 - x2) * x3) - x4) * x5 end a - CODE + CRYSTAL end it "interprets yield with splat, less block arguments" do - interpret(<<-CODE).should eq(2 - 3) + interpret(<<-CRYSTAL).should eq(2 - 3) def foo t = {2, 3, 4} yield *t @@ -596,11 +596,11 @@ describe Crystal::Repl::Interpreter do a = x1 - x2 end a - CODE + CRYSTAL end it "interprets block with splat" do - interpret(<<-CODE).should eq((((1 - 2) * 3) - 4) * 5) + interpret(<<-CRYSTAL).should eq((((1 - 2) * 3) - 4) * 5) def foo yield 1, 2, 3, 4, 5 end @@ -610,11 +610,11 @@ describe Crystal::Repl::Interpreter do a = (((x1 - x[0]) * x[1]) - x[2]) * x5 end a - CODE + CRYSTAL end it "interprets yield with splat, block with splat" do - interpret(<<-CODE).should eq((((1 - 2) * 3) - 4) * 5) + interpret(<<-CRYSTAL).should eq((((1 - 2) * 3) - 4) * 5) def foo t = {1, 2, 3} yield *t, 4, 5 @@ -625,11 +625,11 @@ describe Crystal::Repl::Interpreter do a = (((x1 - x[0]) * x[1]) - x[2]) * x5 end a - CODE + CRYSTAL end it "interprets yield with splat, block with splat (#12227)" do - interpret(<<-CODE).should eq(1) + interpret(<<-CRYSTAL).should eq(1) def foo yield *{ {3, 2} } end @@ -637,11 +637,11 @@ describe Crystal::Repl::Interpreter do foo do |x, y| x &- y end - CODE + CRYSTAL end it "considers block arg without type as having NoReturn type (#12270)" do - interpret(<<-CODE).should eq(42) + interpret(<<-CRYSTAL).should eq(42) def bar if ptr = nil yield ptr @@ -657,11 +657,11 @@ describe Crystal::Repl::Interpreter do end foo - CODE + CRYSTAL end it "considers block arg without type as having NoReturn type (2) (#12270)" do - interpret(<<-CODE).should eq(42) + interpret(<<-CRYSTAL).should eq(42) def bar if ptr = nil yield ptr @@ -677,11 +677,11 @@ describe Crystal::Repl::Interpreter do end foo - CODE + CRYSTAL end it "caches method with captured block (#12276)" do - interpret(<<-CODE).should eq(42) + interpret(<<-CRYSTAL).should eq(42) def execute(x, &block : -> Int32) if x execute(false) do @@ -695,7 +695,7 @@ describe Crystal::Repl::Interpreter do execute(true) do 42 end - CODE + CRYSTAL end end end diff --git a/spec/compiler/interpreter/bugs_spec.cr b/spec/compiler/interpreter/bugs_spec.cr index 6e08de990162..1e51d04a1f98 100644 --- a/spec/compiler/interpreter/bugs_spec.cr +++ b/spec/compiler/interpreter/bugs_spec.cr @@ -4,7 +4,7 @@ require "./spec_helper" describe Crystal::Repl::Interpreter do context "bugs" do it "doesn't pass self to top-level method" do - interpret(<<-CODE).should eq(1) + interpret(<<-CRYSTAL).should eq(1) struct Int32 def foo(x) self @@ -22,11 +22,11 @@ describe Crystal::Repl::Interpreter do end Moo.moo - CODE + CRYSTAL end it "doesn't pass self to top-level method (FileNode)" do - interpret(<<-CODE).should eq(1) + interpret(<<-CRYSTAL).should eq(1) enum Color Red Green @@ -50,11 +50,11 @@ describe Crystal::Repl::Interpreter do other = 2 e = Color::Green.should eq(t :green) e.value - CODE + CRYSTAL end it "breaks from current block, not from outer block" do - interpret(<<-CODE).should eq(2) + interpret(<<-CRYSTAL).should eq(2) def twice # index: 1, block_caller: 0 @@ -92,21 +92,21 @@ describe Crystal::Repl::Interpreter do end x - CODE + CRYSTAL end it "doesn't incorrectly consider a non-closure as closure" do - interpret(<<-CODE, prelude: "prelude").should eq("false") + interpret(<<-CRYSTAL, prelude: "prelude").should eq("false") c = 0 ->{ c ->{}.closure? }.call - CODE + CRYSTAL end it "doesn't override local variable value with block var with the same name" do - interpret(<<-CODE).should eq(0) + interpret(<<-CRYSTAL).should eq(0) def block yield 1 end @@ -126,17 +126,17 @@ describe Crystal::Repl::Interpreter do end foo - CODE + CRYSTAL end it "does leading zeros" do - interpret(<<-CODE, prelude: "prelude").should eq("8") + interpret(<<-CRYSTAL, prelude: "prelude").should eq("8") 0_i8.leading_zeros_count - CODE + CRYSTAL end it "does multidispatch on virtual struct" do - interpret(<<-CODE).should eq(true) + interpret(<<-CRYSTAL).should eq(true) abstract struct Base end @@ -160,11 +160,11 @@ describe Crystal::Repl::Interpreter do address = Foo.new.as(Base) address.foo - CODE + CRYSTAL end it "correctly puts virtual metaclass type in union" do - interpret(<<-CODE).should eq("Bar") + interpret(<<-CRYSTAL).should eq("Bar") abstract struct Foo end @@ -183,11 +183,11 @@ describe Crystal::Repl::Interpreter do foo = Bar.new.as(Foo) foo2 = foo || nil foo2.class.name - CODE + CRYSTAL end it "does multidispatch on virtual struct union nil" do - interpret(<<-CODE).should eq(true) + interpret(<<-CRYSTAL).should eq(true) abstract struct Foo @value = 1 end @@ -208,7 +208,7 @@ describe Crystal::Repl::Interpreter do foo = Bar.new.as(Foo) bar = (foo || nil).itself bar.is_a?(Bar) - CODE + CRYSTAL end end end diff --git a/spec/compiler/interpreter/calls_spec.cr b/spec/compiler/interpreter/calls_spec.cr index 2f246f610d34..43f027341ff1 100644 --- a/spec/compiler/interpreter/calls_spec.cr +++ b/spec/compiler/interpreter/calls_spec.cr @@ -4,17 +4,17 @@ require "./spec_helper" describe Crystal::Repl::Interpreter do context "calls" do it "calls a top-level method without arguments and no local vars" do - interpret(<<-CODE).should eq(3) + interpret(<<-CRYSTAL).should eq(3) def foo 1 + 2 end foo - CODE + CRYSTAL end it "calls a top-level method without arguments but with local vars" do - interpret(<<-CODE).should eq(3) + interpret(<<-CRYSTAL).should eq(3) def foo x = 1 y = 2 @@ -23,42 +23,42 @@ describe Crystal::Repl::Interpreter do x = foo x - CODE + CRYSTAL end it "calls a top-level method with two arguments" do - interpret(<<-CODE).should eq(3) + interpret(<<-CRYSTAL).should eq(3) def foo(x, y) x + y end x = foo(1, 2) x - CODE + CRYSTAL end it "interprets call with default values" do - interpret(<<-CODE).should eq(3) + interpret(<<-CRYSTAL).should eq(3) def foo(x = 1, y = 2) x + y end foo - CODE + CRYSTAL end it "interprets call with named arguments" do - interpret(<<-CODE).should eq(-15) + interpret(<<-CRYSTAL).should eq(-15) def foo(x, y) x - y end foo(y: 25, x: 10) - CODE + CRYSTAL end it "interprets self for primitive types" do - interpret(<<-CODE).should eq(42) + interpret(<<-CRYSTAL).should eq(42) struct Int32 def foo self @@ -66,11 +66,11 @@ describe Crystal::Repl::Interpreter do end 42.foo - CODE + CRYSTAL end it "interprets explicit self call for primitive types" do - interpret(<<-CODE).should eq(42) + interpret(<<-CRYSTAL).should eq(42) struct Int32 def foo self.bar @@ -82,11 +82,11 @@ describe Crystal::Repl::Interpreter do end 42.foo - CODE + CRYSTAL end it "interprets implicit self call for pointer" do - interpret(<<-CODE).should eq(1) + interpret(<<-CRYSTAL).should eq(1) struct Pointer(T) def plus1 self + 1_i64 @@ -96,21 +96,21 @@ describe Crystal::Repl::Interpreter do ptr = Pointer(UInt8).malloc(1_u64) ptr2 = ptr.plus1 (ptr2 - ptr) - CODE + CRYSTAL end it "interprets call with if" do - interpret(<<-CODE).should eq(2) + interpret(<<-CRYSTAL).should eq(2) def foo 1 == 1 ? 2 : 3 end foo - CODE + CRYSTAL end it "does call with struct as obj" do - interpret(<<-CODE).should eq(3) + interpret(<<-CRYSTAL).should eq(3) struct Foo def initialize(@x : Int64) end @@ -129,11 +129,11 @@ describe Crystal::Repl::Interpreter do end foo.x - CODE + CRYSTAL end it "does call with struct as obj (2)" do - interpret(<<-CODE).should eq(2) + interpret(<<-CRYSTAL).should eq(2) struct Foo def two 2 @@ -141,11 +141,11 @@ describe Crystal::Repl::Interpreter do end Foo.new.two - CODE + CRYSTAL end it "does call on instance var that's a struct, from a class" do - interpret(<<-CODE).should eq(10) + interpret(<<-CRYSTAL).should eq(10) class Foo def initialize @x = 0_i64 @@ -174,11 +174,11 @@ describe Crystal::Repl::Interpreter do end Foo.new.foo - CODE + CRYSTAL end it "does call on instance var that's a struct, from a struct" do - interpret(<<-CODE).should eq(10) + interpret(<<-CRYSTAL).should eq(10) struct Foo def initialize @x = 0_i64 @@ -207,11 +207,11 @@ describe Crystal::Repl::Interpreter do end Foo.new.foo - CODE + CRYSTAL end it "discards call with struct as obj" do - interpret(<<-CODE).should eq(4) + interpret(<<-CRYSTAL).should eq(4) struct Foo def initialize(@x : Int64) end @@ -231,11 +231,11 @@ describe Crystal::Repl::Interpreter do foo.x 4 - CODE + CRYSTAL end it "does call on constant that's a struct, takes a pointer to instance var" do - interpret(<<-CODE).should eq(42) + interpret(<<-CRYSTAL).should eq(42) struct Foo def initialize @x = 42 @@ -252,11 +252,11 @@ describe Crystal::Repl::Interpreter do CONST = Foo.new CONST.to_unsafe.value - CODE + CRYSTAL end it "does call on constant that's a struct, takes a pointer to instance var, inside if" do - interpret(<<-CODE).should eq(42) + interpret(<<-CRYSTAL).should eq(42) struct Foo def initialize @x = 42 @@ -274,11 +274,11 @@ describe Crystal::Repl::Interpreter do CONST = Foo.new c = (1 == 1 ? CONST : CONST).to_unsafe c.value - CODE + CRYSTAL end it "does call on var that's a struct, takes a pointer to instance var, inside if" do - interpret(<<-CODE).should eq(42) + interpret(<<-CRYSTAL).should eq(42) struct Foo def initialize @x = 42 @@ -296,11 +296,11 @@ describe Crystal::Repl::Interpreter do a = Foo.new c = (1 == 1 ? a : a).to_unsafe c.value - CODE + CRYSTAL end it "does call on ivar that's a struct, takes a pointer to instance var, inside if" do - interpret(<<-CODE).should eq(42) + interpret(<<-CRYSTAL).should eq(42) struct Foo def initialize @x = 42 @@ -327,11 +327,11 @@ describe Crystal::Repl::Interpreter do end Bar.new.do_it - CODE + CRYSTAL end it "does call on self that's a struct, takes a pointer to instance var, inside if" do - interpret(<<-CODE).should eq(42) + interpret(<<-CRYSTAL).should eq(42) struct Foo def initialize @x = 42 @@ -352,11 +352,11 @@ describe Crystal::Repl::Interpreter do end Foo.new.do_it - CODE + CRYSTAL end it "does call on Pointer#value that's a struct, takes a pointer to instance var" do - interpret(<<-CODE).should eq(42) + interpret(<<-CRYSTAL).should eq(42) struct Foo def initialize @x = 42 @@ -375,11 +375,11 @@ describe Crystal::Repl::Interpreter do ptr = pointerof(foo) c = ptr.value.to_unsafe c.value - CODE + CRYSTAL end it "does call on read instance var that's a struct, takes a pointer to instance var" do - interpret(<<-CODE).should eq(42) + interpret(<<-CRYSTAL).should eq(42) struct Foo def initialize @x = 42 @@ -403,11 +403,11 @@ describe Crystal::Repl::Interpreter do bar = Bar.new(foo) c = bar.@foo.to_unsafe c.value - CODE + CRYSTAL end it "does ReadInstanceVar with wants_struct_pointer" do - interpret(<<-CODE).should eq(42) + interpret(<<-CRYSTAL).should eq(42) struct Foo def initialize @x = 1 @@ -432,11 +432,11 @@ describe Crystal::Repl::Interpreter do entry.value = Foo.new ptr = entry.value.@bar.to_unsafe ptr.value - CODE + CRYSTAL end it "does Assign var with wants_struct_pointer" do - interpret(<<-CODE).should eq(42) + interpret(<<-CRYSTAL).should eq(42) struct Bar def initialize @x = 1 @@ -452,11 +452,11 @@ describe Crystal::Repl::Interpreter do bar = Bar.new ptr = (x = bar).to_unsafe ptr.value - CODE + CRYSTAL end it "does Assign instance var with wants_struct_pointer" do - interpret(<<-CODE).should eq(42) + interpret(<<-CRYSTAL).should eq(42) struct Bar def initialize @x = 1 @@ -480,11 +480,11 @@ describe Crystal::Repl::Interpreter do end Foo.new.foo - CODE + CRYSTAL end it "does Assign class var with wants_struct_pointer" do - interpret(<<-CODE).should eq(42) + interpret(<<-CRYSTAL).should eq(42) struct Bar def initialize @x = 1 @@ -508,11 +508,11 @@ describe Crystal::Repl::Interpreter do end Foo.new.foo - CODE + CRYSTAL end it "inlines method that just reads an instance var" do - interpret(<<-CODE).should eq(42) + interpret(<<-CRYSTAL).should eq(42) struct Foo def initialize @x = 1 @@ -541,11 +541,11 @@ describe Crystal::Repl::Interpreter do entry.value = Foo.new ptr = entry.value.bar.to_unsafe ptr.value - CODE + CRYSTAL end it "inlines method that just reads an instance var, but produces side effects of args" do - interpret(<<-CODE).should eq(42) + interpret(<<-CRYSTAL).should eq(42) struct Foo def initialize @x = 1 @@ -575,11 +575,11 @@ describe Crystal::Repl::Interpreter do a = 1 ptr = entry.value.bar(a = 10).to_unsafe ptr.value + a - CODE + CRYSTAL end it "inlines method that just reads an instance var (2)" do - interpret(<<-CODE).should eq(2) + interpret(<<-CRYSTAL).should eq(2) abstract class Abstract end @@ -594,11 +594,11 @@ describe Crystal::Repl::Interpreter do original = Concrete.new(2).as(Abstract) original.x - CODE + CRYSTAL end it "puts struct pointer after tuple indexer" do - interpret(<<-CODE).should eq(1) + interpret(<<-CRYSTAL).should eq(1) struct Point def initialize(@x : Int64) end @@ -611,11 +611,11 @@ describe Crystal::Repl::Interpreter do a = Point.new(1_u64) t = {a} t[0].x - CODE + CRYSTAL end it "mutates call argument" do - interpret(<<-CODE).should eq(9000) + interpret(<<-CRYSTAL).should eq(9000) def foo(x) if 1 == 0 x = "hello" @@ -629,11 +629,11 @@ describe Crystal::Repl::Interpreter do end foo 9000 - CODE + CRYSTAL end it "inlines call that returns self" do - interpret(<<-CODE).should eq(10) + interpret(<<-CRYSTAL).should eq(10) struct Foo def initialize @x = 0 @@ -659,11 +659,11 @@ describe Crystal::Repl::Interpreter do foo = Foo.new foo.mutate_itself foo.x - CODE + CRYSTAL end it "inlines call that returns self (2)" do - interpret(<<-CODE).should eq(10) + interpret(<<-CRYSTAL).should eq(10) struct Foo def initialize @x = 0 @@ -689,11 +689,11 @@ describe Crystal::Repl::Interpreter do foo = Foo.new foo.mutate_itself foo.x - CODE + CRYSTAL end it "mutates through pointer (1)" do - interpret(<<-CODE).should eq(10) + interpret(<<-CRYSTAL).should eq(10) struct Foo def initialize @x = 0 @@ -718,11 +718,11 @@ describe Crystal::Repl::Interpreter do end foo.mutate.ptr.value - CODE + CRYSTAL end it "mutates through pointer (2)" do - interpret(<<-CODE).should eq(10) + interpret(<<-CRYSTAL).should eq(10) struct Foo def initialize @x = 0 @@ -748,11 +748,11 @@ describe Crystal::Repl::Interpreter do x = foo.mutate.ptr x.value - CODE + CRYSTAL end it "mutates through pointer (3)" do - interpret(<<-CODE).should eq(10) + interpret(<<-CRYSTAL).should eq(10) struct Foo def initialize @x = 0 @@ -771,11 +771,11 @@ describe Crystal::Repl::Interpreter do ptr.value = Foo.new ptr.value.mutate ptr.value.x - CODE + CRYSTAL end it "mutates through read instance var" do - interpret(<<-CODE).should eq(10) + interpret(<<-CRYSTAL).should eq(10) struct Foo def initialize @bar = Bar.new @@ -802,11 +802,11 @@ describe Crystal::Repl::Interpreter do foo = Foo.new foo.@bar.z = 10 foo.bar.z - CODE + CRYSTAL end it "mutates through inlined instance var with receiver" do - interpret(<<-CODE).should eq(10) + interpret(<<-CRYSTAL).should eq(10) struct Foo def initialize @bar = Bar.new @@ -833,11 +833,11 @@ describe Crystal::Repl::Interpreter do foo = Foo.new foo.bar.z = 10 foo.bar.z - CODE + CRYSTAL end it "mutates through inlined instance var without receiver" do - interpret(<<-CODE).should eq(10) + interpret(<<-CRYSTAL).should eq(10) struct Foo def initialize @bar = Bar.new @@ -868,7 +868,7 @@ describe Crystal::Repl::Interpreter do foo = Foo.new foo.mutate foo.bar.z - CODE + CRYSTAL end end end diff --git a/spec/compiler/interpreter/casts_spec.cr b/spec/compiler/interpreter/casts_spec.cr index f8c59faca2d6..ac28197ce534 100644 --- a/spec/compiler/interpreter/casts_spec.cr +++ b/spec/compiler/interpreter/casts_spec.cr @@ -4,16 +4,16 @@ require "./spec_helper" describe Crystal::Repl::Interpreter do context "casts" do it "casts from reference to pointer and back" do - interpret(<<-CODE).should eq("hello") + interpret(<<-CRYSTAL).should eq("hello") x = "hello" p = x.as(UInt8*) y = p.as(String) y - CODE + CRYSTAL end it "casts from reference to nilable reference" do - interpret(<<-CODE).should eq("hello") + interpret(<<-CRYSTAL).should eq("hello") x = "hello" y = x.as(String | Nil) if y @@ -21,22 +21,22 @@ describe Crystal::Repl::Interpreter do else "bye" end - CODE + CRYSTAL end it "casts from mixed union type to another mixed union type for caller" do - interpret(<<-CODE).should eq(true) + interpret(<<-CRYSTAL).should eq(true) a = 1 == 1 ? 1 : (1 == 1 ? 20_i16 : nil) if a a < 2 else false end - CODE + CRYSTAL end it "casts from nilable type to mixed union type" do - interpret(<<-CODE).should eq(2) + interpret(<<-CRYSTAL).should eq(2) ascii = true delimiter = 1 == 1 ? nil : "foo" @@ -45,43 +45,43 @@ describe Crystal::Repl::Interpreter do else 2 end - CODE + CRYSTAL end it "casts from nilable type to mixed union type (2)" do - interpret(<<-CODE).should eq(true) + interpret(<<-CRYSTAL).should eq(true) y = 1 == 1 ? "a" : nil x = true x = y x.is_a?(String) - CODE + CRYSTAL end it "casts from mixed union type to primitive type" do - interpret(<<-CODE, prelude: "prelude").should eq("2") + interpret(<<-CRYSTAL, prelude: "prelude").should eq("2") x = 1 == 1 ? 2 : nil x.as(Int32) - CODE + CRYSTAL end it "casts nilable from mixed union type to primitive type (non-nil case)" do - interpret(<<-CODE).should eq(2) + interpret(<<-CRYSTAL).should eq(2) x = 1 == 1 ? 2 : nil y = x.as?(Int32) y ? y : 20 - CODE + CRYSTAL end it "casts nilable from mixed union type to primitive type (nil case)" do - interpret(<<-CODE).should eq(20) + interpret(<<-CRYSTAL).should eq(20) x = 1 == 1 ? nil : 2 y = x.as?(Int32) y ? y : 20 - CODE + CRYSTAL end it "upcasts between tuple types" do - interpret(<<-CODE, prelude: "prelude").should eq((1 + 'a'.ord).to_s) + interpret(<<-CRYSTAL, prelude: "prelude").should eq((1 + 'a'.ord).to_s) a = if 1 == 1 {1, 'a'} @@ -90,11 +90,11 @@ describe Crystal::Repl::Interpreter do end a[0].as(Int32) + a[1].as(Char).ord - CODE + CRYSTAL end it "upcasts between named tuple types, same order" do - interpret(<<-CODE, prelude: "prelude").should eq((1 + 'a'.ord).to_s) + interpret(<<-CRYSTAL, prelude: "prelude").should eq((1 + 'a'.ord).to_s) a = if 1 == 1 {a: 1, b: 'a'} @@ -103,11 +103,11 @@ describe Crystal::Repl::Interpreter do end a[:a].as(Int32) + a[:b].as(Char).ord - CODE + CRYSTAL end it "upcasts between named tuple types, different order" do - interpret(<<-CODE, prelude: "prelude").should eq((1 + 'a'.ord).to_s) + interpret(<<-CRYSTAL, prelude: "prelude").should eq((1 + 'a'.ord).to_s) a = if 1 == 1 {a: 1, b: 'a'} @@ -116,11 +116,11 @@ describe Crystal::Repl::Interpreter do end a[:a].as(Int32) + a[:b].as(Char).ord - CODE + CRYSTAL end it "upcasts to module type" do - interpret(<<-CODE).should eq(1) + interpret(<<-CRYSTAL).should eq(1) module Moo end @@ -146,11 +146,11 @@ describe Crystal::Repl::Interpreter do else 10 end - CODE + CRYSTAL end it "upcasts virtual type to union" do - interpret(<<-CODE).should eq(2) + interpret(<<-CRYSTAL).should eq(2) class Foo def foo 1 @@ -170,11 +170,11 @@ describe Crystal::Repl::Interpreter do else 20 end - CODE + CRYSTAL end it "casts nil to Void*" do - interpret(<<-CODE).should eq(0) + interpret(<<-CRYSTAL).should eq(0) module Moo def self.moo(r) r.as(Void*) @@ -182,11 +182,11 @@ describe Crystal::Repl::Interpreter do end Moo.moo(nil).address - CODE + CRYSTAL end it "does is_a? with virtual metaclass" do - interpret(<<-CODE).should eq(1) + interpret(<<-CRYSTAL).should eq(1) class A def self.a 2 @@ -210,19 +210,19 @@ describe Crystal::Repl::Interpreter do else 0 end - CODE + CRYSTAL end it "discards cast" do - interpret(<<-CODE, prelude: "prelude").should eq("10") + interpret(<<-CRYSTAL, prelude: "prelude").should eq("10") x = 1 || 'a' x.as(Int32) 10 - CODE + CRYSTAL end it "raises when as fails" do - interpret(<<-CODE, prelude: "prelude").to_s.should contain("cast from Int32 to Char failed") + interpret(<<-CRYSTAL, prelude: "prelude").to_s.should contain("cast from Int32 to Char failed") x = 1 || 'a' begin x.as(Char) @@ -230,17 +230,17 @@ describe Crystal::Repl::Interpreter do rescue ex : TypeCastError ex.message.not_nil! end - CODE + CRYSTAL end it "casts to filtered type, not type in as(...)" do - interpret(<<-CODE, prelude: "prelude").should eq("1") + interpret(<<-CRYSTAL, prelude: "prelude").should eq("1") ({1} || 2).as(Tuple)[0] - CODE + CRYSTAL end it "does is_a? with virtual type (struct)" do - interpret(<<-CODE).should eq(10) + interpret(<<-CRYSTAL).should eq(10) abstract struct Foo end @@ -271,11 +271,11 @@ describe Crystal::Repl::Interpreter do else 0 end - CODE + CRYSTAL end it "puts virtual metaclass into union (#12162)" do - interpret(<<-CODE, prelude: "prelude").should eq(%("ActionA")) + interpret(<<-CRYSTAL, prelude: "prelude").should eq(%("ActionA")) class Action end @@ -288,11 +288,11 @@ describe Crystal::Repl::Interpreter do x = ActionA || ActionB y = x || Nil y.to_s - CODE + CRYSTAL end it "puts tuple type inside union of different tuple type (#12243)" do - interpret(<<-CODE, prelude: "prelude").should eq(%("{180}")) + interpret(<<-CRYSTAL, prelude: "prelude").should eq(%("{180}")) class A def initialize(@x : {Char | Int32}?) end @@ -304,11 +304,11 @@ describe Crystal::Repl::Interpreter do x = A.new({180}).x x.to_s - CODE + CRYSTAL end it "puts named tuple type inside union of different named tuple type (#12243)" do - interpret(<<-CODE, prelude: "prelude").should eq(%("{v: 180}")) + interpret(<<-CRYSTAL, prelude: "prelude").should eq(%("{v: 180}")) class A def initialize(@x : {v: Char | Int32}?) end @@ -320,11 +320,11 @@ describe Crystal::Repl::Interpreter do x = A.new({v: 180}).x x.to_s - CODE + CRYSTAL end it "casts from mixed union type to nilable proc type (#12283)" do - interpret(<<-CODE).should eq("b") + interpret(<<-CRYSTAL).should eq("b") message = ->{ "b" }.as(String | Proc(String) | Nil) if message.is_a?(String) "a" @@ -333,31 +333,31 @@ describe Crystal::Repl::Interpreter do else "c" end - CODE + CRYSTAL end it "does as? with no resulting type (#12327)" do - interpret(<<-CODE).should eq(42) + interpret(<<-CRYSTAL).should eq(42) if nil.as?(Int32) 0 else 42 end - CODE + CRYSTAL end it "does as? with no resulting type, not from nil (#12327)" do - interpret(<<-CODE).should eq(42) + interpret(<<-CRYSTAL).should eq(42) if 1.as?(String) 0 else 42 end - CODE + CRYSTAL end it "does as? with a type that can't match (#12346)" do - interpret(<<-CODE).should eq(1) + interpret(<<-CRYSTAL).should eq(1) abstract class A end @@ -369,11 +369,11 @@ describe Crystal::Repl::Interpreter do a = B.new || C.new a.as?(B | Int32) ? 1 : 2 - CODE + CRYSTAL end it "upcasts mixed union with tuple to mixed union with compatible tuple (1) (#12331)" do - interpret(<<-CODE).should eq(1) + interpret(<<-CRYSTAL).should eq(1) class Foo def initialize(@tuple : Tuple(Int32?) | Tuple(Int32, Int32)) end @@ -396,11 +396,11 @@ describe Crystal::Repl::Interpreter do else 3 end - CODE + CRYSTAL end it "upcasts mixed union with tuple to mixed union with compatible tuple (2) (#12331)" do - interpret(<<-CODE).should eq(2) + interpret(<<-CRYSTAL).should eq(2) class Foo def initialize(@tuple : Tuple(Int32?) | Tuple(Int32, Int32)) end @@ -423,11 +423,11 @@ describe Crystal::Repl::Interpreter do else 3 end - CODE + CRYSTAL end it "upcasts mixed union with tuple to mixed union with compatible tuple (3) (#12331)" do - interpret(<<-CODE).should eq(3) + interpret(<<-CRYSTAL).should eq(3) class Foo def initialize(@tuple : Tuple(Int32?) | Tuple(Int32, Int32)) end @@ -450,11 +450,11 @@ describe Crystal::Repl::Interpreter do else 3 end - CODE + CRYSTAL end it "upcasts in nilable cast (#12532)" do - interpret(<<-CODE).should eq(2) + interpret(<<-CRYSTAL).should eq(2) struct Nil def foo 0 @@ -480,11 +480,11 @@ describe Crystal::Repl::Interpreter do end B.new.as?(A).foo - CODE + CRYSTAL end it "upcasts GenericClassInstanceMetaclassType to VirtualMetaclassType" do - interpret(<<-CODE).should eq(2) + interpret(<<-CRYSTAL).should eq(2) class Foo def self.foo; 1; end end @@ -494,7 +494,7 @@ describe Crystal::Repl::Interpreter do end Gen(Int32).as(Foo.class).foo - CODE + CRYSTAL end end end diff --git a/spec/compiler/interpreter/class_vars_spec.cr b/spec/compiler/interpreter/class_vars_spec.cr index a70529d2325d..ff15ab345b21 100644 --- a/spec/compiler/interpreter/class_vars_spec.cr +++ b/spec/compiler/interpreter/class_vars_spec.cr @@ -4,7 +4,7 @@ require "./spec_helper" describe Crystal::Repl::Interpreter do context "class vars" do it "interprets class var without initializer" do - interpret(<<-CODE).should eq(41) + interpret(<<-CRYSTAL).should eq(41) class Foo @@x : Int32? @@ -30,11 +30,11 @@ describe Crystal::Repl::Interpreter do a += x if x a - CODE + CRYSTAL end it "interprets class var with initializer" do - interpret(<<-CODE).should eq(42) + interpret(<<-CRYSTAL).should eq(42) class Foo @@x = 10 @@ -60,11 +60,11 @@ describe Crystal::Repl::Interpreter do a += x if x a - CODE + CRYSTAL end it "interprets class var for virtual type" do - interpret(<<-CODE).should eq(30) + interpret(<<-CRYSTAL).should eq(30) class Foo @@x = 1 @@ -92,11 +92,11 @@ describe Crystal::Repl::Interpreter do a += foobar.get a += barfoo.get a - CODE + CRYSTAL end it "interprets class var for virtual metaclass type" do - interpret(<<-CODE).should eq(30) + interpret(<<-CRYSTAL).should eq(30) class Foo @@x = 1 @@ -124,11 +124,11 @@ describe Crystal::Repl::Interpreter do a += foobar.get a += barfoo.get a - CODE + CRYSTAL end it "finds self in class var initializer (#12439)" do - interpret(<<-CODE).should eq(42) + interpret(<<-CRYSTAL).should eq(42) class Foo @@value : Int32 = self.int @@ -142,11 +142,11 @@ describe Crystal::Repl::Interpreter do end Foo.value - CODE + CRYSTAL end it "does class var initializer with union (#12633)" do - interpret(<<-CODE).should eq("hello") + interpret(<<-CRYSTAL).should eq("hello") class MyClass @@a : String | Int32 = "hello" @@ -162,11 +162,11 @@ describe Crystal::Repl::Interpreter do in Int32 "bye" end - CODE + CRYSTAL end it "reads class var initializer with union (#12633)" do - interpret(<<-CODE).should eq(2) + interpret(<<-CRYSTAL).should eq(2) class MyClass @@a : Char | Int32 = 1 @@ -183,7 +183,7 @@ describe Crystal::Repl::Interpreter do end MyClass.foo(2) - CODE + CRYSTAL end end end diff --git a/spec/compiler/interpreter/classes_spec.cr b/spec/compiler/interpreter/classes_spec.cr index ea54f0b98fb2..6a5ce23ae22b 100644 --- a/spec/compiler/interpreter/classes_spec.cr +++ b/spec/compiler/interpreter/classes_spec.cr @@ -4,7 +4,7 @@ require "./spec_helper" describe Crystal::Repl::Interpreter do context "classes" do it "does allocate, set instance var and get instance var" do - interpret(<<-CODE).should eq(42) + interpret(<<-CRYSTAL).should eq(42) class Foo @x = 0 @@ -19,11 +19,11 @@ describe Crystal::Repl::Interpreter do foo = Foo.allocate foo.x = 42 foo.x - CODE + CRYSTAL end it "does constructor" do - interpret(<<-CODE).should eq(42) + interpret(<<-CRYSTAL).should eq(42) class Foo def initialize(@x : Int32) end @@ -35,7 +35,7 @@ describe Crystal::Repl::Interpreter do foo = Foo.new(42) foo.x - CODE + CRYSTAL end it "interprets read instance var" do @@ -43,17 +43,17 @@ describe Crystal::Repl::Interpreter do end it "discards allocate" do - interpret(<<-CODE).should eq(3) + interpret(<<-CRYSTAL).should eq(3) class Foo end Foo.allocate 3 - CODE + CRYSTAL end it "calls implicit class self method" do - interpret(<<-CODE).should eq(10) + interpret(<<-CRYSTAL).should eq(10) class Foo def initialize @x = 10 @@ -70,11 +70,11 @@ describe Crystal::Repl::Interpreter do foo = Foo.new foo.foo - CODE + CRYSTAL end it "calls explicit struct self method" do - interpret(<<-CODE).should eq(10) + interpret(<<-CRYSTAL).should eq(10) struct Foo def initialize @x = 10 @@ -91,11 +91,11 @@ describe Crystal::Repl::Interpreter do foo = Foo.new foo.foo - CODE + CRYSTAL end it "calls implicit struct self method" do - interpret(<<-CODE).should eq(10) + interpret(<<-CRYSTAL).should eq(10) struct Foo def initialize @x = 10 @@ -112,11 +112,11 @@ describe Crystal::Repl::Interpreter do foo = Foo.new foo.foo - CODE + CRYSTAL end it "does object_id" do - interpret(<<-CODE).should be_true + interpret(<<-CRYSTAL).should be_true class Foo end @@ -124,12 +124,12 @@ describe Crystal::Repl::Interpreter do object_id = foo.object_id address = foo.as(Void*).address object_id == address - CODE + CRYSTAL end end it "inlines instance var access from virtual type with a single type (#39520)" do - interpret(<<-CODE).should eq(1) + interpret(<<-CRYSTAL).should eq(1) struct Int32 def foo 1 @@ -157,11 +157,11 @@ describe Crystal::Repl::Interpreter do expression = ValueExpression.new.as(Expression) expression.value.foo - CODE + CRYSTAL end it "downcasts virtual type to its only type (#12351)" do - interpret(<<-CODE).should eq(1) + interpret(<<-CRYSTAL).should eq(1) abstract class A end @@ -177,6 +177,6 @@ describe Crystal::Repl::Interpreter do b = B.new.as(A) foo(b) - CODE + CRYSTAL end end diff --git a/spec/compiler/interpreter/closures_spec.cr b/spec/compiler/interpreter/closures_spec.cr index 5487ea21b1e3..d0c51359f122 100644 --- a/spec/compiler/interpreter/closures_spec.cr +++ b/spec/compiler/interpreter/closures_spec.cr @@ -4,16 +4,16 @@ require "./spec_helper" describe Crystal::Repl::Interpreter do context "closures" do it "does closure without args that captures and modifies one local variable" do - interpret(<<-CODE).should eq(42) + interpret(<<-CRYSTAL).should eq(42) a = 0 proc = -> { a = 42 } proc.call a - CODE + CRYSTAL end it "does closure without args that captures and modifies two local variables" do - interpret(<<-CODE).should eq(7) + interpret(<<-CRYSTAL).should eq(7) a = 0 b = 0 proc = ->{ @@ -22,11 +22,11 @@ describe Crystal::Repl::Interpreter do } proc.call a - b - CODE + CRYSTAL end it "does closure with two args that captures and modifies two local variables" do - interpret(<<-CODE).should eq(7) + interpret(<<-CRYSTAL).should eq(7) a = 0 b = 0 proc = ->(x : Int32, y : Int32) { @@ -35,11 +35,11 @@ describe Crystal::Repl::Interpreter do } proc.call(10, 3) a - b - CODE + CRYSTAL end it "does closure and accesses it inside block" do - interpret(<<-CODE).should eq(42) + interpret(<<-CRYSTAL).should eq(42) def foo yield end @@ -53,11 +53,11 @@ describe Crystal::Repl::Interpreter do end x - CODE + CRYSTAL end it "does closure inside def" do - interpret(<<-CODE).should eq(42) + interpret(<<-CRYSTAL).should eq(42) def foo a = 0 proc = -> { a = 42 } @@ -66,11 +66,11 @@ describe Crystal::Repl::Interpreter do end foo - CODE + CRYSTAL end it "closures def arguments" do - interpret(<<-CODE).should eq((41 + 1) - (10 + 2)) + interpret(<<-CRYSTAL).should eq((41 + 1) - (10 + 2)) def foo(a, b) proc = -> { a += 1; b += 2 } proc.call @@ -78,11 +78,11 @@ describe Crystal::Repl::Interpreter do end foo(41, 10) - CODE + CRYSTAL end it "does closure inside proc" do - interpret(<<-CODE).should eq(42) + interpret(<<-CRYSTAL).should eq(42) proc = ->{ a = 0 proc2 = -> { a = 42 } @@ -91,11 +91,11 @@ describe Crystal::Repl::Interpreter do } proc.call - CODE + CRYSTAL end it "does closure inside proc, capture proc argument" do - interpret(<<-CODE).should eq(42) + interpret(<<-CRYSTAL).should eq(42) proc = ->(a : Int32) { proc2 = -> { a += 1 } proc2.call @@ -103,11 +103,11 @@ describe Crystal::Repl::Interpreter do } proc.call(41) - CODE + CRYSTAL end it "does closure inside const" do - interpret(<<-CODE).should eq(42) + interpret(<<-CRYSTAL).should eq(42) FOO = begin a = 0 @@ -117,11 +117,11 @@ describe Crystal::Repl::Interpreter do end FOO - CODE + CRYSTAL end it "does closure inside class variable initializer" do - interpret(<<-CODE).should eq(42) + interpret(<<-CRYSTAL).should eq(42) class Foo @@foo : Int32 = begin @@ -137,11 +137,11 @@ describe Crystal::Repl::Interpreter do end Foo.foo - CODE + CRYSTAL end it "does closure inside block" do - interpret(<<-CODE).should eq(42) + interpret(<<-CRYSTAL).should eq(42) def foo yield end @@ -152,11 +152,11 @@ describe Crystal::Repl::Interpreter do proc.call a end - CODE + CRYSTAL end it "does closure inside block, capture block arg" do - interpret(<<-CODE).should eq(42) + interpret(<<-CRYSTAL).should eq(42) def foo yield 21 end @@ -166,11 +166,11 @@ describe Crystal::Repl::Interpreter do proc.call a end - CODE + CRYSTAL end it "does nested closure inside proc" do - interpret(<<-CODE).should eq(21) + interpret(<<-CRYSTAL).should eq(21) a = 0 proc1 = ->{ @@ -191,11 +191,11 @@ describe Crystal::Repl::Interpreter do y = a y - x - CODE + CRYSTAL end it "does nested closure inside captured blocks" do - interpret(<<-CODE).should eq(21) + interpret(<<-CRYSTAL).should eq(21) def capture(&block : -> _) block end @@ -220,11 +220,11 @@ describe Crystal::Repl::Interpreter do y = a y - x - CODE + CRYSTAL end it "does nested closure inside methods and blocks" do - interpret(<<-CODE).should eq(12) + interpret(<<-CRYSTAL).should eq(12) def foo yield end @@ -241,11 +241,11 @@ describe Crystal::Repl::Interpreter do b end - CODE + CRYSTAL end it "does closure with pointerof local var" do - interpret(<<-CODE).should eq(42) + interpret(<<-CRYSTAL).should eq(42) a = 0 proc = ->do ptr = pointerof(a) @@ -253,11 +253,11 @@ describe Crystal::Repl::Interpreter do end proc.call a - CODE + CRYSTAL end it "closures self in proc literal" do - interpret(<<-CODE).should eq(3) + interpret(<<-CRYSTAL).should eq(3) class Foo def initialize @x = 1 @@ -281,11 +281,11 @@ describe Crystal::Repl::Interpreter do proc.call proc.call foo.x - CODE + CRYSTAL end it "closures self in proc literal (implicit self)" do - interpret(<<-CODE).should eq(3) + interpret(<<-CRYSTAL).should eq(3) class Foo def initialize @x = 1 @@ -309,11 +309,11 @@ describe Crystal::Repl::Interpreter do proc.call proc.call foo.x - CODE + CRYSTAL end it "closures self and modifies instance var" do - interpret(<<-CODE).should eq(3) + interpret(<<-CRYSTAL).should eq(3) class Foo def initialize @x = 1 @@ -333,11 +333,11 @@ describe Crystal::Repl::Interpreter do proc.call proc.call foo.x - CODE + CRYSTAL end it "closures struct and calls method on it" do - interpret(<<-CODE).should eq(2) + interpret(<<-CRYSTAL).should eq(2) struct Foo def initialize @x = 1 @@ -356,11 +356,11 @@ describe Crystal::Repl::Interpreter do proc = ->{ foo.inc } proc.call foo.x - CODE + CRYSTAL end it "doesn't mix local vars with closured vars" do - interpret(<<-CODE).should eq(20) + interpret(<<-CRYSTAL).should eq(20) def foo(x) yield x end @@ -373,11 +373,11 @@ describe Crystal::Repl::Interpreter do end }.call end - CODE + CRYSTAL end it "closures closured block arg" do - interpret(<<-CODE).should eq(1) + interpret(<<-CRYSTAL).should eq(1) def foo(&block : -> Int32) ->{ block.call }.call end @@ -385,11 +385,11 @@ describe Crystal::Repl::Interpreter do foo do 1 end - CODE + CRYSTAL end it "closures block args after 8 bytes (the closure var)" do - interpret(<<-CODE).should eq(6) + interpret(<<-CRYSTAL).should eq(6) def foo yield({1, 2, 3}) end @@ -397,11 +397,11 @@ describe Crystal::Repl::Interpreter do foo do |x, y, z| ->{ x + y + z }.call end - CODE + CRYSTAL end it "passes closured struct instance var as self" do - interpret(<<-CODE).should eq(10) + interpret(<<-CRYSTAL).should eq(10) struct Bar def bar 10 @@ -419,11 +419,11 @@ describe Crystal::Repl::Interpreter do end Foo.new.foo - CODE + CRYSTAL end it "does next inside captured block (#12226)" do - interpret(<<-CODE).should eq(1) + interpret(<<-CRYSTAL).should eq(1) def foo(&block : -> _) block.call end @@ -432,11 +432,11 @@ describe Crystal::Repl::Interpreter do next 1 2 end - CODE + CRYSTAL end it "gets ivar of self closured struct (#12341)" do - interpret(<<-CODE).should eq(860) + interpret(<<-CRYSTAL).should eq(860) def closure(&block : -> Int32) block end @@ -457,11 +457,11 @@ describe Crystal::Repl::Interpreter do foo = Foo.new foo.foo - CODE + CRYSTAL end it "sets ivar of self closured struct (#12341)" do - interpret(<<-CODE).should eq(1) # Yes, not 2. A closured struct's value can't change. + interpret(<<-CRYSTAL).should eq(1) # Yes, not 2. A closured struct's value can't change. def closure(&block) block end @@ -483,11 +483,11 @@ describe Crystal::Repl::Interpreter do foo = Foo.new foo.foo foo.count - CODE + CRYSTAL end it "reads self closured struct (#12341)" do - interpret(<<-CODE).should eq(860) + interpret(<<-CRYSTAL).should eq(860) def closure(&block : -> _) block end @@ -512,7 +512,7 @@ describe Crystal::Repl::Interpreter do foo = Foo.new bar = foo.foo bar.value - CODE + CRYSTAL end end end diff --git a/spec/compiler/interpreter/constants_spec.cr b/spec/compiler/interpreter/constants_spec.cr index aeed305f68fe..5758100d7aea 100644 --- a/spec/compiler/interpreter/constants_spec.cr +++ b/spec/compiler/interpreter/constants_spec.cr @@ -4,31 +4,31 @@ require "./spec_helper" describe Crystal::Repl::Interpreter do context "constants" do it "returns nil in the assignment" do - interpret(<<-CODE).should eq(nil) + interpret(<<-CRYSTAL).should eq(nil) A = 123 - CODE + CRYSTAL end it "interprets constant literal" do - interpret(<<-CODE).should eq(123) + interpret(<<-CRYSTAL).should eq(123) A = 123 A - CODE + CRYSTAL end it "interprets complex constant" do - interpret(<<-CODE).should eq(6) + interpret(<<-CRYSTAL).should eq(6) A = begin a = 1 b = 2 a + b end A + A - CODE + CRYSTAL end it "hoists constants" do - interpret(<<-CODE).should eq(6) + interpret(<<-CRYSTAL).should eq(6) x = A + A A = begin @@ -38,11 +38,11 @@ describe Crystal::Repl::Interpreter do end x - CODE + CRYSTAL end it "interprets self inside constant inside class" do - interpret(<<-CODE).should eq(1) + interpret(<<-CRYSTAL).should eq(1) class Foo X = self.foo @@ -56,19 +56,19 @@ describe Crystal::Repl::Interpreter do end Foo::X - CODE + CRYSTAL end end context "magic constants" do it "does line number" do - interpret(<<-CODE).should eq(6) + interpret(<<-CRYSTAL).should eq(6) def foo(x, line = __LINE__) x + line end foo(1) - CODE + CRYSTAL end end end diff --git a/spec/compiler/interpreter/control_flow_spec.cr b/spec/compiler/interpreter/control_flow_spec.cr index b8c44ce61329..91c6128b09e5 100644 --- a/spec/compiler/interpreter/control_flow_spec.cr +++ b/spec/compiler/interpreter/control_flow_spec.cr @@ -60,47 +60,47 @@ describe Crystal::Repl::Interpreter do end it "interprets while" do - interpret(<<-CODE).should eq(10) + interpret(<<-CRYSTAL).should eq(10) a = 0 while a < 10 a = a + 1 end a - CODE + CRYSTAL end it "interprets while, returns nil" do - interpret(<<-CODE).should eq(nil) + interpret(<<-CRYSTAL).should eq(nil) a = 0 while a < 10 a = a + 1 end - CODE + CRYSTAL end it "interprets until" do - interpret(<<-CODE).should eq(10) + interpret(<<-CRYSTAL).should eq(10) a = 0 until a == 10 a = a + 1 end a - CODE + CRYSTAL end it "interprets break inside while" do - interpret(<<-CODE).should eq(3) + interpret(<<-CRYSTAL).should eq(3) a = 0 while a < 10 a += 1 break if a == 3 end a - CODE + CRYSTAL end it "interprets break inside nested while" do - interpret(<<-CODE).should eq(6) + interpret(<<-CRYSTAL).should eq(6) a = 0 b = 0 c = 0 @@ -118,11 +118,11 @@ describe Crystal::Repl::Interpreter do end c - CODE + CRYSTAL end it "interprets break inside while inside block" do - interpret(<<-CODE).should eq(3) + interpret(<<-CRYSTAL).should eq(3) def foo yield 20 @@ -136,33 +136,33 @@ describe Crystal::Repl::Interpreter do end end a - CODE + CRYSTAL end it "interprets break with value inside while (through break)" do - interpret(<<-CODE).should eq(8) + interpret(<<-CRYSTAL).should eq(8) a = 0 x = while a < 10 a += 1 break 8 if a == 3 end x || 10 - CODE + CRYSTAL end it "interprets break with value inside while (through normal flow)" do - interpret(<<-CODE).should eq(10) + interpret(<<-CRYSTAL).should eq(10) a = 0 x = while a < 10 a += 1 break 8 if a == 20 end x || 10 - CODE + CRYSTAL end it "interprets next inside while" do - interpret(<<-CODE).should eq(1 + 2 + 8 + 9 + 10) + interpret(<<-CRYSTAL).should eq(1 + 2 + 8 + 9 + 10) a = 0 x = 0 while a < 10 @@ -173,11 +173,11 @@ describe Crystal::Repl::Interpreter do x += a end x - CODE + CRYSTAL end it "interprets next inside while inside block" do - interpret(<<-CODE).should eq(1 + 2 + 8 + 9 + 10) + interpret(<<-CRYSTAL).should eq(1 + 2 + 8 + 9 + 10) def foo yield 10 @@ -195,7 +195,7 @@ describe Crystal::Repl::Interpreter do end end x - CODE + CRYSTAL end it "discards while" do @@ -203,7 +203,7 @@ describe Crystal::Repl::Interpreter do end it "interprets return" do - interpret(<<-CODE).should eq(2) + interpret(<<-CRYSTAL).should eq(2) def foo(x) if x == 1 return 2 @@ -213,31 +213,31 @@ describe Crystal::Repl::Interpreter do end foo(1) - CODE + CRYSTAL end it "interprets return Nil" do - interpret(<<-CODE).should be_nil + interpret(<<-CRYSTAL).should be_nil def foo : Nil 1 end foo - CODE + CRYSTAL end it "interprets return Nil with explicit return (#12178)" do - interpret(<<-CODE).should be_nil + interpret(<<-CRYSTAL).should be_nil def foo : Nil return 1 end foo - CODE + CRYSTAL end it "interprets return implicit nil and Int32" do - interpret(<<-CODE).should eq(10) + interpret(<<-CRYSTAL).should eq(10) def foo(x) if x == 1 return @@ -252,7 +252,7 @@ describe Crystal::Repl::Interpreter do else 10 end - CODE + CRYSTAL end end end diff --git a/spec/compiler/interpreter/enum_spec.cr b/spec/compiler/interpreter/enum_spec.cr index ac3bc1efa358..f8e5e3d0ae2d 100644 --- a/spec/compiler/interpreter/enum_spec.cr +++ b/spec/compiler/interpreter/enum_spec.cr @@ -4,7 +4,7 @@ require "./spec_helper" describe Crystal::Repl::Interpreter do context "enum" do it "does enum value" do - interpret(<<-CODE).should eq(2) + interpret(<<-CRYSTAL).should eq(2) enum Color Red Green @@ -12,11 +12,11 @@ describe Crystal::Repl::Interpreter do end Color::Blue.value - CODE + CRYSTAL end it "does enum new" do - interpret(<<-CODE).should eq(2) + interpret(<<-CRYSTAL).should eq(2) enum Color Red Green @@ -25,7 +25,7 @@ describe Crystal::Repl::Interpreter do blue = Color.new(2) blue.value - CODE + CRYSTAL end end end diff --git a/spec/compiler/interpreter/exceptions_spec.cr b/spec/compiler/interpreter/exceptions_spec.cr index 9506325af1d6..f35cf54fdd6c 100644 --- a/spec/compiler/interpreter/exceptions_spec.cr +++ b/spec/compiler/interpreter/exceptions_spec.cr @@ -4,7 +4,7 @@ require "./spec_helper" describe Crystal::Repl::Interpreter do context "exception handling" do it "does ensure without rescue/raise" do - interpret(<<-CODE).should eq(12) + interpret(<<-CRYSTAL).should eq(12) x = 1 y = begin @@ -13,11 +13,11 @@ describe Crystal::Repl::Interpreter do x = 2 end x + y - CODE + CRYSTAL end it "does rescue when nothing is raised" do - interpret(<<-CODE).should eq(1) + interpret(<<-CRYSTAL).should eq(1) a = begin 1 rescue @@ -29,11 +29,11 @@ describe Crystal::Repl::Interpreter do else 10 end - CODE + CRYSTAL end it "raises and rescues anything" do - interpret(<<-CODE, prelude: "prelude").should eq("2") + interpret(<<-CRYSTAL, prelude: "prelude").should eq("2") a = begin if 1 == 1 raise "OH NO" @@ -49,11 +49,11 @@ describe Crystal::Repl::Interpreter do else 10 end - CODE + CRYSTAL end it "raises and rescues anything, does ensure when an exception is rescued" do - interpret(<<-CODE, prelude: "prelude").should eq("3") + interpret(<<-CRYSTAL, prelude: "prelude").should eq("3") a = 0 b = 0 @@ -66,11 +66,11 @@ describe Crystal::Repl::Interpreter do end a + b - CODE + CRYSTAL end it "raises and rescues specific exception type" do - interpret(<<-CODE, prelude: "prelude").should eq("2") + interpret(<<-CRYSTAL, prelude: "prelude").should eq("2") class Ex1 < Exception; end class Ex2 < Exception; end @@ -85,11 +85,11 @@ describe Crystal::Repl::Interpreter do end a - CODE + CRYSTAL end it "captures exception in variable" do - interpret(<<-CODE, prelude: "prelude").should eq("10") + interpret(<<-CRYSTAL, prelude: "prelude").should eq("10") class Ex1 < Exception getter value @@ -106,11 +106,11 @@ describe Crystal::Repl::Interpreter do end a - CODE + CRYSTAL end it "executes ensure when exception is raised in body" do - interpret(<<-CODE, prelude: "prelude").should eq("10") + interpret(<<-CRYSTAL, prelude: "prelude").should eq("10") a = 0 begin @@ -123,11 +123,11 @@ describe Crystal::Repl::Interpreter do end a - CODE + CRYSTAL end it "executes ensure when exception is raised in rescue" do - interpret(<<-CODE, prelude: "prelude").should eq("10") + interpret(<<-CRYSTAL, prelude: "prelude").should eq("10") a = 0 begin @@ -142,11 +142,11 @@ describe Crystal::Repl::Interpreter do end a - CODE + CRYSTAL end it "does else" do - interpret(<<-CODE).should eq(3) + interpret(<<-CRYSTAL).should eq(3) a = begin 'a' @@ -157,11 +157,11 @@ describe Crystal::Repl::Interpreter do end a + 1 - CODE + CRYSTAL end it "does ensure for else" do - interpret(<<-CODE).should eq(2 + ((1 * 2) + 3)) + interpret(<<-CRYSTAL).should eq(2 + ((1 * 2) + 3)) x = 1 a = @@ -177,11 +177,11 @@ describe Crystal::Repl::Interpreter do end a + x - CODE + CRYSTAL end it "does ensure for else when else raises" do - interpret(<<-CODE, prelude: "prelude").should eq("2") + interpret(<<-CRYSTAL, prelude: "prelude").should eq("2") x = 1 begin @@ -198,11 +198,11 @@ describe Crystal::Repl::Interpreter do end x - CODE + CRYSTAL end it "does ensure with explicit return" do - interpret(<<-CODE).should eq(22) + interpret(<<-CRYSTAL).should eq(22) module Global @@property = 0 @@ -235,11 +235,11 @@ describe Crystal::Repl::Interpreter do x = foo Global.property + x - CODE + CRYSTAL end it "executes ensure when returning from a block" do - interpret(<<-CODE).should eq(21) + interpret(<<-CRYSTAL).should eq(21) module Global @@property = 0 @@ -269,11 +269,11 @@ describe Crystal::Repl::Interpreter do x = foo Global.property + x - CODE + CRYSTAL end it "executes ensure when returning from a block (2)" do - interpret(<<-CODE).should eq(21) + interpret(<<-CRYSTAL).should eq(21) module Global @@property = 0 @@ -314,11 +314,11 @@ describe Crystal::Repl::Interpreter do x = foo Global.property + x - CODE + CRYSTAL end it "executes ensure when breaking from a block" do - interpret(<<-CODE).should eq(18) + interpret(<<-CRYSTAL).should eq(18) module Global @@property = 0 @@ -348,11 +348,11 @@ describe Crystal::Repl::Interpreter do x = foo Global.property + x - CODE + CRYSTAL end it "executes ensure when returning a big value from a block" do - interpret(<<-CODE, prelude: "prelude").should eq("32405") + interpret(<<-CRYSTAL, prelude: "prelude").should eq("32405") module Global @@property = 0 @@ -387,7 +387,7 @@ describe Crystal::Repl::Interpreter do else 0 end - CODE + CRYSTAL end end end diff --git a/spec/compiler/interpreter/extern_spec.cr b/spec/compiler/interpreter/extern_spec.cr index b46b23ed3947..bbd860bd3cb2 100644 --- a/spec/compiler/interpreter/extern_spec.cr +++ b/spec/compiler/interpreter/extern_spec.cr @@ -4,7 +4,7 @@ require "./spec_helper" describe Crystal::Repl::Interpreter do context "extern" do it "interprets primitive struct_or_union_set and get (struct)" do - interpret(<<-CODE).should eq(30) + interpret(<<-CRYSTAL).should eq(30) lib LibFoo struct Foo x : Int32 @@ -16,11 +16,11 @@ describe Crystal::Repl::Interpreter do foo.x = 10 foo.y = 20 foo.x + foo.y - CODE + CRYSTAL end it "discards primitive struct_or_union_set and get (struct)" do - interpret(<<-CODE).should eq(10) + interpret(<<-CRYSTAL).should eq(10) lib LibFoo struct Foo x : Int32 @@ -30,11 +30,11 @@ describe Crystal::Repl::Interpreter do foo = LibFoo::Foo.new foo.y = 10 - CODE + CRYSTAL end it "discards primitive struct_or_union_set because it's a copy" do - interpret(<<-CODE).should eq(10) + interpret(<<-CRYSTAL).should eq(10) lib LibFoo struct Foo x : Int32 @@ -47,11 +47,11 @@ describe Crystal::Repl::Interpreter do end copy.y = 10 - CODE + CRYSTAL end it "interprets primitive struct_or_union_set and get (union)" do - interpret(<<-CODE).should eq(-2045911175) + interpret(<<-CRYSTAL).should eq(-2045911175) lib LibFoo union Foo a : Bool @@ -63,11 +63,11 @@ describe Crystal::Repl::Interpreter do foo = LibFoo::Foo.new foo.x = 123456789012345 foo.y - CODE + CRYSTAL end it "sets extern struct proc field" do - interpret(<<-CODE).should eq(13) + interpret(<<-CRYSTAL).should eq(13) lib LibFoo struct Foo proc : Int32 -> Int32 @@ -79,11 +79,11 @@ describe Crystal::Repl::Interpreter do foo.field = 10 foo.proc = ->(x : Int32) { x + 1 } foo.proc.call(2) + foo.field - CODE + CRYSTAL end it "sets struct field through pointer" do - interpret(<<-CODE).should eq(20) + interpret(<<-CRYSTAL).should eq(20) lib LibFoo struct Foo x : Int32 @@ -94,11 +94,11 @@ describe Crystal::Repl::Interpreter do ptr = pointerof(foo) ptr.value.x = 20 foo.x - CODE + CRYSTAL end it "does automatic C cast" do - interpret(<<-CODE).should eq(1) + interpret(<<-CRYSTAL).should eq(1) lib LibFoo struct Foo x : UInt8 @@ -108,7 +108,7 @@ describe Crystal::Repl::Interpreter do foo = LibFoo::Foo.new foo.x = 257 foo.x - CODE + CRYSTAL end end end diff --git a/spec/compiler/interpreter/integration_spec.cr b/spec/compiler/interpreter/integration_spec.cr index 6a2f1027197a..742a5740d22f 100644 --- a/spec/compiler/interpreter/integration_spec.cr +++ b/spec/compiler/interpreter/integration_spec.cr @@ -4,41 +4,41 @@ require "./spec_helper" describe Crystal::Repl::Interpreter do context "integration" do it "does Int32#to_s" do - interpret(<<-CODE, prelude: "prelude").should eq(%("123456789")) + interpret(<<-CRYSTAL, prelude: "prelude").should eq(%("123456789")) 123456789.to_s - CODE + CRYSTAL end it "does Float64#to_s (simple)" do - interpret(<<-CODE, prelude: "prelude").should eq(%("1.5")) + interpret(<<-CRYSTAL, prelude: "prelude").should eq(%("1.5")) 1.5.to_s - CODE + CRYSTAL end it "does Float64#to_s (complex)" do - interpret(<<-CODE, prelude: "prelude").should eq(%("123456789.12345")) + interpret(<<-CRYSTAL, prelude: "prelude").should eq(%("123456789.12345")) 123456789.12345.to_s - CODE + CRYSTAL end it "does Range#to_a, Array#to_s" do - interpret(<<-CODE, prelude: "prelude").should eq(%("[1, 2, 3, 4, 5]")) + interpret(<<-CRYSTAL, prelude: "prelude").should eq(%("[1, 2, 3, 4, 5]")) (1..5).to_a.to_s - CODE + CRYSTAL end it "does some Hash methods" do - interpret(<<-CODE, prelude: "prelude").should eq("90") + interpret(<<-CRYSTAL, prelude: "prelude").should eq("90") h = {} of Int32 => Int32 10.times do |i| h[i] = i * 2 end h.values.sum - CODE + CRYSTAL end it "does CSV" do - interpret(<<-CODE, prelude: "prelude").should eq((1..6).sum.to_s) + interpret(<<-CRYSTAL, prelude: "prelude").should eq((1..6).sum.to_s) require "csv" csv = CSV.new <<-CSV, headers: true @@ -54,22 +54,22 @@ describe Crystal::Repl::Interpreter do end end sum - CODE + CRYSTAL end it "does JSON" do - interpret(<<-CODE, prelude: "prelude").should eq("6") + interpret(<<-CRYSTAL, prelude: "prelude").should eq("6") require "json" json = JSON.parse <<-JSON {"a": [1, 2, 3]} JSON json.as_h["a"].as_a.sum(&.as_i) - CODE + CRYSTAL end it "does JSON::Serializable" do - interpret(<<-CODE, prelude: "prelude").should eq("3") + interpret(<<-CRYSTAL, prelude: "prelude").should eq("3") require "json" record Point, x : Int32, y : Int32 do @@ -80,11 +80,11 @@ describe Crystal::Repl::Interpreter do {"x": 1, "y": 2} JSON point.x + point.y - CODE + CRYSTAL end it "does YAML" do - interpret(<<-CODE, prelude: "prelude").should eq("6") + interpret(<<-CRYSTAL, prelude: "prelude").should eq("6") require "yaml" yaml = YAML.parse <<-YAML @@ -94,11 +94,11 @@ describe Crystal::Repl::Interpreter do - 3 YAML yaml.as_h["a"].as_a.sum(&.as_i) - CODE + CRYSTAL end it "does YAML::Serializable" do - interpret(<<-CODE, prelude: "prelude").should eq("3") + interpret(<<-CRYSTAL, prelude: "prelude").should eq("3") require "yaml" record Point, x : Int32, y : Int32 do @@ -110,11 +110,11 @@ describe Crystal::Repl::Interpreter do y: 2 YAML point.x + point.y - CODE + CRYSTAL end pending "does XML" do - interpret(<<-CODE, prelude: "prelude").should eq("3") + interpret(<<-CRYSTAL, prelude: "prelude").should eq("3") require "xml" doc = XML.parse(<<-XML @@ -130,28 +130,28 @@ describe Crystal::Repl::Interpreter do id = attrs["id"].content.to_i id2 = attrs["id2"].content.to_i id + id2 - CODE + CRYSTAL end it "does String#includes?" do - interpret(<<-CODE, prelude: "prelude").should eq("true") + interpret(<<-CRYSTAL, prelude: "prelude").should eq("true") a = "Negative array size: -1" b = "Negative array size" a.includes?(b) - CODE + CRYSTAL end it "does IO.pipe (checks that StaticArray is passed correctly to C calls)" do - interpret(<<-CODE, prelude: "prelude").should eq(%("hello")) + interpret(<<-CRYSTAL, prelude: "prelude").should eq(%("hello")) IO.pipe do |r, w| w.puts "hello" r.gets.not_nil! end - CODE + CRYSTAL end it "does caller" do - interpret(<<-CODE, prelude: "prelude").should eq(%(":6:5 in 'bar'")) + interpret(<<-CRYSTAL, prelude: "prelude").should eq(%(":6:5 in 'bar'")) def foo bar end @@ -161,7 +161,7 @@ describe Crystal::Repl::Interpreter do end foo - CODE + CRYSTAL end end end diff --git a/spec/compiler/interpreter/is_a_spec.cr b/spec/compiler/interpreter/is_a_spec.cr index e1cf0e28196f..5138ae7cdb8c 100644 --- a/spec/compiler/interpreter/is_a_spec.cr +++ b/spec/compiler/interpreter/is_a_spec.cr @@ -4,18 +4,18 @@ require "./spec_helper" describe Crystal::Repl::Interpreter do context "is_a?" do it "does is_a? from NilableType to NonGenericClassType (true)" do - interpret(<<-CODE).should eq("hello") + interpret(<<-CRYSTAL).should eq("hello") a = "hello" || nil if a.is_a?(String) a else "bar" end - CODE + CRYSTAL end it "does is_a? from NilableType to NonGenericClassType (false)" do - interpret(<<-CODE).should eq("bar") + interpret(<<-CRYSTAL).should eq("bar") a = 1 == 1 ? nil : "hello" if a.is_a?(String) a @@ -23,11 +23,11 @@ describe Crystal::Repl::Interpreter do z = a "bar" end - CODE + CRYSTAL end it "does is_a? from NilableType to GenericClassInstanceType (true)" do - interpret(<<-CODE).should eq(1) + interpret(<<-CRYSTAL).should eq(1) class Foo(T) def initialize(@x : T) end @@ -43,11 +43,11 @@ describe Crystal::Repl::Interpreter do else 2 end - CODE + CRYSTAL end it "does is_a? from NilableType to GenericClassInstanceType (false)" do - interpret(<<-CODE).should eq(2) + interpret(<<-CRYSTAL).should eq(2) class Foo(T) def initialize(@x : T) end @@ -64,11 +64,11 @@ describe Crystal::Repl::Interpreter do z = a 2 end - CODE + CRYSTAL end it "does is_a? from NilableReferenceUnionType to NonGenericClassType (true)" do - interpret(<<-CODE).should eq("hello") + interpret(<<-CRYSTAL).should eq("hello") class Foo end @@ -78,11 +78,11 @@ describe Crystal::Repl::Interpreter do else "bar" end - CODE + CRYSTAL end it "does is_a? from NilableReferenceUnionType to NonGenericClassType (false)" do - interpret(<<-CODE).should eq("baz") + interpret(<<-CRYSTAL).should eq("baz") class Foo end @@ -92,11 +92,11 @@ describe Crystal::Repl::Interpreter do else "baz" end - CODE + CRYSTAL end it "does is_a? from VirtualType to NonGenericClassType (true)" do - interpret(<<-CODE).should eq(2) + interpret(<<-CRYSTAL).should eq(2) class Foo def x 1 @@ -115,11 +115,11 @@ describe Crystal::Repl::Interpreter do else 20 end - CODE + CRYSTAL end it "does is_a? from VirtualType to NonGenericClassType (false)" do - interpret(<<-CODE).should eq(20) + interpret(<<-CRYSTAL).should eq(20) class Foo def x 1 @@ -138,29 +138,29 @@ describe Crystal::Repl::Interpreter do else 20 end - CODE + CRYSTAL end it "does is_a? from NilableProcType to Nil" do - interpret(<<-CODE).should eq(10) + interpret(<<-CRYSTAL).should eq(10) proc = 1 == 1 ? nil : ->{ 1 } if proc.nil? 10 else 20 end - CODE + CRYSTAL end it "does is_a? from NilableProcType to non-Nil" do - interpret(<<-CODE).should eq(10) + interpret(<<-CRYSTAL).should eq(10) proc = 1 == 2 ? nil : ->{ 10 } if proc.is_a?(Proc) proc.call else 20 end - CODE + CRYSTAL end end end diff --git a/spec/compiler/interpreter/lib_spec.cr b/spec/compiler/interpreter/lib_spec.cr index 7b99c1555e99..98e1da7fd294 100644 --- a/spec/compiler/interpreter/lib_spec.cr +++ b/spec/compiler/interpreter/lib_spec.cr @@ -10,29 +10,29 @@ describe Crystal::Repl::Interpreter do end it "promotes float" do - interpret(<<-CR).should eq 3.5 + interpret(<<-CRYSTAL).should eq 3.5 @[Link(ldflags: "-L#{SPEC_CRYSTAL_LOADER_LIB_PATH} -lsum")] lib LibSum fun sum_float(count : Int32, ...) : Float32 end LibSum.sum_float(2, 1.2_f32, 2.3_f32) - CR + CRYSTAL end it "promotes int" do - interpret(<<-CR).should eq 5 + interpret(<<-CRYSTAL).should eq 5 @[Link(ldflags: "-L#{SPEC_CRYSTAL_LOADER_LIB_PATH} -lsum")] lib LibSum fun sum_int(count : Int32, ...) : Int32 end LibSum.sum_int(2, 1_u8, 4_i16) - CR + CRYSTAL end it "promotes enum" do - interpret(<<-CR).should eq 5 + interpret(<<-CRYSTAL).should eq 5 @[Link(ldflags: "-L#{SPEC_CRYSTAL_LOADER_LIB_PATH} -lsum")] lib LibSum fun sum_int(count : Int32, ...) : Int32 @@ -47,7 +47,7 @@ describe Crystal::Repl::Interpreter do end LibSum.sum_int(2, E::ONE, F::FOUR) - CR + CRYSTAL end after_all do @@ -62,14 +62,14 @@ describe Crystal::Repl::Interpreter do end it "expands ldflags" do - interpret(<<-CR).should eq 4 + interpret(<<-CRYSTAL).should eq 4 @[Link(ldflags: "-L#{SPEC_CRYSTAL_LOADER_LIB_PATH} -l`echo sum`")] lib LibSum fun simple_sum_int(a : Int32, b : Int32) : Int32 end LibSum.simple_sum_int(2, 2) - CR + CRYSTAL end after_all do diff --git a/spec/compiler/interpreter/multidispatch_spec.cr b/spec/compiler/interpreter/multidispatch_spec.cr index ebd16a44f720..345228cc4710 100644 --- a/spec/compiler/interpreter/multidispatch_spec.cr +++ b/spec/compiler/interpreter/multidispatch_spec.cr @@ -4,7 +4,7 @@ require "./spec_helper" describe Crystal::Repl::Interpreter do context "multidispatch" do it "does dispatch on one argument" do - interpret(<<-CODE).should eq(42) + interpret(<<-CRYSTAL).should eq(42) def foo(x : Char) x.ord.to_i32 end @@ -15,11 +15,11 @@ describe Crystal::Repl::Interpreter do a = 42 || 'a' foo(a) - CODE + CRYSTAL end it "does dispatch on one argument inside module with implicit self" do - interpret(<<-CODE).should eq(42) + interpret(<<-CRYSTAL).should eq(42) module Moo def self.foo(x : Char) x.ord.to_i32 @@ -36,11 +36,11 @@ describe Crystal::Repl::Interpreter do end Moo.bar - CODE + CRYSTAL end it "does dispatch on one argument inside module with explicit receiver" do - interpret(<<-CODE).should eq(42) + interpret(<<-CRYSTAL).should eq(42) module Moo def self.foo(x : Char) x.ord.to_i32 @@ -56,11 +56,11 @@ describe Crystal::Repl::Interpreter do a = 42 || 'a' Moo.foo(a) - CODE + CRYSTAL end it "does dispatch on receiver type" do - interpret(<<-CODE).should eq(42) + interpret(<<-CRYSTAL).should eq(42) struct Char def foo self.ord.to_i32 @@ -75,11 +75,11 @@ describe Crystal::Repl::Interpreter do a = 42 || 'a' a.foo - CODE + CRYSTAL end it "does dispatch on receiver type and argument type" do - interpret(<<-CODE).should eq(42 + 'b'.ord) + interpret(<<-CRYSTAL).should eq(42 + 'b'.ord) struct Char def foo(x : Int32) self.ord.to_i32 + x @@ -103,11 +103,11 @@ describe Crystal::Repl::Interpreter do a = 42 || 'a' b = 'b' || 43 a.foo(b) - CODE + CRYSTAL end it "does dispatch on receiver type and argument type, multiple times" do - interpret(<<-CODE).should eq(2 * (42 + 'b'.ord)) + interpret(<<-CRYSTAL).should eq(2 * (42 + 'b'.ord)) struct Char def foo(x : Int32) self.ord.to_i32 + x @@ -133,11 +133,11 @@ describe Crystal::Repl::Interpreter do x = a.foo(b) y = a.foo(b) x + y - CODE + CRYSTAL end it "does dispatch on one argument with struct receiver, and modifies it" do - interpret(<<-CODE).should eq(32) + interpret(<<-CRYSTAL).should eq(32) struct Foo def initialize @x = 2_i64 @@ -165,11 +165,11 @@ describe Crystal::Repl::Interpreter do a = 20 || 'a' b = foo.foo(a) b + foo.x - CODE + CRYSTAL end it "downcasts self from union to struct (pass pointer to self)" do - interpret(<<-CODE).should eq(2) + interpret(<<-CRYSTAL).should eq(2) class Foo def initialize @x = 1_i64 @@ -192,11 +192,11 @@ describe Crystal::Repl::Interpreter do obj = Point.new || Foo.new obj.x - CODE + CRYSTAL end it "does dispatch on virtual type" do - interpret(<<-CODE).should eq(4) + interpret(<<-CRYSTAL).should eq(4) abstract class Foo def foo 1 @@ -222,11 +222,11 @@ describe Crystal::Repl::Interpreter do y = foo.foo x + y - CODE + CRYSTAL end it "does dispatch on one argument with block" do - interpret(<<-CODE).should eq(42) + interpret(<<-CRYSTAL).should eq(42) def foo(x : Char) yield x.ord.to_i32 end @@ -239,11 +239,11 @@ describe Crystal::Repl::Interpreter do foo(a) do |x| x + 10 end - CODE + CRYSTAL end it "doesn't compile block if it's not used (no yield)" do - interpret(<<-CODE).should eq(2) + interpret(<<-CRYSTAL).should eq(2) class Object def try yield self @@ -259,11 +259,11 @@ describe Crystal::Repl::Interpreter do a = 1 || nil b = a.try { |x| x + 1 } b || 10 - CODE + CRYSTAL end it "does multidispatch on virtual metaclass type (1)" do - interpret(<<-CODE).should eq("BB") + interpret(<<-CRYSTAL).should eq("BB") class Class def lt(other : T.class) : String forall T {% @type %} @@ -283,11 +283,11 @@ describe Crystal::Repl::Interpreter do t = B || A t.lt(t) - CODE + CRYSTAL end it "does multidispatch on virtual metaclass type (2)" do - interpret(<<-CODE).should eq("BB") + interpret(<<-CRYSTAL).should eq("BB") class Class def lt(other : T.class) : String forall T {% @type %} @@ -310,11 +310,11 @@ describe Crystal::Repl::Interpreter do t = B || A t.lt(t) - CODE + CRYSTAL end it "passes self as pointer when doing multidispatch" do - interpret(<<-CODE).should eq(10) + interpret(<<-CRYSTAL).should eq(10) struct Foo def initialize(@x : Int32) end @@ -344,11 +344,11 @@ describe Crystal::Repl::Interpreter do foo = Foo.new(0) || Bar.new(1) foo.to_unsafe.value = 10 foo.x - CODE + CRYSTAL end it "passes self as pointer when doing multidispatch (2)" do - interpret(<<-CODE).should be_true + interpret(<<-CRYSTAL).should be_true struct Tuple def ==(other) false @@ -357,11 +357,11 @@ describe Crystal::Repl::Interpreter do a = 1.as(Int32 | Tuple(Int64, Int64)) a == 1 - CODE + CRYSTAL end it "initialize multidispatch" do - interpret(<<-CODE).should eq(1) + interpret(<<-CRYSTAL).should eq(1) struct Foo def initialize(x : Int64) initialize(x, 1 || 'a') @@ -379,11 +379,11 @@ describe Crystal::Repl::Interpreter do end Foo.new(1_i64).x - CODE + CRYSTAL end it "does multidispatch with mandatory named arguments" do - interpret(<<-CODE).should eq(1) + interpret(<<-CRYSTAL).should eq(1) class Object def foo(obj, *, file = "") obj @@ -391,11 +391,11 @@ describe Crystal::Repl::Interpreter do end ("" || nil).foo 1, file: "" - CODE + CRYSTAL end it "does multidispatch with captured block (#12217)" do - interpret(<<-CODE).should eq(42) + interpret(<<-CRYSTAL).should eq(42) class A def then(&callback : Int32 -> Int32) callback.call(70) @@ -420,11 +420,11 @@ describe Crystal::Repl::Interpreter do end a_value - b_value - CODE + CRYSTAL end it "casts multidispatch argument to the def's arg type" do - interpret(<<-CODE) + interpret(<<-CRYSTAL) def foo(a : String) forall T end @@ -433,7 +433,7 @@ describe Crystal::Repl::Interpreter do end foo("b" || nil) - CODE + CRYSTAL end end end diff --git a/spec/compiler/interpreter/named_tuple_spec.cr b/spec/compiler/interpreter/named_tuple_spec.cr index 4ec9dfc3d264..e97cb4bccef4 100644 --- a/spec/compiler/interpreter/named_tuple_spec.cr +++ b/spec/compiler/interpreter/named_tuple_spec.cr @@ -4,14 +4,14 @@ require "./spec_helper" describe Crystal::Repl::Interpreter do context "named tuple" do it "interprets named tuple literal and access by known index" do - interpret(<<-CODE).should eq(6) + interpret(<<-CRYSTAL).should eq(6) a = {a: 1, b: 2, c: 3} a[:a] + a[:b] + a[:c] - CODE + CRYSTAL end it "interprets named tuple metaclass indexer" do - interpret(<<-CODE).should eq(2) + interpret(<<-CRYSTAL).should eq(2) struct Int32 def self.foo 2 @@ -20,13 +20,13 @@ describe Crystal::Repl::Interpreter do a = {a: 1, b: 'a'} a.class[:a].foo - CODE + CRYSTAL end it "discards named tuple (#12383)" do - interpret(<<-CODE).should eq(3) + interpret(<<-CRYSTAL).should eq(3) 1 + ({a: 1, b: 2, c: 3, d: 4}; 2) - CODE + CRYSTAL end end end diff --git a/spec/compiler/interpreter/pointers_spec.cr b/spec/compiler/interpreter/pointers_spec.cr index 9064956c210f..571e63843925 100644 --- a/spec/compiler/interpreter/pointers_spec.cr +++ b/spec/compiler/interpreter/pointers_spec.cr @@ -4,59 +4,59 @@ require "./spec_helper" describe Crystal::Repl::Interpreter do context "pointers" do it "interprets pointer set and get (int)" do - interpret(<<-CODE).should eq(10) + interpret(<<-CRYSTAL).should eq(10) ptr = Pointer(Int32).malloc(1_u64) ptr.value = 10 ptr.value - CODE + CRYSTAL end it "interprets pointer set and get (bool)" do - interpret(<<-CODE).should be_true + interpret(<<-CRYSTAL).should be_true ptr = Pointer(Bool).malloc(1_u64) ptr.value = true ptr.value - CODE + CRYSTAL end it "interprets pointer set and get (clear stack)" do - interpret(<<-CODE).should eq(50.unsafe_chr) + interpret(<<-CRYSTAL).should eq(50.unsafe_chr) ptr = Pointer(UInt8).malloc(1_u64) ptr.value = 50_u8 ptr.value.unsafe_chr - CODE + CRYSTAL end it "interprets pointerof, mutates pointer, read var" do - interpret(<<-CODE).should eq(2) + interpret(<<-CRYSTAL).should eq(2) a = 1 ptr = pointerof(a) ptr.value = 2 a - CODE + CRYSTAL end it "interprets pointerof, mutates var, read pointer" do - interpret(<<-CODE).should eq(2) + interpret(<<-CRYSTAL).should eq(2) a = 1 ptr = pointerof(a) a = 2 ptr.value - CODE + CRYSTAL end it "interprets pointerof and mutates memory (there are more variables)" do - interpret(<<-CODE).should eq(2) + interpret(<<-CRYSTAL).should eq(2) x = 42 a = 1 ptr = pointerof(a) ptr.value = 2 a - CODE + CRYSTAL end it "pointerof instance var" do - interpret(<<-CODE).should eq(2) + interpret(<<-CRYSTAL).should eq(2) class Foo def initialize(@x : Int32) end @@ -74,11 +74,11 @@ describe Crystal::Repl::Interpreter do ptr = foo.x_ptr ptr.value = 2 foo.x - CODE + CRYSTAL end it "pointerof class var" do - interpret(<<-CODE).should eq(2) + interpret(<<-CRYSTAL).should eq(2) class Foo @@x : Int32? @@ -96,11 +96,11 @@ describe Crystal::Repl::Interpreter do ptr.value = 2 x = Foo.x x || 0 - CODE + CRYSTAL end it "pointerof read instance var" do - interpret(<<-CODE).should eq(2) + interpret(<<-CRYSTAL).should eq(2) class Foo def initialize(@x : Int32) end @@ -118,11 +118,11 @@ describe Crystal::Repl::Interpreter do ptr = pointerof(foo.@x) ptr.value = 2 foo.x - CODE + CRYSTAL end it "interprets pointer set and get (union type)" do - interpret(<<-CODE).should eq(10) + interpret(<<-CRYSTAL).should eq(10) ptr = Pointer(Int32 | Bool).malloc(1_u64) ptr.value = 10 value = ptr.value @@ -131,119 +131,119 @@ describe Crystal::Repl::Interpreter do else 20 end - CODE + CRYSTAL end it "interprets pointer set and get (union type, setter value)" do - interpret(<<-CODE).should eq(10) + interpret(<<-CRYSTAL).should eq(10) ptr = Pointer(Int32 | Bool).malloc(1_u64) ptr.value = 10 - CODE + CRYSTAL end it "interprets pointer new and pointer address" do - interpret(<<-CODE).should eq(123_u64) + interpret(<<-CRYSTAL).should eq(123_u64) ptr = Pointer(Int32 | Bool).new(123_u64) ptr.address - CODE + CRYSTAL end it "interprets pointer diff" do - interpret(<<-CODE).should eq(8_i64) + interpret(<<-CRYSTAL).should eq(8_i64) ptr1 = Pointer(Int32).new(133_u64) ptr2 = Pointer(Int32).new(100_u64) ptr1 - ptr2 - CODE + CRYSTAL end it "interprets pointer diff, negative" do - interpret(<<-CODE).should eq(-8_i64) + interpret(<<-CRYSTAL).should eq(-8_i64) ptr1 = Pointer(Int32).new(133_u64) ptr2 = Pointer(Int32).new(100_u64) ptr2 - ptr1 - CODE + CRYSTAL end it "discards pointer malloc" do - interpret(<<-CODE).should eq(1) + interpret(<<-CRYSTAL).should eq(1) Pointer(Int32).malloc(1_u64) 1 - CODE + CRYSTAL end it "discards pointer get" do - interpret(<<-CODE).should eq(1) + interpret(<<-CRYSTAL).should eq(1) ptr = Pointer(Int32).malloc(1_u64) ptr.value 1 - CODE + CRYSTAL end it "discards pointer set" do - interpret(<<-CODE).should eq(1) + interpret(<<-CRYSTAL).should eq(1) ptr = Pointer(Int32).malloc(1_u64) ptr.value = 1 - CODE + CRYSTAL end it "discards pointer new" do - interpret(<<-CODE).should eq(1) + interpret(<<-CRYSTAL).should eq(1) Pointer(Int32).new(1_u64) 1 - CODE + CRYSTAL end it "discards pointer diff" do - interpret(<<-CODE).should eq(1) + interpret(<<-CRYSTAL).should eq(1) ptr1 = Pointer(Int32).new(133_u64) ptr2 = Pointer(Int32).new(100_u64) ptr1 - ptr2 1 - CODE + CRYSTAL end it "discards pointerof" do - interpret(<<-CODE).should eq(3) + interpret(<<-CRYSTAL).should eq(3) a = 1 pointerof(a) 3 - CODE + CRYSTAL end it "interprets pointer add" do - interpret(<<-CODE).should eq(9) + interpret(<<-CRYSTAL).should eq(9) ptr = Pointer(Int32).new(1_u64) ptr2 = ptr + 2_i64 ptr2.address - CODE + CRYSTAL end it "discards pointer add" do - interpret(<<-CODE).should eq(3) + interpret(<<-CRYSTAL).should eq(3) ptr = Pointer(Int32).new(1_u64) ptr + 2_i64 3 - CODE + CRYSTAL end it "interprets pointer realloc" do - interpret(<<-CODE).should eq(3) + interpret(<<-CRYSTAL).should eq(3) ptr = Pointer(Int32).malloc(1_u64) ptr2 = ptr.realloc(2_u64) 3 - CODE + CRYSTAL end it "discards pointer realloc" do - interpret(<<-CODE).should eq(3) + interpret(<<-CRYSTAL).should eq(3) ptr = Pointer(Int32).malloc(1_u64) ptr.realloc(2_u64) 3 - CODE + CRYSTAL end it "interprets pointer realloc wrapper" do - interpret(<<-CODE).should eq(3) + interpret(<<-CRYSTAL).should eq(3) struct Pointer(T) def realloc(n) realloc(n.to_u64) @@ -253,18 +253,18 @@ describe Crystal::Repl::Interpreter do ptr = Pointer(Int32).malloc(1_u64) ptr2 = ptr.realloc(2) 3 - CODE + CRYSTAL end it "interprets nilable pointer truthiness" do - interpret(<<-CODE).should eq(1) + interpret(<<-CRYSTAL).should eq(1) ptr = 1 == 1 ? Pointer(UInt8).malloc(1) : nil if ptr 1 else 2 end - CODE + CRYSTAL end end end diff --git a/spec/compiler/interpreter/primitives_spec.cr b/spec/compiler/interpreter/primitives_spec.cr index 21aad383a01c..f003ebdb92ee 100644 --- a/spec/compiler/interpreter/primitives_spec.cr +++ b/spec/compiler/interpreter/primitives_spec.cr @@ -95,68 +95,68 @@ describe Crystal::Repl::Interpreter do end it "uses a string pool" do - interpret(<<-CODE).should eq(true) + interpret(<<-CRYSTAL).should eq(true) "a".object_id == "a".object_id - CODE + CRYSTAL end it "precomputes string literal length" do - interpret(<<-CODE).should eq(1) + interpret(<<-CRYSTAL).should eq(1) "旅".@length - CODE + CRYSTAL end end context "local variables" do it "interprets variable set" do - interpret(<<-CODE).should eq(1) + interpret(<<-CRYSTAL).should eq(1) a = 1 - CODE + CRYSTAL end it "interprets variable set and get" do - interpret(<<-CODE).should eq(1) + interpret(<<-CRYSTAL).should eq(1) a = 1 a - CODE + CRYSTAL end it "interprets variable set and get, second local var" do - interpret(<<-CODE).should eq(1) + interpret(<<-CRYSTAL).should eq(1) x = 10 a = 1 a - CODE + CRYSTAL end it "interprets variable set and get with operations" do - interpret(<<-CODE).should eq(6) + interpret(<<-CRYSTAL).should eq(6) a = 1 b = 2 c = 3 a + b + c - CODE + CRYSTAL end it "interprets uninitialized" do - interpret(<<-CODE).should eq(3) + interpret(<<-CRYSTAL).should eq(3) a = uninitialized Int32 a = 3 a - CODE + CRYSTAL end it "doesn't declare variable with no type" do - interpret(<<-CODE).should eq(nil) + interpret(<<-CRYSTAL).should eq(nil) x = nil if x y = x end - CODE + CRYSTAL end it "doesn't declare variable with no type inside method" do - interpret(<<-CODE).should eq(nil) + interpret(<<-CRYSTAL).should eq(nil) def foo(x) if x y = x @@ -164,25 +164,25 @@ describe Crystal::Repl::Interpreter do end foo(nil) - CODE + CRYSTAL end it "assigns to underscore" do - interpret(<<-CODE).should eq(1) + interpret(<<-CRYSTAL).should eq(1) _ = (a = 1) a - CODE + CRYSTAL end it "doesn't discard underscore right hand side" do - interpret(<<-CODE).should eq(1) + interpret(<<-CRYSTAL).should eq(1) a = (_ = 1) a - CODE + CRYSTAL end it "interprets at the class level" do - interpret(<<-CODE).should eq(1) + interpret(<<-CRYSTAL).should eq(1) x = 0 class Foo @@ -198,14 +198,14 @@ describe Crystal::Repl::Interpreter do end x - CODE + CRYSTAL end it "interprets local variable declaration (#12229)" do - interpret(<<-CODE).should eq(1) + interpret(<<-CRYSTAL).should eq(1) a : Int32 = 1 a - CODE + CRYSTAL end end @@ -293,18 +293,18 @@ describe Crystal::Repl::Interpreter do end it "discards conversion" do - interpret(<<-CODE).should eq(3) + interpret(<<-CRYSTAL).should eq(3) 1.to_i8! 3 - CODE + CRYSTAL end it "discards conversion with local var" do - interpret(<<-CODE).should eq(3) + interpret(<<-CRYSTAL).should eq(3) x = 1 x.to_i8! 3 - CODE + CRYSTAL end end @@ -489,7 +489,7 @@ describe Crystal::Repl::Interpreter do end it "interprets Int32.unsafe_shl(Int32) with self" do - interpret(<<-CODE).should eq(4) + interpret(<<-CRYSTAL).should eq(4) struct Int32 def shl2 unsafe_shl(2) @@ -498,7 +498,7 @@ describe Crystal::Repl::Interpreter do a = 1 a.shl2 - CODE + CRYSTAL end end @@ -680,11 +680,11 @@ describe Crystal::Repl::Interpreter do end it "interprets UInt64.unsafe_mod(UInt64)" do - interpret(<<-CODE).should eq(906272454103984) + interpret(<<-CRYSTAL).should eq(906272454103984) a = 10097976637018756016_u64 b = 9007199254740992_u64 a.unsafe_mod(b) - CODE + CRYSTAL end it "discards comparison" do @@ -746,7 +746,7 @@ describe Crystal::Repl::Interpreter do end it "interprets not for nilable proc type (true)" do - interpret(<<-CODE).should eq(true) + interpret(<<-CRYSTAL).should eq(true) a = if 1 == 1 nil @@ -754,11 +754,11 @@ describe Crystal::Repl::Interpreter do ->{ 1 } end !a - CODE + CRYSTAL end it "interprets not for nilable proc type (false)" do - interpret(<<-CODE).should eq(false) + interpret(<<-CRYSTAL).should eq(false) a = if 1 == 1 ->{ 1 } @@ -766,21 +766,21 @@ describe Crystal::Repl::Interpreter do nil end !a - CODE + CRYSTAL end it "interprets not for generic class instance type" do - interpret(<<-CODE).should eq(false) + interpret(<<-CRYSTAL).should eq(false) class Foo(T) end foo = Foo(Int32).new !foo - CODE + CRYSTAL end it "interprets not for nilable type (false)" do - interpret(<<-CODE).should eq(false) + interpret(<<-CRYSTAL).should eq(false) class Foo end @@ -793,11 +793,11 @@ describe Crystal::Repl::Interpreter do nil end !a - CODE + CRYSTAL end it "interprets not for nilable type (true)" do - interpret(<<-CODE).should eq(true) + interpret(<<-CRYSTAL).should eq(true) class Foo end @@ -810,7 +810,7 @@ describe Crystal::Repl::Interpreter do "a" end !a - CODE + CRYSTAL end end end diff --git a/spec/compiler/interpreter/procs_spec.cr b/spec/compiler/interpreter/procs_spec.cr index 863f8cf1fa93..2eca72820607 100644 --- a/spec/compiler/interpreter/procs_spec.cr +++ b/spec/compiler/interpreter/procs_spec.cr @@ -4,21 +4,21 @@ require "./spec_helper" describe Crystal::Repl::Interpreter do context "procs" do it "interprets no args proc literal" do - interpret(<<-CODE).should eq(42) + interpret(<<-CRYSTAL).should eq(42) proc = ->{ 40 } proc.call + 2 - CODE + CRYSTAL end it "interprets proc literal with args" do - interpret(<<-CODE).should eq(30) + interpret(<<-CRYSTAL).should eq(30) proc = ->(x : Int32, y : Int32) { x + y } proc.call(10, 20) - CODE + CRYSTAL end it "interprets call inside Proc type" do - interpret(<<-CODE).should eq(42) + interpret(<<-CRYSTAL).should eq(42) struct Proc def call2 call @@ -27,11 +27,11 @@ describe Crystal::Repl::Interpreter do proc = ->{ 40 } proc.call2 + 2 - CODE + CRYSTAL end it "casts from nilable proc type to proc type" do - interpret(<<-CODE).should eq(42) + interpret(<<-CRYSTAL).should eq(42) proc = if 1 == 1 ->{ 42 } @@ -44,19 +44,19 @@ describe Crystal::Repl::Interpreter do else 1 end - CODE + CRYSTAL end it "discards proc call" do - interpret(<<-CODE).should eq(2) + interpret(<<-CRYSTAL).should eq(2) proc = ->{ 40 } proc.call 2 - CODE + CRYSTAL end it "can downcast Proc(T) to Proc(Nil)" do - interpret(<<-CODE) + interpret(<<-CRYSTAL) class Foo def initialize(@proc : ->) end @@ -67,12 +67,12 @@ describe Crystal::Repl::Interpreter do end Foo.new(->{ 1 }).call - CODE + CRYSTAL end end it "casts proc call arguments to proc arg types (#12350)" do - interpret(<<-CODE).should eq(42) + interpret(<<-CRYSTAL).should eq(42) abstract struct Base end @@ -98,11 +98,11 @@ describe Crystal::Repl::Interpreter do bar = Foo.new(42) proc.call(bar) - CODE + CRYSTAL end it "does call without receiver inside closure" do - interpret(<<-CODE).should eq(42) + interpret(<<-CRYSTAL).should eq(42) struct Proc def foo ->{ @@ -112,6 +112,6 @@ describe Crystal::Repl::Interpreter do end ->{ 42 }.foo.call - CODE + CRYSTAL end end diff --git a/spec/compiler/interpreter/responds_to_spec.cr b/spec/compiler/interpreter/responds_to_spec.cr index be04637bbf5f..f972f0ae8d65 100644 --- a/spec/compiler/interpreter/responds_to_spec.cr +++ b/spec/compiler/interpreter/responds_to_spec.cr @@ -4,7 +4,7 @@ require "./spec_helper" describe Crystal::Repl::Interpreter do context "responds_to?" do it "does responds_to?" do - interpret(<<-CODE).should eq(3) + interpret(<<-CRYSTAL).should eq(3) class Foo def initialize @x = 1 @@ -38,11 +38,11 @@ describe Crystal::Repl::Interpreter do end a - CODE + CRYSTAL end it "doesn't crash if def body ends up with no type (#12219)" do - interpret(<<-CODE, prelude: "prelude").should eq("1") + interpret(<<-CRYSTAL, prelude: "prelude").should eq("1") class Base def foo raise "OH NO" @@ -69,7 +69,7 @@ describe Crystal::Repl::Interpreter do rescue 1 end - CODE + CRYSTAL end end end diff --git a/spec/compiler/interpreter/special_vars_spec.cr b/spec/compiler/interpreter/special_vars_spec.cr index 13738bcf31ca..131190ffee43 100644 --- a/spec/compiler/interpreter/special_vars_spec.cr +++ b/spec/compiler/interpreter/special_vars_spec.cr @@ -4,7 +4,7 @@ require "./spec_helper" describe Crystal::Repl::Interpreter do context "special vars" do it "does special var that's a reference" do - interpret(<<-CODE).should eq("hey") + interpret(<<-CRYSTAL).should eq("hey") class Object; def not_nil!; self; end; end def foo(x) @@ -13,11 +13,11 @@ describe Crystal::Repl::Interpreter do foo(2) $? || "oops" - CODE + CRYSTAL end it "does special var that's a struct" do - interpret(<<-CODE).should eq(3) + interpret(<<-CRYSTAL).should eq(3) class Object; def not_nil!; self; end; end def foo(x) @@ -26,11 +26,11 @@ describe Crystal::Repl::Interpreter do foo(2) $? || 4 - CODE + CRYSTAL end it "does special var that's a reference inside block" do - interpret(<<-CODE).should eq("hey") + interpret(<<-CRYSTAL).should eq("hey") class Object; def not_nil!; self; end; end def bar @@ -45,11 +45,11 @@ describe Crystal::Repl::Interpreter do foo(2) $? || "oops" - CODE + CRYSTAL end it "does special var that's a reference when there are optional arguments" do - interpret(<<-CODE).should eq("hey") + interpret(<<-CRYSTAL).should eq("hey") class Object; def not_nil!; self; end; end def foo(x = 1) @@ -58,11 +58,11 @@ describe Crystal::Repl::Interpreter do foo $? || "oops" - CODE + CRYSTAL end it "does special var that's a reference for multidispatch" do - interpret(<<-CODE).should eq("hey") + interpret(<<-CRYSTAL).should eq("hey") class Object; def not_nil!; self; end; end def foo(x : Int32) @@ -76,11 +76,11 @@ describe Crystal::Repl::Interpreter do a = 1 || "a" foo(a) $? || "oops" - CODE + CRYSTAL end it "sets special var inside call inside block (#12250)" do - interpret(<<-CODE).should eq("hey") + interpret(<<-CRYSTAL).should eq("hey") class Object; def not_nil!; self; end; end def foo @@ -93,7 +93,7 @@ describe Crystal::Repl::Interpreter do bar { foo } $? || "oops" - CODE + CRYSTAL end end end diff --git a/spec/compiler/interpreter/structs_spec.cr b/spec/compiler/interpreter/structs_spec.cr index a573866d1e84..1e969f60d1b2 100644 --- a/spec/compiler/interpreter/structs_spec.cr +++ b/spec/compiler/interpreter/structs_spec.cr @@ -4,7 +4,7 @@ require "./spec_helper" describe Crystal::Repl::Interpreter do context "structs" do it "does allocate, set instance var and get instance var" do - interpret(<<-CODE).should eq(42) + interpret(<<-CRYSTAL).should eq(42) struct Foo @x = 0_i64 @y = 0_i64 @@ -28,11 +28,11 @@ describe Crystal::Repl::Interpreter do foo.x = 22_i64 foo.y = 20_i64 foo.x + foo.y - CODE + CRYSTAL end it "does constructor" do - interpret(<<-CODE).should eq(42) + interpret(<<-CRYSTAL).should eq(42) struct Foo def initialize(@x : Int32) end @@ -44,11 +44,11 @@ describe Crystal::Repl::Interpreter do foo = Foo.new(42) foo.x - CODE + CRYSTAL end it "interprets read instance var of struct" do - interpret(<<-CODE).should eq(20) + interpret(<<-CRYSTAL).should eq(20) struct Foo @x = 0_i64 @y = 0_i64 @@ -64,11 +64,11 @@ describe Crystal::Repl::Interpreter do foo = Foo.allocate foo.y = 20_i64 foo.@y - CODE + CRYSTAL end it "casts def body to def type" do - interpret(<<-CODE).should eq(1) + interpret(<<-CRYSTAL).should eq(1) struct Foo def foo return nil if 1 == 2 @@ -79,21 +79,21 @@ describe Crystal::Repl::Interpreter do value = Foo.new.foo value ? 1 : 2 - CODE + CRYSTAL end it "discards allocate" do - interpret(<<-CODE).should eq(3) + interpret(<<-CRYSTAL).should eq(3) struct Foo end Foo.allocate 3 - CODE + CRYSTAL end it "mutates struct inside union" do - interpret(<<-CODE).should eq(2) + interpret(<<-CRYSTAL).should eq(2) struct Foo def initialize @x = 1 @@ -118,11 +118,11 @@ describe Crystal::Repl::Interpreter do else 0 end - CODE + CRYSTAL end it "mutates struct stored in class var" do - interpret(<<-CODE).should eq(3) + interpret(<<-CRYSTAL).should eq(3) struct Foo def initialize @x = 1 @@ -153,11 +153,11 @@ describe Crystal::Repl::Interpreter do Moo.mutate after = Moo.foo.x before + after - CODE + CRYSTAL end it "does simple class instance var initializer" do - interpret(<<-CODE).should eq(42) + interpret(<<-CRYSTAL).should eq(42) class Foo @x = 42 @@ -168,11 +168,11 @@ describe Crystal::Repl::Interpreter do foo = Foo.allocate foo.x - CODE + CRYSTAL end it "does complex class instance var initializer" do - interpret(<<-CODE).should eq(42) + interpret(<<-CRYSTAL).should eq(42) class Foo @x : Int32 = begin a = 20 @@ -187,11 +187,11 @@ describe Crystal::Repl::Interpreter do foo = Foo.allocate foo.x - CODE + CRYSTAL end it "does class instance var initializer inheritance" do - interpret(<<-CODE).should eq(6) + interpret(<<-CRYSTAL).should eq(6) module Moo @z = 3 @@ -220,11 +220,11 @@ describe Crystal::Repl::Interpreter do bar = Bar.allocate bar.x + bar.y + bar.z - CODE + CRYSTAL end it "does simple struct instance var initializer" do - interpret(<<-CODE).should eq(42) + interpret(<<-CRYSTAL).should eq(42) struct Foo @x = 42 @@ -235,11 +235,11 @@ describe Crystal::Repl::Interpreter do foo = Foo.allocate foo.x - CODE + CRYSTAL end it "does call receiver by value from VirtualType abstract struct to concrete struct (#12190)" do - interpret(<<-CODE).should eq(42) + interpret(<<-CRYSTAL).should eq(42) abstract struct Base end @@ -262,11 +262,11 @@ describe Crystal::Repl::Interpreter do else 1 end - CODE + CRYSTAL end it "does call receiver by value from VirtualType abstract struct to union" do - interpret(<<-CODE).should eq(42) + interpret(<<-CRYSTAL).should eq(42) abstract struct Base end @@ -298,11 +298,11 @@ describe Crystal::Repl::Interpreter do else 1 end - CODE + CRYSTAL end it "sets multiple instance vars in virtual abstract struct call (#12187)" do - interpret(<<-CODE).should eq(6) + interpret(<<-CRYSTAL).should eq(6) abstract struct Foo @x = 0 @y = 0 @@ -336,11 +336,11 @@ describe Crystal::Repl::Interpreter do f = Bar.new || Baz.new f.set f.x + f.y + f.z - CODE + CRYSTAL end it "inlines struct method that returns self (#12253)" do - interpret(<<-CODE).should eq(42) + interpret(<<-CRYSTAL).should eq(42) struct Foo def initialize(@x : Int32) end @@ -361,7 +361,7 @@ describe Crystal::Repl::Interpreter do a = Foo.new(42) b = a.foo b.x - CODE + CRYSTAL end end end diff --git a/spec/compiler/interpreter/symbol_spec.cr b/spec/compiler/interpreter/symbol_spec.cr index bd2be22a1654..56000d5dd640 100644 --- a/spec/compiler/interpreter/symbol_spec.cr +++ b/spec/compiler/interpreter/symbol_spec.cr @@ -4,23 +4,23 @@ require "./spec_helper" describe Crystal::Repl::Interpreter do context "symbol" do it "Symbol#to_s" do - interpret(<<-CODE).should eq("hello") + interpret(<<-CRYSTAL).should eq("hello") x = :hello x.to_s - CODE + CRYSTAL end it "Symbol#to_i" do - interpret(<<-CODE).should eq(0 + 1 + 2) + interpret(<<-CRYSTAL).should eq(0 + 1 + 2) x = :hello y = :bye z = :foo x.to_i + y.to_i + z.to_i - CODE + CRYSTAL end it "symbol equality" do - interpret(<<-CODE).should eq(9) + interpret(<<-CRYSTAL).should eq(9) s1 = :foo s2 = :bar @@ -30,7 +30,7 @@ describe Crystal::Repl::Interpreter do a += 4 if s1 != s1 a += 8 if s1 != s2 a - CODE + CRYSTAL end end end diff --git a/spec/compiler/interpreter/tuple_spec.cr b/spec/compiler/interpreter/tuple_spec.cr index 75c58d983d67..15ea8d5dea00 100644 --- a/spec/compiler/interpreter/tuple_spec.cr +++ b/spec/compiler/interpreter/tuple_spec.cr @@ -4,57 +4,57 @@ require "./spec_helper" describe Crystal::Repl::Interpreter do context "tuple" do it "interprets tuple literal and access by known index" do - interpret(<<-CODE).should eq(6) + interpret(<<-CRYSTAL).should eq(6) a = {1, 2, 3} a[0] + a[1] + a[2] - CODE + CRYSTAL end it "interprets tuple range indexer" do - interpret(<<-CODE).should eq(6) + interpret(<<-CRYSTAL).should eq(6) #{range_new} a = {1, 2, 4, 8, 16} b = a[1...-2] b[0] + b[1] - CODE + CRYSTAL end it "interprets tuple range indexer (2)" do - interpret(<<-CODE).should eq(24) + interpret(<<-CRYSTAL).should eq(24) #{range_new} a = {1_i8, 2_i8, 4_i8, 8_i8, 16_i32} b = a[3..] b[1] + b[0] - CODE + CRYSTAL end it "interprets tuple literal of different types (1)" do - interpret(<<-CODE).should eq(3) + interpret(<<-CRYSTAL).should eq(3) a = {1, true} a[0] + (a[1] ? 2 : 3) - CODE + CRYSTAL end it "interprets tuple literal of different types (2)" do - interpret(<<-CODE).should eq(3) + interpret(<<-CRYSTAL).should eq(3) a = {true, 1} a[1] + (a[0] ? 2 : 3) - CODE + CRYSTAL end it "discards tuple access" do - interpret(<<-CODE).should eq(1) + interpret(<<-CRYSTAL).should eq(1) foo = {1, 2} a = foo[0] foo[1] a - CODE + CRYSTAL end it "interprets tuple self" do - interpret(<<-CODE).should eq(6) + interpret(<<-CRYSTAL).should eq(6) struct Tuple def itself self @@ -64,19 +64,19 @@ describe Crystal::Repl::Interpreter do a = {1, 2, 3} b = a.itself b[0] + b[1] + b[2] - CODE + CRYSTAL end it "extends sign when doing to_i32" do - interpret(<<-CODE).should eq(-50) + interpret(<<-CRYSTAL).should eq(-50) t = {-50_i16} exp = t[0] z = exp.to_i32 - CODE + CRYSTAL end it "unpacks tuple in block arguments" do - interpret(<<-CODE).should eq(6) + interpret(<<-CRYSTAL).should eq(6) def foo t = {1, 2, 3} yield t @@ -85,11 +85,11 @@ describe Crystal::Repl::Interpreter do foo do |x, y, z| x + y + z end - CODE + CRYSTAL end it "interprets tuple metaclass indexer" do - interpret(<<-CODE).should eq(2) + interpret(<<-CRYSTAL).should eq(2) struct Int32 def self.foo 2 @@ -98,11 +98,11 @@ describe Crystal::Repl::Interpreter do a = {1, 'a'} a.class[0].foo - CODE + CRYSTAL end it "interprets tuple metaclass range indexer" do - interpret(<<-CODE).should eq(3) + interpret(<<-CRYSTAL).should eq(3) #{range_new} struct Int32 @@ -120,13 +120,13 @@ describe Crystal::Repl::Interpreter do a = {true, 1, "a", 'a', 1.0} b = a.class[1...-2] b[0].foo + b[1].bar - CODE + CRYSTAL end it "discards tuple (#12383)" do - interpret(<<-CODE).should eq(3) + interpret(<<-CRYSTAL).should eq(3) 1 + ({1, 2, 3, 4}; 2) - CODE + CRYSTAL end end end diff --git a/spec/compiler/interpreter/typeof_spec.cr b/spec/compiler/interpreter/typeof_spec.cr index 5b1c230cc216..90cbdbeda91f 100644 --- a/spec/compiler/interpreter/typeof_spec.cr +++ b/spec/compiler/interpreter/typeof_spec.cr @@ -14,7 +14,7 @@ describe Crystal::Repl::Interpreter do end it "interprets typeof virtual type" do - interpret(<<-CODE, prelude: "prelude").should eq(%("Foo")) + interpret(<<-CRYSTAL, prelude: "prelude").should eq(%("Foo")) abstract class Foo end @@ -26,7 +26,7 @@ describe Crystal::Repl::Interpreter do foo = Baz.new.as(Foo) typeof(foo).to_s - CODE + CRYSTAL end end end diff --git a/spec/compiler/interpreter/types_spec.cr b/spec/compiler/interpreter/types_spec.cr index 02bae1be40a1..0158561cd197 100644 --- a/spec/compiler/interpreter/types_spec.cr +++ b/spec/compiler/interpreter/types_spec.cr @@ -18,28 +18,28 @@ describe Crystal::Repl::Interpreter do end it "interprets class for virtual_type type" do - interpret(<<-CODE, prelude: "prelude").should eq(%("Bar")) + interpret(<<-CRYSTAL, prelude: "prelude").should eq(%("Bar")) class Foo; end class Bar < Foo; end bar = Bar.new || Foo.new bar.class.to_s - CODE + CRYSTAL end it "interprets class for virtual_type type (struct)" do - interpret(<<-CODE, prelude: "prelude").should eq(%("Baz")) + interpret(<<-CRYSTAL, prelude: "prelude").should eq(%("Baz")) abstract struct Foo; end struct Bar < Foo; end struct Baz < Foo; end baz = Baz.new || Bar.new baz.class.to_s - CODE + CRYSTAL end it "does class method on virtual metaclass casted to generic metaclass (#12302)" do - interpret(<<-CODE).should eq(42) + interpret(<<-CRYSTAL).should eq(42) class A def self.foo 1 @@ -54,22 +54,22 @@ describe Crystal::Repl::Interpreter do b = B(String).new.as(A) b.class.foo - CODE + CRYSTAL end it "discards class for virtual_type type" do - interpret(<<-CODE).should eq(2) + interpret(<<-CRYSTAL).should eq(2) class Foo; end class Bar < Foo; end bar = Bar.new || Foo.new bar.class 2 - CODE + CRYSTAL end it "interprets class for module type (#12203)" do - interpret(<<-CODE).should eq("A") + interpret(<<-CRYSTAL).should eq("A") class Class def name : String {{ @type.name.stringify }} @@ -94,7 +94,7 @@ describe Crystal::Repl::Interpreter do e = E.new(A.new) base = e.@base base.class.name - CODE + CRYSTAL end it "interprets crystal_type_id for nil" do @@ -107,7 +107,7 @@ describe Crystal::Repl::Interpreter do end it "interprets crystal_type_id for virtual metaclass type (#12228)" do - interpret(<<-CODE).should eq(true) + interpret(<<-CRYSTAL).should eq(true) class P end @@ -116,16 +116,16 @@ describe Crystal::Repl::Interpreter do p = A.as(P.class) p.crystal_type_id == A.crystal_type_id - CODE + CRYSTAL end it "interprets class_crystal_instance_type_id" do - interpret(<<-CODE, prelude: "prelude").should eq("true") + interpret(<<-CRYSTAL, prelude: "prelude").should eq("true") class Foo end Foo.new.crystal_type_id == Foo.crystal_instance_type_id - CODE + CRYSTAL end it "discards Path" do diff --git a/spec/compiler/interpreter/unions_spec.cr b/spec/compiler/interpreter/unions_spec.cr index 5839f05f0dac..11bde229b44d 100644 --- a/spec/compiler/interpreter/unions_spec.cr +++ b/spec/compiler/interpreter/unions_spec.cr @@ -4,21 +4,21 @@ require "./spec_helper" describe Crystal::Repl::Interpreter do context "unions" do it "put and remove from union, together with is_a? (truthy case)" do - interpret(<<-CODE).should eq(2) + interpret(<<-CRYSTAL).should eq(2) a = 1 == 1 ? 2 : true a.is_a?(Int32) ? a : 4 - CODE + CRYSTAL end it "put and remove from union, together with is_a? (falsey case)" do - interpret(<<-CODE).should eq(true) + interpret(<<-CRYSTAL).should eq(true) a = 1 == 2 ? 2 : true a.is_a?(Int32) ? true : a - CODE + CRYSTAL end it "returns union type" do - interpret(<<-CODE).should eq('a') + interpret(<<-CRYSTAL).should eq('a') def foo if 1 == 1 return 'a' @@ -33,19 +33,19 @@ describe Crystal::Repl::Interpreter do else 'b' end - CODE + CRYSTAL end it "put and remove from union in local var" do - interpret(<<-CODE).should eq(3) + interpret(<<-CRYSTAL).should eq(3) a = 1 == 1 ? 2 : true a = 3 a.is_a?(Int32) ? a : 4 - CODE + CRYSTAL end it "put and remove from union in instance var" do - interpret(<<-CODE).should eq(2) + interpret(<<-CRYSTAL).should eq(2) class Foo @x : Int32 | Char @@ -69,26 +69,26 @@ describe Crystal::Repl::Interpreter do else 10 end - CODE + CRYSTAL end it "discards is_a?" do - interpret(<<-CODE).should eq(3) + interpret(<<-CRYSTAL).should eq(3) a = 1 == 1 ? 2 : true a.is_a?(Int32) 3 - CODE + CRYSTAL end it "converts from NilableType to NonGenericClassType" do - interpret(<<-CODE).should eq("a") + interpret(<<-CRYSTAL).should eq("a") a = 1 == 1 ? "a" : nil a || "b" - CODE + CRYSTAL end it "puts union inside union" do - interpret(<<-CODE).should eq('a'.ord) + interpret(<<-CRYSTAL).should eq('a'.ord) a = 'a' || 1 || true case a in Char @@ -98,7 +98,7 @@ describe Crystal::Repl::Interpreter do in Bool 20 end - CODE + CRYSTAL end end end diff --git a/spec/compiler/macro/macro_methods_spec.cr b/spec/compiler/macro/macro_methods_spec.cr index b39fa26e9f15..d711b93ccd2e 100644 --- a/spec/compiler/macro/macro_methods_spec.cr +++ b/spec/compiler/macro/macro_methods_spec.cr @@ -2165,14 +2165,14 @@ module Crystal {x: TypeNode.new(mod)} end - assert_type(<<-CR) { int32 } + assert_type(<<-CRYSTAL) { int32 } class Foo(T) end alias Bar = Foo(Bar)? {{ Bar.nilable? ? 1 : 'a' }} - CR + CRYSTAL end end @@ -3134,9 +3134,9 @@ module Crystal end it "reads file (doesn't exist)" do - assert_error <<-CR, + assert_error <<-CRYSTAL, {{read_file("#{__DIR__}/../data/build_foo")}} - CR + CRYSTAL "No such file or directory" end end @@ -3149,9 +3149,9 @@ module Crystal end it "reads file (doesn't exist)" do - assert_error <<-CR, + assert_error <<-CRYSTAL, {{read_file("spec/compiler/data/build_foo")}} - CR + CRYSTAL "No such file or directory" end end diff --git a/spec/compiler/normalize/array_literal_spec.cr b/spec/compiler/normalize/array_literal_spec.cr index ab247427e144..9c180349bcac 100644 --- a/spec/compiler/normalize/array_literal_spec.cr +++ b/spec/compiler/normalize/array_literal_spec.cr @@ -6,27 +6,27 @@ describe "Normalize: array literal" do end it "normalizes non-empty with of" do - assert_expand "[1, 2] of Int8", <<-CR + assert_expand "[1, 2] of Int8", <<-CRYSTAL __temp_1 = ::Array(Int8).unsafe_build(2) __temp_2 = __temp_1.to_unsafe __temp_2[0] = 1 __temp_2[1] = 2 __temp_1 - CR + CRYSTAL end it "normalizes non-empty without of" do - assert_expand "[1, 2]", <<-CR + assert_expand "[1, 2]", <<-CRYSTAL __temp_1 = ::Array(typeof(1, 2)).unsafe_build(2) __temp_2 = __temp_1.to_unsafe __temp_2[0] = 1 __temp_2[1] = 2 __temp_1 - CR + CRYSTAL end it "normalizes non-empty with of, with splat" do - assert_expand "[1, *2, *3, 4, 5] of Int8", <<-CR + assert_expand "[1, *2, *3, 4, 5] of Int8", <<-CRYSTAL __temp_1 = ::Array(Int8).new(3) __temp_1 << 1 __temp_1.concat(2) @@ -34,11 +34,11 @@ describe "Normalize: array literal" do __temp_1 << 4 __temp_1 << 5 __temp_1 - CR + CRYSTAL end it "normalizes non-empty without of, with splat" do - assert_expand "[1, *2, *3, 4, 5]", <<-CR + assert_expand "[1, *2, *3, 4, 5]", <<-CRYSTAL __temp_1 = ::Array(typeof(1, ::Enumerable.element_type(2), ::Enumerable.element_type(3), 4, 5)).new(3) __temp_1 << 1 __temp_1.concat(2) @@ -46,38 +46,38 @@ describe "Normalize: array literal" do __temp_1 << 4 __temp_1 << 5 __temp_1 - CR + CRYSTAL end it "normalizes non-empty without of, with splat only" do - assert_expand "[*1]", <<-CR + assert_expand "[*1]", <<-CRYSTAL __temp_1 = ::Array(typeof(::Enumerable.element_type(1))).new(0) __temp_1.concat(1) __temp_1 - CR + CRYSTAL end it "hoists complex element expressions" do - assert_expand "[[1]]", <<-CR + assert_expand "[[1]]", <<-CRYSTAL __temp_1 = [1] __temp_2 = ::Array(typeof(__temp_1)).unsafe_build(1) __temp_3 = __temp_2.to_unsafe __temp_3[0] = __temp_1 __temp_2 - CR + CRYSTAL end it "hoists complex element expressions, with splat" do - assert_expand "[*[1]]", <<-CR + assert_expand "[*[1]]", <<-CRYSTAL __temp_1 = [1] __temp_2 = ::Array(typeof(::Enumerable.element_type(__temp_1))).new(0) __temp_2.concat(__temp_1) __temp_2 - CR + CRYSTAL end it "hoists complex element expressions, array-like" do - assert_expand_named "Foo{[1], *[2]}", <<-CR + assert_expand_named "Foo{[1], *[2]}", <<-CRYSTAL __temp_1 = [1] __temp_2 = [2] __temp_3 = Foo.new @@ -86,11 +86,11 @@ describe "Normalize: array literal" do __temp_3 << __temp_4 end __temp_3 - CR + CRYSTAL end it "hoists complex element expressions, array-like generic" do - assert_expand_named "Foo{[1], *[2]}", <<-CR, generic: "Foo" + assert_expand_named "Foo{[1], *[2]}", <<-CRYSTAL, generic: "Foo" __temp_1 = [1] __temp_2 = [2] __temp_3 = Foo(typeof(__temp_1, ::Enumerable.element_type(__temp_2))).new @@ -99,6 +99,6 @@ describe "Normalize: array literal" do __temp_3 << __temp_4 end __temp_3 - CR + CRYSTAL end end diff --git a/spec/compiler/normalize/hash_literal_spec.cr b/spec/compiler/normalize/hash_literal_spec.cr index 67f95424368a..3701733957a4 100644 --- a/spec/compiler/normalize/hash_literal_spec.cr +++ b/spec/compiler/normalize/hash_literal_spec.cr @@ -6,53 +6,53 @@ describe "Normalize: hash literal" do end it "normalizes non-empty with of" do - assert_expand "{1 => 2, 3 => 4} of Int => Float", <<-CR + assert_expand "{1 => 2, 3 => 4} of Int => Float", <<-CRYSTAL __temp_1 = ::Hash(Int, Float).new __temp_1[1] = 2 __temp_1[3] = 4 __temp_1 - CR + CRYSTAL end it "normalizes non-empty without of" do - assert_expand "{1 => 2, 3 => 4}", <<-CR + assert_expand "{1 => 2, 3 => 4}", <<-CRYSTAL __temp_1 = ::Hash(typeof(1, 3), typeof(2, 4)).new __temp_1[1] = 2 __temp_1[3] = 4 __temp_1 - CR + CRYSTAL end it "hoists complex element expressions" do - assert_expand "{[1] => 2, 3 => [4]}", <<-CR + assert_expand "{[1] => 2, 3 => [4]}", <<-CRYSTAL __temp_1 = [1] __temp_2 = [4] __temp_3 = ::Hash(typeof(__temp_1, 3), typeof(2, __temp_2)).new __temp_3[__temp_1] = 2 __temp_3[3] = __temp_2 __temp_3 - CR + CRYSTAL end it "hoists complex element expressions, hash-like" do - assert_expand_named "Foo{[1] => 2, 3 => [4]}", <<-CR + assert_expand_named "Foo{[1] => 2, 3 => [4]}", <<-CRYSTAL __temp_1 = [1] __temp_2 = [4] __temp_3 = Foo.new __temp_3[__temp_1] = 2 __temp_3[3] = __temp_2 __temp_3 - CR + CRYSTAL end it "hoists complex element expressions, hash-like generic" do - assert_expand_named "Foo{[1] => 2, 3 => [4]}", <<-CR, generic: "Foo" + assert_expand_named "Foo{[1] => 2, 3 => [4]}", <<-CRYSTAL, generic: "Foo" __temp_1 = [1] __temp_2 = [4] __temp_3 = Foo(typeof(__temp_1, 3), typeof(2, __temp_2)).new __temp_3[__temp_1] = 2 __temp_3[3] = __temp_2 __temp_3 - CR + CRYSTAL end end diff --git a/spec/compiler/normalize/multi_assign_spec.cr b/spec/compiler/normalize/multi_assign_spec.cr index 08c8fb44f0ce..3757ee9d45d9 100644 --- a/spec/compiler/normalize/multi_assign_spec.cr +++ b/spec/compiler/normalize/multi_assign_spec.cr @@ -2,64 +2,64 @@ require "../../spec_helper" describe "Normalize: multi assign" do it "normalizes n to n" do - assert_expand "a, b, c = 1, 2, 3", <<-CR + assert_expand "a, b, c = 1, 2, 3", <<-CRYSTAL __temp_1 = 1 __temp_2 = 2 __temp_3 = 3 a = __temp_1 b = __temp_2 c = __temp_3 - CR + CRYSTAL end it "normalizes n to n with []" do - assert_expand_third "a = 1; b = 2; a[0], b[1] = 2, 3", <<-CR + assert_expand_third "a = 1; b = 2; a[0], b[1] = 2, 3", <<-CRYSTAL __temp_1 = 2 __temp_2 = 3 a[0] = __temp_1 b[1] = __temp_2 - CR + CRYSTAL end it "normalizes n to n with call" do - assert_expand_third "a = 1; b = 2; a.foo, b.bar = 2, 3", <<-CR + assert_expand_third "a = 1; b = 2; a.foo, b.bar = 2, 3", <<-CRYSTAL __temp_1 = 2 __temp_2 = 3 a.foo = __temp_1 b.bar = __temp_2 - CR + CRYSTAL end context "without strict_multi_assign" do it "normalizes 1 to n" do - assert_expand_second "d = 1; a, b, c = d", <<-CR + assert_expand_second "d = 1; a, b, c = d", <<-CRYSTAL __temp_1 = d a = __temp_1[0] b = __temp_1[1] c = __temp_1[2] - CR + CRYSTAL end it "normalizes 1 to n with []" do - assert_expand_third "a = 1; b = 2; a[0], b[1] = 2", <<-CR + assert_expand_third "a = 1; b = 2; a[0], b[1] = 2", <<-CRYSTAL __temp_1 = 2 a[0] = __temp_1[0] b[1] = __temp_1[1] - CR + CRYSTAL end it "normalizes 1 to n with call" do - assert_expand_third "a = 1; b = 2; a.foo, b.bar = 2", <<-CR + assert_expand_third "a = 1; b = 2; a.foo, b.bar = 2", <<-CRYSTAL __temp_1 = 2 a.foo = __temp_1[0] b.bar = __temp_1[1] - CR + CRYSTAL end end context "strict_multi_assign" do it "normalizes 1 to n" do - assert_expand_second "d = 1; a, b, c = d", <<-CR, flags: "strict_multi_assign" + assert_expand_second "d = 1; a, b, c = d", <<-CRYSTAL, flags: "strict_multi_assign" __temp_1 = d if __temp_1.size != 3 ::raise(::IndexError.new("Multiple assignment count mismatch")) @@ -67,56 +67,56 @@ describe "Normalize: multi assign" do a = __temp_1[0] b = __temp_1[1] c = __temp_1[2] - CR + CRYSTAL end it "normalizes 1 to n with []" do - assert_expand_third "a = 1; b = 2; a[0], b[1] = 2", <<-CR, flags: "strict_multi_assign" + assert_expand_third "a = 1; b = 2; a[0], b[1] = 2", <<-CRYSTAL, flags: "strict_multi_assign" __temp_1 = 2 if __temp_1.size != 2 ::raise(::IndexError.new("Multiple assignment count mismatch")) end a[0] = __temp_1[0] b[1] = __temp_1[1] - CR + CRYSTAL end it "normalizes 1 to n with call" do - assert_expand_third "a = 1; b = 2; a.foo, b.bar = 2", <<-CR, flags: "strict_multi_assign" + assert_expand_third "a = 1; b = 2; a.foo, b.bar = 2", <<-CRYSTAL, flags: "strict_multi_assign" __temp_1 = 2 if __temp_1.size != 2 ::raise(::IndexError.new("Multiple assignment count mismatch")) end a.foo = __temp_1[0] b.bar = __temp_1[1] - CR + CRYSTAL end end it "normalizes m to n, with splat on left-hand side, splat is empty" do - assert_expand_third "a = 1; b = 2; *a[0], b.foo, c = 3, 4", <<-CR + assert_expand_third "a = 1; b = 2; *a[0], b.foo, c = 3, 4", <<-CRYSTAL __temp_1 = ::Tuple.new __temp_2 = 3 __temp_3 = 4 a[0] = __temp_1 b.foo = __temp_2 c = __temp_3 - CR + CRYSTAL end it "normalizes m to n, with splat on left-hand side, splat is non-empty" do - assert_expand_third "a = 1; b = 2; a[0], *b.foo, c = 3, 4, 5, 6, 7", <<-CR + assert_expand_third "a = 1; b = 2; a[0], *b.foo, c = 3, 4, 5, 6, 7", <<-CRYSTAL __temp_1 = 3 __temp_2 = ::Tuple.new(4, 5, 6) __temp_3 = 7 a[0] = __temp_1 b.foo = __temp_2 c = __temp_3 - CR + CRYSTAL end it "normalizes m to n, with *_ on left-hand side (1)" do - assert_expand "a, *_, b, c = 1, 2, 3, 4, 5", <<-CR + assert_expand "a, *_, b, c = 1, 2, 3, 4, 5", <<-CRYSTAL __temp_1 = 1 2 3 @@ -125,11 +125,11 @@ describe "Normalize: multi assign" do a = __temp_1 b = __temp_2 c = __temp_3 - CR + CRYSTAL end it "normalizes m to n, with *_ on left-hand side (2)" do - assert_expand "*_, a, b, c = 1, 2, 3, 4, 5", <<-CR + assert_expand "*_, a, b, c = 1, 2, 3, 4, 5", <<-CRYSTAL 1 2 __temp_1 = 3 @@ -138,11 +138,11 @@ describe "Normalize: multi assign" do a = __temp_1 b = __temp_2 c = __temp_3 - CR + CRYSTAL end it "normalizes m to n, with *_ on left-hand side (3)" do - assert_expand "a, b, c, *_ = 1, 2, 3, 4, 5", <<-CR + assert_expand "a, b, c, *_ = 1, 2, 3, 4, 5", <<-CRYSTAL __temp_1 = 1 __temp_2 = 2 __temp_3 = 3 @@ -151,11 +151,11 @@ describe "Normalize: multi assign" do a = __temp_1 b = __temp_2 c = __temp_3 - CR + CRYSTAL end it "normalizes 1 to n, with splat on left-hand side" do - assert_expand_third "c = 1; d = 2; a, b, *c.foo, d[0], e, f = 3", <<-CR + assert_expand_third "c = 1; d = 2; a, b, *c.foo, d[0], e, f = 3", <<-CRYSTAL __temp_1 = 3 if __temp_1.size < 5 ::raise(::IndexError.new("Multiple assignment count mismatch")) @@ -166,31 +166,31 @@ describe "Normalize: multi assign" do d[0] = __temp_1[-3] e = __temp_1[-2] f = __temp_1[-1] - CR + CRYSTAL end it "normalizes 1 to n, with splat on left-hand side, splat before other targets" do - assert_expand "*a, b, c, d = 3", <<-CR + assert_expand "*a, b, c, d = 3", <<-CRYSTAL __temp_1 = 3 a = __temp_1[0..-4] b = __temp_1[-3] c = __temp_1[-2] d = __temp_1[-1] - CR + CRYSTAL end it "normalizes 1 to n, with splat on left-hand side, splat after other targets" do - assert_expand "a, b, c, *d = 3", <<-CR + assert_expand "a, b, c, *d = 3", <<-CRYSTAL __temp_1 = 3 a = __temp_1[0] b = __temp_1[1] c = __temp_1[2] d = __temp_1[3..-1] - CR + CRYSTAL end it "normalizes 1 to n, with *_ on left-hand side (1)" do - assert_expand "a, *_, b, c = 1", <<-CR + assert_expand "a, *_, b, c = 1", <<-CRYSTAL __temp_1 = 1 if __temp_1.size < 3 ::raise(::IndexError.new("Multiple assignment count mismatch")) @@ -198,53 +198,53 @@ describe "Normalize: multi assign" do a = __temp_1[0] b = __temp_1[-2] c = __temp_1[-1] - CR + CRYSTAL end it "normalizes 1 to n, with *_ on left-hand side (2)" do - assert_expand "*_, a, b, c = 1", <<-CR + assert_expand "*_, a, b, c = 1", <<-CRYSTAL __temp_1 = 1 a = __temp_1[-3] b = __temp_1[-2] c = __temp_1[-1] - CR + CRYSTAL end it "normalizes 1 to n, with *_ on left-hand side (3)" do - assert_expand "a, b, c, *_ = 1", <<-CR + assert_expand "a, b, c, *_ = 1", <<-CRYSTAL __temp_1 = 1 a = __temp_1[0] b = __temp_1[1] c = __temp_1[2] - CR + CRYSTAL end it "normalizes n to splat on left-hand side" do - assert_expand "*a = 1, 2, 3, 4", <<-CR + assert_expand "*a = 1, 2, 3, 4", <<-CRYSTAL __temp_1 = ::Tuple.new(1, 2, 3, 4) a = __temp_1 - CR + CRYSTAL end it "normalizes n to *_ on left-hand side" do - assert_expand "*_ = 1, 2, 3, 4", <<-CR + assert_expand "*_ = 1, 2, 3, 4", <<-CRYSTAL 1 2 3 4 - CR + CRYSTAL end it "normalizes 1 to splat on left-hand side" do - assert_expand "*a = 1", <<-CR + assert_expand "*a = 1", <<-CRYSTAL __temp_1 = 1 a = __temp_1[0..-1] - CR + CRYSTAL end it "normalizes 1 to *_ on left-hand side" do - assert_expand "*_ = 1", <<-CR + assert_expand "*_ = 1", <<-CRYSTAL __temp_1 = 1 - CR + CRYSTAL end end diff --git a/spec/compiler/normalize/proc_pointer_spec.cr b/spec/compiler/normalize/proc_pointer_spec.cr index 0c11bf5d0abc..bd91332f23f6 100644 --- a/spec/compiler/normalize/proc_pointer_spec.cr +++ b/spec/compiler/normalize/proc_pointer_spec.cr @@ -2,69 +2,69 @@ require "../../spec_helper" describe "Normalize: proc pointer" do it "normalizes proc pointer without object" do - assert_expand "->foo", <<-CR + assert_expand "->foo", <<-CRYSTAL -> do foo end - CR + CRYSTAL end it "normalizes proc pointer with parameters, without object" do - assert_expand "->foo(Int32, String)", <<-CR + assert_expand "->foo(Int32, String)", <<-CRYSTAL ->(__temp_1 : Int32, __temp_2 : String) do foo(__temp_1, __temp_2) end - CR + CRYSTAL end it "normalizes proc pointer of global call" do - assert_expand "->::foo(Int32)", <<-CR + assert_expand "->::foo(Int32)", <<-CRYSTAL ->(__temp_1 : Int32) do ::foo(__temp_1) end - CR + CRYSTAL end it "normalizes proc pointer with const receiver" do - assert_expand "->Foo.foo(Int32)", <<-CR + assert_expand "->Foo.foo(Int32)", <<-CRYSTAL ->(__temp_1 : Int32) do Foo.foo(__temp_1) end - CR + CRYSTAL end it "normalizes proc pointer with global const receiver" do - assert_expand "->::Foo.foo(Int32)", <<-CR + assert_expand "->::Foo.foo(Int32)", <<-CRYSTAL ->(__temp_1 : Int32) do ::Foo.foo(__temp_1) end - CR + CRYSTAL end it "normalizes proc pointer with variable receiver" do - assert_expand_second "foo = 1; ->foo.bar(Int32)", <<-CR + assert_expand_second "foo = 1; ->foo.bar(Int32)", <<-CRYSTAL __temp_1 = foo ->(__temp_2 : Int32) do __temp_1.bar(__temp_2) end - CR + CRYSTAL end it "normalizes proc pointer with ivar receiver" do - assert_expand "->@foo.bar(Int32)", <<-CR + assert_expand "->@foo.bar(Int32)", <<-CRYSTAL __temp_1 = @foo ->(__temp_2 : Int32) do __temp_1.bar(__temp_2) end - CR + CRYSTAL end it "normalizes proc pointer with cvar receiver" do - assert_expand "->@@foo.bar(Int32)", <<-CR + assert_expand "->@@foo.bar(Int32)", <<-CRYSTAL __temp_1 = @@foo ->(__temp_2 : Int32) do __temp_1.bar(__temp_2) end - CR + CRYSTAL end end diff --git a/spec/compiler/normalize/select_spec.cr b/spec/compiler/normalize/select_spec.cr index 236ca0eae311..cd7231de4375 100644 --- a/spec/compiler/normalize/select_spec.cr +++ b/spec/compiler/normalize/select_spec.cr @@ -2,7 +2,7 @@ require "../../spec_helper" describe "Normalize: case" do it "normalizes select with call" do - assert_expand "select; when foo; body; when bar; baz; end", <<-CODE + assert_expand "select; when foo; body; when bar; baz; end", <<-CRYSTAL __temp_1, __temp_2 = ::Channel.select({foo_select_action, bar_select_action}) case __temp_1 when 0 @@ -12,11 +12,11 @@ describe "Normalize: case" do else ::raise("BUG: invalid select index") end - CODE + CRYSTAL end it "normalizes select with assign" do - assert_expand "select; when x = foo; x + 1; end", <<-CODE + assert_expand "select; when x = foo; x + 1; end", <<-CRYSTAL __temp_1, __temp_2 = ::Channel.select({foo_select_action}) case __temp_1 when 0 @@ -25,11 +25,11 @@ describe "Normalize: case" do else ::raise("BUG: invalid select index") end - CODE + CRYSTAL end it "normalizes select with else" do - assert_expand "select; when foo; body; else; baz; end", <<-CODE + assert_expand "select; when foo; body; else; baz; end", <<-CRYSTAL __temp_1, __temp_2 = ::Channel.non_blocking_select({foo_select_action}) case __temp_1 when 0 @@ -37,11 +37,11 @@ describe "Normalize: case" do else baz end - CODE + CRYSTAL end it "normalizes select with assign and question method" do - assert_expand "select; when x = foo?; x + 1; end", <<-CODE + assert_expand "select; when x = foo?; x + 1; end", <<-CRYSTAL __temp_1, __temp_2 = ::Channel.select({foo_select_action?}) case __temp_1 when 0 @@ -50,11 +50,11 @@ describe "Normalize: case" do else ::raise("BUG: invalid select index") end - CODE + CRYSTAL end it "normalizes select with assign and bang method" do - assert_expand "select; when x = foo!; x + 1; end", <<-CODE + assert_expand "select; when x = foo!; x + 1; end", <<-CRYSTAL __temp_1, __temp_2 = ::Channel.select({foo_select_action!}) case __temp_1 when 0 @@ -63,6 +63,6 @@ describe "Normalize: case" do else ::raise("BUG: invalid select index") end - CODE + CRYSTAL end end diff --git a/spec/compiler/parser/parser_spec.cr b/spec/compiler/parser/parser_spec.cr index 311b0c3e0344..61f1a369154f 100644 --- a/spec/compiler/parser/parser_spec.cr +++ b/spec/compiler/parser/parser_spec.cr @@ -339,13 +339,13 @@ module Crystal it_parses "def foo(@[Foo] &@block); end", Def.new("foo", body: Assign.new("@block".instance_var, "block".var), block_arg: "block".arg(annotations: ["Foo".ann]), yields: 0) it_parses "def foo(@[Foo] *args); end", Def.new("foo", args: ["args".arg(annotations: ["Foo".ann])], splat_index: 0) it_parses "def foo(@[Foo] **args); end", Def.new("foo", double_splat: "args".arg(annotations: ["Foo".ann])) - it_parses <<-CR, Def.new("foo", ["id".arg(restriction: "Int32".path, annotations: ["Foo".ann]), "name".arg(restriction: "String".path, annotations: ["Bar".ann])]) + it_parses <<-CRYSTAL, Def.new("foo", ["id".arg(restriction: "Int32".path, annotations: ["Foo".ann]), "name".arg(restriction: "String".path, annotations: ["Bar".ann])]) def foo( @[Foo] id : Int32, @[Bar] name : String ); end - CR + CRYSTAL it_parses "def foo(\n&block\n); end", Def.new("foo", block_arg: Arg.new("block"), yields: 0) it_parses "def foo(&block :\n Int ->); end", Def.new("foo", block_arg: Arg.new("block", restriction: ProcNotation.new(["Int".path] of ASTNode)), yields: 1) @@ -1052,13 +1052,13 @@ module Crystal it_parses "macro foo(a, @[Foo] &block);end", Macro.new("foo", ["a".arg], Expressions.new, block_arg: "block".arg(annotations: ["Foo".ann])) it_parses "macro foo(@[Foo] *args);end", Macro.new("foo", ["args".arg(annotations: ["Foo".ann])], Expressions.new, splat_index: 0) it_parses "macro foo(@[Foo] **args);end", Macro.new("foo", body: Expressions.new, double_splat: "args".arg(annotations: ["Foo".ann])) - it_parses <<-CR, Macro.new("foo", ["id".arg(annotations: ["Foo".ann]), "name".arg(annotations: ["Bar".ann])], Expressions.new) + it_parses <<-CRYSTAL, Macro.new("foo", ["id".arg(annotations: ["Foo".ann]), "name".arg(annotations: ["Bar".ann])], Expressions.new) macro foo( @[Foo] id, @[Bar] name );end - CR + CRYSTAL assert_syntax_error "macro foo; {% foo = 1 }; end" assert_syntax_error "macro def foo : String; 1; end" @@ -2069,29 +2069,29 @@ module Crystal it_parses "{[] of Foo, ::foo}", TupleLiteral.new([ArrayLiteral.new([] of ASTNode, "Foo".path), Call.new(nil, "foo", global: true)] of ASTNode) it_parses "{[] of Foo, self.foo}", TupleLiteral.new([ArrayLiteral.new([] of ASTNode, "Foo".path), Call.new("self".var, "foo")] of ASTNode) - it_parses <<-'CR', Macro.new("foo", body: Expressions.new([MacroLiteral.new(" <<-FOO\n \#{ "), MacroVar.new("var"), MacroLiteral.new(" }\n FOO\n")] of ASTNode)) + it_parses <<-'CRYSTAL', Macro.new("foo", body: Expressions.new([MacroLiteral.new(" <<-FOO\n \#{ "), MacroVar.new("var"), MacroLiteral.new(" }\n FOO\n")] of ASTNode)) macro foo <<-FOO #{ %var } FOO end - CR + CRYSTAL - it_parses <<-'CR', Macro.new("foo", body: MacroLiteral.new(" <<-FOO, <<-BAR + \"\"\n FOO\n BAR\n")) + it_parses <<-'CRYSTAL', Macro.new("foo", body: MacroLiteral.new(" <<-FOO, <<-BAR + \"\"\n FOO\n BAR\n")) macro foo <<-FOO, <<-BAR + "" FOO BAR end - CR + CRYSTAL - it_parses <<-'CR', Macro.new("foo", body: MacroLiteral.new(" <<-FOO\n %foo\n FOO\n")) + it_parses <<-'CRYSTAL', Macro.new("foo", body: MacroLiteral.new(" <<-FOO\n %foo\n FOO\n")) macro foo <<-FOO %foo FOO end - CR + CRYSTAL it_parses "macro foo; bar class: 1; end", Macro.new("foo", body: MacroLiteral.new(" bar class: 1; ")) @@ -2406,7 +2406,7 @@ module Crystal end it "correctly computes line number after `\\{%\n` (#9857)" do - code = <<-CODE + code = <<-CRYSTAL macro foo \\{% 1 @@ -2414,7 +2414,7 @@ module Crystal end 1 - CODE + CRYSTAL exps = Parser.parse(code).as(Expressions) exps.expressions[1].location.not_nil!.line_number.should eq(7) diff --git a/spec/compiler/semantic/abstract_def_spec.cr b/spec/compiler/semantic/abstract_def_spec.cr index 0cbff757099f..b7a7da5d431f 100644 --- a/spec/compiler/semantic/abstract_def_spec.cr +++ b/spec/compiler/semantic/abstract_def_spec.cr @@ -116,14 +116,14 @@ describe "Semantic: abstract def" do end it "errors if abstract method is not implemented by subclass" do - exc = assert_error <<-CR, + exc = assert_error <<-CRYSTAL, abstract class Foo abstract def foo end class Bar < Foo end - CR + CRYSTAL "abstract `def Foo#foo()` must be implemented by Bar" exc.line_number.should eq 5 exc.column_number.should eq 1 @@ -185,7 +185,7 @@ describe "Semantic: abstract def" do end it "doesn't error if abstract method is implemented by subclass" do - assert_no_errors <<-CR + assert_no_errors <<-CRYSTAL abstract class Foo abstract def foo end @@ -194,11 +194,11 @@ describe "Semantic: abstract def" do def foo end end - CR + CRYSTAL end it "doesn't error if abstract method with args is implemented by subclass" do - assert_no_errors <<-CR + assert_no_errors <<-CRYSTAL abstract class Foo abstract def foo(x, y) end @@ -207,11 +207,11 @@ describe "Semantic: abstract def" do def foo(x, y) end end - CR + CRYSTAL end it "doesn't error if abstract method with args is implemented by subclass (restriction -> no restriction)" do - assert_no_errors <<-CR + assert_no_errors <<-CRYSTAL abstract class Foo abstract def foo(x, y : Int32) end @@ -220,11 +220,11 @@ describe "Semantic: abstract def" do def foo(x, y) end end - CR + CRYSTAL end it "doesn't error if abstract method with args is implemented by subclass (don't check subclasses)" do - assert_no_errors <<-CR + assert_no_errors <<-CRYSTAL abstract class Foo abstract def foo end @@ -236,18 +236,18 @@ describe "Semantic: abstract def" do class Baz < Bar end - CR + CRYSTAL end it "errors if abstract method of private type is not implemented by subclass" do - assert_error <<-CR, "abstract `def Foo#foo()` must be implemented by Bar" + assert_error <<-CRYSTAL, "abstract `def Foo#foo()` must be implemented by Bar" private abstract class Foo abstract def foo end class Bar < Foo end - CR + CRYSTAL end it "errors if abstract method is not implemented by subclass of subclass" do @@ -266,7 +266,7 @@ describe "Semantic: abstract def" do end it "doesn't error if abstract method is implemented by subclass via module inclusion" do - assert_no_errors <<-CR + assert_no_errors <<-CRYSTAL abstract class Foo abstract def foo end @@ -279,7 +279,7 @@ describe "Semantic: abstract def" do class Bar < Foo include Moo end - CR + CRYSTAL end it "errors if abstract method is not implemented by including class" do @@ -296,7 +296,7 @@ describe "Semantic: abstract def" do end it "doesn't error if abstract method is implemented by including class" do - assert_no_errors <<-CR + assert_no_errors <<-CRYSTAL module Foo abstract def foo end @@ -307,11 +307,11 @@ describe "Semantic: abstract def" do def foo end end - CR + CRYSTAL end it "errors if abstract method of private type is not implemented by including class" do - assert_error <<-CR, "abstract `def Foo#foo()` must be implemented by Bar" + assert_error <<-CRYSTAL, "abstract `def Foo#foo()` must be implemented by Bar" private module Foo abstract def foo end @@ -319,11 +319,11 @@ describe "Semantic: abstract def" do class Bar include Foo end - CR + CRYSTAL end it "doesn't error if abstract method is not implemented by including module" do - assert_no_errors <<-CR + assert_no_errors <<-CRYSTAL module Foo abstract def foo end @@ -331,7 +331,7 @@ describe "Semantic: abstract def" do module Bar include Foo end - CR + CRYSTAL end it "errors if abstract method is not implemented by subclass (nested in module)" do @@ -349,7 +349,7 @@ describe "Semantic: abstract def" do end it "doesn't error if abstract method with args is implemented by subclass (with one default arg)" do - assert_no_errors <<-CR + assert_no_errors <<-CRYSTAL abstract class Foo abstract def foo(x) end @@ -358,7 +358,7 @@ describe "Semantic: abstract def" do def foo(x, y = 1) end end - CR + CRYSTAL end it "doesn't error if implements with parent class" do @@ -471,7 +471,7 @@ describe "Semantic: abstract def" do end it "finds implements in included module in disorder (#4052)" do - assert_no_errors <<-CR + assert_no_errors <<-CRYSTAL module B abstract def x end @@ -486,11 +486,11 @@ describe "Semantic: abstract def" do include C include B end - CR + CRYSTAL end it "errors if missing return type" do - assert_error <<-CR, + assert_error <<-CRYSTAL, abstract class Foo abstract def foo : Int32 end @@ -500,12 +500,12 @@ describe "Semantic: abstract def" do 1 end end - CR + CRYSTAL "this method overrides Foo#foo() which has an explicit return type of Int32.\n\nPlease add an explicit return type (Int32 or a subtype of it) to this method as well." end it "errors if different return type" do - assert_error <<-CR, + assert_error <<-CRYSTAL, abstract class Foo abstract def foo : Int32 end @@ -518,7 +518,7 @@ describe "Semantic: abstract def" do 1 end end - CR + CRYSTAL "this method must return Int32, which is the return type of the overridden method Foo#foo(), or a subtype of it, not Bar::Int32" end @@ -546,7 +546,7 @@ describe "Semantic: abstract def" do end it "matches instantiated generic types" do - assert_no_errors <<-CR + assert_no_errors <<-CRYSTAL abstract class Foo(T) abstract def foo(x : T) end @@ -558,11 +558,11 @@ describe "Semantic: abstract def" do def foo(x : Int32) end end - CR + CRYSTAL end it "matches generic types" do - assert_no_errors <<-CR + assert_no_errors <<-CRYSTAL abstract class Foo(T) abstract def foo(x : T) end @@ -571,11 +571,11 @@ describe "Semantic: abstract def" do def foo(x : U) end end - CR + CRYSTAL end it "matches instantiated generic module" do - assert_no_errors <<-CR + assert_no_errors <<-CRYSTAL module Foo(T) abstract def foo(x : T) end @@ -586,11 +586,11 @@ describe "Semantic: abstract def" do def foo(x : Int32) end end - CR + CRYSTAL end it "matches generic module" do - assert_no_errors <<-CR + assert_no_errors <<-CRYSTAL module Foo(T) abstract def foo(x : T) end @@ -601,11 +601,11 @@ describe "Semantic: abstract def" do def foo(x : U) end end - CR + CRYSTAL end it "matches generic module (a bit more complex)" do - assert_no_errors <<-CR + assert_no_errors <<-CRYSTAL class Gen(T) end @@ -619,11 +619,11 @@ describe "Semantic: abstract def" do def foo(x : Gen(Int32)) end end - CR + CRYSTAL end it "matches generic return type" do - assert_no_errors <<-CR + assert_no_errors <<-CRYSTAL abstract class Foo(T) abstract def foo : T end @@ -633,11 +633,11 @@ describe "Semantic: abstract def" do 1 end end - CR + CRYSTAL end it "errors if missing a return type in subclass of generic subclass" do - assert_error <<-CR, + assert_error <<-CRYSTAL, abstract class Foo(T) abstract def foo : T end @@ -646,12 +646,12 @@ describe "Semantic: abstract def" do def foo end end - CR + CRYSTAL "this method overrides Foo(T)#foo() which has an explicit return type of T.\n\nPlease add an explicit return type (Int32 or a subtype of it) to this method as well." end it "errors if can't find parent return type" do - assert_error <<-CR, + assert_error <<-CRYSTAL, abstract class Foo abstract def foo : Unknown end @@ -660,12 +660,12 @@ describe "Semantic: abstract def" do def foo end end - CR + CRYSTAL "can't resolve return type Unknown" end it "errors if can't find child return type" do - assert_error <<-CR, + assert_error <<-CRYSTAL, abstract class Foo abstract def foo : Int32 end @@ -674,12 +674,12 @@ describe "Semantic: abstract def" do def foo : Unknown end end - CR + CRYSTAL "can't resolve return type Unknown" end it "implements through extend (considers original type for generic lookup) (#8096)" do - assert_no_errors <<-CR + assert_no_errors <<-CRYSTAL module ICallable(T) abstract def call(foo : T) end @@ -693,11 +693,11 @@ describe "Semantic: abstract def" do extend ICallable(Int32) extend Moo end - CR + CRYSTAL end it "implements through extend (considers original type for generic lookup) (2) (#8096)" do - assert_no_errors <<-CR + assert_no_errors <<-CRYSTAL module ICallable(T) abstract def call(foo : T) end @@ -709,11 +709,11 @@ describe "Semantic: abstract def" do def call(foo : Int32) end end - CR + CRYSTAL end it "can implement even if yield comes later in macro code" do - assert_no_errors <<-CR + assert_no_errors <<-CRYSTAL module Moo abstract def each(& : Int32 -> _) end @@ -729,11 +729,11 @@ describe "Semantic: abstract def" do {% end %} end end - CR + CRYSTAL end it "can implement by block signature even if yield comes later in macro code" do - assert_no_errors <<-CR + assert_no_errors <<-CRYSTAL module Moo abstract def each(& : Int32 -> _) end @@ -747,11 +747,11 @@ describe "Semantic: abstract def" do {% end %} end end - CR + CRYSTAL end it "error shows full signature of block parameter" do - assert_error(<<-CR, "abstract `def Moo#each(& : (Int32 -> _))` must be implemented by Foo") + assert_error(<<-CRYSTAL, "abstract `def Moo#each(& : (Int32 -> _))` must be implemented by Foo") module Moo abstract def each(& : Int32 -> _) end @@ -759,11 +759,11 @@ describe "Semantic: abstract def" do class Foo include Moo end - CR + CRYSTAL end it "doesn't error if implementation have default value" do - assert_no_errors <<-CR + assert_no_errors <<-CRYSTAL abstract class Foo abstract def foo(x) end @@ -772,7 +772,7 @@ describe "Semantic: abstract def" do def foo(x = 1) end end - CR + CRYSTAL end it "errors if implementation doesn't have default value" do @@ -845,7 +845,7 @@ describe "Semantic: abstract def" do end it "doesn't error if implementation matches keyword argument" do - assert_no_errors <<-CR + assert_no_errors <<-CRYSTAL abstract class Foo abstract def foo(*, x) end @@ -854,7 +854,7 @@ describe "Semantic: abstract def" do def foo(*, x) end end - CR + CRYSTAL end it "errors if implementation doesn't match keyword argument type" do @@ -872,7 +872,7 @@ describe "Semantic: abstract def" do end it "doesn't error if implementation have keyword arguments in different order" do - assert_no_errors <<-CR + assert_no_errors <<-CRYSTAL abstract class Foo abstract def foo(*, x : Int32, y : String) end @@ -881,7 +881,7 @@ describe "Semantic: abstract def" do def foo(*, y : String, x : Int32) end end - CR + CRYSTAL end it "errors if implementation has more keyword arguments" do @@ -899,7 +899,7 @@ describe "Semantic: abstract def" do end it "doesn't error if implementation has more keyword arguments with default values" do - assert_no_errors <<-CR + assert_no_errors <<-CRYSTAL abstract class Foo abstract def foo(*, x) end @@ -908,7 +908,7 @@ describe "Semantic: abstract def" do def foo(*, x, y = 1) end end - CR + CRYSTAL end it "errors if implementation doesn't have a splat" do @@ -940,7 +940,7 @@ describe "Semantic: abstract def" do end it "doesn't error with splat" do - assert_no_errors <<-CR + assert_no_errors <<-CRYSTAL abstract class Foo abstract def foo(*args) end @@ -949,11 +949,11 @@ describe "Semantic: abstract def" do def foo(*args) end end - CR + CRYSTAL end it "doesn't error with splat and args with default value" do - assert_no_errors <<-CR + assert_no_errors <<-CRYSTAL abstract class Foo abstract def foo(*args) end @@ -962,11 +962,11 @@ describe "Semantic: abstract def" do def foo(a = 1, *args) end end - CR + CRYSTAL end it "allows arguments to be collapsed into splat" do - assert_no_errors <<-CR + assert_no_errors <<-CRYSTAL abstract class Foo abstract def foo(a : Int32, b : String) end @@ -975,7 +975,7 @@ describe "Semantic: abstract def" do def foo(*args : Int32 | String) end end - CR + CRYSTAL end it "errors if keyword argument doesn't have the same default value" do @@ -992,7 +992,7 @@ describe "Semantic: abstract def" do end it "allow double splat argument" do - assert_no_errors <<-CR + assert_no_errors <<-CRYSTAL abstract class Foo abstract def foo(**kargs) end @@ -1001,11 +1001,11 @@ describe "Semantic: abstract def" do def foo(**kargs) end end - CR + CRYSTAL end it "allow double splat when abstract doesn't have it" do - assert_no_errors <<-CR + assert_no_errors <<-CRYSTAL abstract class Foo abstract def foo end @@ -1014,7 +1014,7 @@ describe "Semantic: abstract def" do def foo(**kargs) end end - CR + CRYSTAL end it "errors if implementation misses the double splat" do @@ -1044,7 +1044,7 @@ describe "Semantic: abstract def" do end it "allow splat instead of keyword argument" do - assert_no_errors <<-CR + assert_no_errors <<-CRYSTAL abstract class Foo abstract def foo(*, foo) end @@ -1053,7 +1053,7 @@ describe "Semantic: abstract def" do def foo(**kargs) end end - CR + CRYSTAL end it "extra keyword arguments must have compatible type to double splat" do @@ -1103,7 +1103,7 @@ describe "Semantic: abstract def" do describe "implementation is not inherited from supertype" do it "nongeneric class" do - assert_error <<-CR, "abstract `def Abstract#foo()` must be implemented by Concrete" + assert_error <<-CRYSTAL, "abstract `def Abstract#foo()` must be implemented by Concrete" class Supertype def foo; end end @@ -1114,11 +1114,11 @@ describe "Semantic: abstract def" do class Concrete < Abstract end - CR + CRYSTAL end it "generic class" do - assert_error <<-CR, "abstract `def Abstract(T)#foo()` must be implemented by Concrete" + assert_error <<-CRYSTAL, "abstract `def Abstract(T)#foo()` must be implemented by Concrete" class Supertype(T) def foo; end end @@ -1129,11 +1129,11 @@ describe "Semantic: abstract def" do class Concrete(T) < Abstract(T) end - CR + CRYSTAL end it "nongeneric module" do - assert_error <<-CR, "abstract `def Abstract#size()` must be implemented by Concrete" + assert_error <<-CRYSTAL, "abstract `def Abstract#size()` must be implemented by Concrete" module Supertype def size end @@ -1148,11 +1148,11 @@ describe "Semantic: abstract def" do class Concrete include Abstract end - CR + CRYSTAL end it "generic module" do - assert_error <<-CR, "abstract `def Abstract(T)#size()` must be implemented by Concrete(T)" + assert_error <<-CRYSTAL, "abstract `def Abstract(T)#size()` must be implemented by Concrete(T)" module Supertype(T) def size end @@ -1167,7 +1167,7 @@ describe "Semantic: abstract def" do class Concrete(T) include Abstract(T) end - CR + CRYSTAL end end end diff --git a/spec/compiler/semantic/annotation_spec.cr b/spec/compiler/semantic/annotation_spec.cr index 94ea352e4177..249a23c149a0 100644 --- a/spec/compiler/semantic/annotation_spec.cr +++ b/spec/compiler/semantic/annotation_spec.cr @@ -1130,12 +1130,12 @@ describe "Semantic: annotation" do end it "doesn't carry link annotation from lib to fun" do - assert_no_errors <<-CR + assert_no_errors <<-CRYSTAL @[Link("foo")] lib LibFoo fun foo end - CR + CRYSTAL end it "finds annotation in generic parent (#7885)" do @@ -1271,13 +1271,13 @@ describe "Semantic: annotation" do end it "doesn't bleed annotation from class into class variable (#8314)" do - assert_no_errors <<-CR + assert_no_errors <<-CRYSTAL annotation Attr; end @[Attr] class Bar @@x = 0 end - CR + CRYSTAL end end diff --git a/spec/compiler/semantic/automatic_cast_spec.cr b/spec/compiler/semantic/automatic_cast_spec.cr index 19273cce90b3..c10eea7e9e19 100644 --- a/spec/compiler/semantic/automatic_cast_spec.cr +++ b/spec/compiler/semantic/automatic_cast_spec.cr @@ -730,7 +730,7 @@ describe "Semantic: automatic cast" do end it "errors when autocast default value doesn't match enum member" do - assert_error <<-CR, + assert_error <<-CRYSTAL, enum Foo FOO end @@ -739,7 +739,7 @@ describe "Semantic: automatic cast" do end foo - CR + CRYSTAL "can't autocast :bar to Foo: no matching enum member" end end diff --git a/spec/compiler/semantic/block_spec.cr b/spec/compiler/semantic/block_spec.cr index 74b2c272d8d0..021ba2a6738a 100644 --- a/spec/compiler/semantic/block_spec.cr +++ b/spec/compiler/semantic/block_spec.cr @@ -1318,7 +1318,7 @@ describe "Block inference" do end it "auto-unpacks tuple, captured empty block" do - assert_no_errors <<-CR, inject_primitives: true + assert_no_errors <<-CRYSTAL, inject_primitives: true def foo(&block : {Int32, Char} -> _) tup = {1, 'a'} block.call tup @@ -1326,7 +1326,7 @@ describe "Block inference" do foo do |x, y| end - CR + CRYSTAL end it "auto-unpacks tuple, captured block with multiple statements" do @@ -1457,18 +1457,18 @@ describe "Block inference" do end it "reports mismatch with generic argument type in output type" do - assert_error(<<-CR, "expected block to return String, not Int32") + assert_error(<<-CRYSTAL, "expected block to return String, not Int32") class Foo(T) def foo(&block : -> T) end end Foo(String).new.foo { 1 } - CR + CRYSTAL end it "reports mismatch with generic argument type in input type" do - assert_error(<<-CR, "argument #1 of yield expected to be String, not Int32") + assert_error(<<-CRYSTAL, "argument #1 of yield expected to be String, not Int32") class Foo(T) def foo(&block : T -> ) yield 1 @@ -1476,7 +1476,7 @@ describe "Block inference" do end Foo(String).new.foo {} - CR + CRYSTAL end it "correctly types unpacked tuple block arg after block (#3339)" do @@ -1522,14 +1522,14 @@ describe "Block inference" do end it "doesn't crash on cleaning up typeof node without dependencies (#8669)" do - assert_no_errors <<-CR + assert_no_errors <<-CRYSTAL def foo(&) end foo do typeof(bar) end - CR + CRYSTAL end it "respects block arg restriction when block has a splat parameter (#6473)" do @@ -1565,7 +1565,7 @@ describe "Block inference" do end it "allows underscore in block return type even if the return type can't be computed" do - assert_no_errors <<-CR + assert_no_errors <<-CRYSTAL def foo(& : -> _) yield end @@ -1577,11 +1577,11 @@ describe "Block inference" do end recursive - CR + CRYSTAL end it "doesn't fail with 'already had enclosing call' (#11200)" do - assert_no_errors <<-CR + assert_no_errors <<-CRYSTAL def capture(&block) block end @@ -1606,6 +1606,6 @@ describe "Block inference" do foo = Bar(Bool).new.as(Foo) foo.foo - CR + CRYSTAL end end diff --git a/spec/compiler/semantic/case_spec.cr b/spec/compiler/semantic/case_spec.cr index 9cd3f5d2a3e4..e9eff0a92ca4 100644 --- a/spec/compiler/semantic/case_spec.cr +++ b/spec/compiler/semantic/case_spec.cr @@ -648,17 +648,17 @@ describe "semantic: case" do end private def bool_case_eq - <<-CODE + <<-CRYSTAL struct Bool def ===(other) true end end - CODE + CRYSTAL end private def enum_eq - <<-CODE + <<-CRYSTAL struct Enum def ==(other : self) value == other.value @@ -668,5 +668,5 @@ private def enum_eq true end end - CODE + CRYSTAL end diff --git a/spec/compiler/semantic/class_spec.cr b/spec/compiler/semantic/class_spec.cr index 584d89041310..88c3dd474a3f 100644 --- a/spec/compiler/semantic/class_spec.cr +++ b/spec/compiler/semantic/class_spec.cr @@ -1116,7 +1116,7 @@ describe "Semantic: class" do end it "errors if inherits from metaclass" do - assert_error <<-CR, "Foo.class is not a class, it's a metaclass" + assert_error <<-CRYSTAL, "Foo.class is not a class, it's a metaclass" class Foo end @@ -1124,7 +1124,7 @@ describe "Semantic: class" do class Bar < FooClass end - CR + CRYSTAL end it "can use short name for top-level type" do diff --git a/spec/compiler/semantic/closure_spec.cr b/spec/compiler/semantic/closure_spec.cr index f73e1c83d3a0..9fb359f0bb36 100644 --- a/spec/compiler/semantic/closure_spec.cr +++ b/spec/compiler/semantic/closure_spec.cr @@ -617,7 +617,7 @@ describe "Semantic: closure" do end it "doesn't assign all types to metavar if closured but only assigned to once" do - assert_no_errors <<-CR, inject_primitives: true + assert_no_errors <<-CRYSTAL, inject_primitives: true def capture(&block) block end @@ -627,7 +627,7 @@ describe "Semantic: closure" do x &+ 1 end end - CR + CRYSTAL end it "does assign all types to metavar if closured but only assigned to once in a loop" do diff --git a/spec/compiler/semantic/const_spec.cr b/spec/compiler/semantic/const_spec.cr index 5815a11f4123..164a412756e4 100644 --- a/spec/compiler/semantic/const_spec.cr +++ b/spec/compiler/semantic/const_spec.cr @@ -319,7 +319,7 @@ describe "Semantic: const" do "1 + 2", "1 + ZED", "ZED - 1", "ZED * 2", "ZED // 2", "1 &+ ZED", "ZED &- 1", "ZED &* 2"].each do |node| it "doesn't errors if constant depends on another one defined later through method, but constant is simple (#{node})" do - assert_no_errors <<-CR, inject_primitives: true + assert_no_errors <<-CRYSTAL, inject_primitives: true ZED = 10 struct Int32 @@ -337,7 +337,7 @@ describe "Semantic: const" do end CONST1 - CR + CRYSTAL end end @@ -452,19 +452,19 @@ describe "Semantic: const" do end it "errors if using const in proc notation parameter type" do - assert_error <<-CR, "A is not a type, it's a constant" + assert_error <<-CRYSTAL, "A is not a type, it's a constant" A = 1 x : A -> - CR + CRYSTAL end it "errors if using const in proc notation return type" do - assert_error <<-CR, "A is not a type, it's a constant" + assert_error <<-CRYSTAL, "A is not a type, it's a constant" A = 1 x : -> A - CR + CRYSTAL end it "errors if using return inside constant value (#5391)" do diff --git a/spec/compiler/semantic/def_overload_spec.cr b/spec/compiler/semantic/def_overload_spec.cr index 439830c9bfc7..f0e2a93864dc 100644 --- a/spec/compiler/semantic/def_overload_spec.cr +++ b/spec/compiler/semantic/def_overload_spec.cr @@ -1,7 +1,7 @@ require "../../spec_helper" def assert_stricter(params1, params2, args, *, file = __FILE__, line = __LINE__) - assert_type(<<-CR, file: file, line: line, flags: "preview_overload_order") { tuple_of([int32, int32]) } + assert_type(<<-CRYSTAL, file: file, line: line, flags: "preview_overload_order") { tuple_of([int32, int32]) } def foo(#{params1}); 1; end def foo(#{params2}); 'x'; end @@ -11,11 +11,11 @@ def assert_stricter(params1, params2, args, *, file = __FILE__, line = __LINE__) a = foo(#{args}) b = bar(#{args}) {a, b} - CR + CRYSTAL end def assert_unordered(params1, params2, args, *, file = __FILE__, line = __LINE__) - assert_type(<<-CR, file: file, line: line, flags: "preview_overload_order") { tuple_of([int32, int32]) } + assert_type(<<-CRYSTAL, file: file, line: line, flags: "preview_overload_order") { tuple_of([int32, int32]) } def foo(#{params1}); 1; end def foo(#{params2}); 'x'; end @@ -25,7 +25,7 @@ def assert_unordered(params1, params2, args, *, file = __FILE__, line = __LINE__ a = foo(#{args}) b = bar(#{args}) {a, b} - CR + CRYSTAL end describe "Semantic: def overload" do @@ -763,16 +763,16 @@ describe "Semantic: def overload" do end it "does not consider global paths as free variables (1)" do - assert_error <<-CR, "undefined constant ::Foo" + assert_error <<-CRYSTAL, "undefined constant ::Foo" def foo(x : ::Foo) forall Foo end foo(1) - CR + CRYSTAL end it "does not consider global paths as free variables (2)" do - assert_error <<-CR, "expected argument #1 to 'foo' to be Foo, not Int32" + assert_error <<-CRYSTAL, "expected argument #1 to 'foo' to be Foo, not Int32" class Foo end @@ -780,7 +780,7 @@ describe "Semantic: def overload" do end foo(1) - CR + CRYSTAL end it "prefers more specific overload than one with free variables" do @@ -1646,7 +1646,7 @@ describe "Semantic: def overload" do end it "treats single splats with same restriction as equivalent (#12579)" do - assert_type(<<-CR) { int32 } + assert_type(<<-CRYSTAL) { int32 } def foo(*x : Int32) 'a' end @@ -1656,11 +1656,11 @@ describe "Semantic: def overload" do end foo(1) - CR + CRYSTAL end it "treats single splats with same restriction as equivalent (2) (#12579)" do - assert_type(<<-CR) { int32 } + assert_type(<<-CRYSTAL) { int32 } def foo(*x : Int32) 'a' end @@ -1670,7 +1670,7 @@ describe "Semantic: def overload" do end foo(1) - CR + CRYSTAL end end diff --git a/spec/compiler/semantic/def_spec.cr b/spec/compiler/semantic/def_spec.cr index bf758e298dd4..fee65d2f3b5d 100644 --- a/spec/compiler/semantic/def_spec.cr +++ b/spec/compiler/semantic/def_spec.cr @@ -557,10 +557,10 @@ describe "Semantic: def" do end it "points error at name (#6937)" do - ex = assert_error <<-CODE, + ex = assert_error <<-CRYSTAL, 1. foobar - CODE + CRYSTAL "undefined method" ex.line_number.should eq(2) ex.column_number.should eq(3) diff --git a/spec/compiler/semantic/did_you_mean_spec.cr b/spec/compiler/semantic/did_you_mean_spec.cr index 593dcd24160c..cd3f0856ebcb 100644 --- a/spec/compiler/semantic/did_you_mean_spec.cr +++ b/spec/compiler/semantic/did_you_mean_spec.cr @@ -102,14 +102,14 @@ describe "Semantic: did you mean" do end it "doesn't suggest for operator" do - error = assert_error <<-CR + error = assert_error <<-CRYSTAL class Foo def + end end Foo.new.a - CR + CRYSTAL error.to_s.should_not contain("Did you mean") end diff --git a/spec/compiler/semantic/doc_spec.cr b/spec/compiler/semantic/doc_spec.cr index e46f7a9df640..9b38339b6485 100644 --- a/spec/compiler/semantic/doc_spec.cr +++ b/spec/compiler/semantic/doc_spec.cr @@ -419,7 +419,7 @@ describe "Semantic: doc" do end it "stores doc for macro defined in macro call" do - result = semantic <<-CR, wants_doc: true + result = semantic <<-CRYSTAL, wants_doc: true macro def_foo macro foo end @@ -427,7 +427,7 @@ describe "Semantic: doc" do # Hello def_foo - CR + CRYSTAL program = result.program foo = program.macros.not_nil!["foo"].first foo.doc.should eq("Hello") diff --git a/spec/compiler/semantic/enum_spec.cr b/spec/compiler/semantic/enum_spec.cr index fd9083c888f4..ee4ac5a7a0e5 100644 --- a/spec/compiler/semantic/enum_spec.cr +++ b/spec/compiler/semantic/enum_spec.cr @@ -476,23 +476,23 @@ describe "Semantic: enum" do end it "doesn't overflow when going from negative to zero (#7874)" do - assert_no_errors <<-CR + assert_no_errors <<-CRYSTAL enum Nums Zero = -2 One Two end - CR + CRYSTAL end it "doesn't overflow on flags member (#7877)" do - assert_no_errors <<-CR + assert_no_errors <<-CRYSTAL @[Flags] enum Filter A = 1 << 29 B end - CR + CRYSTAL end it "doesn't visit enum members generated by macros twice (#10104)" do diff --git a/spec/compiler/semantic/exception_spec.cr b/spec/compiler/semantic/exception_spec.cr index 776af95c7121..1b5328e5edeb 100644 --- a/spec/compiler/semantic/exception_spec.cr +++ b/spec/compiler/semantic/exception_spec.cr @@ -695,7 +695,7 @@ describe "Semantic: exception" do end it "gets a non-nilable type if all rescue are unreachable (#8751)" do - assert_no_errors <<-CR, inject_primitives: true + assert_no_errors <<-CRYSTAL, inject_primitives: true while true begin foo = 1 @@ -707,7 +707,7 @@ describe "Semantic: exception" do foo &+ 2 end - CR + CRYSTAL end it "correctly types variable assigned inside nested exception handler (#9769)" do diff --git a/spec/compiler/semantic/generic_class_spec.cr b/spec/compiler/semantic/generic_class_spec.cr index bb2e27e62a0b..5f809229f96e 100644 --- a/spec/compiler/semantic/generic_class_spec.cr +++ b/spec/compiler/semantic/generic_class_spec.cr @@ -655,14 +655,14 @@ describe "Semantic: generic class" do end it "doesn't duplicate overload on generic class with class method (#2385)" do - error = assert_error <<-CR + error = assert_error <<-CRYSTAL class Foo(T) def self.foo(x : Int32) end end Foo(String).foo(35.7) - CR + CRYSTAL error.to_s.lines.count(" - Foo(T).foo(x : Int32)").should eq(1) end diff --git a/spec/compiler/semantic/instance_var_spec.cr b/spec/compiler/semantic/instance_var_spec.cr index 08827918646c..3e268bf7dd8e 100644 --- a/spec/compiler/semantic/instance_var_spec.cr +++ b/spec/compiler/semantic/instance_var_spec.cr @@ -776,7 +776,7 @@ describe "Semantic: instance var" do end it "infers type from proc literal with return type" do - assert_type(<<-CR) { proc_of([int32, bool, string]) } + assert_type(<<-CRYSTAL) { proc_of([int32, bool, string]) } class Foo def initialize @x = ->(x : Int32, y : Bool) : String { "" } @@ -788,7 +788,7 @@ describe "Semantic: instance var" do end Foo.new.x - CR + CRYSTAL end it "infers type from new expression" do @@ -5482,7 +5482,7 @@ describe "Semantic: instance var" do end it "errors when overriding inherited instance variable with incompatible type" do - assert_error <<-CR, "instance variable '@a' of A must be Int32, not (Char | Int32)" + assert_error <<-CRYSTAL, "instance variable '@a' of A must be Int32, not (Char | Int32)" class A @a = 1 end @@ -5490,11 +5490,11 @@ describe "Semantic: instance var" do class B < A @a = 'a' end - CR + CRYSTAL end it "accepts overriding inherited instance variable with compatible type" do - semantic <<-CR + semantic <<-CRYSTAL class A @a = 1 end @@ -5502,7 +5502,7 @@ describe "Semantic: instance var" do class B < A @a = 2 end - CR + CRYSTAL end it "looks up return type restriction in defining type, not instantiated type (#11961)" do @@ -5537,7 +5537,7 @@ describe "Semantic: instance var" do end it "looks up self restriction in instantiated type, not defined type" do - assert_type(<<-CR) { types["Foo2"] } + assert_type(<<-CRYSTAL) { types["Foo2"] } class Foo1 def foo : self self @@ -5558,7 +5558,7 @@ describe "Semantic: instance var" do end Bar.new.bar - CR + CRYSTAL end it "inferrs Proc(Void) to Proc(Nil)" do @@ -5680,7 +5680,7 @@ describe "Semantic: instance var" do end it "accepts module and module, with definitions" do - semantic <<-CR + semantic <<-CRYSTAL module M @a = 1 end @@ -5693,11 +5693,11 @@ describe "Semantic: instance var" do include N include M end - CR + CRYSTAL end it "accepts module and module, with declarations" do - semantic <<-CR + semantic <<-CRYSTAL module M @a : Int32? end @@ -5710,13 +5710,13 @@ describe "Semantic: instance var" do include N include M end - CR + CRYSTAL end end context "with incompatible type" do it "module and class, with definitions" do - assert_error <<-CR, "instance variable '@a' of A, with B < A, is already declared as Int32 (trying to re-declare it in B as Char)" + assert_error <<-CRYSTAL, "instance variable '@a' of A, with B < A, is already declared as Int32 (trying to re-declare it in B as Char)" module M @a = 'a' end @@ -5728,11 +5728,11 @@ describe "Semantic: instance var" do class B < A include M end - CR + CRYSTAL end it "module and class, with declarations" do - assert_error <<-CR, "instance variable '@a' of A, with B < A, is already declared as Int32 (trying to re-declare it in B as Char)" + assert_error <<-CRYSTAL, "instance variable '@a' of A, with B < A, is already declared as Int32 (trying to re-declare it in B as Char)" module M @a : Char = 'a' end @@ -5744,11 +5744,11 @@ describe "Semantic: instance var" do class B < A include M end - CR + CRYSTAL end it "errors module and module, with definitions" do - assert_error <<-CR, "instance variable '@a' of B must be Char, not (Char | Int32)" + assert_error <<-CRYSTAL, "instance variable '@a' of B must be Char, not (Char | Int32)" module M @a = 'c' end @@ -5761,11 +5761,11 @@ describe "Semantic: instance var" do include N include M end - CR + CRYSTAL end it "errors module and module, with declarations" do - assert_error <<-CR, "instance variable '@a' of B must be Int32, not (Char | Int32)" + assert_error <<-CRYSTAL, "instance variable '@a' of B must be Int32, not (Char | Int32)" module M @a : Char = 'c' end @@ -5778,7 +5778,7 @@ describe "Semantic: instance var" do include N include M end - CR + CRYSTAL end end end diff --git a/spec/compiler/semantic/lib_spec.cr b/spec/compiler/semantic/lib_spec.cr index 5908a341d999..70c2a7b11819 100644 --- a/spec/compiler/semantic/lib_spec.cr +++ b/spec/compiler/semantic/lib_spec.cr @@ -377,20 +377,20 @@ describe "Semantic: lib" do end it "warns if @[Link(static: true)] is specified" do - assert_warning <<-CR, + assert_warning <<-CRYSTAL, @[Link("foo", static: true)] lib Foo end - CR + CRYSTAL "warning in line 1\nWarning: specifying static linking for individual libraries is deprecated" end it "warns if Link annotations use positional arguments" do - assert_warning <<-CR, + assert_warning <<-CRYSTAL, @[Link("foo", "bar")] lib Foo end - CR + CRYSTAL "warning in line 1\nWarning: using non-named arguments for Link annotations is deprecated" end diff --git a/spec/compiler/semantic/macro_spec.cr b/spec/compiler/semantic/macro_spec.cr index f7bd191801ea..4e75f621387b 100644 --- a/spec/compiler/semantic/macro_spec.cr +++ b/spec/compiler/semantic/macro_spec.cr @@ -2,13 +2,13 @@ require "../../spec_helper" describe "Semantic: macro" do it "types macro" do - assert_type(<<-CR) { int32 } + assert_type(<<-CRYSTAL) { int32 } macro foo 1 end foo - CR + CRYSTAL end it "errors if macro uses undefined variable" do @@ -17,7 +17,7 @@ describe "Semantic: macro" do end it "types macro def" do - assert_type(<<-CR) { int32 } + assert_type(<<-CRYSTAL) { int32 } class Foo def foo : Int32 {{ @type }} @@ -26,11 +26,11 @@ describe "Semantic: macro" do end Foo.new.foo - CR + CRYSTAL end it "errors if macro def type not found" do - assert_error <<-CR, "undefined constant Foo" + assert_error <<-CRYSTAL, "undefined constant Foo" class Baz def foo : Foo {{ @type }} @@ -38,11 +38,11 @@ describe "Semantic: macro" do end Baz.new.foo - CR + CRYSTAL end it "errors if macro def type doesn't match found" do - assert_error <<-CR, "method Foo#foo must return Int32 but it is returning Char" + assert_error <<-CRYSTAL, "method Foo#foo must return Int32 but it is returning Char" class Foo def foo : Int32 {{ @type}} @@ -51,7 +51,7 @@ describe "Semantic: macro" do end Foo.new.foo - CR + CRYSTAL end it "allows subclasses of return type for macro def" do @@ -127,7 +127,7 @@ describe "Semantic: macro" do Baz.new.foobar.foo }).to_i.should eq(2) - assert_error(<<-CR, "method Bar#bar must return Foo(String) but it is returning Foo(Int32)") + assert_error(<<-CRYSTAL, "method Bar#bar must return Foo(String) but it is returning Foo(Int32)") class Foo(T) def initialize(@foo : T) end @@ -141,11 +141,11 @@ describe "Semantic: macro" do end Bar.new.bar - CR + CRYSTAL end it "allows union return types for macro def" do - assert_type(<<-CR) { int32 } + assert_type(<<-CRYSTAL) { int32 } class Foo def foo : String | Int32 {{ @type }} @@ -154,11 +154,11 @@ describe "Semantic: macro" do end Foo.new.foo - CR + CRYSTAL end it "types macro def that calls another method" do - assert_type(<<-CR) { int32 } + assert_type(<<-CRYSTAL) { int32 } def bar_baz 1 end @@ -173,11 +173,11 @@ describe "Semantic: macro" do end Foo.new.foo - CR + CRYSTAL end it "types macro def that calls another method inside a class" do - assert_type(<<-CR) { int32 } + assert_type(<<-CRYSTAL) { int32 } class Foo def bar_baz 1 @@ -192,11 +192,11 @@ describe "Semantic: macro" do end Foo.new.foo - CR + CRYSTAL end it "types macro def that calls another method inside a class" do - assert_type(<<-CR) { int32 } + assert_type(<<-CRYSTAL) { int32 } class Foo def foo : Int32 {{ @type }} @@ -213,11 +213,11 @@ describe "Semantic: macro" do end Bar.new.foo - CR + CRYSTAL end it "types macro def with argument" do - assert_type(<<-CR) { int32 } + assert_type(<<-CRYSTAL) { int32 } class Foo def foo(x) : Int32 {{ @type }} @@ -226,11 +226,11 @@ describe "Semantic: macro" do end Foo.new.foo(1) - CR + CRYSTAL end it "expands macro with block" do - assert_type(<<-CR) { int32 } + assert_type(<<-CRYSTAL) { int32 } macro foo {{yield}} end @@ -242,11 +242,11 @@ describe "Semantic: macro" do end bar - CR + CRYSTAL end it "expands macro with block and argument to yield" do - assert_type(<<-CR) { int32 } + assert_type(<<-CRYSTAL) { int32 } macro foo {{yield 1}} end @@ -258,56 +258,56 @@ describe "Semantic: macro" do end bar - CR + CRYSTAL end it "errors if find macros but wrong arguments" do - assert_error(<<-CR, "wrong number of arguments for macro 'foo' (given 1, expected 0)", inject_primitives: true) + assert_error(<<-CRYSTAL, "wrong number of arguments for macro 'foo' (given 1, expected 0)", inject_primitives: true) macro foo 1 end foo(1) - CR + CRYSTAL end it "executes raise inside macro" do - ex = assert_error(<<-CR, "OH NO") + ex = assert_error(<<-CRYSTAL, "OH NO") macro foo {{ raise "OH NO" }} end foo - CR + CRYSTAL ex.to_s.should_not contain("expanding macro") end it "executes raise inside macro, with node (#5669)" do - ex = assert_error(<<-CR, "OH") + ex = assert_error(<<-CRYSTAL, "OH") macro foo(x) {{ x.raise "OH\nNO" }} end foo(1) - CR + CRYSTAL ex.to_s.should contain "NO" ex.to_s.should_not contain("expanding macro") end it "executes raise inside macro, with empty message (#8631)" do - assert_error(<<-CR, "") + assert_error(<<-CRYSTAL, "") macro foo {{ raise "" }} end foo - CR + CRYSTAL end it "can specify tuple as return type" do - assert_type(<<-CR) { tuple_of([int32, int32] of Type) } + assert_type(<<-CRYSTAL) { tuple_of([int32, int32] of Type) } class Foo def foo : {Int32, Int32} {{ @type }} @@ -316,11 +316,11 @@ describe "Semantic: macro" do end Foo.new.foo - CR + CRYSTAL end it "allows specifying self as macro def return type" do - assert_type(<<-CR) { types["Foo"] } + assert_type(<<-CRYSTAL) { types["Foo"] } class Foo def foo : self {{ @type }} @@ -329,11 +329,11 @@ describe "Semantic: macro" do end Foo.new.foo - CR + CRYSTAL end it "allows specifying self as macro def return type (2)" do - assert_type(<<-CR) { types["Bar"] } + assert_type(<<-CRYSTAL) { types["Bar"] } class Foo def foo : self {{ @type }} @@ -345,7 +345,7 @@ describe "Semantic: macro" do end Bar.new.foo - CR + CRYSTAL end it "preserves correct self in restriction when macro def is to be instantiated in subtypes (#5044)" do @@ -407,27 +407,27 @@ describe "Semantic: macro" do end it "errors if non-existent named arg" do - assert_error(<<-CR, "no parameter named 'y'") + assert_error(<<-CRYSTAL, "no parameter named 'y'") macro foo(x = 1) {{x}} + 1 end foo y: 2 - CR + CRYSTAL end it "errors if named arg already specified" do - assert_error(<<-CR, "argument for parameter 'x' already specified") + assert_error(<<-CRYSTAL, "argument for parameter 'x' already specified") macro foo(x = 1) {{x}} + 1 end foo 2, x: 2 - CR + CRYSTAL end it "finds macro in included module" do - assert_type(<<-CR) { int32 } + assert_type(<<-CRYSTAL) { int32 } module Moo macro bar 1 @@ -443,11 +443,11 @@ describe "Semantic: macro" do end Foo.new.foo - CR + CRYSTAL end it "errors when trying to define def inside def with macro expansion" do - assert_error(<<-CR, "can't define def inside def") + assert_error(<<-CRYSTAL, "can't define def inside def") macro foo def bar; end end @@ -457,11 +457,11 @@ describe "Semantic: macro" do end baz - CR + CRYSTAL end it "gives precise location info when doing yield inside macro" do - assert_error(<<-CR, "in line 6") + assert_error(<<-CRYSTAL, "in line 6") macro foo {{yield}} end @@ -469,11 +469,11 @@ describe "Semantic: macro" do foo do 1 + 'a' end - CR + CRYSTAL end it "transforms with {{yield}} and call" do - assert_type(<<-CR) { int32 } + assert_type(<<-CRYSTAL) { int32 } macro foo bar({{yield}}) end @@ -489,11 +489,11 @@ describe "Semantic: macro" do foo do baz end - CR + CRYSTAL end it "can return class type in macro def" do - assert_type(<<-CR) { types["Int32"].metaclass } + assert_type(<<-CRYSTAL) { types["Int32"].metaclass } class Foo def foo : Int32.class {{ @type }} @@ -502,11 +502,11 @@ describe "Semantic: macro" do end Foo.new.foo - CR + CRYSTAL end it "can return virtual class type in macro def" do - assert_type(<<-CR, inject_primitives: true) { types["Foo"].metaclass.virtual_type } + assert_type(<<-CRYSTAL, inject_primitives: true) { types["Foo"].metaclass.virtual_type } class Foo end @@ -521,24 +521,24 @@ describe "Semantic: macro" do end Foo.new.foo - CR + CRYSTAL end it "can't define new variables (#466)" do - error = assert_error <<-CR + error = assert_error <<-CRYSTAL macro foo hello = 1 end foo hello - CR + CRYSTAL error.to_s.should_not contain("did you mean") end it "finds macro in included generic module" do - assert_type(<<-CR) { int32 } + assert_type(<<-CRYSTAL) { int32 } module Moo(T) macro moo 1 @@ -554,11 +554,11 @@ describe "Semantic: macro" do end Foo.new.foo - CR + CRYSTAL end it "finds macro in inherited generic class" do - assert_type(<<-CR) { int32 } + assert_type(<<-CRYSTAL) { int32 } class Moo(T) macro moo 1 @@ -572,21 +572,21 @@ describe "Semantic: macro" do end Foo.new.foo - CR + CRYSTAL end it "doesn't die on && inside if (bug)" do - assert_type(<<-CR) { int32 } + assert_type(<<-CRYSTAL) { int32 } macro foo 1 && 2 end foo ? 3 : 4 - CR + CRYSTAL end it "checks if macro expansion returns (#821)" do - assert_type(<<-CR) { nilable symbol } + assert_type(<<-CRYSTAL) { nilable symbol } macro pass return :pass end @@ -597,43 +597,43 @@ describe "Semantic: macro" do end me - CR + CRYSTAL end it "errors if declares macro inside if" do - assert_error(<<-CR, "can't declare macro dynamically") + assert_error(<<-CRYSTAL, "can't declare macro dynamically") if 1 == 2 macro foo; end end - CR + CRYSTAL end it "allows declaring class with macro if" do - assert_type(<<-CR) { types["Foo"] } + assert_type(<<-CRYSTAL) { types["Foo"] } {% if true %} class Foo; end {% end %} Foo.new - CR + CRYSTAL end it "allows declaring class with macro for" do - assert_type(<<-CR) { types["Foo"] } + assert_type(<<-CRYSTAL) { types["Foo"] } {% for i in 0..0 %} class Foo; end {% end %} Foo.new - CR + CRYSTAL end it "allows declaring class with inline macro expression (#1333)" do - assert_type(<<-CR) { types["Foo"] } + assert_type(<<-CRYSTAL) { types["Foo"] } {{ "class Foo; end".id }} Foo.new - CR + CRYSTAL end it "errors if requires inside class through macro expansion" do @@ -652,7 +652,7 @@ describe "Semantic: macro" do end it "errors if requires inside if through macro expansion" do - assert_error(<<-CR, "can't require dynamically") + assert_error(<<-CRYSTAL, "can't require dynamically") macro req require "bar" end @@ -660,11 +660,11 @@ describe "Semantic: macro" do if 1 == 2 req end - CR + CRYSTAL end it "can define constant via macro included" do - assert_type(<<-CR) { int32 } + assert_type(<<-CRYSTAL) { int32 } module Mod macro included CONST = 1 @@ -674,11 +674,11 @@ describe "Semantic: macro" do include Mod CONST - CR + CRYSTAL end it "errors if applying protected modifier to macro" do - assert_error(<<-CR, "can only use 'private' for macros") + assert_error(<<-CRYSTAL, "can only use 'private' for macros") class Foo protected macro foo 1 @@ -686,11 +686,11 @@ describe "Semantic: macro" do end Foo.foo - CR + CRYSTAL end it "expands macro with break inside while (#1852)" do - assert_type(<<-CR) { nil_type } + assert_type(<<-CRYSTAL) { nil_type } macro test foo = "bar" break @@ -699,11 +699,11 @@ describe "Semantic: macro" do while true test end - CR + CRYSTAL end it "can access variable inside macro expansion (#2057)" do - assert_type(<<-CR) { int32 } + assert_type(<<-CRYSTAL) { int32 } macro foo x end @@ -715,11 +715,11 @@ describe "Semantic: macro" do method do |x| foo end - CR + CRYSTAL end it "declares variable for macro with out" do - assert_type(<<-CR) { int32 } + assert_type(<<-CRYSTAL) { int32 } lib LibFoo fun foo(x : Int32*) end @@ -730,44 +730,44 @@ describe "Semantic: macro" do LibFoo.foo(out z) some_macro - CR + CRYSTAL end it "show macro trace in errors (1)" do - ex = assert_error(<<-CR, "Error: expanding macro") + ex = assert_error(<<-CRYSTAL, "Error: expanding macro") macro foo Bar end foo - CR + CRYSTAL ex.to_s.should contain "error in line 5" end it "show macro trace in errors (2)" do - ex = assert_error(<<-CR, "Error: expanding macro") + ex = assert_error(<<-CRYSTAL, "Error: expanding macro") {% begin %} Bar {% end %} - CR + CRYSTAL ex.to_s.should contain "error in line 1" end it "errors if using macro that is defined later" do - assert_error(<<-CR, "macro 'foo' must be defined before this point but is defined later") + assert_error(<<-CRYSTAL, "macro 'foo' must be defined before this point but is defined later") class Bar foo end macro foo end - CR + CRYSTAL end it "looks up argument types in macro owner, not in subclass (#2395)" do - assert_type(<<-CR) { int32 } + assert_type(<<-CRYSTAL) { int32 } struct Nil def method(x : Problem) 0 @@ -796,11 +796,11 @@ describe "Semantic: macro" do end Moo::Bar.new.method(Problem.new) - CR + CRYSTAL end it "doesn't error when adding macro call to constant (#2457)" do - assert_type(<<-CR) { int32 } + assert_type(<<-CRYSTAL) { int32 } macro foo end @@ -814,75 +814,75 @@ describe "Semantic: macro" do coco do foo end - CR + CRYSTAL end it "errors if named arg matches single splat parameter" do - assert_error(<<-CR, "no parameter named 'x'") + assert_error(<<-CRYSTAL, "no parameter named 'x'") macro foo(*y) end foo x: 1, y: 2 - CR + CRYSTAL end it "errors if named arg matches splat parameter" do - assert_error(<<-CR, "wrong number of arguments for macro 'foo' (given 0, expected 1+)") + assert_error(<<-CRYSTAL, "wrong number of arguments for macro 'foo' (given 0, expected 1+)") macro foo(x, *y) end foo x: 1, y: 2 - CR + CRYSTAL end it "says missing argument because positional args don't match past splat" do - assert_error(<<-CR, "missing argument: z") + assert_error(<<-CRYSTAL, "missing argument: z") macro foo(x, *y, z) end foo 1, 2 - CR + CRYSTAL end it "allows named args after splat" do - assert_type(<<-CR) { tuple_of([tuple_of([int32]), char]) } + assert_type(<<-CRYSTAL) { tuple_of([tuple_of([int32]), char]) } macro foo(*y, x) { {{y}}, {{x}} } end foo 1, x: 'a' - CR + CRYSTAL end it "errors if missing one argument" do - assert_error(<<-CR, "missing argument: z") + assert_error(<<-CRYSTAL, "missing argument: z") macro foo(x, y, z) end foo x: 1, y: 2 - CR + CRYSTAL end it "errors if missing two arguments" do - assert_error(<<-CR, "missing arguments: x, z") + assert_error(<<-CRYSTAL, "missing arguments: x, z") macro foo(x, y, z) end foo y: 2 - CR + CRYSTAL end it "doesn't include parameters with default values in missing arguments error" do - assert_error(<<-CR, "missing argument: z") + assert_error(<<-CRYSTAL, "missing argument: z") macro foo(x, z, y = 1) end foo(x: 1) - CR + CRYSTAL end it "solves macro expression arguments before macro expansion (type)" do - assert_type(<<-CR) { int32 } + assert_type(<<-CRYSTAL) { int32 } macro foo(x) {% if x.is_a?(TypeNode) && x.name == "String" %} 1 @@ -892,11 +892,11 @@ describe "Semantic: macro" do end foo({{ String }}) - CR + CRYSTAL end it "solves macro expression arguments before macro expansion (constant)" do - assert_type(<<-CR) { int32 } + assert_type(<<-CRYSTAL) { int32 } macro foo(x) {% if x.is_a?(NumberLiteral) && x == 1 %} 1 @@ -907,11 +907,11 @@ describe "Semantic: macro" do CONST = 1 foo({{ CONST }}) - CR + CRYSTAL end it "solves named macro expression arguments before macro expansion (type) (#2423)" do - assert_type(<<-CR) { int32 } + assert_type(<<-CRYSTAL) { int32 } macro foo(x) {% if x.is_a?(TypeNode) && x.name == "String" %} 1 @@ -921,11 +921,11 @@ describe "Semantic: macro" do end foo(x: {{ String }}) - CR + CRYSTAL end it "solves named macro expression arguments before macro expansion (constant) (#2423)" do - assert_type(<<-CR) { int32 } + assert_type(<<-CRYSTAL) { int32 } macro foo(x) {% if x.is_a?(NumberLiteral) && x == 1 %} 1 @@ -936,11 +936,11 @@ describe "Semantic: macro" do CONST = 1 foo(x: {{ CONST }}) - CR + CRYSTAL end it "finds generic type argument of included module" do - assert_type(<<-CR) { int32.metaclass } + assert_type(<<-CRYSTAL) { int32.metaclass } module Bar(T) def t {{ T }} @@ -952,11 +952,11 @@ describe "Semantic: macro" do end Foo(Int32).new.t - CR + CRYSTAL end it "finds generic type argument of included module with self" do - assert_type(<<-CR) { generic_class("Foo", int32).metaclass } + assert_type(<<-CRYSTAL) { generic_class("Foo", int32).metaclass } module Bar(T) def t {{ T }} @@ -968,11 +968,11 @@ describe "Semantic: macro" do end Foo(Int32).new.t - CR + CRYSTAL end it "finds free type vars" do - assert_type(<<-CR) { tuple_of([int32.metaclass, string.metaclass]) } + assert_type(<<-CRYSTAL) { tuple_of([int32.metaclass, string.metaclass]) } module Foo(T) def self.foo(foo : U) forall U { {{ T }}, {{ U }} } @@ -980,41 +980,41 @@ describe "Semantic: macro" do end Foo(Int32).foo("foo") - CR + CRYSTAL end it "gets named arguments in double splat" do - assert_type(<<-CR) { named_tuple_of({"x": string, "y": bool}) } + assert_type(<<-CRYSTAL) { named_tuple_of({"x": string, "y": bool}) } macro foo(**options) {{options}} end foo x: "foo", y: true - CR + CRYSTAL end it "uses splat and double splat" do - assert_type(<<-CR) { tuple_of([tuple_of([int32, char]), named_tuple_of({"x": string, "y": bool})]) } + assert_type(<<-CRYSTAL) { tuple_of([tuple_of([int32, char]), named_tuple_of({"x": string, "y": bool})]) } macro foo(*args, **options) { {{args}}, {{options}} } end foo 1, 'a', x: "foo", y: true - CR + CRYSTAL end it "double splat and regular args" do - assert_type(<<-CR) { tuple_of([int32, bool, named_tuple_of({"w": char, "z": string})]) } + assert_type(<<-CRYSTAL) { tuple_of([int32, bool, named_tuple_of({"w": char, "z": string})]) } macro foo(x, y, **options) { {{x}}, {{y}}, {{options}} } end foo 1, w: 'a', y: true, z: "z" - CR + CRYSTAL end it "declares multi-assign vars for macro" do - assert_type(<<-CR) { int32 } + assert_type(<<-CRYSTAL) { int32 } macro id(x, y) {{x}} {{y}} @@ -1023,11 +1023,11 @@ describe "Semantic: macro" do a, b = 1, 2 id(a, b) 1 - CR + CRYSTAL end it "declares rescue variable inside for macro" do - assert_type(<<-CR) { int32 } + assert_type(<<-CRYSTAL) { int32 } macro id(x) {{x}} end @@ -1038,49 +1038,49 @@ describe "Semantic: macro" do end 1 - CR + CRYSTAL end it "matches with default value after splat" do - assert_type(<<-CR) { tuple_of([int32, tuple_of([char]), bool]) } + assert_type(<<-CRYSTAL) { tuple_of([int32, tuple_of([char]), bool]) } macro foo(x, *y, z = true) { {{x}}, {{y}}, {{z}} } end foo 1, 'a' - CR + CRYSTAL end it "uses bare *" do - assert_type(<<-CR) { tuple_of([int32, char]) } + assert_type(<<-CRYSTAL) { tuple_of([int32, char]) } macro foo(x, *, y) { {{x}}, {{y}} } end foo 10, y: 'a' - CR + CRYSTAL end it "uses bare *, doesn't let more args" do - assert_error(<<-CR, "wrong number of arguments for macro 'foo' (given 2, expected 1)") + assert_error(<<-CRYSTAL, "wrong number of arguments for macro 'foo' (given 2, expected 1)") macro foo(x, *, y) end foo 10, 20, y: 30 - CR + CRYSTAL end it "uses bare *, doesn't let more args" do - assert_error(<<-CR, "no overload matches") + assert_error(<<-CRYSTAL, "no overload matches") def foo(x, *, y) end foo 10, 20, y: 30 - CR + CRYSTAL end it "finds macro through alias (#2706)" do - assert_type(<<-CR) { int32 } + assert_type(<<-CRYSTAL) { int32 } module Moo macro bar 1 @@ -1090,11 +1090,11 @@ describe "Semantic: macro" do alias Foo = Moo Foo.bar - CR + CRYSTAL end it "can override macro (#2773)" do - assert_type(<<-CR) { char } + assert_type(<<-CRYSTAL) { char } macro foo 1 end @@ -1104,31 +1104,31 @@ describe "Semantic: macro" do end foo - CR + CRYSTAL end it "works inside proc literal (#2984)" do - assert_type(<<-CR, inject_primitives: true) { int32 } + assert_type(<<-CRYSTAL, inject_primitives: true) { int32 } macro foo 1 end ->{ foo }.call - CR + CRYSTAL end it "finds var in proc for macros" do - assert_type(<<-CR, inject_primitives: true) { int32 } + assert_type(<<-CRYSTAL, inject_primitives: true) { int32 } macro foo(x) {{x}} end ->(x : Int32) { foo(x) }.call(1) - CR + CRYSTAL end it "applies visibility modifier only to first level" do - assert_type(<<-CR) { int32 } + assert_type(<<-CRYSTAL) { int32 } macro foo class Foo def self.foo @@ -1140,11 +1140,11 @@ describe "Semantic: macro" do private foo Foo.foo - CR + CRYSTAL end it "gives correct error when method is invoked but macro exists at the same scope" do - assert_error(<<-CR, "undefined method 'foo'") + assert_error(<<-CRYSTAL, "undefined method 'foo'") macro foo(x) end @@ -1152,23 +1152,23 @@ describe "Semantic: macro" do end Foo.new.foo - CR + CRYSTAL end it "uses uninitialized variable with macros" do - assert_type(<<-CR) { int32 } + assert_type(<<-CRYSTAL) { int32 } macro foo(x) {{x}} end a = uninitialized Int32 foo(a) - CR + CRYSTAL end describe "skip_file macro directive" do it "skips expanding the rest of the current file" do - res = semantic(<<-CR) + res = semantic(<<-CRYSTAL) class A end @@ -1176,14 +1176,14 @@ describe "Semantic: macro" do class B end - CR + CRYSTAL res.program.types.has_key?("A").should be_true res.program.types.has_key?("B").should be_false end it "skips file inside an if macro expression" do - res = semantic(<<-CR) + res = semantic(<<-CRYSTAL) class A end @@ -1195,7 +1195,7 @@ describe "Semantic: macro" do class B end - CR + CRYSTAL res.program.types.has_key?("A").should be_true res.program.types.has_key?("B").should be_false @@ -1205,7 +1205,7 @@ describe "Semantic: macro" do end it "finds method before macro (#236)" do - assert_type(<<-CR) { char } + assert_type(<<-CRYSTAL) { char } macro global 1 end @@ -1221,11 +1221,11 @@ describe "Semantic: macro" do end Foo.new.bar - CR + CRYSTAL end it "finds macro and method at the same scope" do - assert_type(<<-CR) { tuple_of [int32, char] } + assert_type(<<-CRYSTAL) { tuple_of [int32, char] } macro global(x) 1 end @@ -1235,11 +1235,11 @@ describe "Semantic: macro" do end {global(1), global(1, 2)} - CR + CRYSTAL end it "finds macro and method at the same scope inside included module" do - assert_type(<<-CR) { tuple_of [int32, char] } + assert_type(<<-CRYSTAL) { tuple_of [int32, char] } module Moo macro global(x) 1 @@ -1259,11 +1259,11 @@ describe "Semantic: macro" do end Foo.new.main - CR + CRYSTAL end it "finds macro in included module at class level (#4639)" do - assert_type(<<-CR) { int32 } + assert_type(<<-CRYSTAL) { int32 } module Moo macro foo def self.bar @@ -1279,11 +1279,11 @@ describe "Semantic: macro" do end Foo.bar - CR + CRYSTAL end it "finds macro in module in Object" do - assert_type(<<-CR) { int32 } + assert_type(<<-CRYSTAL) { int32 } class Object macro foo def self.bar @@ -1297,11 +1297,11 @@ describe "Semantic: macro" do end Moo.bar - CR + CRYSTAL end it "finds metaclass instance of instance method (#4739)" do - assert_type(<<-CR) { int32 } + assert_type(<<-CRYSTAL) { int32 } class Parent macro foo def self.bar @@ -1320,11 +1320,11 @@ describe "Semantic: macro" do end GrandChild.bar - CR + CRYSTAL end it "finds metaclass instance of instance method (#4639)" do - assert_type(<<-CR) { int32 } + assert_type(<<-CRYSTAL) { int32 } module Include macro foo def foo @@ -1344,11 +1344,11 @@ describe "Semantic: macro" do end Foo.new.foo - CR + CRYSTAL end it "can lookup type parameter when macro is called inside class (#5343)" do - assert_type(<<-CR) { int32.metaclass } + assert_type(<<-CRYSTAL) { int32.metaclass } class Foo(T) macro foo {{T}} @@ -1364,11 +1364,11 @@ describe "Semantic: macro" do end Bar.foo - CR + CRYSTAL end it "cannot lookup type defined in caller class" do - assert_error(<<-CR, "undefined constant Baz") + assert_error(<<-CRYSTAL, "undefined constant Baz") class Foo macro foo {{Baz}} @@ -1385,11 +1385,11 @@ describe "Semantic: macro" do end Bar.foo - CR + CRYSTAL end it "clones default value before expanding" do - assert_type(<<-CR) { nil_type } + assert_type(<<-CRYSTAL) { nil_type } FOO = {} of String => String? macro foo(x = {} of String => String) @@ -1400,11 +1400,11 @@ describe "Semantic: macro" do foo foo {{ FOO["foo"] }} - CR + CRYSTAL end it "does macro verbatim inside macro" do - assert_type(<<-CR) { types["Bar"].metaclass } + assert_type(<<-CRYSTAL) { types["Bar"].metaclass } class Foo macro inherited {% verbatim do %} @@ -1419,19 +1419,19 @@ describe "Semantic: macro" do end Bar.new.foo - CR + CRYSTAL end it "does macro verbatim outside macro" do - assert_type(<<-CR) { int32 } + assert_type(<<-CRYSTAL) { int32 } {% verbatim do %} 1 {% end %} - CR + CRYSTAL end it "evaluates yield expression (#2924)" do - assert_type(<<-CR) { string } + assert_type(<<-CRYSTAL) { string } macro a(b) {{yield b}} end @@ -1439,19 +1439,19 @@ describe "Semantic: macro" do a("foo") do |c| {{c}} end - CR + CRYSTAL end it "finds generic in macro code" do - assert_type(<<-CR) { array_of(string).metaclass } + assert_type(<<-CRYSTAL) { array_of(string).metaclass } {% begin %} {{ Array(String) }} {% end %} - CR + CRYSTAL end it "finds generic in macro code using free var" do - assert_type(<<-CR) { array_of(int32).metaclass } + assert_type(<<-CRYSTAL) { array_of(int32).metaclass } class Foo(T) def self.foo {% begin %} @@ -1461,11 +1461,11 @@ describe "Semantic: macro" do end Foo(Int32).foo - CR + CRYSTAL end it "expands multiline macro expression in verbatim (#6643)" do - assert_type(<<-CR) { int32 } + assert_type(<<-CRYSTAL) { int32 } {% verbatim do %} {{ if true @@ -1475,11 +1475,11 @@ describe "Semantic: macro" do end }} {% end %} - CR + CRYSTAL end it "can use macro in instance var initializer (#7666)" do - assert_type(<<-CR) { string } + assert_type(<<-CRYSTAL) { string } class Foo macro m "test" @@ -1493,11 +1493,11 @@ describe "Semantic: macro" do end Foo.new.x - CR + CRYSTAL end it "can use macro in instance var initializer (just assignment) (#7666)" do - assert_type(<<-CR) { string } + assert_type(<<-CRYSTAL) { string } class Foo macro m "test" @@ -1511,11 +1511,11 @@ describe "Semantic: macro" do end Foo.new.x - CR + CRYSTAL end it "shows correct error message in macro expansion (#7083)" do - assert_error(<<-CR, "can't instantiate abstract class Foo") + assert_error(<<-CRYSTAL, "can't instantiate abstract class Foo") abstract class Foo {% begin %} def self.new @@ -1525,19 +1525,19 @@ describe "Semantic: macro" do end Foo.new - CR + CRYSTAL end it "doesn't crash on syntax error inside macro (regression, #8038)" do expect_raises(Crystal::SyntaxException, "unterminated array literal") do - semantic(<<-CR) + semantic(<<-CRYSTAL) {% begin %}[{% end %} - CR + CRYSTAL end end it "has correct location after expanding assignment after instance var" do - result = semantic <<-CR + result = semantic <<-CRYSTAL macro foo(x) # 1 @{{x}} # 2 # 3 @@ -1548,14 +1548,14 @@ describe "Semantic: macro" do class Foo # 8 foo(x = 1) # 9 end - CR + CRYSTAL method = result.program.types["Foo"].lookup_first_def("bar", false).not_nil! method.location.not_nil!.expanded_location.not_nil!.line_number.should eq(9) end it "executes OpAssign (#9356)" do - assert_type(<<-CR) { int32 } + assert_type(<<-CRYSTAL) { int32 } {% begin %} {% a = nil %} {% a ||= 1 %} @@ -1565,25 +1565,25 @@ describe "Semantic: macro" do 'a' {% end %} {% end %} - CR + CRYSTAL end it "executes MultiAssign" do - assert_type(<<-CR) { tuple_of([int32, int32] of Type) } + assert_type(<<-CRYSTAL) { tuple_of([int32, int32] of Type) } {% begin %} {% a, b = 1, 2 %} { {{a}}, {{b}} } {% end %} - CR + CRYSTAL end it "executes MultiAssign with ArrayLiteral value" do - assert_type(<<-CR) { tuple_of([int32, int32] of Type) } + assert_type(<<-CRYSTAL) { tuple_of([int32, int32] of Type) } {% begin %} {% xs = [1, 2] %} {% a, b = xs %} { {{a}}, {{b}} } {% end %} - CR + CRYSTAL end end diff --git a/spec/compiler/semantic/metaclass_spec.cr b/spec/compiler/semantic/metaclass_spec.cr index aa2457a3e255..d38b454a80f3 100644 --- a/spec/compiler/semantic/metaclass_spec.cr +++ b/spec/compiler/semantic/metaclass_spec.cr @@ -260,7 +260,7 @@ describe "Semantic: metaclass" do end it "can't reopen as struct" do - assert_error <<-CR, "Bar is not a struct, it's a metaclass" + assert_error <<-CRYSTAL, "Bar is not a struct, it's a metaclass" class Foo end @@ -268,11 +268,11 @@ describe "Semantic: metaclass" do struct Bar end - CR + CRYSTAL end it "can't reopen as module" do - assert_error <<-CR, "Bar is not a module, it's a metaclass" + assert_error <<-CRYSTAL, "Bar is not a module, it's a metaclass" class Foo end @@ -280,6 +280,6 @@ describe "Semantic: metaclass" do module Bar end - CR + CRYSTAL end end diff --git a/spec/compiler/semantic/multi_assign_spec.cr b/spec/compiler/semantic/multi_assign_spec.cr index 58549c8900c2..9a4fb430af9c 100644 --- a/spec/compiler/semantic/multi_assign_spec.cr +++ b/spec/compiler/semantic/multi_assign_spec.cr @@ -13,7 +13,7 @@ describe "Semantic: multi assign" do end it "doesn't error if assigning non-Indexable (#11414)" do - assert_no_errors <<-CR + assert_no_errors <<-CRYSTAL class Foo def [](index) end @@ -24,11 +24,11 @@ describe "Semantic: multi assign" do end a, b, c = Foo.new - CR + CRYSTAL end it "errors if assigning non-Indexable to splat (#11414)" do - assert_error <<-CR, "right-hand side of one-to-many assignment must be an Indexable, not Foo" + assert_error <<-CRYSTAL, "right-hand side of one-to-many assignment must be an Indexable, not Foo" require "prelude" class Foo @@ -41,7 +41,7 @@ describe "Semantic: multi assign" do end a, *b, c = Foo.new - CR + CRYSTAL end end @@ -94,7 +94,7 @@ describe "Semantic: multi assign" do end it "errors if assigning non-Indexable (#11414)" do - assert_error <<-CR, "right-hand side of one-to-many assignment must be an Indexable, not Foo", flags: "strict_multi_assign" + assert_error <<-CRYSTAL, "right-hand side of one-to-many assignment must be an Indexable, not Foo", flags: "strict_multi_assign" require "prelude" class Foo @@ -107,11 +107,11 @@ describe "Semantic: multi assign" do end a, b, c = Foo.new - CR + CRYSTAL end it "errors if assigning non-Indexable to splat (#11414)" do - assert_error <<-CR, "right-hand side of one-to-many assignment must be an Indexable, not Foo", flags: "strict_multi_assign" + assert_error <<-CRYSTAL, "right-hand side of one-to-many assignment must be an Indexable, not Foo", flags: "strict_multi_assign" require "prelude" class Foo @@ -124,12 +124,12 @@ describe "Semantic: multi assign" do end a, *b, c = Foo.new - CR + CRYSTAL end end it "can pass splat variable at top-level to macros (#11596)" do - assert_type(<<-CR) { tuple_of [int32, int32, int32] } + assert_type(<<-CRYSTAL) { tuple_of [int32, int32, int32] } struct Tuple def self.new(*args) args @@ -142,6 +142,6 @@ describe "Semantic: multi assign" do a, *b, c = 1, 2, 3, 4, 5 foo(b) - CR + CRYSTAL end end diff --git a/spec/compiler/semantic/pointer_spec.cr b/spec/compiler/semantic/pointer_spec.cr index 978c20992c0e..6ca9b320235e 100644 --- a/spec/compiler/semantic/pointer_spec.cr +++ b/spec/compiler/semantic/pointer_spec.cr @@ -99,21 +99,21 @@ describe "Semantic: pointer" do end it "detects recursive pointerof expansion (3)" do - assert_error <<-CR, "recursive pointerof expansion" + assert_error <<-CRYSTAL, "recursive pointerof expansion" x = {1} x = pointerof(x) - CR + CRYSTAL end it "detects recursive pointerof expansion (4)" do - assert_error <<-CR, "recursive pointerof expansion" + assert_error <<-CRYSTAL, "recursive pointerof expansion" x = 1 x = {pointerof(x)} - CR + CRYSTAL end it "doesn't crash if pointerof expansion type has generic splat parameter (#11808)" do - assert_type(<<-CR) { pointer_of(union_of int32, generic_class("Foo", string)) } + assert_type(<<-CRYSTAL) { pointer_of(union_of int32, generic_class("Foo", string)) } class Foo(*T) end @@ -121,7 +121,7 @@ describe "Semantic: pointer" do pointer = pointerof(x) x = Foo(String).new pointer - CR + CRYSTAL end it "can assign nil to void pointer" do @@ -182,7 +182,7 @@ describe "Semantic: pointer" do end it "can assign pointerof virtual type (#8216)" do - assert_no_errors <<-CR + assert_no_errors <<-CRYSTAL class Base end @@ -193,7 +193,7 @@ describe "Semantic: pointer" do x : Pointer(Base) x = pointerof(u) - CR + CRYSTAL end it "errors with non-matching generic value with value= (#10211)" do diff --git a/spec/compiler/semantic/private_spec.cr b/spec/compiler/semantic/private_spec.cr index e2c589e97f58..4f339370b02f 100644 --- a/spec/compiler/semantic/private_spec.cr +++ b/spec/compiler/semantic/private_spec.cr @@ -517,7 +517,7 @@ describe "Semantic: private" do end it "doesn't inherit visibility from class node in macro hook (#8794)" do - assert_no_errors <<-CR + assert_no_errors <<-CRYSTAL module M1 macro included include M2 @@ -559,6 +559,6 @@ describe "Semantic: private" do end Foo.new(1) - CR + CRYSTAL end end diff --git a/spec/compiler/semantic/proc_spec.cr b/spec/compiler/semantic/proc_spec.cr index b1673df3b60b..9e4179477ba6 100644 --- a/spec/compiler/semantic/proc_spec.cr +++ b/spec/compiler/semantic/proc_spec.cr @@ -834,25 +834,25 @@ describe "Semantic: proc" do end it "allows metaclass in procs" do - assert_type(<<-CR) { proc_of(types["Foo"].metaclass, types["Foo"]) } + assert_type(<<-CRYSTAL) { proc_of(types["Foo"].metaclass, types["Foo"]) } class Foo end ->(x : Foo.class) { x.new } - CR + CRYSTAL end it "allows metaclass in proc return types" do - assert_type(<<-CR) { proc_of(types["Foo"].metaclass) } + assert_type(<<-CRYSTAL) { proc_of(types["Foo"].metaclass) } class Foo end -> : Foo.class { Foo } - CR + CRYSTAL end it "allows metaclass in captured block" do - assert_type(<<-CR) { proc_of(types["Foo"].metaclass, types["Foo"]) } + assert_type(<<-CRYSTAL) { proc_of(types["Foo"].metaclass, types["Foo"]) } class Foo end @@ -861,11 +861,11 @@ describe "Semantic: proc" do end foo { |x| x.new } - CR + CRYSTAL end it "allows metaclass in proc pointer" do - assert_type(<<-CR) { proc_of(types["Foo"].metaclass, types["Foo"]) } + assert_type(<<-CRYSTAL) { proc_of(types["Foo"].metaclass, types["Foo"]) } class Foo end @@ -874,11 +874,11 @@ describe "Semantic: proc" do end ->foo(Foo.class) - CR + CRYSTAL end it "allows metaclass in proc notation parameter type" do - assert_type(<<-CR) { proc_of(types["Foo"].metaclass, nil_type) } + assert_type(<<-CRYSTAL) { proc_of(types["Foo"].metaclass, nil_type) } class Foo end @@ -886,16 +886,16 @@ describe "Semantic: proc" do x : Foo.class -> = Proc(Foo.class, Nil).new { } x - CR + CRYSTAL end it "allows metaclass in proc notation return type" do - assert_type(<<-CR) { proc_of(types["Foo"].metaclass) } + assert_type(<<-CRYSTAL) { proc_of(types["Foo"].metaclass) } class Foo end x : -> Foo.class = ->{ Foo } x - CR + CRYSTAL end it "..." do @@ -1318,11 +1318,11 @@ describe "Semantic: proc" do end private def proc_new - <<-CODE + <<-CRYSTAL struct Proc def self.new(&block : self) block end end - CODE + CRYSTAL end diff --git a/spec/compiler/semantic/recursive_struct_check_spec.cr b/spec/compiler/semantic/recursive_struct_check_spec.cr index 29535516b24d..fd8f70541e72 100644 --- a/spec/compiler/semantic/recursive_struct_check_spec.cr +++ b/spec/compiler/semantic/recursive_struct_check_spec.cr @@ -186,13 +186,13 @@ describe "Semantic: recursive struct check" do end it "errors on private recursive type" do - assert_error <<-CR, "recursive struct Test detected" + assert_error <<-CRYSTAL, "recursive struct Test detected" private struct Test def initialize(@test : Test?) end end Test.new(Test.new(nil)) - CR + CRYSTAL end end diff --git a/spec/compiler/semantic/restrictions_augmenter_spec.cr b/spec/compiler/semantic/restrictions_augmenter_spec.cr index 8fb358295e06..7d55c7cf1258 100644 --- a/spec/compiler/semantic/restrictions_augmenter_spec.cr +++ b/spec/compiler/semantic/restrictions_augmenter_spec.cr @@ -12,23 +12,23 @@ end private def it_augments_for_ivar(ivar_type : String, expected_type : String, file = __FILE__, line = __LINE__) it "augments #{ivar_type}", file, line do - before = <<-BEFORE + before = <<-CRYSTAL class Foo @x : #{ivar_type} def initialize(value) @x = value end end - BEFORE + CRYSTAL - after = <<-AFTER + after = <<-CRYSTAL class Foo @x : #{ivar_type} def initialize(value : #{expected_type}) @x = value end end - AFTER + CRYSTAL expect_augment before, after end @@ -57,7 +57,7 @@ describe "Semantic: restrictions augmenter" do it_augments_for_ivar "Enumerable(Int32).class", "::Enumerable(::Int32).class" it "augments relative public type" do - before = <<-BEFORE + before = <<-CRYSTAL class Foo class Bar class Baz @@ -70,9 +70,9 @@ describe "Semantic: restrictions augmenter" do @x = value end end - BEFORE + CRYSTAL - after = <<-AFTER + after = <<-CRYSTAL class Foo class Bar class Baz @@ -83,13 +83,13 @@ describe "Semantic: restrictions augmenter" do @x = value end end - AFTER + CRYSTAL expect_augment before, after end it "augments relative private type" do - before = <<-BEFORE + before = <<-CRYSTAL class Foo private class Bar class Baz @@ -102,9 +102,9 @@ describe "Semantic: restrictions augmenter" do @x = value end end - BEFORE + CRYSTAL - after = <<-AFTER + after = <<-CRYSTAL class Foo private class Bar class Baz @@ -115,13 +115,13 @@ describe "Semantic: restrictions augmenter" do @x = value end end - AFTER + CRYSTAL expect_augment before, after end it "augments relative private type in same namespace" do - before = <<-BEFORE + before = <<-CRYSTAL class Foo private class Bar end @@ -132,9 +132,9 @@ describe "Semantic: restrictions augmenter" do end end end - BEFORE + CRYSTAL - after = <<-AFTER + after = <<-CRYSTAL class Foo private class Bar end @@ -145,57 +145,57 @@ describe "Semantic: restrictions augmenter" do end end end - AFTER + CRYSTAL expect_augment before, after end it "augments generic uninstantiated type" do - before = <<-BEFORE + before = <<-CRYSTAL class Foo(T) @x : Array(T) def initialize(value) @x = value end end - BEFORE + CRYSTAL - after = <<-AFTER + after = <<-CRYSTAL class Foo(T) @x : Array(T) def initialize(value : ::Array(T)) @x = value end end - AFTER + CRYSTAL expect_augment before, after end it "augments for class var" do - before = <<-BEFORE + before = <<-CRYSTAL class Foo @@x = 1 def self.set(value) @@x = value end end - BEFORE + CRYSTAL - after = <<-AFTER + after = <<-CRYSTAL class Foo @@x = 1 def self.set(value : ::Int32) @@x = value end end - AFTER + CRYSTAL expect_augment before, after end it "doesn't augment if assigned inside if" do - expect_no_augment <<-CODE + expect_no_augment <<-CRYSTAL class Foo @x : Int32 def initialize(value) @@ -204,11 +204,11 @@ describe "Semantic: restrictions augmenter" do end end end - CODE + CRYSTAL end it "doesn't augment if assigned inside while" do - expect_no_augment <<-CODE + expect_no_augment <<-CRYSTAL class Foo @x : Int32 def initialize(value) @@ -217,11 +217,11 @@ describe "Semantic: restrictions augmenter" do end end end - CODE + CRYSTAL end it "doesn't augment if assigned inside block" do - expect_no_augment <<-CODE + expect_no_augment <<-CRYSTAL def foo yield end @@ -233,44 +233,44 @@ describe "Semantic: restrictions augmenter" do end end end - CODE + CRYSTAL end it "doesn't augment if the no_restrictions_augmenter flag is present" do - expect_no_augment <<-CODE, flags: "no_restrictions_augmenter" + expect_no_augment <<-CRYSTAL, flags: "no_restrictions_augmenter" class Foo @x : Int32 def initialize(value) @x = value end end - CODE + CRYSTAL end it "augments recursive alias type (#12134)" do - before = <<-BEFORE + before = <<-CRYSTAL alias BasicObject = Array(BasicObject) | Hash(String, BasicObject) class Foo def initialize(value = Hash(String, BasicObject).new) @x = value end end - BEFORE + CRYSTAL - after = <<-AFTER + after = <<-CRYSTAL alias BasicObject = Array(BasicObject) | Hash(String, BasicObject) class Foo def initialize(value : ::Hash(::String, ::BasicObject) = Hash(String, BasicObject).new) @x = value end end - AFTER + CRYSTAL expect_augment before, after end it "augments typedef" do - before = <<-BEFORE + before = <<-CRYSTAL lib LibFoo type X = Void* end @@ -280,9 +280,9 @@ describe "Semantic: restrictions augmenter" do @x = value end end - BEFORE + CRYSTAL - after = <<-AFTER + after = <<-CRYSTAL lib LibFoo type X = Void* end @@ -292,13 +292,13 @@ describe "Semantic: restrictions augmenter" do @x = value end end - AFTER + CRYSTAL expect_augment before, after end it "augments virtual type" do - before = <<-BEFORE + before = <<-CRYSTAL class A end class B < A @@ -309,9 +309,9 @@ describe "Semantic: restrictions augmenter" do @x = value end end - BEFORE + CRYSTAL - after = <<-AFTER + after = <<-CRYSTAL class A end class B < A @@ -322,13 +322,13 @@ describe "Semantic: restrictions augmenter" do @x = value end end - AFTER + CRYSTAL expect_augment before, after end it "augments virtual metaclass type" do - before = <<-BEFORE + before = <<-CRYSTAL class A end class B < A @@ -339,9 +339,9 @@ describe "Semantic: restrictions augmenter" do @x = value end end - BEFORE + CRYSTAL - after = <<-AFTER + after = <<-CRYSTAL class A end class B < A @@ -352,35 +352,35 @@ describe "Semantic: restrictions augmenter" do @x = value end end - AFTER + CRYSTAL expect_augment before, after end it "augments type splat" do - before = <<-BEFORE + before = <<-CRYSTAL class Foo(T) @x : Array(*T) def initialize(value) @x = value end end - BEFORE + CRYSTAL - after = <<-AFTER + after = <<-CRYSTAL class Foo(T) @x : Array(*T) def initialize(value : ::Array(*T)) @x = value end end - AFTER + CRYSTAL expect_augment before, after end it "doesn't crash on macro that yields and defines class (#12142)" do - before = <<-BEFORE + before = <<-CRYSTAL macro foo {{yield}} end @@ -394,9 +394,9 @@ describe "Semantic: restrictions augmenter" do @x = value end end - BEFORE + CRYSTAL - after = <<-AFTER + after = <<-CRYSTAL macro foo {{ yield }} end @@ -408,29 +408,29 @@ describe "Semantic: restrictions augmenter" do @x = value end end - AFTER + CRYSTAL expect_augment before, after end it "augments for Union(*T) (#12435)" do - before = <<-BEFORE + before = <<-CRYSTAL class Foo(*T) @x : Union(*T) def initialize(value) @x = value end end - BEFORE + CRYSTAL - after = <<-AFTER + after = <<-CRYSTAL class Foo(*T) @x : Union(*T) def initialize(value : ::Union(*T)) @x = value end end - AFTER + CRYSTAL expect_augment before, after end diff --git a/spec/compiler/semantic/restrictions_spec.cr b/spec/compiler/semantic/restrictions_spec.cr index 4336b142b8ca..baf69551da21 100644 --- a/spec/compiler/semantic/restrictions_spec.cr +++ b/spec/compiler/semantic/restrictions_spec.cr @@ -249,23 +249,23 @@ describe "Restrictions" do {% end %} it "doesn't error if path is undefined and method is not called (1) (#12516)" do - assert_no_errors <<-CR + assert_no_errors <<-CRYSTAL def foo(a : Int32.class) end def foo(a : Foo) end - CR + CRYSTAL end it "doesn't error if path is undefined and method is not called (2) (#12516)" do - assert_no_errors <<-CR + assert_no_errors <<-CRYSTAL def foo(a : Foo) end def foo(a : Int32.class) end - CR + CRYSTAL end end @@ -550,7 +550,7 @@ describe "Restrictions" do describe "Path vs NumberLiteral" do it "inserts constant before number literal of same value with generic arguments" do - assert_type(<<-CR) { bool } + assert_type(<<-CRYSTAL) { bool } X = 1 class Foo(N) @@ -565,11 +565,11 @@ describe "Restrictions" do end foo(Foo(1).new) - CR + CRYSTAL end it "inserts number literal before constant of same value with generic arguments" do - assert_type(<<-CR) { bool } + assert_type(<<-CRYSTAL) { bool } X = 1 class Foo(N) @@ -584,13 +584,13 @@ describe "Restrictions" do end foo(Foo(1).new) - CR + CRYSTAL end end describe "free variables" do it "inserts path before free variable with same name" do - assert_type(<<-CR) { tuple_of([char, bool]) } + assert_type(<<-CRYSTAL) { tuple_of([char, bool]) } def foo(x : Int32) forall Int32 true end @@ -600,11 +600,11 @@ describe "Restrictions" do end {foo(1), foo("")} - CR + CRYSTAL end it "keeps path before free variable with same name" do - assert_type(<<-CR) { tuple_of([char, bool]) } + assert_type(<<-CRYSTAL) { tuple_of([char, bool]) } def foo(x : Int32) 'a' end @@ -614,12 +614,12 @@ describe "Restrictions" do end {foo(1), foo("")} - CR + CRYSTAL end # TODO: enable in #12784 pending "inserts constant before free variable with same name" do - assert_type(<<-CR) { tuple_of([char, bool]) } + assert_type(<<-CRYSTAL) { tuple_of([char, bool]) } class Foo(T); end X = 1 @@ -633,11 +633,11 @@ describe "Restrictions" do end {foo(Foo(1).new), foo(Foo(2).new)} - CR + CRYSTAL end pending "keeps constant before free variable with same name" do - assert_type(<<-CR) { tuple_of([char, bool]) } + assert_type(<<-CRYSTAL) { tuple_of([char, bool]) } class Foo(T); end X = 1 @@ -651,11 +651,11 @@ describe "Restrictions" do end {foo(Foo(1).new), foo(Foo(2).new)} - CR + CRYSTAL end it "inserts path before free variable even if free var resolves to a more specialized type" do - assert_type(<<-CR) { tuple_of([int32, int32, bool]) } + assert_type(<<-CRYSTAL) { tuple_of([int32, int32, bool]) } class Foo end @@ -671,11 +671,11 @@ describe "Restrictions" do end {foo(Foo.new), foo(Bar.new), foo('a')} - CR + CRYSTAL end it "keeps path before free variable even if free var resolves to a more specialized type" do - assert_type(<<-CR) { tuple_of([int32, int32, bool]) } + assert_type(<<-CRYSTAL) { tuple_of([int32, int32, bool]) } class Foo end @@ -691,13 +691,13 @@ describe "Restrictions" do end {foo(Foo.new), foo(Bar.new), foo('a')} - CR + CRYSTAL end end describe "Union" do it "handles redefinitions (1) (#12330)" do - assert_type(<<-CR) { bool } + assert_type(<<-CRYSTAL) { bool } def foo(x : Int32 | String) 'a' end @@ -707,11 +707,11 @@ describe "Restrictions" do end foo(1) - CR + CRYSTAL end it "handles redefinitions (2) (#12330)" do - assert_type(<<-CR) { bool } + assert_type(<<-CRYSTAL) { bool } def foo(x : Int32 | String) 'a' end @@ -721,11 +721,11 @@ describe "Restrictions" do end foo(1) - CR + CRYSTAL end it "orders union before generic (#12330)" do - assert_type(<<-CR) { bool } + assert_type(<<-CRYSTAL) { bool } module Foo(T) end @@ -746,7 +746,7 @@ describe "Restrictions" do end foo(Bar1.new) - CR + CRYSTAL end end end @@ -1158,16 +1158,16 @@ describe "Restrictions" do end it "errors if using Tuple with named args" do - assert_error <<-CR, "can only instantiate NamedTuple with named arguments" + assert_error <<-CRYSTAL, "can only instantiate NamedTuple with named arguments" def foo(x : Tuple(a: Int32)) end foo({1}) - CR + CRYSTAL end it "doesn't error if using Tuple with no args" do - assert_type(<<-CR) { tuple_of([] of Type) } + assert_type(<<-CRYSTAL) { tuple_of([] of Type) } def foo(x : Tuple()) x end @@ -1177,20 +1177,20 @@ describe "Restrictions" do end foo(bar) - CR + CRYSTAL end it "errors if using NamedTuple with positional args" do - assert_error <<-CR, "can only instantiate NamedTuple with named arguments" + assert_error <<-CRYSTAL, "can only instantiate NamedTuple with named arguments" def foo(x : NamedTuple(Int32)) end foo({a: 1}) - CR + CRYSTAL end it "doesn't error if using NamedTuple with no args" do - assert_type(<<-CR) { named_tuple_of({} of String => Type) } + assert_type(<<-CRYSTAL) { named_tuple_of({} of String => Type) } def foo(x : NamedTuple()) x end @@ -1200,6 +1200,6 @@ describe "Restrictions" do end foo(bar) - CR + CRYSTAL end end diff --git a/spec/compiler/semantic/return_spec.cr b/spec/compiler/semantic/return_spec.cr index e23f3c4c503a..ddf1b4b9281e 100644 --- a/spec/compiler/semantic/return_spec.cr +++ b/spec/compiler/semantic/return_spec.cr @@ -183,7 +183,7 @@ describe "Semantic: return" do end it "can use non-type free var in return type (#6543)" do - assert_type(<<-CR) { generic_class "Foo", 1.int32 } + assert_type(<<-CRYSTAL) { generic_class "Foo", 1.int32 } class Foo(A) end @@ -192,11 +192,11 @@ describe "Semantic: return" do end foo(Foo(1).new) - CR + CRYSTAL end it "can use non-type free var in return type (2) (#6543)" do - assert_type(<<-CR) { generic_class "Matrix", 3.int32, 4.int32 } + assert_type(<<-CRYSTAL) { generic_class "Matrix", 3.int32, 4.int32 } class Matrix(N, M) def *(other : Matrix(M, P)) : Matrix(N, P) forall P Matrix(N, P).new @@ -204,11 +204,11 @@ describe "Semantic: return" do end Matrix(3, 2).new * Matrix(2, 4).new - CR + CRYSTAL end it "errors if non-type free var cannot be inferred" do - assert_error <<-CR, "undefined constant P" + assert_error <<-CRYSTAL, "undefined constant P" class Foo(A) end @@ -217,7 +217,7 @@ describe "Semantic: return" do end foo(Foo(1).new) - CR + CRYSTAL end it "forms a tuple from multiple return values" do diff --git a/spec/compiler/semantic/sizeof_spec.cr b/spec/compiler/semantic/sizeof_spec.cr index 3828099bade0..1cdab6a6ed3b 100644 --- a/spec/compiler/semantic/sizeof_spec.cr +++ b/spec/compiler/semantic/sizeof_spec.cr @@ -75,12 +75,12 @@ describe "Semantic: sizeof" do end it "gives error if using instance_sizeof on a metaclass" do - assert_error <<-CR, "instance_sizeof can only be used with a class, but Foo.class is a metaclass" + assert_error <<-CRYSTAL, "instance_sizeof can only be used with a class, but Foo.class is a metaclass" class Foo end instance_sizeof(Foo.class) - CR + CRYSTAL end it "gives error if using instance_sizeof on a generic type without type vars" do diff --git a/spec/compiler/semantic/splat_spec.cr b/spec/compiler/semantic/splat_spec.cr index ec7a829558c4..f4582d622be9 100644 --- a/spec/compiler/semantic/splat_spec.cr +++ b/spec/compiler/semantic/splat_spec.cr @@ -812,14 +812,14 @@ describe "Semantic: splat" do end it "doesn't shift a call's location" do - result = semantic <<-CR + result = semantic <<-CRYSTAL class Foo def bar(x) bar(*{"test"}) end end Foo.new.bar("test") - CR + CRYSTAL program = result.program a_typ = program.types["Foo"].as(NonGenericClassType) a_def = a_typ.def_instances.values[0] diff --git a/spec/compiler/semantic/virtual_metaclass_spec.cr b/spec/compiler/semantic/virtual_metaclass_spec.cr index db65875ef487..b2e003a094c9 100644 --- a/spec/compiler/semantic/virtual_metaclass_spec.cr +++ b/spec/compiler/semantic/virtual_metaclass_spec.cr @@ -152,7 +152,7 @@ describe "Semantic: virtual metaclass" do end it "restricts virtual metaclass to Class (#11376)" do - assert_type(<<-CR) { nilable types["Foo"].virtual_type.metaclass } + assert_type(<<-CRYSTAL) { nilable types["Foo"].virtual_type.metaclass } class Foo end @@ -161,6 +161,6 @@ describe "Semantic: virtual metaclass" do x = Foo || Bar x if x.is_a?(Class) - CR + CRYSTAL end end diff --git a/spec/compiler/semantic/virtual_spec.cr b/spec/compiler/semantic/virtual_spec.cr index 76a8d6834f87..15a87f7ab62a 100644 --- a/spec/compiler/semantic/virtual_spec.cr +++ b/spec/compiler/semantic/virtual_spec.cr @@ -135,7 +135,7 @@ describe "Semantic: virtual" do end it "works with restriction alpha" do - assert_no_errors <<-CR + assert_no_errors <<-CRYSTAL require "prelude" class Foo @@ -154,7 +154,7 @@ describe "Semantic: virtual" do a = [nil, Foo.new, Bar.new, Baz.new] a.push(Baz.new || Ban.new) - CR + CRYSTAL end it "doesn't check cover for subclasses" do diff --git a/spec/compiler/semantic/visibility_modifiers_spec.cr b/spec/compiler/semantic/visibility_modifiers_spec.cr index b3a509db9be2..e6ce6ae9317f 100644 --- a/spec/compiler/semantic/visibility_modifiers_spec.cr +++ b/spec/compiler/semantic/visibility_modifiers_spec.cr @@ -414,7 +414,7 @@ describe "Visibility modifiers" do end it "handles virtual types (#8561)" do - assert_no_errors <<-CR + assert_no_errors <<-CRYSTAL module Namespace class Foo protected def foo @@ -439,6 +439,6 @@ describe "Visibility modifiers" do end Namespace::Baz.new.bar - CR + CRYSTAL end end diff --git a/spec/compiler/semantic/warnings_spec.cr b/spec/compiler/semantic/warnings_spec.cr index 26b509d14aad..c9b606b36a35 100644 --- a/spec/compiler/semantic/warnings_spec.cr +++ b/spec/compiler/semantic/warnings_spec.cr @@ -3,7 +3,7 @@ require "../spec_helper" describe "Semantic: warnings" do describe "deprecated annotations" do it "detects deprecated annotations" do - assert_warning <<-CR, + assert_warning <<-CRYSTAL, @[Deprecated] annotation Foo; end @@ -11,12 +11,12 @@ describe "Semantic: warnings" do def bar; end bar - CR + CRYSTAL "warning in line 2\nWarning: Deprecated annotation Foo." end it "detects deprecated namespaced annotations" do - assert_warning <<-CR, + assert_warning <<-CRYSTAL, module MyNamespace @[Deprecated] annotation Foo; end @@ -26,36 +26,36 @@ describe "Semantic: warnings" do def bar; end bar - CR + CRYSTAL "warning in line 3\nWarning: Deprecated annotation MyNamespace::Foo." end end describe "deprecated methods" do it "detects top-level deprecated methods" do - assert_warning <<-CR, + assert_warning <<-CRYSTAL, @[Deprecated("Do not use me")] def foo end foo - CR + CRYSTAL "warning in line 5\nWarning: Deprecated top-level foo. Do not use me" end it "deprecation reason is optional" do - assert_warning <<-CR, + assert_warning <<-CRYSTAL, @[Deprecated] def foo end foo - CR + CRYSTAL "warning in line 5\nWarning: Deprecated top-level foo." end it "detects deprecated instance methods" do - assert_warning <<-CR, + assert_warning <<-CRYSTAL, class Foo @[Deprecated("Do not use me")] def m @@ -63,12 +63,12 @@ describe "Semantic: warnings" do end Foo.new.m - CR + CRYSTAL "warning in line 7\nWarning: Deprecated Foo#m. Do not use me" end it "detects deprecated class methods" do - assert_warning <<-CR, + assert_warning <<-CRYSTAL, class Foo @[Deprecated("Do not use me")] def self.m @@ -76,12 +76,12 @@ describe "Semantic: warnings" do end Foo.m - CR + CRYSTAL "warning in line 7\nWarning: Deprecated Foo.m. Do not use me" end it "detects deprecated generic instance methods" do - assert_warning <<-CR, + assert_warning <<-CRYSTAL, class Foo(T) @[Deprecated("Do not use me")] def m @@ -89,12 +89,12 @@ describe "Semantic: warnings" do end Foo(Int32).new.m - CR + CRYSTAL "warning in line 7\nWarning: Deprecated Foo(Int32)#m. Do not use me" end it "detects deprecated generic class methods" do - assert_warning <<-CR, + assert_warning <<-CRYSTAL, class Foo(T) @[Deprecated("Do not use me")] def self.m @@ -102,12 +102,12 @@ describe "Semantic: warnings" do end Foo(Int32).m - CR + CRYSTAL "warning in line 7\nWarning: Deprecated Foo(Int32).m. Do not use me" end it "detects deprecated module methods" do - assert_warning <<-CR, + assert_warning <<-CRYSTAL, module Foo @[Deprecated("Do not use me")] def self.m @@ -115,23 +115,23 @@ describe "Semantic: warnings" do end Foo.m - CR + CRYSTAL "warning in line 7\nWarning: Deprecated Foo.m. Do not use me" end it "detects deprecated methods with named arguments" do - assert_warning <<-CR, + assert_warning <<-CRYSTAL, @[Deprecated] def foo(*, a) end foo(a: 2) - CR + CRYSTAL "warning in line 5\nWarning: Deprecated top-level foo:a." end it "detects deprecated initialize" do - assert_warning <<-CR, + assert_warning <<-CRYSTAL, class Foo @[Deprecated] def initialize @@ -139,12 +139,12 @@ describe "Semantic: warnings" do end Foo.new - CR + CRYSTAL "warning in line 7\nWarning: Deprecated Foo.new." end it "detects deprecated initialize with named arguments" do - assert_warning <<-CR, + assert_warning <<-CRYSTAL, class Foo @[Deprecated] def initialize(*, a) @@ -152,12 +152,12 @@ describe "Semantic: warnings" do end Foo.new(a: 2) - CR + CRYSTAL "warning in line 7\nWarning: Deprecated Foo.new:a." end it "informs warnings once per call site location (a)" do - warning_failures = warnings_result <<-CR + warning_failures = warnings_result <<-CRYSTAL class Foo @[Deprecated("Do not use me")] def m @@ -170,12 +170,12 @@ describe "Semantic: warnings" do Foo.new.b Foo.new.b - CR + CRYSTAL warning_failures.size.should eq(1) end it "informs warnings once per call site location (b)" do - warning_failures = warnings_result <<-CR + warning_failures = warnings_result <<-CRYSTAL class Foo @[Deprecated("Do not use me")] def m @@ -184,13 +184,13 @@ describe "Semantic: warnings" do Foo.new.m Foo.new.m - CR + CRYSTAL warning_failures.size.should eq(2) end it "informs warnings once per yield" do - warning_failures = warnings_result <<-CR + warning_failures = warnings_result <<-CRYSTAL class Foo @[Deprecated("Do not use me")] def m @@ -203,13 +203,13 @@ describe "Semantic: warnings" do end twice { Foo.new.m } - CR + CRYSTAL warning_failures.size.should eq(1) end it "informs warnings once per target type" do - warning_failures = warnings_result <<-CR + warning_failures = warnings_result <<-CRYSTAL class Foo(T) @[Deprecated("Do not use me")] def m @@ -222,7 +222,7 @@ describe "Semantic: warnings" do Foo(Int32).new.b Foo(Int64).new.b - CR + CRYSTAL warning_failures.size.should eq(2) end @@ -240,13 +240,13 @@ describe "Semantic: warnings" do output_filename = File.join(path, "main") Dir.cd(path) do - File.write main_filename, <<-CR + File.write main_filename, <<-CRYSTAL require "./lib/foo" bar foo - CR - File.write File.join(path, "lib", "foo.cr"), <<-CR + CRYSTAL + File.write File.join(path, "lib", "foo.cr"), <<-CRYSTAL @[Deprecated("Do not use me")] def foo end @@ -254,7 +254,7 @@ describe "Semantic: warnings" do def bar foo end - CR + CRYSTAL compiler = create_spec_compiler compiler.warnings.level = :all @@ -268,29 +268,29 @@ describe "Semantic: warnings" do end it "errors if invalid argument type" do - assert_error <<-CR, + assert_error <<-CRYSTAL, @[Deprecated(42)] def foo end - CR + CRYSTAL "first argument must be a String" end it "errors if too many arguments" do - assert_error <<-CR, + assert_error <<-CRYSTAL, @[Deprecated("Do not use me", "extra arg")] def foo end - CR + CRYSTAL "wrong number of deprecated annotation arguments (given 2, expected 1)" end it "errors if invalid named arguments" do - assert_error <<-CR, + assert_error <<-CRYSTAL, @[Deprecated(invalid: "Do not use me")] def foo end - CR + CRYSTAL "too many named arguments (given 1, expected maximum 0)" end end @@ -466,27 +466,27 @@ describe "Semantic: warnings" do describe "deprecated constants" do it "detects deprecated constants" do - assert_warning <<-CR, + assert_warning <<-CRYSTAL, @[Deprecated("Do not use me")] FOO = 1 FOO - CR + CRYSTAL "warning in line 4\nWarning: Deprecated FOO. Do not use me" end it "detects deprecated constants inside macros" do - assert_warning <<-CR, + assert_warning <<-CRYSTAL, @[Deprecated("Do not use me")] FOO = 1 {% FOO %} - CR + CRYSTAL "warning in line 4\nWarning: Deprecated FOO. Do not use me" end it "detects deprecated constants in type declarations (1)" do - assert_warning <<-CR, + assert_warning <<-CRYSTAL, @[Deprecated("Do not use me")] FOO = 1 @@ -495,12 +495,12 @@ describe "Semantic: warnings" do class Bar < Foo(FOO) end - CR + CRYSTAL "warning in line 7\nWarning: Deprecated FOO. Do not use me" end it "detects deprecated constants in type declarations (2)" do - assert_warning <<-CR, + assert_warning <<-CRYSTAL, @[Deprecated("Do not use me")] FOO = 1 @@ -510,12 +510,12 @@ describe "Semantic: warnings" do class Bar include Foo(FOO) end - CR + CRYSTAL "warning in line 8\nWarning: Deprecated FOO. Do not use me" end it "detects deprecated constants in type declarations (3)" do - assert_warning <<-CR, + assert_warning <<-CRYSTAL, @[Deprecated("Do not use me")] FOO = 1 @@ -523,14 +523,14 @@ describe "Semantic: warnings" do end alias Bar = Foo(FOO) - CR + CRYSTAL "warning in line 7\nWarning: Deprecated FOO. Do not use me" end end describe "abstract def positional parameter name mismatch" do it "detects mismatch with single parameter" do - assert_warning <<-CR, "warning in line 6\nWarning: positional parameter 'y' corresponds to parameter 'x' of the overridden method" + assert_warning <<-CRYSTAL, "warning in line 6\nWarning: positional parameter 'y' corresponds to parameter 'x' of the overridden method" abstract class Foo abstract def foo(x) end @@ -538,11 +538,11 @@ describe "Semantic: warnings" do class Bar < Foo def foo(y); end end - CR + CRYSTAL end it "detects mismatch within many parameters" do - assert_warning <<-CR, "warning in line 6\nWarning: positional parameter 'e' corresponds to parameter 'c' of the overridden method" + assert_warning <<-CRYSTAL, "warning in line 6\nWarning: positional parameter 'e' corresponds to parameter 'c' of the overridden method" abstract class Foo abstract def foo(a, b, c, d) end @@ -550,11 +550,11 @@ describe "Semantic: warnings" do class Bar < Foo def foo(a, b, e, d); end end - CR + CRYSTAL end it "detects multiple mismatches" do - warnings_result(<<-CR).size.should eq(2) + warnings_result(<<-CRYSTAL).size.should eq(2) abstract class Foo abstract def foo(src, dst) end @@ -562,11 +562,11 @@ describe "Semantic: warnings" do class Bar < Foo def foo(dst, src); end end - CR + CRYSTAL end it "respects external names of positional parameters (1)" do - assert_warning <<-CR, "warning in line 6\nWarning: positional parameter 'a' corresponds to parameter 'b' of the overridden method" + assert_warning <<-CRYSTAL, "warning in line 6\nWarning: positional parameter 'a' corresponds to parameter 'b' of the overridden method" abstract class Foo abstract def foo(b) end @@ -574,11 +574,11 @@ describe "Semantic: warnings" do class Bar < Foo def foo(a b); end end - CR + CRYSTAL end it "respects external names of positional parameters (2)" do - assert_warning <<-CR, "warning in line 6\nWarning: positional parameter 'b' corresponds to parameter 'a' of the overridden method" + assert_warning <<-CRYSTAL, "warning in line 6\nWarning: positional parameter 'b' corresponds to parameter 'a' of the overridden method" abstract class Foo abstract def foo(a b) end @@ -586,11 +586,11 @@ describe "Semantic: warnings" do class Bar < Foo def foo(b); end end - CR + CRYSTAL end it "doesn't warn if external parameter name matches (1)" do - warnings_result(<<-CR).should be_empty + warnings_result(<<-CRYSTAL).should be_empty abstract class Foo abstract def foo(a) end @@ -598,11 +598,11 @@ describe "Semantic: warnings" do class Bar < Foo def foo(a b); end end - CR + CRYSTAL end it "doesn't warn if external parameter name matches (2)" do - warnings_result(<<-CR).should be_empty + warnings_result(<<-CRYSTAL).should be_empty abstract class Foo abstract def foo(a b) end @@ -610,11 +610,11 @@ describe "Semantic: warnings" do class Bar < Foo def foo(a c); end end - CR + CRYSTAL end it "doesn't compare positional parameters to single splat" do - warnings_result(<<-CR).should be_empty + warnings_result(<<-CRYSTAL).should be_empty abstract class Foo abstract def foo(x) end @@ -622,11 +622,11 @@ describe "Semantic: warnings" do class Bar < Foo def foo(*y); end end - CR + CRYSTAL end it "doesn't compare single splats" do - warnings_result(<<-CR).should be_empty + warnings_result(<<-CRYSTAL).should be_empty abstract class Foo abstract def foo(*x) end @@ -634,11 +634,11 @@ describe "Semantic: warnings" do class Bar < Foo def foo(*y); end end - CR + CRYSTAL end it "informs warnings once per matching overload (1)" do - assert_warning <<-CR, "warning in line 6\nWarning: positional parameter 'y' corresponds to parameter 'x' of the overridden method" + assert_warning <<-CRYSTAL, "warning in line 6\nWarning: positional parameter 'y' corresponds to parameter 'x' of the overridden method" abstract class Foo abstract def foo(x : Int32) end @@ -647,11 +647,11 @@ describe "Semantic: warnings" do def foo(y : Int32 | Char); end def foo(x : Int32 | String); end end - CR + CRYSTAL end it "informs warnings once per matching overload (2)" do - warnings_result(<<-CR).size.should eq(2) + warnings_result(<<-CRYSTAL).size.should eq(2) abstract class Foo abstract def foo(x : Int32) end @@ -660,12 +660,12 @@ describe "Semantic: warnings" do def foo(y : Int32 | Char); end def foo(z : Int32 | String); end end - CR + CRYSTAL end describe "stops warning after implementation with matching parameters is found (#12150)" do it "exact match" do - warnings_result(<<-CR).should be_empty + warnings_result(<<-CRYSTAL).should be_empty abstract class Foo abstract def foo(x : Int32) end @@ -674,11 +674,11 @@ describe "Semantic: warnings" do def foo(x : Int32); end def foo(y : Int32 | String); end end - CR + CRYSTAL end it "contravariant restrictions" do - warnings_result(<<-CR).should be_empty + warnings_result(<<-CRYSTAL).should be_empty abstract class Foo abstract def foo(x : Int32, y : Int32) end @@ -687,11 +687,11 @@ describe "Semantic: warnings" do def foo(x : Int32 | Char, y : Int); end def foo(y : Int32 | String, z : Int32); end end - CR + CRYSTAL end it "different single splats" do - warnings_result(<<-CR).should be_empty + warnings_result(<<-CRYSTAL).should be_empty abstract class Foo abstract def foo(x : Int32, *y) end @@ -700,11 +700,11 @@ describe "Semantic: warnings" do def foo(x : Int32, *z); end def foo(y : Int32 | String, *z); end end - CR + CRYSTAL end it "reordered named parameters" do - warnings_result(<<-CR).should be_empty + warnings_result(<<-CRYSTAL).should be_empty abstract class Foo abstract def foo(x : Int32, *, y : Int32, z : Int32) end @@ -713,13 +713,13 @@ describe "Semantic: warnings" do def foo(x : Int32, *, z : Int32, y : Int32); end def foo(w : Int, *, y : Int32, z : Int32); end end - CR + CRYSTAL end end describe "continues warning if implementation with matching parameters is not found (#12150)" do it "not a full implementation" do - assert_warning <<-CR, "warning in line 8\nWarning: positional parameter 'y' corresponds to parameter 'x' of the overridden method" + assert_warning <<-CRYSTAL, "warning in line 8\nWarning: positional parameter 'y' corresponds to parameter 'x' of the overridden method" abstract class Foo abstract def foo(x : Int32 | String) end @@ -729,11 +729,11 @@ describe "Semantic: warnings" do def foo(x : String); end def foo(y : Int32 | String); end end - CR + CRYSTAL end it "single splat" do - assert_warning <<-CR, "warning in line 7\nWarning: positional parameter 'y' corresponds to parameter 'x' of the overridden method" + assert_warning <<-CRYSTAL, "warning in line 7\nWarning: positional parameter 'y' corresponds to parameter 'x' of the overridden method" abstract class Foo abstract def foo(x : Int32) end @@ -742,11 +742,11 @@ describe "Semantic: warnings" do def foo(x : Int32, *y); end def foo(y : Int32 | String); end end - CR + CRYSTAL end it "double splat" do - assert_warning <<-CR, "warning in line 7\nWarning: positional parameter 'z' corresponds to parameter 'x' of the overridden method" + assert_warning <<-CRYSTAL, "warning in line 7\nWarning: positional parameter 'z' corresponds to parameter 'x' of the overridden method" abstract class Foo abstract def foo(x : Int32, *, y) end @@ -755,12 +755,12 @@ describe "Semantic: warnings" do def foo(x : Int32, **opts); end def foo(z : Int32, *, y); end end - CR + CRYSTAL end end it "doesn't warn if current type is abstract (#12266)" do - warnings_result(<<-CR).should be_empty + warnings_result(<<-CRYSTAL).should be_empty class Foo def foo(x); end end @@ -771,11 +771,11 @@ describe "Semantic: warnings" do abstract class Baz < Bar end - CR + CRYSTAL end it "doesn't warn if current type is a module (#12266)" do - warnings_result(<<-CR).should be_empty + warnings_result(<<-CRYSTAL).should be_empty module Foo def foo(x); end # Warning: positional parameter 'x' corresponds to parameter 'y' of the overridden method Bar#foo(y), which has a different name and may affect named argument passing end @@ -788,7 +788,7 @@ describe "Semantic: warnings" do module Baz include Bar end - CR + CRYSTAL end end diff --git a/spec/std/crystal/syntax_highlighter/colorize_spec.cr b/spec/std/crystal/syntax_highlighter/colorize_spec.cr index 5cd04a7a156c..c989112264fc 100644 --- a/spec/std/crystal/syntax_highlighter/colorize_spec.cr +++ b/spec/std/crystal/syntax_highlighter/colorize_spec.cr @@ -127,13 +127,13 @@ describe Crystal::SyntaxHighlighter::Colorize do it_highlights "Set{1, 2, 3}", %(\e[36mSet\e[0m{\e[35m1\e[0m, \e[35m2\e[0m, \e[35m3\e[0m}) - it_highlights <<-CR, <<-ANSI + it_highlights <<-CRYSTAL, <<-ANSI foo, bar = <<-FOO, <<-BAR foo FOO bar BAR - CR + CRYSTAL foo, bar \e[91m=\e[0m \e[93m<<-FOO\e[0m, \e[93m<<-BAR\e[0m \e[93m foo FOO\e[0m @@ -145,16 +145,16 @@ describe Crystal::SyntaxHighlighter::Colorize do describe ".highlight!" do it_highlights! %(foo = bar("baz\#{PI + 1}") # comment), %(foo \e[91m=\e[0m bar(\e[93m"baz\#{\e[0m\e[36mPI\e[0m \e[91m+\e[0m \e[35m1\e[0m\e[93m}"\e[0m) \e[90m# comment\e[0m) - it_highlights! <<-CR + it_highlights! <<-CRYSTAL foo, bar = <<-FOO, <<-BAR foo FOO - CR + CRYSTAL - it_highlights! <<-CR + it_highlights! <<-CRYSTAL foo, bar = <<-FOO, <<-BAR foo - CR + CRYSTAL it_highlights! "\"foo" it_highlights! "%w[foo" diff --git a/spec/std/crystal/syntax_highlighter/html_spec.cr b/spec/std/crystal/syntax_highlighter/html_spec.cr index a30c6f77a9dc..fc1a3d25672c 100644 --- a/spec/std/crystal/syntax_highlighter/html_spec.cr +++ b/spec/std/crystal/syntax_highlighter/html_spec.cr @@ -122,13 +122,13 @@ describe Crystal::SyntaxHighlighter::HTML do it_highlights "Set{1, 2, 3}", %(Set{1, 2, 3}) - it_highlights <<-CR, <<-HTML + it_highlights <<-CRYSTAL, <<-HTML foo, bar = <<-FOO, <<-BAR foo FOO bar BAR - CR + CRYSTAL foo, bar = <<-FOO, <<-BAR foo FOO @@ -140,20 +140,20 @@ describe Crystal::SyntaxHighlighter::HTML do describe "#highlight!" do it_highlights! %(foo = bar("baz\#{PI + 1}") # comment), "foo = bar("baz\#{PI + 1}") # comment" - it_highlights! <<-CR, <<-HTML + it_highlights! <<-CRYSTAL, <<-HTML foo, bar = <<-FOO, <<-BAR foo FOO - CR + CRYSTAL foo, bar = <<-FOO, <<-BAR foo FOO HTML - it_highlights! <<-CR, <<-HTML + it_highlights! <<-CRYSTAL, <<-HTML foo, bar = <<-FOO, <<-BAR foo - CR + CRYSTAL foo, bar = <<-FOO, <<-BAR foo HTML diff --git a/spec/std/exception/call_stack_spec.cr b/spec/std/exception/call_stack_spec.cr index fa3565b0ed79..b5bf92597dc2 100644 --- a/spec/std/exception/call_stack_spec.cr +++ b/spec/std/exception/call_stack_spec.cr @@ -33,13 +33,13 @@ describe "Backtrace" do source_path = Path.new(source_file) source_path.absolute?.should be_true - File.write source_file, <<-EOF + File.write source_file, <<-CRYSTAL def callee1 puts caller.join('\n') end callee1 - EOF + CRYSTAL _, output, _ = compile_and_run_file(source_file) output.should match /\A(#{Regex.escape(source_path.to_s)}):/ diff --git a/spec/std/http/client/client_spec.cr b/spec/std/http/client/client_spec.cr index 10bc63f2e7af..1f35c086e9af 100644 --- a/spec/std/http/client/client_spec.cr +++ b/spec/std/http/client/client_spec.cr @@ -400,13 +400,13 @@ module HTTP end it "works with IO" do - io_response = IO::Memory.new <<-RESPONSE.gsub('\n', "\r\n") + io_response = IO::Memory.new <<-HTTP.gsub('\n', "\r\n") HTTP/1.1 200 OK Content-Type: text/plain Content-Length: 3 Hi! - RESPONSE + HTTP io_request = IO::Memory.new io = IO::Stapled.new(io_response, io_request) client = Client.new(io) diff --git a/spec/std/http/request_spec.cr b/spec/std/http/request_spec.cr index 78e6b72994f2..b030692fc842 100644 --- a/spec/std/http/request_spec.cr +++ b/spec/std/http/request_spec.cr @@ -480,7 +480,7 @@ module HTTP end it "doesn't raise on request with multiple Content_length headers" do - io = IO::Memory.new <<-REQ + io = IO::Memory.new <<-HTTP GET / HTTP/1.1 Host: host Content-Length: 5 @@ -488,12 +488,12 @@ module HTTP Content-Type: text/plain abcde - REQ + HTTP HTTP::Request.from_io(io) end it "raises if request has multiple and differing content-length headers" do - io = IO::Memory.new <<-REQ + io = IO::Memory.new <<-HTTP GET / HTTP/1.1 Host: host Content-Length: 5 @@ -501,7 +501,7 @@ module HTTP Content-Type: text/plain abcde - REQ + HTTP expect_raises(ArgumentError) do HTTP::Request.from_io(io) end diff --git a/spec/std/http/server/request_processor_spec.cr b/spec/std/http/server/request_processor_spec.cr index 677580d961cc..42a6ff7c7f70 100644 --- a/spec/std/http/server/request_processor_spec.cr +++ b/spec/std/http/server/request_processor_spec.cr @@ -18,14 +18,14 @@ describe HTTP::Server::RequestProcessor do output = IO::Memory.new processor.process(input, output) output.rewind - output.gets_to_end.should eq(requestize(<<-RESPONSE + output.gets_to_end.should eq(requestize(<<-HTTP HTTP/1.1 200 OK Connection: keep-alive Content-Type: text/plain Content-Length: 11 Hello world - RESPONSE + HTTP )) end @@ -37,7 +37,7 @@ describe HTTP::Server::RequestProcessor do context.response << "\r\n" end - input = IO::Memory.new(requestize(<<-REQUEST + input = IO::Memory.new(requestize(<<-HTTP POST / HTTP/1.1 Content-Length: 7 @@ -46,12 +46,12 @@ describe HTTP::Server::RequestProcessor do Content-Length: 7 hello - REQUEST + HTTP )) output = IO::Memory.new processor.process(input, output) output.rewind - output.gets_to_end.should eq(requestize(<<-RESPONSE + output.gets_to_end.should eq(requestize(<<-HTTP HTTP/1.1 200 OK Connection: keep-alive Content-Type: text/plain @@ -65,7 +65,7 @@ describe HTTP::Server::RequestProcessor do hello - RESPONSE + HTTP )) end @@ -75,19 +75,19 @@ describe HTTP::Server::RequestProcessor do context.response.puts "Hello world\r" end - input = IO::Memory.new(requestize(<<-REQUEST + input = IO::Memory.new(requestize(<<-HTTP POST / HTTP/1.1 POST / HTTP/1.1 Content-Length: 7 hello - REQUEST + HTTP )) output = IO::Memory.new processor.process(input, output) output.rewind - output.gets_to_end.should eq(requestize(<<-RESPONSE + output.gets_to_end.should eq(requestize(<<-HTTP HTTP/1.1 200 OK Connection: keep-alive Content-Type: text/plain @@ -101,7 +101,7 @@ describe HTTP::Server::RequestProcessor do Hello world - RESPONSE + HTTP )) end @@ -111,7 +111,7 @@ describe HTTP::Server::RequestProcessor do context.response.puts "Hello world\r" end - input = IO::Memory.new(requestize(<<-REQUEST + input = IO::Memory.new(requestize(<<-HTTP POST / HTTP/1.1 hello @@ -119,12 +119,12 @@ describe HTTP::Server::RequestProcessor do Content-Length: 7 hello - REQUEST + HTTP )) output = IO::Memory.new processor.process(input, output) output.rewind - output.gets_to_end.should eq(requestize(<<-RESPONSE + output.gets_to_end.should eq(requestize(<<-HTTP HTTP/1.1 200 OK Connection: keep-alive Content-Type: text/plain @@ -136,7 +136,7 @@ describe HTTP::Server::RequestProcessor do Content-Length: 16 400 Bad Request\\n - RESPONSE + HTTP ).gsub("\\n", "\n")) end @@ -145,7 +145,7 @@ describe HTTP::Server::RequestProcessor do context.response.headers["Connection"] = "close" end - input = IO::Memory.new(requestize(<<-REQUEST + input = IO::Memory.new(requestize(<<-HTTP POST / HTTP/1.1 Content-Length: 7 @@ -154,18 +154,18 @@ describe HTTP::Server::RequestProcessor do Content-Length: 7 hello - REQUEST + HTTP )) output = IO::Memory.new processor.process(input, output) output.rewind - output.gets_to_end.should eq(requestize(<<-RESPONSE + output.gets_to_end.should eq(requestize(<<-HTTP HTTP/1.1 200 OK Connection: close Content-Length: 0 - RESPONSE + HTTP )) end @@ -173,7 +173,7 @@ describe HTTP::Server::RequestProcessor do processor = HTTP::Server::RequestProcessor.new do |context| end - input = IO::Memory.new(requestize(<<-REQUEST + input = IO::Memory.new(requestize(<<-HTTP POST / HTTP/1.1 Content-Length: 4 @@ -182,18 +182,18 @@ describe HTTP::Server::RequestProcessor do Content-Length: 7 hello - REQUEST + HTTP )) output = IO::Memory.new processor.process(input, output) output.rewind - output.gets_to_end.should eq(requestize(<<-RESPONSE + output.gets_to_end.should eq(requestize(<<-HTTP HTTP/1.1 200 OK Connection: keep-alive Content-Length: 0 - RESPONSE + HTTP )) end @@ -203,7 +203,7 @@ describe HTTP::Server::RequestProcessor do io.gets_to_end end - input = IO::Memory.new(requestize(<<-REQUEST + input = IO::Memory.new(requestize(<<-HTTP POST / HTTP/1.1 Content-Length: 16387 @@ -212,12 +212,12 @@ describe HTTP::Server::RequestProcessor do Content-Length: 7 hello - REQUEST + HTTP )) output = IO::Memory.new processor.process(input, output) output.rewind - output.gets_to_end.should eq(requestize(<<-RESPONSE + output.gets_to_end.should eq(requestize(<<-HTTP HTTP/1.1 200 OK Connection: keep-alive Content-Length: 0 @@ -227,7 +227,7 @@ describe HTTP::Server::RequestProcessor do Content-Length: 0 - RESPONSE + HTTP )) end end diff --git a/spec/std/http/server/server_spec.cr b/spec/std/http/server/server_spec.cr index 040d3b5f8afe..1cc5b20f666b 100644 --- a/spec/std/http/server/server_spec.cr +++ b/spec/std/http/server/server_spec.cr @@ -121,12 +121,12 @@ describe HTTP::Server do run_server(server) do TCPSocket.open(address.address, address.port) do |socket| - socket << requestize(<<-REQUEST + socket << requestize(<<-HTTP POST / HTTP/1.1 Expect: 100-continue Content-Length: 5 - REQUEST + HTTP ) socket << "\r\n" socket.flush @@ -153,12 +153,12 @@ describe HTTP::Server do run_server(server) do TCPSocket.open(address.address, address.port) do |socket| - socket << requestize(<<-REQUEST + socket << requestize(<<-HTTP POST / HTTP/1.1 Expect: 100-continue Content-Length: 5 - REQUEST + HTTP ) socket << "\r\n" socket.flush diff --git a/spec/std/kernel_spec.cr b/spec/std/kernel_spec.cr index d79c816f5ff4..fefe880d9350 100644 --- a/spec/std/kernel_spec.cr +++ b/spec/std/kernel_spec.cr @@ -49,31 +49,31 @@ end describe "at_exit" do it "runs handlers on normal program ending" do - status, output, _ = compile_and_run_source <<-CODE + status, output, _ = compile_and_run_source <<-CRYSTAL at_exit do print "handler code." end - CODE + CRYSTAL status.success?.should be_true output.should eq("handler code.") end it "runs handlers on explicit program ending" do - status, output, _ = compile_and_run_source <<-'CODE' + status, output, _ = compile_and_run_source <<-'CRYSTAL' at_exit do |exit_code| print "handler code, exit code: #{exit_code}." end exit 42 - CODE + CRYSTAL status.exit_code.should eq(42) output.should eq("handler code, exit code: 42.") end it "runs handlers in reverse order" do - status, output, _ = compile_and_run_source <<-CODE + status, output, _ = compile_and_run_source <<-CRYSTAL at_exit do print "first handler code." end @@ -81,14 +81,14 @@ describe "at_exit" do at_exit do print "second handler code." end - CODE + CRYSTAL status.success?.should be_true output.should eq("second handler code.first handler code.") end it "runs all handlers maximum once" do - status, output, _ = compile_and_run_source <<-CODE + status, output, _ = compile_and_run_source <<-CRYSTAL at_exit do print "first handler code." end @@ -103,14 +103,14 @@ describe "at_exit" do at_exit do print "third handler code." end - CODE + CRYSTAL status.success?.should be_true output.should eq("third handler code.second handler code, explicit exit!first handler code.") end it "allows handlers to change the exit code with explicit `exit` call" do - status, output, _ = compile_and_run_source <<-'CODE' + status, output, _ = compile_and_run_source <<-'CRYSTAL' at_exit do |exit_code| print "first handler code, exit code: #{exit_code}." end @@ -125,7 +125,7 @@ describe "at_exit" do at_exit do |exit_code| print "third handler code, exit code: #{exit_code}." end - CODE + CRYSTAL status.success?.should be_false status.exit_code.should eq(42) @@ -133,7 +133,7 @@ describe "at_exit" do end it "allows handlers to change the exit code with explicit `exit` call (2)" do - status, output, _ = compile_and_run_source <<-'CODE' + status, output, _ = compile_and_run_source <<-'CRYSTAL' at_exit do |exit_code| print "first handler code, exit code: #{exit_code}." end @@ -150,7 +150,7 @@ describe "at_exit" do end exit 21 - CODE + CRYSTAL status.success?.should be_false status.exit_code.should eq(42) @@ -158,7 +158,7 @@ describe "at_exit" do end it "changes final exit code when an handler raises an error" do - status, output, error = compile_and_run_source <<-'CODE' + status, output, error = compile_and_run_source <<-'CRYSTAL' at_exit do |exit_code| print "first handler code, exit code: #{exit_code}." end @@ -173,7 +173,7 @@ describe "at_exit" do at_exit do |exit_code| print "third handler code, exit code: #{exit_code}." end - CODE + CRYSTAL status.success?.should be_false status.exit_code.should eq(1) @@ -182,7 +182,7 @@ describe "at_exit" do end it "shows unhandled exceptions after at_exit handlers" do - status, _, error = compile_and_run_source <<-CODE + status, _, error = compile_and_run_source <<-CRYSTAL at_exit do STDERR.print "first handler code." end @@ -192,27 +192,27 @@ describe "at_exit" do end raise "Kaboom!" - CODE + CRYSTAL status.success?.should be_false error.should contain("second handler code.first handler code.Unhandled exception: Kaboom!") end it "can get unhandled exception in at_exit handler" do - status, _, error = compile_and_run_source <<-CODE + status, _, error = compile_and_run_source <<-CRYSTAL at_exit do |_, ex| STDERR.print ex.try &.message end raise "Kaboom!" - CODE + CRYSTAL status.success?.should be_false error.should contain("Kaboom!Unhandled exception: Kaboom!") end it "allows at_exit inside at_exit" do - status, output, _ = compile_and_run_source <<-CODE + status, output, _ = compile_and_run_source <<-CRYSTAL at_exit do print "1" at_exit do @@ -226,16 +226,16 @@ describe "at_exit" do print "4" end end - CODE + CRYSTAL status.success?.should be_true output.should eq("3412") end it "prints unhandled exception with cause" do - status, _, error = compile_and_run_source <<-CODE + status, _, error = compile_and_run_source <<-CRYSTAL raise Exception.new("secondary", cause: Exception.new("primary")) - CODE + CRYSTAL status.success?.should be_false error.should contain "Unhandled exception: secondary" @@ -245,9 +245,9 @@ end describe "hardware exception" do it "reports invalid memory access" do - status, _, error = compile_and_run_source <<-'CODE' + status, _, error = compile_and_run_source <<-'CRYSTAL' puts Pointer(Int64).null.value - CODE + CRYSTAL status.success?.should be_false error.should contain("Invalid memory access") @@ -263,13 +263,13 @@ describe "hardware exception" do # the default stack size is 0.5G. Setting a # smaller stack size with `ulimit -s 8192` # will address this. - status, _, error = compile_and_run_source <<-'CODE' + status, _, error = compile_and_run_source <<-'CRYSTAL' def foo y = StaticArray(Int8, 512).new(0) foo end foo - CODE + CRYSTAL status.success?.should be_false error.should contain("Stack overflow") @@ -277,7 +277,7 @@ describe "hardware exception" do {% end %} pending_win32 "detects stack overflow on a fiber stack" do - status, _, error = compile_and_run_source <<-'CODE' + status, _, error = compile_and_run_source <<-'CRYSTAL' def foo y = StaticArray(Int8, 512).new(0) foo @@ -288,7 +288,7 @@ describe "hardware exception" do end sleep 60.seconds - CODE + CRYSTAL status.success?.should be_false error.should contain("Stack overflow") diff --git a/spec/std/process_spec.cr b/spec/std/process_spec.cr index 3655dad0a3f7..e051e328c66e 100644 --- a/spec/std/process_spec.cr +++ b/spec/std/process_spec.cr @@ -168,14 +168,14 @@ describe Process do {% if flag?(:unix) %} it "chroot raises when unprivileged" do - status, output, _ = compile_and_run_source <<-'CODE' + status, output, _ = compile_and_run_source <<-'CRYSTAL' begin Process.chroot(".") puts "FAIL" rescue ex puts ex.inspect end - CODE + CRYSTAL status.success?.should be_true output.should eq("#\n") diff --git a/spec/std/spec/hooks_spec.cr b/spec/std/spec/hooks_spec.cr index 105f5bd9db0e..a0667acbf51f 100644 --- a/spec/std/spec/hooks_spec.cr +++ b/spec/std/spec/hooks_spec.cr @@ -3,7 +3,7 @@ require "./spec_helper" describe Spec do describe "hooks" do it "runs in correct order" do - compile_and_run_source(<<-CR, flags: %w(--no-debug))[1].lines[..-5].should eq <<-OUT.lines + compile_and_run_source(<<-CRYSTAL, flags: %w(--no-debug))[1].lines[..-5].should eq <<-OUT.lines require "prelude" require "spec" @@ -90,7 +90,7 @@ describe Spec do it {} end - CR + CRYSTAL Can't call `before_all` outside of a describe/context Can't call `before_each` outside of a describe/context Can't call `after_all` outside of a describe/context diff --git a/spec/std/string/grapheme_break_spec.cr b/spec/std/string/grapheme_break_spec.cr index 9ff17889fe05..1e757064d43f 100644 --- a/spec/std/string/grapheme_break_spec.cr +++ b/spec/std/string/grapheme_break_spec.cr @@ -10,8 +10,8 @@ require "./spec_helper" describe "String#each_grapheme" do it_iterates_graphemes " ", [' ', ' '] # ÷ [0.2] SPACE (Other) ÷ [999.0] SPACE (Other) ÷ [0.3] it_iterates_graphemes " \u0308 ", [" \u0308", ' '] # ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] - it_iterates_graphemes " \r", [' ', '\r'] # ÷ [0.2] SPACE (Other) ÷ [5.0] (CR) ÷ [0.3] - it_iterates_graphemes " \u0308\r", [" \u0308", '\r'] # ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] + it_iterates_graphemes " \r", [' ', '\r'] # ÷ [0.2] SPACE (Other) ÷ [5.0] (CRYSTAL) ÷ [0.3] + it_iterates_graphemes " \u0308\r", [" \u0308", '\r'] # ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CRYSTAL) ÷ [0.3] it_iterates_graphemes " \n", [' ', '\n'] # ÷ [0.2] SPACE (Other) ÷ [5.0] (LF) ÷ [0.3] it_iterates_graphemes " \u0308\n", [" \u0308", '\n'] # ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] it_iterates_graphemes " \u0001", [' ', '\u0001'] # ÷ [0.2] SPACE (Other) ÷ [5.0] (Control) ÷ [0.3] @@ -42,44 +42,44 @@ describe "String#each_grapheme" do it_iterates_graphemes " \u0308\u200D", [" \u0308\u200D"] # ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] it_iterates_graphemes " \u0378", [' ', '\u0378'] # ÷ [0.2] SPACE (Other) ÷ [999.0] (Other) ÷ [0.3] it_iterates_graphemes " \u0308\u0378", [" \u0308", '\u0378'] # ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] - it_iterates_graphemes "\r ", ['\r', ' '] # ÷ [0.2] (CR) ÷ [4.0] SPACE (Other) ÷ [0.3] - it_iterates_graphemes "\r\u0308 ", ['\r', '\u0308', ' '] # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] - it_iterates_graphemes "\r\r", ['\r', '\r'] # ÷ [0.2] (CR) ÷ [4.0] (CR) ÷ [0.3] - it_iterates_graphemes "\r\u0308\r", ['\r', '\u0308', '\r'] # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] - it_iterates_graphemes "\r\n", ["\r\n"] # ÷ [0.2] (CR) × [3.0] (LF) ÷ [0.3] - it_iterates_graphemes "\r\u0308\n", ['\r', '\u0308', '\n'] # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] - it_iterates_graphemes "\r\u0001", ['\r', '\u0001'] # ÷ [0.2] (CR) ÷ [4.0] (Control) ÷ [0.3] - it_iterates_graphemes "\r\u0308\u0001", ['\r', '\u0308', '\u0001'] # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3] - it_iterates_graphemes "\r\u034F", ['\r', '\u034F'] # ÷ [0.2] (CR) ÷ [4.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3] - it_iterates_graphemes "\r\u0308\u034F", ['\r', "\u0308\u034F"] # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3] - it_iterates_graphemes "\r\u{1F1E6}", ['\r', '\u{1F1E6}'] # ÷ [0.2] (CR) ÷ [4.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] - it_iterates_graphemes "\r\u0308\u{1F1E6}", ['\r', '\u0308', '\u{1F1E6}'] # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] - it_iterates_graphemes "\r\u0600", ['\r', '\u0600'] # ÷ [0.2] (CR) ÷ [4.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] - it_iterates_graphemes "\r\u0308\u0600", ['\r', '\u0308', '\u0600'] # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] - it_iterates_graphemes "\r\u0903", ['\r', '\u0903'] # ÷ [0.2] (CR) ÷ [4.0] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] - it_iterates_graphemes "\r\u0308\u0903", ['\r', "\u0308\u0903"] # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] - it_iterates_graphemes "\r\u1100", ['\r', '\u1100'] # ÷ [0.2] (CR) ÷ [4.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] - it_iterates_graphemes "\r\u0308\u1100", ['\r', '\u0308', '\u1100'] # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] - it_iterates_graphemes "\r\u1160", ['\r', '\u1160'] # ÷ [0.2] (CR) ÷ [4.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] - it_iterates_graphemes "\r\u0308\u1160", ['\r', '\u0308', '\u1160'] # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] - it_iterates_graphemes "\r\u11A8", ['\r', '\u11A8'] # ÷ [0.2] (CR) ÷ [4.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] - it_iterates_graphemes "\r\u0308\u11A8", ['\r', '\u0308', '\u11A8'] # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] - it_iterates_graphemes "\r\uAC00", ['\r', '\uAC00'] # ÷ [0.2] (CR) ÷ [4.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] - it_iterates_graphemes "\r\u0308\uAC00", ['\r', '\u0308', '\uAC00'] # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] - it_iterates_graphemes "\r\uAC01", ['\r', '\uAC01'] # ÷ [0.2] (CR) ÷ [4.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] - it_iterates_graphemes "\r\u0308\uAC01", ['\r', '\u0308', '\uAC01'] # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] - it_iterates_graphemes "\r\u231A", ['\r', '\u231A'] # ÷ [0.2] (CR) ÷ [4.0] WATCH (ExtPict) ÷ [0.3] - it_iterates_graphemes "\r\u0308\u231A", ['\r', '\u0308', '\u231A'] # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] - it_iterates_graphemes "\r\u0300", ['\r', '\u0300'] # ÷ [0.2] (CR) ÷ [4.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] - it_iterates_graphemes "\r\u0308\u0300", ['\r', "\u0308\u0300"] # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] - it_iterates_graphemes "\r\u200D", ['\r', '\u200D'] # ÷ [0.2] (CR) ÷ [4.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] - it_iterates_graphemes "\r\u0308\u200D", ['\r', "\u0308\u200D"] # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] - it_iterates_graphemes "\r\u0378", ['\r', '\u0378'] # ÷ [0.2] (CR) ÷ [4.0] (Other) ÷ [0.3] - it_iterates_graphemes "\r\u0308\u0378", ['\r', '\u0308', '\u0378'] # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] + it_iterates_graphemes "\r ", ['\r', ' '] # ÷ [0.2] (CRYSTAL) ÷ [4.0] SPACE (Other) ÷ [0.3] + it_iterates_graphemes "\r\u0308 ", ['\r', '\u0308', ' '] # ÷ [0.2] (CRYSTAL) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] + it_iterates_graphemes "\r\r", ['\r', '\r'] # ÷ [0.2] (CRYSTAL) ÷ [4.0] (CRYSTAL) ÷ [0.3] + it_iterates_graphemes "\r\u0308\r", ['\r', '\u0308', '\r'] # ÷ [0.2] (CRYSTAL) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CRYSTAL) ÷ [0.3] + it_iterates_graphemes "\r\n", ["\r\n"] # ÷ [0.2] (CRYSTAL) × [3.0] (LF) ÷ [0.3] + it_iterates_graphemes "\r\u0308\n", ['\r', '\u0308', '\n'] # ÷ [0.2] (CRYSTAL) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] + it_iterates_graphemes "\r\u0001", ['\r', '\u0001'] # ÷ [0.2] (CRYSTAL) ÷ [4.0] (Control) ÷ [0.3] + it_iterates_graphemes "\r\u0308\u0001", ['\r', '\u0308', '\u0001'] # ÷ [0.2] (CRYSTAL) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3] + it_iterates_graphemes "\r\u034F", ['\r', '\u034F'] # ÷ [0.2] (CRYSTAL) ÷ [4.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3] + it_iterates_graphemes "\r\u0308\u034F", ['\r', "\u0308\u034F"] # ÷ [0.2] (CRYSTAL) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3] + it_iterates_graphemes "\r\u{1F1E6}", ['\r', '\u{1F1E6}'] # ÷ [0.2] (CRYSTAL) ÷ [4.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] + it_iterates_graphemes "\r\u0308\u{1F1E6}", ['\r', '\u0308', '\u{1F1E6}'] # ÷ [0.2] (CRYSTAL) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] + it_iterates_graphemes "\r\u0600", ['\r', '\u0600'] # ÷ [0.2] (CRYSTAL) ÷ [4.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] + it_iterates_graphemes "\r\u0308\u0600", ['\r', '\u0308', '\u0600'] # ÷ [0.2] (CRYSTAL) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] + it_iterates_graphemes "\r\u0903", ['\r', '\u0903'] # ÷ [0.2] (CRYSTAL) ÷ [4.0] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] + it_iterates_graphemes "\r\u0308\u0903", ['\r', "\u0308\u0903"] # ÷ [0.2] (CRYSTAL) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] + it_iterates_graphemes "\r\u1100", ['\r', '\u1100'] # ÷ [0.2] (CRYSTAL) ÷ [4.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] + it_iterates_graphemes "\r\u0308\u1100", ['\r', '\u0308', '\u1100'] # ÷ [0.2] (CRYSTAL) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] + it_iterates_graphemes "\r\u1160", ['\r', '\u1160'] # ÷ [0.2] (CRYSTAL) ÷ [4.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] + it_iterates_graphemes "\r\u0308\u1160", ['\r', '\u0308', '\u1160'] # ÷ [0.2] (CRYSTAL) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] + it_iterates_graphemes "\r\u11A8", ['\r', '\u11A8'] # ÷ [0.2] (CRYSTAL) ÷ [4.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] + it_iterates_graphemes "\r\u0308\u11A8", ['\r', '\u0308', '\u11A8'] # ÷ [0.2] (CRYSTAL) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] + it_iterates_graphemes "\r\uAC00", ['\r', '\uAC00'] # ÷ [0.2] (CRYSTAL) ÷ [4.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] + it_iterates_graphemes "\r\u0308\uAC00", ['\r', '\u0308', '\uAC00'] # ÷ [0.2] (CRYSTAL) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] + it_iterates_graphemes "\r\uAC01", ['\r', '\uAC01'] # ÷ [0.2] (CRYSTAL) ÷ [4.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] + it_iterates_graphemes "\r\u0308\uAC01", ['\r', '\u0308', '\uAC01'] # ÷ [0.2] (CRYSTAL) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] + it_iterates_graphemes "\r\u231A", ['\r', '\u231A'] # ÷ [0.2] (CRYSTAL) ÷ [4.0] WATCH (ExtPict) ÷ [0.3] + it_iterates_graphemes "\r\u0308\u231A", ['\r', '\u0308', '\u231A'] # ÷ [0.2] (CRYSTAL) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] + it_iterates_graphemes "\r\u0300", ['\r', '\u0300'] # ÷ [0.2] (CRYSTAL) ÷ [4.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] + it_iterates_graphemes "\r\u0308\u0300", ['\r', "\u0308\u0300"] # ÷ [0.2] (CRYSTAL) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] + it_iterates_graphemes "\r\u200D", ['\r', '\u200D'] # ÷ [0.2] (CRYSTAL) ÷ [4.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] + it_iterates_graphemes "\r\u0308\u200D", ['\r', "\u0308\u200D"] # ÷ [0.2] (CRYSTAL) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] + it_iterates_graphemes "\r\u0378", ['\r', '\u0378'] # ÷ [0.2] (CRYSTAL) ÷ [4.0] (Other) ÷ [0.3] + it_iterates_graphemes "\r\u0308\u0378", ['\r', '\u0308', '\u0378'] # ÷ [0.2] (CRYSTAL) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] it_iterates_graphemes "\n ", ['\n', ' '] # ÷ [0.2] (LF) ÷ [4.0] SPACE (Other) ÷ [0.3] it_iterates_graphemes "\n\u0308 ", ['\n', '\u0308', ' '] # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] - it_iterates_graphemes "\n\r", ['\n', '\r'] # ÷ [0.2] (LF) ÷ [4.0] (CR) ÷ [0.3] - it_iterates_graphemes "\n\u0308\r", ['\n', '\u0308', '\r'] # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] + it_iterates_graphemes "\n\r", ['\n', '\r'] # ÷ [0.2] (LF) ÷ [4.0] (CRYSTAL) ÷ [0.3] + it_iterates_graphemes "\n\u0308\r", ['\n', '\u0308', '\r'] # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CRYSTAL) ÷ [0.3] it_iterates_graphemes "\n\n", ['\n', '\n'] # ÷ [0.2] (LF) ÷ [4.0] (LF) ÷ [0.3] it_iterates_graphemes "\n\u0308\n", ['\n', '\u0308', '\n'] # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] it_iterates_graphemes "\n\u0001", ['\n', '\u0001'] # ÷ [0.2] (LF) ÷ [4.0] (Control) ÷ [0.3] @@ -112,8 +112,8 @@ describe "String#each_grapheme" do it_iterates_graphemes "\n\u0308\u0378", ['\n', '\u0308', '\u0378'] # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] it_iterates_graphemes "\u0001 ", ['\u0001', ' '] # ÷ [0.2] (Control) ÷ [4.0] SPACE (Other) ÷ [0.3] it_iterates_graphemes "\u0001\u0308 ", ['\u0001', '\u0308', ' '] # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] - it_iterates_graphemes "\u0001\r", ['\u0001', '\r'] # ÷ [0.2] (Control) ÷ [4.0] (CR) ÷ [0.3] - it_iterates_graphemes "\u0001\u0308\r", ['\u0001', '\u0308', '\r'] # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] + it_iterates_graphemes "\u0001\r", ['\u0001', '\r'] # ÷ [0.2] (Control) ÷ [4.0] (CRYSTAL) ÷ [0.3] + it_iterates_graphemes "\u0001\u0308\r", ['\u0001', '\u0308', '\r'] # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CRYSTAL) ÷ [0.3] it_iterates_graphemes "\u0001\n", ['\u0001', '\n'] # ÷ [0.2] (Control) ÷ [4.0] (LF) ÷ [0.3] it_iterates_graphemes "\u0001\u0308\n", ['\u0001', '\u0308', '\n'] # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] it_iterates_graphemes "\u0001\u0001", ['\u0001', '\u0001'] # ÷ [0.2] (Control) ÷ [4.0] (Control) ÷ [0.3] @@ -146,8 +146,8 @@ describe "String#each_grapheme" do it_iterates_graphemes "\u0001\u0308\u0378", ['\u0001', '\u0308', '\u0378'] # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] it_iterates_graphemes "\u034F ", ['\u034F', ' '] # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) ÷ [999.0] SPACE (Other) ÷ [0.3] it_iterates_graphemes "\u034F\u0308 ", ["\u034F\u0308", ' '] # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] - it_iterates_graphemes "\u034F\r", ['\u034F', '\r'] # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) ÷ [5.0] (CR) ÷ [0.3] - it_iterates_graphemes "\u034F\u0308\r", ["\u034F\u0308", '\r'] # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] + it_iterates_graphemes "\u034F\r", ['\u034F', '\r'] # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) ÷ [5.0] (CRYSTAL) ÷ [0.3] + it_iterates_graphemes "\u034F\u0308\r", ["\u034F\u0308", '\r'] # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CRYSTAL) ÷ [0.3] it_iterates_graphemes "\u034F\n", ['\u034F', '\n'] # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) ÷ [5.0] (LF) ÷ [0.3] it_iterates_graphemes "\u034F\u0308\n", ["\u034F\u0308", '\n'] # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] it_iterates_graphemes "\u034F\u0001", ['\u034F', '\u0001'] # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) ÷ [5.0] (Control) ÷ [0.3] @@ -180,8 +180,8 @@ describe "String#each_grapheme" do it_iterates_graphemes "\u034F\u0308\u0378", ["\u034F\u0308", '\u0378'] # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] it_iterates_graphemes "\u{1F1E6} ", ['\u{1F1E6}', ' '] # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] SPACE (Other) ÷ [0.3] it_iterates_graphemes "\u{1F1E6}\u0308 ", ["\u{1F1E6}\u0308", ' '] # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] - it_iterates_graphemes "\u{1F1E6}\r", ['\u{1F1E6}', '\r'] # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [5.0] (CR) ÷ [0.3] - it_iterates_graphemes "\u{1F1E6}\u0308\r", ["\u{1F1E6}\u0308", '\r'] # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] + it_iterates_graphemes "\u{1F1E6}\r", ['\u{1F1E6}', '\r'] # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [5.0] (CRYSTAL) ÷ [0.3] + it_iterates_graphemes "\u{1F1E6}\u0308\r", ["\u{1F1E6}\u0308", '\r'] # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CRYSTAL) ÷ [0.3] it_iterates_graphemes "\u{1F1E6}\n", ['\u{1F1E6}', '\n'] # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [5.0] (LF) ÷ [0.3] it_iterates_graphemes "\u{1F1E6}\u0308\n", ["\u{1F1E6}\u0308", '\n'] # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] it_iterates_graphemes "\u{1F1E6}\u0001", ['\u{1F1E6}', '\u0001'] # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [5.0] (Control) ÷ [0.3] @@ -214,8 +214,8 @@ describe "String#each_grapheme" do it_iterates_graphemes "\u{1F1E6}\u0308\u0378", ["\u{1F1E6}\u0308", '\u0378'] # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] it_iterates_graphemes "\u0600 ", ["\u0600 "] # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.2] SPACE (Other) ÷ [0.3] it_iterates_graphemes "\u0600\u0308 ", ["\u0600\u0308", ' '] # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] - it_iterates_graphemes "\u0600\r", ['\u0600', '\r'] # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) ÷ [5.0] (CR) ÷ [0.3] - it_iterates_graphemes "\u0600\u0308\r", ["\u0600\u0308", '\r'] # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] + it_iterates_graphemes "\u0600\r", ['\u0600', '\r'] # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) ÷ [5.0] (CRYSTAL) ÷ [0.3] + it_iterates_graphemes "\u0600\u0308\r", ["\u0600\u0308", '\r'] # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CRYSTAL) ÷ [0.3] it_iterates_graphemes "\u0600\n", ['\u0600', '\n'] # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) ÷ [5.0] (LF) ÷ [0.3] it_iterates_graphemes "\u0600\u0308\n", ["\u0600\u0308", '\n'] # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] it_iterates_graphemes "\u0600\u0001", ['\u0600', '\u0001'] # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) ÷ [5.0] (Control) ÷ [0.3] @@ -248,8 +248,8 @@ describe "String#each_grapheme" do it_iterates_graphemes "\u0600\u0308\u0378", ["\u0600\u0308", '\u0378'] # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] it_iterates_graphemes "\u0903 ", ['\u0903', ' '] # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] SPACE (Other) ÷ [0.3] it_iterates_graphemes "\u0903\u0308 ", ["\u0903\u0308", ' '] # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] - it_iterates_graphemes "\u0903\r", ['\u0903', '\r'] # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [5.0] (CR) ÷ [0.3] - it_iterates_graphemes "\u0903\u0308\r", ["\u0903\u0308", '\r'] # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] + it_iterates_graphemes "\u0903\r", ['\u0903', '\r'] # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [5.0] (CRYSTAL) ÷ [0.3] + it_iterates_graphemes "\u0903\u0308\r", ["\u0903\u0308", '\r'] # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CRYSTAL) ÷ [0.3] it_iterates_graphemes "\u0903\n", ['\u0903', '\n'] # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [5.0] (LF) ÷ [0.3] it_iterates_graphemes "\u0903\u0308\n", ["\u0903\u0308", '\n'] # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] it_iterates_graphemes "\u0903\u0001", ['\u0903', '\u0001'] # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [5.0] (Control) ÷ [0.3] @@ -282,8 +282,8 @@ describe "String#each_grapheme" do it_iterates_graphemes "\u0903\u0308\u0378", ["\u0903\u0308", '\u0378'] # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] it_iterates_graphemes "\u1100 ", ['\u1100', ' '] # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] SPACE (Other) ÷ [0.3] it_iterates_graphemes "\u1100\u0308 ", ["\u1100\u0308", ' '] # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] - it_iterates_graphemes "\u1100\r", ['\u1100', '\r'] # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [5.0] (CR) ÷ [0.3] - it_iterates_graphemes "\u1100\u0308\r", ["\u1100\u0308", '\r'] # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] + it_iterates_graphemes "\u1100\r", ['\u1100', '\r'] # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [5.0] (CRYSTAL) ÷ [0.3] + it_iterates_graphemes "\u1100\u0308\r", ["\u1100\u0308", '\r'] # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CRYSTAL) ÷ [0.3] it_iterates_graphemes "\u1100\n", ['\u1100', '\n'] # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [5.0] (LF) ÷ [0.3] it_iterates_graphemes "\u1100\u0308\n", ["\u1100\u0308", '\n'] # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] it_iterates_graphemes "\u1100\u0001", ['\u1100', '\u0001'] # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [5.0] (Control) ÷ [0.3] @@ -316,8 +316,8 @@ describe "String#each_grapheme" do it_iterates_graphemes "\u1100\u0308\u0378", ["\u1100\u0308", '\u0378'] # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] it_iterates_graphemes "\u1160 ", ['\u1160', ' '] # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] SPACE (Other) ÷ [0.3] it_iterates_graphemes "\u1160\u0308 ", ["\u1160\u0308", ' '] # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] - it_iterates_graphemes "\u1160\r", ['\u1160', '\r'] # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [5.0] (CR) ÷ [0.3] - it_iterates_graphemes "\u1160\u0308\r", ["\u1160\u0308", '\r'] # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] + it_iterates_graphemes "\u1160\r", ['\u1160', '\r'] # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [5.0] (CRYSTAL) ÷ [0.3] + it_iterates_graphemes "\u1160\u0308\r", ["\u1160\u0308", '\r'] # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CRYSTAL) ÷ [0.3] it_iterates_graphemes "\u1160\n", ['\u1160', '\n'] # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [5.0] (LF) ÷ [0.3] it_iterates_graphemes "\u1160\u0308\n", ["\u1160\u0308", '\n'] # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] it_iterates_graphemes "\u1160\u0001", ['\u1160', '\u0001'] # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [5.0] (Control) ÷ [0.3] @@ -350,8 +350,8 @@ describe "String#each_grapheme" do it_iterates_graphemes "\u1160\u0308\u0378", ["\u1160\u0308", '\u0378'] # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] it_iterates_graphemes "\u11A8 ", ['\u11A8', ' '] # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] SPACE (Other) ÷ [0.3] it_iterates_graphemes "\u11A8\u0308 ", ["\u11A8\u0308", ' '] # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] - it_iterates_graphemes "\u11A8\r", ['\u11A8', '\r'] # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [5.0] (CR) ÷ [0.3] - it_iterates_graphemes "\u11A8\u0308\r", ["\u11A8\u0308", '\r'] # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] + it_iterates_graphemes "\u11A8\r", ['\u11A8', '\r'] # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [5.0] (CRYSTAL) ÷ [0.3] + it_iterates_graphemes "\u11A8\u0308\r", ["\u11A8\u0308", '\r'] # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CRYSTAL) ÷ [0.3] it_iterates_graphemes "\u11A8\n", ['\u11A8', '\n'] # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [5.0] (LF) ÷ [0.3] it_iterates_graphemes "\u11A8\u0308\n", ["\u11A8\u0308", '\n'] # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] it_iterates_graphemes "\u11A8\u0001", ['\u11A8', '\u0001'] # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [5.0] (Control) ÷ [0.3] @@ -384,8 +384,8 @@ describe "String#each_grapheme" do it_iterates_graphemes "\u11A8\u0308\u0378", ["\u11A8\u0308", '\u0378'] # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] it_iterates_graphemes "\uAC00 ", ['\uAC00', ' '] # ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] SPACE (Other) ÷ [0.3] it_iterates_graphemes "\uAC00\u0308 ", ["\uAC00\u0308", ' '] # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] - it_iterates_graphemes "\uAC00\r", ['\uAC00', '\r'] # ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [5.0] (CR) ÷ [0.3] - it_iterates_graphemes "\uAC00\u0308\r", ["\uAC00\u0308", '\r'] # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] + it_iterates_graphemes "\uAC00\r", ['\uAC00', '\r'] # ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [5.0] (CRYSTAL) ÷ [0.3] + it_iterates_graphemes "\uAC00\u0308\r", ["\uAC00\u0308", '\r'] # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CRYSTAL) ÷ [0.3] it_iterates_graphemes "\uAC00\n", ['\uAC00', '\n'] # ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [5.0] (LF) ÷ [0.3] it_iterates_graphemes "\uAC00\u0308\n", ["\uAC00\u0308", '\n'] # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] it_iterates_graphemes "\uAC00\u0001", ['\uAC00', '\u0001'] # ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [5.0] (Control) ÷ [0.3] @@ -418,8 +418,8 @@ describe "String#each_grapheme" do it_iterates_graphemes "\uAC00\u0308\u0378", ["\uAC00\u0308", '\u0378'] # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] it_iterates_graphemes "\uAC01 ", ['\uAC01', ' '] # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] SPACE (Other) ÷ [0.3] it_iterates_graphemes "\uAC01\u0308 ", ["\uAC01\u0308", ' '] # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] - it_iterates_graphemes "\uAC01\r", ['\uAC01', '\r'] # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [5.0] (CR) ÷ [0.3] - it_iterates_graphemes "\uAC01\u0308\r", ["\uAC01\u0308", '\r'] # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] + it_iterates_graphemes "\uAC01\r", ['\uAC01', '\r'] # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [5.0] (CRYSTAL) ÷ [0.3] + it_iterates_graphemes "\uAC01\u0308\r", ["\uAC01\u0308", '\r'] # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CRYSTAL) ÷ [0.3] it_iterates_graphemes "\uAC01\n", ['\uAC01', '\n'] # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [5.0] (LF) ÷ [0.3] it_iterates_graphemes "\uAC01\u0308\n", ["\uAC01\u0308", '\n'] # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] it_iterates_graphemes "\uAC01\u0001", ['\uAC01', '\u0001'] # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [5.0] (Control) ÷ [0.3] @@ -452,8 +452,8 @@ describe "String#each_grapheme" do it_iterates_graphemes "\uAC01\u0308\u0378", ["\uAC01\u0308", '\u0378'] # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] it_iterates_graphemes "\u231A ", ['\u231A', ' '] # ÷ [0.2] WATCH (ExtPict) ÷ [999.0] SPACE (Other) ÷ [0.3] it_iterates_graphemes "\u231A\u0308 ", ["\u231A\u0308", ' '] # ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] - it_iterates_graphemes "\u231A\r", ['\u231A', '\r'] # ÷ [0.2] WATCH (ExtPict) ÷ [5.0] (CR) ÷ [0.3] - it_iterates_graphemes "\u231A\u0308\r", ["\u231A\u0308", '\r'] # ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] + it_iterates_graphemes "\u231A\r", ['\u231A', '\r'] # ÷ [0.2] WATCH (ExtPict) ÷ [5.0] (CRYSTAL) ÷ [0.3] + it_iterates_graphemes "\u231A\u0308\r", ["\u231A\u0308", '\r'] # ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CRYSTAL) ÷ [0.3] it_iterates_graphemes "\u231A\n", ['\u231A', '\n'] # ÷ [0.2] WATCH (ExtPict) ÷ [5.0] (LF) ÷ [0.3] it_iterates_graphemes "\u231A\u0308\n", ["\u231A\u0308", '\n'] # ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] it_iterates_graphemes "\u231A\u0001", ['\u231A', '\u0001'] # ÷ [0.2] WATCH (ExtPict) ÷ [5.0] (Control) ÷ [0.3] @@ -486,8 +486,8 @@ describe "String#each_grapheme" do it_iterates_graphemes "\u231A\u0308\u0378", ["\u231A\u0308", '\u0378'] # ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] it_iterates_graphemes "\u0300 ", ['\u0300', ' '] # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] it_iterates_graphemes "\u0300\u0308 ", ["\u0300\u0308", ' '] # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] - it_iterates_graphemes "\u0300\r", ['\u0300', '\r'] # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] - it_iterates_graphemes "\u0300\u0308\r", ["\u0300\u0308", '\r'] # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] + it_iterates_graphemes "\u0300\r", ['\u0300', '\r'] # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [5.0] (CRYSTAL) ÷ [0.3] + it_iterates_graphemes "\u0300\u0308\r", ["\u0300\u0308", '\r'] # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CRYSTAL) ÷ [0.3] it_iterates_graphemes "\u0300\n", ['\u0300', '\n'] # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] it_iterates_graphemes "\u0300\u0308\n", ["\u0300\u0308", '\n'] # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] it_iterates_graphemes "\u0300\u0001", ['\u0300', '\u0001'] # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3] @@ -520,8 +520,8 @@ describe "String#each_grapheme" do it_iterates_graphemes "\u0300\u0308\u0378", ["\u0300\u0308", '\u0378'] # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] it_iterates_graphemes "\u200D ", ['\u200D', ' '] # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] it_iterates_graphemes "\u200D\u0308 ", ["\u200D\u0308", ' '] # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] - it_iterates_graphemes "\u200D\r", ['\u200D', '\r'] # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] - it_iterates_graphemes "\u200D\u0308\r", ["\u200D\u0308", '\r'] # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] + it_iterates_graphemes "\u200D\r", ['\u200D', '\r'] # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [5.0] (CRYSTAL) ÷ [0.3] + it_iterates_graphemes "\u200D\u0308\r", ["\u200D\u0308", '\r'] # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CRYSTAL) ÷ [0.3] it_iterates_graphemes "\u200D\n", ['\u200D', '\n'] # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] it_iterates_graphemes "\u200D\u0308\n", ["\u200D\u0308", '\n'] # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] it_iterates_graphemes "\u200D\u0001", ['\u200D', '\u0001'] # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3] @@ -554,8 +554,8 @@ describe "String#each_grapheme" do it_iterates_graphemes "\u200D\u0308\u0378", ["\u200D\u0308", '\u0378'] # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] it_iterates_graphemes "\u0378 ", ['\u0378', ' '] # ÷ [0.2] (Other) ÷ [999.0] SPACE (Other) ÷ [0.3] it_iterates_graphemes "\u0378\u0308 ", ["\u0378\u0308", ' '] # ÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] - it_iterates_graphemes "\u0378\r", ['\u0378', '\r'] # ÷ [0.2] (Other) ÷ [5.0] (CR) ÷ [0.3] - it_iterates_graphemes "\u0378\u0308\r", ["\u0378\u0308", '\r'] # ÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] + it_iterates_graphemes "\u0378\r", ['\u0378', '\r'] # ÷ [0.2] (Other) ÷ [5.0] (CRYSTAL) ÷ [0.3] + it_iterates_graphemes "\u0378\u0308\r", ["\u0378\u0308", '\r'] # ÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CRYSTAL) ÷ [0.3] it_iterates_graphemes "\u0378\n", ['\u0378', '\n'] # ÷ [0.2] (Other) ÷ [5.0] (LF) ÷ [0.3] it_iterates_graphemes "\u0378\u0308\n", ["\u0378\u0308", '\n'] # ÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] it_iterates_graphemes "\u0378\u0001", ['\u0378', '\u0001'] # ÷ [0.2] (Other) ÷ [5.0] (Control) ÷ [0.3] @@ -586,7 +586,7 @@ describe "String#each_grapheme" do it_iterates_graphemes "\u0378\u0308\u200D", ["\u0378\u0308\u200D"] # ÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] it_iterates_graphemes "\u0378\u0378", ['\u0378', '\u0378'] # ÷ [0.2] (Other) ÷ [999.0] (Other) ÷ [0.3] it_iterates_graphemes "\u0378\u0308\u0378", ["\u0378\u0308", '\u0378'] # ÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] - it_iterates_graphemes "\r\na\n\u0308", ["\r\n", 'a', '\n', '\u0308'] # ÷ [0.2] (CR) × [3.0] (LF) ÷ [4.0] LATIN SMALL LETTER A (Other) ÷ [5.0] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [0.3] + it_iterates_graphemes "\r\na\n\u0308", ["\r\n", 'a', '\n', '\u0308'] # ÷ [0.2] (CRYSTAL) × [3.0] (LF) ÷ [4.0] LATIN SMALL LETTER A (Other) ÷ [5.0] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [0.3] it_iterates_graphemes "a\u0308", ["a\u0308"] # ÷ [0.2] LATIN SMALL LETTER A (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [0.3] it_iterates_graphemes " \u200D\u0646", [" \u200D", '\u0646'] # ÷ [0.2] SPACE (Other) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] ARABIC LETTER NOON (Other) ÷ [0.3] it_iterates_graphemes "\u0646\u200D ", ["\u0646\u200D", ' '] # ÷ [0.2] ARABIC LETTER NOON (Other) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] diff --git a/spec/std/yaml/yaml_spec.cr b/spec/std/yaml/yaml_spec.cr index 4eb6a345d9bd..996b74f06716 100644 --- a/spec/std/yaml/yaml_spec.cr +++ b/spec/std/yaml/yaml_spec.cr @@ -137,11 +137,11 @@ describe "YAML" do it "has correct message (#4006)" do expect_raises YAML::ParseException, "could not find expected ':' at line 4, column 1, while scanning a simple key at line 3, column 5" do - YAML.parse <<-END + YAML.parse <<-YAML a: - "b": > c - END + YAML end end diff --git a/src/compiler/crystal/tools/doc/templates.cr b/src/compiler/crystal/tools/doc/templates.cr index 14e7f4daba62..77496edb1fd4 100644 --- a/src/compiler/crystal/tools/doc/templates.cr +++ b/src/compiler/crystal/tools/doc/templates.cr @@ -1,24 +1,24 @@ require "ecr/macros" module Crystal::Doc - SVG_DEFS = <<-SVGS + SVG_DEFS = <<-SVG - SVGS + SVG def self.anchor_link(anchor : String) anchor = anchor.downcase.gsub(' ', '-') - <<-ANCHOR + <<-HTML - ANCHOR + HTML end record TypeTemplate, type : Type, types : Array(Type), project_info : ProjectInfo do diff --git a/src/compiler/crystal/tools/playground/server.cr b/src/compiler/crystal/tools/playground/server.cr index 12914e593305..654d0863faba 100644 --- a/src/compiler/crystal/tools/playground/server.cr +++ b/src/compiler/crystal/tools/playground/server.cr @@ -22,7 +22,7 @@ module Crystal::Playground instrumented = Playground::AgentInstrumentorTransformer.transform(ast).to_s Log.info { "Code instrumentation (session=#{session_key}, tag=#{tag}).\n#{instrumented}" } - prelude = <<-CR + prelude = <<-CRYSTAL require "compiler/crystal/tools/playground/agent" class Crystal::Playground::Agent @@ -36,7 +36,7 @@ module Crystal::Playground def _p Crystal::Playground::Agent.instance end - CR + CRYSTAL [ Compiler::Source.new("playground_prelude", prelude), @@ -390,7 +390,7 @@ module Crystal::Playground class EnvironmentHandler include HTTP::Handler - DEFAULT_SOURCE = <<-CR + DEFAULT_SOURCE = <<-CRYSTAL def find_string(text, word) (0..text.size-word.size).each do |i| { i, text[i..i+word.size-1] } @@ -404,7 +404,7 @@ module Crystal::Playground find_string "Crystal is awesome!", "awesome" find_string "Crystal is awesome!", "not sure" - CR + CRYSTAL def initialize(@server : Playground::Server) end diff --git a/src/yaml.cr b/src/yaml.cr index 813b456c084f..23a5843dfecb 100644 --- a/src/yaml.cr +++ b/src/yaml.cr @@ -19,14 +19,14 @@ require "base64" # ``` # require "yaml" # -# data = YAML.parse <<-END +# data = YAML.parse <<-YAML # --- # foo: # bar: # baz: # - qux # - fox -# END +# YAML # data["foo"]["bar"]["baz"][1].as_s # => "fox" # ``` # diff --git a/src/yaml/any.cr b/src/yaml/any.cr index f0a7978369b9..e8e3a82e58b3 100644 --- a/src/yaml/any.cr +++ b/src/yaml/any.cr @@ -5,14 +5,14 @@ # ``` # require "yaml" # -# data = YAML.parse <<-END +# data = YAML.parse <<-YAML # --- # foo: # bar: # baz: # - qux # - fox -# END +# YAML # data["foo"]["bar"]["baz"][0].as_s # => "qux" # data["foo"]["bar"]["baz"].as_a # => ["qux", "fox"] # ``` From 87813d1f282f940a30c6b964fad5b06c65b80787 Mon Sep 17 00:00:00 2001 From: Hugo Parente Lima Date: Sun, 4 Dec 2022 08:38:45 -0300 Subject: [PATCH 16/41] Swap documentation for `String#split` array and block versions. (#12808) --- src/string.cr | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/string.cr b/src/string.cr index dd3918252002..4e2fe6064108 100644 --- a/src/string.cr +++ b/src/string.cr @@ -3947,7 +3947,7 @@ class String yield String.new(to_unsafe + byte_offset, piece_bytesize, piece_size) end - # Splits the string after each regex *separator* and yields each part to a block. + # Makes an `Array` by splitting the string on *separator* (and removing instances of *separator*). # # If *limit* is present, the array will be limited to *limit* items and # the final item will contain the remainder of the string. @@ -3957,15 +3957,9 @@ class String # If *remove_empty* is `true`, any empty strings are removed from the result. # # ``` - # ary = [] of String # long_river_name = "Mississippi" - # - # long_river_name.split(/s+/) { |s| ary << s } - # ary # => ["Mi", "i", "ippi"] - # ary.clear - # - # long_river_name.split(//) { |s| ary << s } - # ary # => ["M", "i", "s", "s", "i", "s", "s", "i", "p", "p", "i"] + # long_river_name.split(/s+/) # => ["Mi", "i", "ippi"] + # long_river_name.split(//) # => ["M", "i", "s", "s", "i", "s", "s", "i", "p", "p", "i"] # ``` def split(separator : Regex, limit = nil, *, remove_empty = false) : Array(String) ary = Array(String).new @@ -3975,7 +3969,7 @@ class String ary end - # Makes an `Array` by splitting the string on *separator* (and removing instances of *separator*). + # Splits the string after each regex *separator* and yields each part to a block. # # If *limit* is present, the array will be limited to *limit* items and # the final item will contain the remainder of the string. @@ -3985,9 +3979,15 @@ class String # If *remove_empty* is `true`, any empty strings are removed from the result. # # ``` + # ary = [] of String # long_river_name = "Mississippi" - # long_river_name.split(/s+/) # => ["Mi", "i", "ippi"] - # long_river_name.split(//) # => ["M", "i", "s", "s", "i", "s", "s", "i", "p", "p", "i"] + # + # long_river_name.split(/s+/) { |s| ary << s } + # ary # => ["Mi", "i", "ippi"] + # ary.clear + # + # long_river_name.split(//) { |s| ary << s } + # ary # => ["M", "i", "s", "s", "i", "s", "s", "i", "p", "p", "i"] # ``` def split(separator : Regex, limit = nil, *, remove_empty = false, &block : String -> _) if empty? From 5a33430465bd0a6f1731f98bfe7d7a543c02bad3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20M=C3=BCller?= Date: Sun, 4 Dec 2022 23:41:20 +0100 Subject: [PATCH 17/41] Fix `BigInt#%` for unsigned integers (#12773) --- spec/std/big/big_int_spec.cr | 1 + src/big/big_int.cr | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/spec/std/big/big_int_spec.cr b/spec/std/big/big_int_spec.cr index 4aa7aaadcd6a..69be160b848f 100644 --- a/spec/std/big/big_int_spec.cr +++ b/spec/std/big/big_int_spec.cr @@ -228,6 +228,7 @@ describe "BigInt" do it "does modulo" do (10.to_big_i % 3.to_big_i).should eq(1.to_big_i) (10.to_big_i % 3).should eq(1.to_big_i) + (10.to_big_i % 3u8).should eq(1.to_big_i) (10 % 3.to_big_i).should eq(1.to_big_i) end diff --git a/src/big/big_int.cr b/src/big/big_int.cr index 1f8e95f1aa7c..f703a6854126 100644 --- a/src/big/big_int.cr +++ b/src/big/big_int.cr @@ -253,7 +253,7 @@ struct BigInt < Int check_division_by_zero other if other < 0 - -(-self).unsafe_floored_mod(-other) + -(-self).unsafe_floored_mod(other.abs) else unsafe_floored_mod(other) end From 6b19d661430c3a04bda315dc26b0364958e16852 Mon Sep 17 00:00:00 2001 From: Vlad Zarakovsky Date: Mon, 5 Dec 2022 01:41:58 +0300 Subject: [PATCH 18/41] Replace `if !blank?` with `unless blank?` (#12800) --- src/string.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/string.cr b/src/string.cr index 4e2fe6064108..c741f004334d 100644 --- a/src/string.cr +++ b/src/string.cr @@ -2958,7 +2958,7 @@ class String # # See also: `Nil#presence`. def presence : self? - self if !blank? + self unless blank? end # Returns `true` if this string is equal to `*other*. From 33b1920a553980487bd801120264d881ebddfa90 Mon Sep 17 00:00:00 2001 From: Quinton Miller Date: Tue, 6 Dec 2022 05:01:22 +0800 Subject: [PATCH 19/41] Fix restriction of numeral generic argument against non-free variable `Path` (#12784) --- spec/compiler/semantic/restrictions_spec.cr | 30 +++++++++++++++-- src/compiler/crystal/semantic/restrictions.cr | 33 ++++++++++++++----- 2 files changed, 51 insertions(+), 12 deletions(-) diff --git a/spec/compiler/semantic/restrictions_spec.cr b/spec/compiler/semantic/restrictions_spec.cr index baf69551da21..7201bb816f69 100644 --- a/spec/compiler/semantic/restrictions_spec.cr +++ b/spec/compiler/semantic/restrictions_spec.cr @@ -617,8 +617,7 @@ describe "Restrictions" do CRYSTAL end - # TODO: enable in #12784 - pending "inserts constant before free variable with same name" do + it "inserts constant before free variable with same name" do assert_type(<<-CRYSTAL) { tuple_of([char, bool]) } class Foo(T); end @@ -636,7 +635,7 @@ describe "Restrictions" do CRYSTAL end - pending "keeps constant before free variable with same name" do + it "keeps constant before free variable with same name" do assert_type(<<-CRYSTAL) { tuple_of([char, bool]) } class Foo(T); end @@ -1140,6 +1139,31 @@ describe "Restrictions" do "expected argument #2 to 'foo' to be StaticArray(UInt8, 10), not StaticArray(UInt8, 11)" end + it "does not treat single path as free variable when given number (1) (#11859)" do + assert_error <<-CR, "expected argument #1 to 'Foo(1)#foo' to be Foo(1), not Foo(2)" + class Foo(T) + def foo(x : Foo(T)) + end + end + + Foo(1).new.foo(Foo(2).new) + CR + end + + it "does not treat single path as free variable when given number (2) (#11859)" do + assert_error <<-CR, "expected argument #1 to 'foo' to be Foo(1), not Foo(2)" + X = 1 + + class Foo(T) + end + + def foo(x : Foo(X)) + end + + foo(Foo(2).new) + CR + end + it "restricts aliased typedef type (#9474)" do assert_type(%( lib A diff --git a/src/compiler/crystal/semantic/restrictions.cr b/src/compiler/crystal/semantic/restrictions.cr index 523030444d2e..c391dfdb4ee6 100644 --- a/src/compiler/crystal/semantic/restrictions.cr +++ b/src/compiler/crystal/semantic/restrictions.cr @@ -705,7 +705,20 @@ module Crystal end def restriction_of?(other : Generic, owner, self_free_vars = nil, other_free_vars = nil) - return true if self == other + # The two `Foo(X)`s below are not equal because only one of them is bound + # and the other one is unbound, so we compare the free variables too: + # (`X` is an alias or a numeric constant) + # + # ``` + # def foo(x : Foo(X)) forall X + # end + # + # def foo(x : Foo(X)) + # end + # ``` + # + # See also the todo in `Path#restriction_of?(Path)` + return true if self == other && self_free_vars == other_free_vars return false unless name == other.name && type_vars.size == other.type_vars.size # Special case: NamedTuple against NamedTuple @@ -1242,15 +1255,17 @@ module Crystal end when Path if first_name = other_type_var.single_name? - # If the free variable is already set to another - # number, there's no match - existing = context.get_free_var(first_name) - if existing && existing != type_var - return nil - end + if context.has_def_free_var?(first_name) + # If the free variable is already set to another + # number, there's no match + existing = context.get_free_var(first_name) + if existing && existing != type_var + return nil + end - context.set_free_var(first_name, type_var) - return type_var + context.set_free_var(first_name, type_var) + return type_var + end end else # Restriction is not possible (maybe return nil here?) From ae173ee134b62f291f721a3b8fea001564668495 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20M=C3=BCller?= Date: Mon, 5 Dec 2022 22:01:42 +0100 Subject: [PATCH 20/41] Fix explicit type conversion to u64 for `GC::Stats` (#12779) Resolves https://github.com/crystal-lang/crystal/pull/12634 --- spec/std/gc_spec.cr | 8 ++++++++ src/gc/boehm.cr | 30 +++++++++++++++--------------- 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/spec/std/gc_spec.cr b/spec/std/gc_spec.cr index 7fd19817128b..4ceb1ad173f9 100644 --- a/spec/std/gc_spec.cr +++ b/spec/std/gc_spec.cr @@ -10,4 +10,12 @@ describe "GC" do GC.enable end end + + it ".stats" do + GC.stats.should be_a(GC::Stats) + end + + it ".prof_stats" do + GC.prof_stats.should be_a(GC::ProfStats) + end end diff --git a/src/gc/boehm.cr b/src/gc/boehm.cr index 2a76eb950a4b..f9a93aef154b 100644 --- a/src/gc/boehm.cr +++ b/src/gc/boehm.cr @@ -217,11 +217,11 @@ module GC Stats.new( # collections: collections, # bytes_found: bytes_found, - heap_size: heap_size, - free_bytes: free_bytes, - unmapped_bytes: unmapped_bytes, - bytes_since_gc: bytes_since_gc, - total_bytes: total_bytes + heap_size: heap_size.to_u64!, + free_bytes: free_bytes.to_u64!, + unmapped_bytes: unmapped_bytes.to_u64!, + bytes_since_gc: bytes_since_gc.to_u64!, + total_bytes: total_bytes.to_u64! ) end @@ -229,16 +229,16 @@ module GC LibGC.get_prof_stats(out stats, sizeof(LibGC::ProfStats)) ProfStats.new( - heap_size: stats.heap_size, - free_bytes: stats.free_bytes, - unmapped_bytes: stats.unmapped_bytes, - bytes_since_gc: stats.bytes_since_gc, - bytes_before_gc: stats.bytes_before_gc, - non_gc_bytes: stats.non_gc_bytes, - gc_no: stats.gc_no, - markers_m1: stats.markers_m1, - bytes_reclaimed_since_gc: stats.bytes_reclaimed_since_gc, - reclaimed_bytes_before_gc: stats.reclaimed_bytes_before_gc) + heap_size: stats.heap_size.to_u64!, + free_bytes: stats.free_bytes.to_u64!, + unmapped_bytes: stats.unmapped_bytes.to_u64!, + bytes_since_gc: stats.bytes_since_gc.to_u64!, + bytes_before_gc: stats.bytes_before_gc.to_u64!, + non_gc_bytes: stats.non_gc_bytes.to_u64!, + gc_no: stats.gc_no.to_u64!, + markers_m1: stats.markers_m1.to_u64!, + bytes_reclaimed_since_gc: stats.bytes_reclaimed_since_gc.to_u64!, + reclaimed_bytes_before_gc: stats.reclaimed_bytes_before_gc.to_u64!) end {% unless flag?(:win32) %} From 5e3ea1cac9d815c088aa1beb5f646283fe69c390 Mon Sep 17 00:00:00 2001 From: Caspian Baska Date: Thu, 8 Dec 2022 03:34:35 +0800 Subject: [PATCH 21/41] Fix calls with do-end blocks within index operators (#12824) --- spec/compiler/parser/parser_spec.cr | 9 +++++++++ src/compiler/crystal/syntax/parser.cr | 3 +++ 2 files changed, 12 insertions(+) diff --git a/spec/compiler/parser/parser_spec.cr b/spec/compiler/parser/parser_spec.cr index 61f1a369154f..b11874676354 100644 --- a/spec/compiler/parser/parser_spec.cr +++ b/spec/compiler/parser/parser_spec.cr @@ -681,6 +681,15 @@ module Crystal it_parses "[] of {String, ->}", ArrayLiteral.new([] of ASTNode, Generic.new(Path.global("Tuple"), ["String".path, ProcNotation.new] of ASTNode)) it_parses "x([] of Foo, Bar.new)", Call.new(nil, "x", ArrayLiteral.new([] of ASTNode, "Foo".path), Call.new("Bar".path, "new")) + context "calls with blocks within index operator (#12818)" do + it_parses "foo[bar { 1 }]", Call.new("foo".call, "[]", Call.new(nil, "bar", block: Block.new(body: 1.int32))) + it_parses "foo.[bar { 1 }]", Call.new("foo".call, "[]", Call.new(nil, "bar", block: Block.new(body: 1.int32))) + it_parses "foo.[](bar { 1 })", Call.new("foo".call, "[]", Call.new(nil, "bar", block: Block.new(body: 1.int32))) + it_parses "foo[bar do; 1; end]", Call.new("foo".call, "[]", Call.new(nil, "bar", block: Block.new(body: 1.int32))) + it_parses "foo.[bar do; 1; end]", Call.new("foo".call, "[]", Call.new(nil, "bar", block: Block.new(body: 1.int32))) + it_parses "foo.[](bar do; 1; end)", Call.new("foo".call, "[]", Call.new(nil, "bar", block: Block.new(body: 1.int32))) + end + it_parses "Foo(x: U)", Generic.new("Foo".path, [] of ASTNode, named_args: [NamedArgument.new("x", "U".path)]) it_parses "Foo(x: U, y: V)", Generic.new("Foo".path, [] of ASTNode, named_args: [NamedArgument.new("x", "U".path), NamedArgument.new("y", "V".path)]) it_parses "Foo(X: U, Y: V)", Generic.new("Foo".path, [] of ASTNode, named_args: [NamedArgument.new("X", "U".path), NamedArgument.new("Y", "V".path)]) diff --git a/src/compiler/crystal/syntax/parser.cr b/src/compiler/crystal/syntax/parser.cr index e554f500b738..5b772962c98d 100644 --- a/src/compiler/crystal/syntax/parser.cr +++ b/src/compiler/crystal/syntax/parser.cr @@ -804,14 +804,17 @@ module Crystal name_location = @token.location next_token_skip_space_or_newline + call_args = preserve_stop_on_do do parse_call_args_space_consumed( check_plus_and_minus: false, allow_curly: true, end_token: :OP_RSQUARE, allow_beginless_range: true, + control: true, ) end + skip_space_or_newline check :OP_RSQUARE end_location = token_end_location From 65a17bfaad1ad7c531cc95c5c71761692c0feec5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20M=C3=BCller?= Date: Wed, 7 Dec 2022 20:34:59 +0100 Subject: [PATCH 22/41] Improve `Benchmark` docs (#12782) Co-authored-by: r00ster --- src/benchmark.cr | 6 ++---- src/benchmark/bm.cr | 4 ++-- src/benchmark/ips.cr | 4 ++-- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/benchmark.cr b/src/benchmark.cr index c836ddf18dd6..da2a10be9def 100644 --- a/src/benchmark.cr +++ b/src/benchmark.cr @@ -33,8 +33,6 @@ require "./benchmark/**" # end # ``` # -# Make sure to always benchmark code by compiling with the `--release` flag. -# # ### Measure the time to construct the string given by the expression: `"a"*1_000_000_000` # # ``` @@ -81,7 +79,7 @@ require "./benchmark/**" # upto: 0.010000 0.000000 0.010000 ( 0.010466) # ``` # -# Make sure to always benchmark code by compiling with the `--release` flag. +# NOTE: Make sure to always benchmark code by compiling with the `--release` flag. module Benchmark extend self @@ -104,7 +102,7 @@ module Benchmark # The optional parameters *calculation* and *warmup* set the duration of # those stages in seconds. For more detail on these stages see # `Benchmark::IPS`. When the *interactive* parameter is `true`, results are - # displayed and updated as they are calculated, otherwise all at once. + # displayed and updated as they are calculated, otherwise all at once after they finished. def ips(calculation = 5, warmup = 2, interactive = STDOUT.tty?) {% if !flag?(:release) %} puts "Warning: benchmarking without the `--release` flag won't yield useful results" diff --git a/src/benchmark/bm.cr b/src/benchmark/bm.cr index ab0f87704d6e..4b94e4ca6eee 100644 --- a/src/benchmark/bm.cr +++ b/src/benchmark/bm.cr @@ -24,12 +24,12 @@ module Benchmark def initialize(@utime, @stime, @cutime, @cstime, @real, @label) end - # Total time, that is utime + stime + cutime + cstime + # Total time, that is `utime` + `stime` + `cutime` + `cstime` def total : Float64 utime + stime + cutime + cstime end - # Prints *utime*, *stime*, *total* and *real* to the given IO. + # Prints `utime`, `stime`, `total` and `real` to *io*. def to_s(io : IO) : Nil io.printf " %.6f %.6f %.6f ( %.6f)", utime, stime, total, real end diff --git a/src/benchmark/ips.cr b/src/benchmark/ips.cr index 85d1df46723e..cb952325eca0 100644 --- a/src/benchmark/ips.cr +++ b/src/benchmark/ips.cr @@ -14,7 +14,7 @@ module Benchmark module IPS class Job # List of all entries in the benchmark. - # After #execute, these are populated with the resulting statistics. + # After `#execute`, these are populated with the resulting statistics. property items : Array(Entry) @warmup_time : Time::Span @@ -129,7 +129,7 @@ module Benchmark # Code to be benchmarked property action : -> - # Number of cycles needed to run for approx 100ms + # Number of cycles needed to run `action` for approximately 100ms. # Calculated during the warmup stage property! cycles : Int32 From a37e97d02f369a8bec32897f1fa8c668727d0016 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20M=C3=BCller?= Date: Thu, 8 Dec 2022 23:15:46 +0100 Subject: [PATCH 23/41] Optimize uniqueness filter in `Channel.select_impl` (#12814) --- src/channel.cr | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/src/channel.cr b/src/channel.cr index 59fe55e38cf4..5a7a5957c786 100644 --- a/src/channel.cr +++ b/src/channel.cr @@ -425,20 +425,19 @@ class Channel(T) # This is to avoid deadlocks between concurrent `select` calls ops_locks = ops .to_a - .uniq!(&.lock_object_id) - .sort_by!(&.lock_object_id) + .unstable_sort_by!(&.lock_object_id) - ops_locks.each &.lock + each_skip_duplicates(ops_locks, &.lock) ops.each_with_index do |op, index| state = op.execute case state in .delivered? - ops_locks.each &.unlock + each_skip_duplicates(ops_locks, &.unlock) return index, op.result in .closed? - ops_locks.each &.unlock + each_skip_duplicates(ops_locks, &.unlock) return index, op.default_result in .none? # do nothing @@ -446,7 +445,7 @@ class Channel(T) end if non_blocking - ops_locks.each &.unlock + each_skip_duplicates(ops_locks, &.unlock) return ops.size, NotReady.new end @@ -456,7 +455,7 @@ class Channel(T) shared_state = SelectContextSharedState.new(SelectState::Active) contexts = ops.map &.create_context_and_wait(shared_state) - ops_locks.each &.unlock + each_skip_duplicates(ops_locks, &.unlock) Crystal::Scheduler.reschedule contexts.each_with_index do |context, index| @@ -475,6 +474,19 @@ class Channel(T) raise "BUG: Fiber was awaken from select but no action was activated" end + private def self.each_skip_duplicates(ops_locks) + # Avoid deadlocks from trying to lock the same lock twice. + # `ops_lock` is sorted by `lock_object_id`, so identical onces will be in + # a row and we skip repeats while iterating. + last_lock_id = nil + ops_locks.each do |op| + if op.lock_object_id != last_lock_id + last_lock_id = op.lock_object_id + yield op + end + end + end + # :nodoc: def send_select_action(value : T) SendAction.new(self, value) From 6acf0319f5850bf5ccf65028105640e46fa17a01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20M=C3=BCller?= Date: Sun, 11 Dec 2022 21:07:53 +0100 Subject: [PATCH 24/41] Add references between String equality, comparison methods (#10531) Co-authored-by: Vladislav Zarakovsky --- src/string.cr | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/string.cr b/src/string.cr index c741f004334d..06df4ce97834 100644 --- a/src/string.cr +++ b/src/string.cr @@ -2963,12 +2963,22 @@ class String # Returns `true` if this string is equal to `*other*. # - # Comparison is done byte-per-byte: if a byte is different from the corresponding - # byte, `false` is returned and so on. This means two strings containing invalid + # Equality is checked byte-per-byte: if any byte is different from the corresponding + # byte, it returns `false`. This means two strings containing invalid # UTF-8 byte sequences may compare unequal, even when they both produce the # Unicode replacement character at the same string indices. # - # See `#compare` for more comparison options. + # Thus equality is case-sensitive, as it is with the comparison operator (`#<=>`). + # `#compare` offers a case-insensitive alternative. + # + # ``` + # "abcdef" == "abcde" # => false + # "abcdef" == "abcdef" # => true + # "abcdef" == "abcdefg" # => false + # "abcdef" == "ABCDEF" # => false + # + # "abcdef".compare("ABCDEF", case_sensitive: false) == 0 # => true + # ``` def ==(other : self) : Bool return true if same?(other) return false unless bytesize == other.bytesize @@ -2995,6 +3005,8 @@ class String # "abcdef" <=> "abcdefg" # => -1 # "abcdef" <=> "ABCDEF" # => 1 # ``` + # + # The comparison is case-sensitive. `#compare` is a case-insensitive alternative. def <=>(other : self) : Int32 return 0 if same?(other) min_bytesize = Math.min(bytesize, other.bytesize) @@ -3021,6 +3033,8 @@ class String # # "heIIo".compare("heııo", case_insensitive: true, options: Unicode::CaseOptions::Turkic) # => 0 # ``` + # + # Case-sensitive only comparison is provided by the comparison operator `#<=>`. def compare(other : String, case_insensitive = false, options = Unicode::CaseOptions::None) : Int32 return self <=> other unless case_insensitive From a3f9199b8aed928d91c797e0aa9656f302804e18 Mon Sep 17 00:00:00 2001 From: Quinton Miller Date: Mon, 12 Dec 2022 04:08:36 +0800 Subject: [PATCH 25/41] Implement multithreading primitives on Windows (#11647) --- spec/std/thread/mutex_spec.cr | 9 +- src/crystal/system/thread.cr | 2 +- .../system/thread_condition_variable.cr | 31 +++++++ src/crystal/system/thread_mutex.cr | 2 +- .../system/unix/pthread_condition_variable.cr | 2 +- src/crystal/system/wasi/thread.cr | 16 ---- .../system/wasi/thread_condition_variable.cr | 16 ++++ src/crystal/system/win32/process.cr | 2 +- src/crystal/system/win32/thread.cr | 85 +++++++++++++++---- .../system/win32/thread_condition_variable.cr | 41 +++++++++ src/crystal/system/win32/thread_mutex.cr | 57 ++++++++++++- src/gc/boehm.cr | 16 +++- src/gc/none.cr | 13 ++- src/lib_c/x86_64-windows-msvc/c/process.cr | 5 ++ .../c/processthreadsapi.cr | 2 + src/lib_c/x86_64-windows-msvc/c/synchapi.cr | 28 ++++++ src/lib_c/x86_64-windows-msvc/c/winbase.cr | 5 ++ src/lib_c/x86_64-windows-msvc/c/winsock2.cr | 6 +- 18 files changed, 286 insertions(+), 52 deletions(-) create mode 100644 src/crystal/system/thread_condition_variable.cr create mode 100644 src/crystal/system/wasi/thread_condition_variable.cr create mode 100644 src/crystal/system/win32/thread_condition_variable.cr create mode 100644 src/lib_c/x86_64-windows-msvc/c/process.cr diff --git a/spec/std/thread/mutex_spec.cr b/spec/std/thread/mutex_spec.cr index 3346a1575615..405c812b0888 100644 --- a/spec/std/thread/mutex_spec.cr +++ b/spec/std/thread/mutex_spec.cr @@ -15,11 +15,11 @@ describe Thread::Mutex do a = 0 mutex = Thread::Mutex.new - threads = 10.times.map do + threads = Array.new(10) do Thread.new do mutex.synchronize { a += 1 } end - end.to_a + end threads.each(&.join) a.should eq(10) @@ -29,15 +29,16 @@ describe Thread::Mutex do mutex = Thread::Mutex.new mutex.try_lock.should be_true mutex.try_lock.should be_false - expect_raises(RuntimeError, "pthread_mutex_lock: ") { mutex.lock } + expect_raises(RuntimeError) { mutex.lock } mutex.unlock + Thread.new { mutex.synchronize { } }.join end it "won't unlock from another thread" do mutex = Thread::Mutex.new mutex.lock - expect_raises(RuntimeError, "pthread_mutex_unlock: ") do + expect_raises(RuntimeError) do Thread.new { mutex.unlock }.join end diff --git a/src/crystal/system/thread.cr b/src/crystal/system/thread.cr index 71f6286d9d36..3666b7ad512a 100644 --- a/src/crystal/system/thread.cr +++ b/src/crystal/system/thread.cr @@ -27,12 +27,12 @@ class Thread end require "./thread_linked_list" +require "./thread_condition_variable" {% if flag?(:wasi) %} require "./wasi/thread" {% elsif flag?(:unix) %} require "./unix/pthread" - require "./unix/pthread_condition_variable" {% elsif flag?(:win32) %} require "./win32/thread" {% else %} diff --git a/src/crystal/system/thread_condition_variable.cr b/src/crystal/system/thread_condition_variable.cr new file mode 100644 index 000000000000..ea5923601aec --- /dev/null +++ b/src/crystal/system/thread_condition_variable.cr @@ -0,0 +1,31 @@ +class Thread + class ConditionVariable + # Creates a new condition variable. + # def initialize + + # Unblocks one thread that is waiting on `self`. + # def signal : Nil + + # Unblocks all threads that are waiting on `self`. + # def broadcast : Nil + + # Causes the calling thread to wait on `self` and unlock the given *mutex* + # atomically. + # def wait(mutex : Thread::Mutex) : Nil + + # Causes the calling thread to wait on `self` and unlock the given *mutex* + # atomically within the given *time* span. Yields to the given block if a + # timeout occurs. + # def wait(mutex : Thread::Mutex, time : Time::Span, & : ->) + end +end + +{% if flag?(:wasi) %} + require "./wasi/thread_condition_variable" +{% elsif flag?(:unix) %} + require "./unix/pthread_condition_variable" +{% elsif flag?(:win32) %} + require "./win32/thread_condition_variable" +{% else %} + {% raise "thread condition variable not supported" %} +{% end %} diff --git a/src/crystal/system/thread_mutex.cr b/src/crystal/system/thread_mutex.cr index 1a5f3006d6eb..e3cf9ffeb6cb 100644 --- a/src/crystal/system/thread_mutex.cr +++ b/src/crystal/system/thread_mutex.cr @@ -24,5 +24,5 @@ end {% elsif flag?(:win32) %} require "./win32/thread_mutex" {% else %} - {% raise "thread not supported" %} + {% raise "thread mutex not supported" %} {% end %} diff --git a/src/crystal/system/unix/pthread_condition_variable.cr b/src/crystal/system/unix/pthread_condition_variable.cr index 225aad1c7105..a09811c79281 100644 --- a/src/crystal/system/unix/pthread_condition_variable.cr +++ b/src/crystal/system/unix/pthread_condition_variable.cr @@ -33,7 +33,7 @@ class Thread raise RuntimeError.from_os_error("pthread_cond_wait", Errno.new(ret)) unless ret == 0 end - def wait(mutex : Thread::Mutex, time : Time::Span) + def wait(mutex : Thread::Mutex, time : Time::Span, & : ->) ret = {% if flag?(:darwin) %} ts = uninitialized LibC::Timespec diff --git a/src/crystal/system/wasi/thread.cr b/src/crystal/system/wasi/thread.cr index b10439852f55..805c7fbb77a6 100644 --- a/src/crystal/system/wasi/thread.cr +++ b/src/crystal/system/wasi/thread.cr @@ -54,20 +54,4 @@ class Thread # TODO: Implement Pointer(Void).null end - - # :nodoc: - # TODO: Implement - class ConditionVariable - def signal : Nil - end - - def broadcast : Nil - end - - def wait(mutex : Thread::Mutex) : Nil - end - - def wait(mutex : Thread::Mutex, time : Time::Span, &) - end - end end diff --git a/src/crystal/system/wasi/thread_condition_variable.cr b/src/crystal/system/wasi/thread_condition_variable.cr new file mode 100644 index 000000000000..eb205333acdd --- /dev/null +++ b/src/crystal/system/wasi/thread_condition_variable.cr @@ -0,0 +1,16 @@ +# TODO: Implement +class Thread + class ConditionVariable + def signal : Nil + end + + def broadcast : Nil + end + + def wait(mutex : Thread::Mutex) : Nil + end + + def wait(mutex : Thread::Mutex, time : Time::Span, &) + end + end +end diff --git a/src/crystal/system/win32/process.cr b/src/crystal/system/win32/process.cr index 291a66d228e6..ef9bd87dcb75 100644 --- a/src/crystal/system/win32/process.cr +++ b/src/crystal/system/win32/process.cr @@ -21,7 +21,7 @@ struct Crystal::System::Process end def wait - if LibC.WaitForSingleObject(@process_handle, LibC::INFINITE) != 0 + if LibC.WaitForSingleObject(@process_handle, LibC::INFINITE) != LibC::WAIT_OBJECT_0 raise RuntimeError.from_winerror("WaitForSingleObject") end diff --git a/src/crystal/system/win32/thread.cr b/src/crystal/system/win32/thread.cr index 1af2ee9660f2..5e7ae49b30fc 100644 --- a/src/crystal/system/win32/thread.cr +++ b/src/crystal/system/win32/thread.cr @@ -1,10 +1,11 @@ require "c/processthreadsapi" +require "c/synchapi" -# TODO: Implement for multithreading. class Thread # all thread objects, so the GC can see them (it doesn't scan thread locals) - @@threads = Thread::LinkedList(Thread).new + protected class_getter(threads) { Thread::LinkedList(Thread).new } + @th : LibC::HANDLE @exception : Exception? @detached = Atomic(UInt8).new(0) @main_fiber : Fiber? @@ -16,42 +17,87 @@ class Thread property previous : Thread? def self.unsafe_each - @@threads.unsafe_each { |thread| yield thread } + threads.unsafe_each { |thread| yield thread } end + # Starts a new system thread. + def initialize(&@func : ->) + @th = uninitialized LibC::HANDLE + + @th = GC.beginthreadex( + security: Pointer(Void).null, + stack_size: LibC::UInt.zero, + start_address: ->(data : Void*) { data.as(Thread).start; LibC::UInt.zero }, + arglist: self.as(Void*), + initflag: LibC::UInt.zero, + thrdaddr: Pointer(LibC::UInt).null) + end + + # Used once to initialize the thread object representing the main thread of + # the process (that already exists). def initialize + # `GetCurrentThread` returns a _constant_ and is only meaningful as an + # argument to Win32 APIs; to uniquely identify it we must duplicate the handle + @th = uninitialized LibC::HANDLE + cur_proc = LibC.GetCurrentProcess + LibC.DuplicateHandle(cur_proc, LibC.GetCurrentThread, cur_proc, pointerof(@th), 0, true, LibC::DUPLICATE_SAME_ACCESS) + + @func = ->{} @main_fiber = Fiber.new(stack_address, self) - @@threads.push(self) + + Thread.threads.push(self) + end + + private def detach + if @detached.compare_and_set(0, 1).last + yield + end end - @@current : Thread? = nil + # Suspends the current thread until this thread terminates. + def join : Nil + detach do + if LibC.WaitForSingleObject(@th, LibC::INFINITE) != LibC::WAIT_OBJECT_0 + @exception ||= RuntimeError.from_winerror("WaitForSingleObject") + end + if LibC.CloseHandle(@th) == 0 + @exception ||= RuntimeError.from_winerror("CloseHandle") + end + end - # Associates the Thread object to the running system thread. - protected def self.current=(@@current : Thread) : Thread + if exception = @exception + raise exception + end end + @[ThreadLocal] + @@current : Thread? + # Returns the Thread object associated to the running system thread. def self.current : Thread - @@current || raise "BUG: Thread.current returned NULL" + @@current ||= new + end + + # Associates the Thread object to the running system thread. + protected def self.current=(@@current : Thread) : Thread end - # Create the thread object for the current thread (aka the main thread of the - # process). - # - # TODO: consider moving to `kernel.cr` or `crystal/main.cr` - self.current = new + def self.yield : Nil + LibC.SwitchToThread + end # Returns the Fiber representing the thread's main stack. - def main_fiber + def main_fiber : Fiber @main_fiber.not_nil! end # :nodoc: - def scheduler + def scheduler : Crystal::Scheduler @scheduler ||= Crystal::Scheduler.new(main_fiber) end protected def start + Thread.threads.push(self) Thread.current = self @main_fiber = fiber = Fiber.new(stack_address, self) @@ -60,9 +106,9 @@ class Thread rescue ex @exception = ex ensure - @@threads.delete(self) + Thread.threads.delete(self) Fiber.inactive(fiber) - detach_self + detach { LibC.CloseHandle(@th) } end end @@ -71,4 +117,9 @@ class Thread Pointer(Void).new(low_limit) end + + # :nodoc: + def to_unsafe + @th + end end diff --git a/src/crystal/system/win32/thread_condition_variable.cr b/src/crystal/system/win32/thread_condition_variable.cr new file mode 100644 index 000000000000..423de9dc57f3 --- /dev/null +++ b/src/crystal/system/win32/thread_condition_variable.cr @@ -0,0 +1,41 @@ +require "c/synchapi" + +# :nodoc: +class Thread + # :nodoc: + class ConditionVariable + def initialize + @cond = uninitialized LibC::CONDITION_VARIABLE + LibC.InitializeConditionVariable(self) + end + + def signal : Nil + LibC.WakeConditionVariable(self) + end + + def broadcast : Nil + LibC.WakeAllConditionVariable(self) + end + + def wait(mutex : Thread::Mutex) : Nil + ret = LibC.SleepConditionVariableCS(self, mutex, LibC::INFINITE) + raise RuntimeError.from_winerror("SleepConditionVariableCS") if ret == 0 + end + + def wait(mutex : Thread::Mutex, time : Time::Span, & : ->) + ret = LibC.SleepConditionVariableCS(self, mutex, time.total_milliseconds) + return if ret != 0 + + error = WinError.value + if error == WinError::ERROR_TIMEOUT + yield + else + raise RuntimeError.from_os_error("SleepConditionVariableCS", error) + end + end + + def to_unsafe + pointerof(@cond) + end + end +end diff --git a/src/crystal/system/win32/thread_mutex.cr b/src/crystal/system/win32/thread_mutex.cr index be681c31398a..afd4cb1fbdcb 100644 --- a/src/crystal/system/win32/thread_mutex.cr +++ b/src/crystal/system/win32/thread_mutex.cr @@ -1,8 +1,61 @@ -# TODO: Implement +require "c/synchapi" + +# :nodoc: class Thread + # :nodoc: + # for Win32 condition variable interop we must use either a critical section + # or a slim reader/writer lock, not a Win32 mutex + # also note critical sections are reentrant; to match the behaviour in + # `../unix/pthread_mutex.cr` we must do extra housekeeping ourselves class Mutex + def initialize + @cs = uninitialized LibC::CRITICAL_SECTION + LibC.InitializeCriticalSectionAndSpinCount(self, 1000) + end + + def lock : Nil + LibC.EnterCriticalSection(self) + if @cs.recursionCount > 1 + LibC.LeaveCriticalSection(self) + raise RuntimeError.new "Attempt to lock a mutex recursively (deadlock)" + end + end + + def try_lock : Bool + if LibC.TryEnterCriticalSection(self) != 0 + if @cs.recursionCount > 1 + LibC.LeaveCriticalSection(self) + false + else + true + end + else + false + end + end + + def unlock : Nil + # `owningThread` is declared as `LibC::HANDLE` for historical reasons, so + # the following comparison is correct + unless @cs.owningThread == LibC::HANDLE.new(LibC.GetCurrentThreadId) + raise RuntimeError.new "Attempt to unlock a mutex locked by another thread" + end + LibC.LeaveCriticalSection(self) + end + def synchronize - yield + lock + yield self + ensure + unlock + end + + def finalize + LibC.DeleteCriticalSection(self) + end + + def to_unsafe + pointerof(@cs) end end end diff --git a/src/gc/boehm.cr b/src/gc/boehm.cr index f9a93aef154b..325fa82f31b0 100644 --- a/src/gc/boehm.cr +++ b/src/gc/boehm.cr @@ -107,8 +107,11 @@ lib LibGC fun size = GC_size(addr : Void*) : LibC::SizeT - {% unless flag?(:win32) || flag?(:wasm32) %} - # Boehm GC requires to use GC_pthread_create and GC_pthread_join instead of pthread_create and pthread_join + # Boehm GC requires to use its own thread manipulation routines instead of pthread's or Win32's + {% if flag?(:win32) %} + fun beginthreadex = GC_beginthreadex(security : Void*, stack_size : LibC::UInt, start_address : Void* -> LibC::UInt, + arglist : Void*, initflag : LibC::UInt, thrdaddr : LibC::UInt*) : Void* + {% elsif !flag?(:wasm32) %} fun pthread_create = GC_pthread_create(thread : LibC::PthreadT*, attr : LibC::PthreadAttrT*, start : Void* -> Void*, arg : Void*) : LibC::Int fun pthread_join = GC_pthread_join(thread : LibC::PthreadT, value : Void**) : LibC::Int fun pthread_detach = GC_pthread_detach(thread : LibC::PthreadT) : LibC::Int @@ -241,7 +244,14 @@ module GC reclaimed_bytes_before_gc: stats.reclaimed_bytes_before_gc.to_u64!) end - {% unless flag?(:win32) %} + {% if flag?(:win32) %} + # :nodoc: + def self.beginthreadex(security : Void*, stack_size : LibC::UInt, start_address : Void* -> LibC::UInt, arglist : Void*, initflag : LibC::UInt, thrdaddr : LibC::UInt*) : LibC::HANDLE + ret = LibGC.beginthreadex(security, stack_size, start_address, arglist, initflag, thrdaddr) + raise RuntimeError.from_errno("GC_beginthreadex") if ret.null? + ret.as(LibC::HANDLE) + end + {% else %} # :nodoc: def self.pthread_create(thread : LibC::PthreadT*, attr : LibC::PthreadAttrT*, start : Void* -> Void*, arg : Void*) LibGC.pthread_create(thread, attr, start, arg) diff --git a/src/gc/none.cr b/src/gc/none.cr index c243a071d3b9..2c0530e7599d 100644 --- a/src/gc/none.cr +++ b/src/gc/none.cr @@ -1,3 +1,7 @@ +{% if flag?(:win32) %} + require "c/process" +{% end %} + module GC def self.init end @@ -65,7 +69,14 @@ module GC reclaimed_bytes_before_gc: 0) end - {% unless flag?(:win32) || flag?(:wasm32) %} + {% if flag?(:win32) %} + # :nodoc: + def self.beginthreadex(security : Void*, stack_size : LibC::UInt, start_address : Void* -> LibC::UInt, arglist : Void*, initflag : LibC::UInt, thrdaddr : LibC::UInt*) : LibC::HANDLE + ret = LibC._beginthreadex(security, stack_size, start_address, arglist, initflag, thrdaddr) + raise RuntimeError.from_errno("_beginthreadex") if ret.null? + ret.as(LibC::HANDLE) + end + {% elsif !flag?(:wasm32) %} # :nodoc: def self.pthread_create(thread : LibC::PthreadT*, attr : LibC::PthreadAttrT*, start : Void* -> Void*, arg : Void*) LibC.pthread_create(thread, attr, start, arg) diff --git a/src/lib_c/x86_64-windows-msvc/c/process.cr b/src/lib_c/x86_64-windows-msvc/c/process.cr new file mode 100644 index 000000000000..e567068fb1e7 --- /dev/null +++ b/src/lib_c/x86_64-windows-msvc/c/process.cr @@ -0,0 +1,5 @@ +require "lib_c" + +lib LibC + fun _beginthreadex(security : Void*, stack_size : UInt, start_address : Void* -> UInt, arglist : Void*, initflag : UInt, thrdaddr : UInt*) : Void* +end diff --git a/src/lib_c/x86_64-windows-msvc/c/processthreadsapi.cr b/src/lib_c/x86_64-windows-msvc/c/processthreadsapi.cr index 6d0336a37e16..441d9acac697 100644 --- a/src/lib_c/x86_64-windows-msvc/c/processthreadsapi.cr +++ b/src/lib_c/x86_64-windows-msvc/c/processthreadsapi.cr @@ -33,6 +33,7 @@ lib LibC end fun GetCurrentThread : HANDLE + fun GetCurrentThreadId : DWORD fun GetCurrentThreadStackLimits(lowLimit : ULONG_PTR*, highLimit : ULONG_PTR*) : Void fun GetCurrentProcess : HANDLE fun GetCurrentProcessId : DWORD @@ -46,6 +47,7 @@ lib LibC fun SetThreadStackGuarantee(stackSizeInBytes : DWORD*) : BOOL fun GetProcessTimes(hProcess : HANDLE, lpCreationTime : FILETIME*, lpExitTime : FILETIME*, lpKernelTime : FILETIME*, lpUserTime : FILETIME*) : BOOL + fun SwitchToThread : BOOL PROCESS_QUERY_INFORMATION = 0x0400 end diff --git a/src/lib_c/x86_64-windows-msvc/c/synchapi.cr b/src/lib_c/x86_64-windows-msvc/c/synchapi.cr index 23804d0f3aaf..e101b7f6284b 100644 --- a/src/lib_c/x86_64-windows-msvc/c/synchapi.cr +++ b/src/lib_c/x86_64-windows-msvc/c/synchapi.cr @@ -1,7 +1,35 @@ require "c/basetsd" require "c/int_safe" +require "c/winbase" +require "c/wtypesbase" lib LibC + # the meanings of these fields are documented not in the Win32 API docs but in + # https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/displaying-a-critical-section + struct CRITICAL_SECTION + debugInfo : Void* # PRTL_CRITICAL_SECTION_DEBUG + lockCount : LONG + recursionCount : LONG + owningThread : HANDLE + lockSemaphore : HANDLE + spinCount : UInt64 + end + + struct CONDITION_VARIABLE + ptr : Void* + end + + fun InitializeCriticalSectionAndSpinCount(lpCriticalSection : CRITICAL_SECTION*, dwSpinCount : DWORD) : BOOL + fun DeleteCriticalSection(lpCriticalSection : CRITICAL_SECTION*) + fun EnterCriticalSection(lpCriticalSection : CRITICAL_SECTION*) + fun TryEnterCriticalSection(lpCriticalSection : CRITICAL_SECTION*) : BOOL + fun LeaveCriticalSection(lpCriticalSection : CRITICAL_SECTION*) + + fun InitializeConditionVariable(conditionVariable : CONDITION_VARIABLE*) + fun SleepConditionVariableCS(conditionVariable : CONDITION_VARIABLE*, criticalSection : CRITICAL_SECTION*, dwMilliseconds : DWORD) : BOOL + fun WakeConditionVariable(conditionVariable : CONDITION_VARIABLE*) + fun WakeAllConditionVariable(conditionVariable : CONDITION_VARIABLE*) + fun Sleep(dwMilliseconds : DWORD) fun WaitForSingleObject(hHandle : HANDLE, dwMilliseconds : DWORD) : DWORD end diff --git a/src/lib_c/x86_64-windows-msvc/c/winbase.cr b/src/lib_c/x86_64-windows-msvc/c/winbase.cr index b213335b0d14..f8b27e4850df 100644 --- a/src/lib_c/x86_64-windows-msvc/c/winbase.cr +++ b/src/lib_c/x86_64-windows-msvc/c/winbase.cr @@ -31,6 +31,11 @@ lib LibC INFINITE = 0xFFFFFFFF + WAIT_OBJECT_0 = 0x00000000_u32 + WAIT_IO_COMPLETION = 0x000000C0_u32 + WAIT_TIMEOUT = 0x00000102_u32 + WAIT_FAILED = 0xFFFFFFFF_u32 + STARTF_USESTDHANDLES = 0x00000100 MOVEFILE_REPLACE_EXISTING = 0x1_u32 diff --git a/src/lib_c/x86_64-windows-msvc/c/winsock2.cr b/src/lib_c/x86_64-windows-msvc/c/winsock2.cr index 4cf807c18622..223c2366b072 100644 --- a/src/lib_c/x86_64-windows-msvc/c/winsock2.cr +++ b/src/lib_c/x86_64-windows-msvc/c/winsock2.cr @@ -1,6 +1,7 @@ require "./ws2def" require "./basetsd" require "./guiddef" +require "./winbase" @[Link("WS2_32")] lib LibC @@ -65,13 +66,8 @@ lib LibC WSA_INVALID_EVENT = Pointer(WSAEVENT).null WSA_MAXIMUM_WAIT_EVENTS = MAXIMUM_WAIT_OBJECTS WSA_WAIT_FAILED = WAIT_FAILED - STATUS_WAIT_0 = 0_i64 - WAIT_OBJECT_0 = ((STATUS_WAIT_0) + 0) WSA_WAIT_EVENT_0 = WAIT_OBJECT_0 - STATUS_USER_APC = 0xc0 - WAIT_IO_COMPLETION = STATUS_USER_APC WSA_WAIT_IO_COMPLETION = WAIT_IO_COMPLETION - WAIT_TIMEOUT = 258_i64 WSA_WAIT_TIMEOUT = WAIT_TIMEOUT WSA_INFINITE = INFINITE From eb1ebb4400e05497ca94ebc2fb66f24261b0bf08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20M=C3=BCller?= Date: Tue, 13 Dec 2022 11:36:02 +0100 Subject: [PATCH 26/41] Rename `Def#yields` to `Def#block_arity` (#12833) --- .../compiler/crystal/tools/doc/method_spec.cr | 2 +- spec/compiler/macro/macro_methods_spec.cr | 2 +- spec/compiler/parser/parser_spec.cr | 58 +++++++++---------- .../crystal/interpreter/multidispatch.cr | 2 +- src/compiler/crystal/macros/methods.cr | 2 +- .../crystal/semantic/abstract_def_checker.cr | 2 +- src/compiler/crystal/semantic/call.cr | 2 +- src/compiler/crystal/semantic/call_error.cr | 6 +- .../crystal/semantic/default_arguments.cr | 6 +- .../crystal/semantic/method_missing.cr | 2 +- src/compiler/crystal/semantic/new.cr | 10 ++-- .../crystal/semantic/type_guess_visitor.cr | 2 +- src/compiler/crystal/syntax/ast.cr | 10 ++-- src/compiler/crystal/syntax/parser.cr | 18 +++--- src/compiler/crystal/tools/context.cr | 2 +- src/compiler/crystal/tools/doc/method.cr | 4 +- src/compiler/crystal/tools/doc/to_json.cr | 3 +- src/compiler/crystal/types.cr | 2 +- 18 files changed, 69 insertions(+), 66 deletions(-) diff --git a/spec/compiler/crystal/tools/doc/method_spec.cr b/spec/compiler/crystal/tools/doc/method_spec.cr index 7a605c4c0ef2..6353115da23e 100644 --- a/spec/compiler/crystal/tools/doc/method_spec.cr +++ b/spec/compiler/crystal/tools/doc/method_spec.cr @@ -72,7 +72,7 @@ describe Doc::Method do generator = Doc::Generator.new program, ["."] doc_type = Doc::Type.new generator, program - a_def = Def.new "foo", yields: 1 + a_def = Def.new "foo", block_arity: 1 doc_method = Doc::Method.new generator, doc_type, a_def, false assert_args_to_s(doc_method, "(&)") end diff --git a/spec/compiler/macro/macro_methods_spec.cr b/spec/compiler/macro/macro_methods_spec.cr index d711b93ccd2e..562185f0ed8e 100644 --- a/spec/compiler/macro/macro_methods_spec.cr +++ b/spec/compiler/macro/macro_methods_spec.cr @@ -2337,7 +2337,7 @@ module Crystal end it "executes accepts_block?" do - assert_macro %({{x.accepts_block?}}), "true", {x: Def.new("some_def", ["x".arg, "y".arg], yields: 1)} + assert_macro %({{x.accepts_block?}}), "true", {x: Def.new("some_def", ["x".arg, "y".arg], block_arity: 1)} assert_macro %({{x.accepts_block?}}), "false", {x: Def.new("some_def")} end diff --git a/spec/compiler/parser/parser_spec.cr b/spec/compiler/parser/parser_spec.cr index b11874676354..3f0e70358a52 100644 --- a/spec/compiler/parser/parser_spec.cr +++ b/spec/compiler/parser/parser_spec.cr @@ -299,44 +299,44 @@ module Crystal it_parses "def foo(var : Char[N]); end", Def.new("foo", [Arg.new("var", restriction: "Char".static_array_of("N".path))]) it_parses "def foo(var : Int32 = 1); end", Def.new("foo", [Arg.new("var", 1.int32, "Int32".path)]) it_parses "def foo(var : Int32 -> = 1); end", Def.new("foo", [Arg.new("var", 1.int32, ProcNotation.new(["Int32".path] of ASTNode))]) - it_parses "def foo; yield; end", Def.new("foo", body: Yield.new, yields: 0) - it_parses "def foo; yield 1; end", Def.new("foo", body: Yield.new([1.int32] of ASTNode), yields: 1) - it_parses "def foo; yield 1; yield; end", Def.new("foo", body: [Yield.new([1.int32] of ASTNode), Yield.new] of ASTNode, yields: 1) - it_parses "def foo; yield(1); end", Def.new("foo", body: [Yield.new([1.int32] of ASTNode, has_parentheses: true)] of ASTNode, yields: 1) + it_parses "def foo; yield; end", Def.new("foo", body: Yield.new, block_arity: 0) + it_parses "def foo; yield 1; end", Def.new("foo", body: Yield.new([1.int32] of ASTNode), block_arity: 1) + it_parses "def foo; yield 1; yield; end", Def.new("foo", body: [Yield.new([1.int32] of ASTNode), Yield.new] of ASTNode, block_arity: 1) + it_parses "def foo; yield(1); end", Def.new("foo", body: [Yield.new([1.int32] of ASTNode, has_parentheses: true)] of ASTNode, block_arity: 1) it_parses "def foo(a, b = a); end", Def.new("foo", [Arg.new("a"), Arg.new("b", "a".var)]) - it_parses "def foo(&block); end", Def.new("foo", block_arg: Arg.new("block"), yields: 0) - it_parses "def foo(&); end", Def.new("foo", block_arg: Arg.new(""), yields: 0) - it_parses "def foo(&\n); end", Def.new("foo", block_arg: Arg.new(""), yields: 0) - it_parses "def foo(a, &block); end", Def.new("foo", [Arg.new("a")], block_arg: Arg.new("block"), yields: 0) - it_parses "def foo(a, &block : Int -> Double); end", Def.new("foo", [Arg.new("a")], block_arg: Arg.new("block", restriction: ProcNotation.new(["Int".path] of ASTNode, "Double".path)), yields: 1) - it_parses "def foo(a, & : Int -> Double); end", Def.new("foo", [Arg.new("a")], block_arg: Arg.new("", restriction: ProcNotation.new(["Int".path] of ASTNode, "Double".path)), yields: 1) - it_parses "def foo(a, &block : Int, Float -> Double); end", Def.new("foo", [Arg.new("a")], block_arg: Arg.new("block", restriction: ProcNotation.new(["Int".path, "Float".path] of ASTNode, "Double".path)), yields: 2) - it_parses "def foo(a, &block : Int, self -> Double); end", Def.new("foo", [Arg.new("a")], block_arg: Arg.new("block", restriction: ProcNotation.new(["Int".path, Self.new] of ASTNode, "Double".path)), yields: 2) - it_parses "def foo(a, &block : -> Double); end", Def.new("foo", [Arg.new("a")], block_arg: Arg.new("block", restriction: ProcNotation.new(nil, "Double".path)), yields: 0) - it_parses "def foo(a, &block : Int -> ); end", Def.new("foo", [Arg.new("a")], block_arg: Arg.new("block", restriction: ProcNotation.new(["Int".path] of ASTNode)), yields: 1) - it_parses "def foo(a, &block : self -> self); end", Def.new("foo", [Arg.new("a")], block_arg: Arg.new("block", restriction: ProcNotation.new([Self.new] of ASTNode, Self.new)), yields: 1) - it_parses "def foo(a, &block : Foo); end", Def.new("foo", [Arg.new("a")], block_arg: Arg.new("block", restriction: Path.new("Foo")), yields: 0) - it_parses "def foo; with a yield; end", Def.new("foo", body: Yield.new(scope: "a".call), yields: 1) - it_parses "def foo; with a yield 1; end", Def.new("foo", body: Yield.new([1.int32] of ASTNode, "a".call), yields: 1) - it_parses "def foo; a = 1; with a yield a; end", Def.new("foo", body: [Assign.new("a".var, 1.int32), Yield.new(["a".var] of ASTNode, "a".var)] of ASTNode, yields: 1) + it_parses "def foo(&block); end", Def.new("foo", block_arg: Arg.new("block"), block_arity: 0) + it_parses "def foo(&); end", Def.new("foo", block_arg: Arg.new(""), block_arity: 0) + it_parses "def foo(&\n); end", Def.new("foo", block_arg: Arg.new(""), block_arity: 0) + it_parses "def foo(a, &block); end", Def.new("foo", [Arg.new("a")], block_arg: Arg.new("block"), block_arity: 0) + it_parses "def foo(a, &block : Int -> Double); end", Def.new("foo", [Arg.new("a")], block_arg: Arg.new("block", restriction: ProcNotation.new(["Int".path] of ASTNode, "Double".path)), block_arity: 1) + it_parses "def foo(a, & : Int -> Double); end", Def.new("foo", [Arg.new("a")], block_arg: Arg.new("", restriction: ProcNotation.new(["Int".path] of ASTNode, "Double".path)), block_arity: 1) + it_parses "def foo(a, &block : Int, Float -> Double); end", Def.new("foo", [Arg.new("a")], block_arg: Arg.new("block", restriction: ProcNotation.new(["Int".path, "Float".path] of ASTNode, "Double".path)), block_arity: 2) + it_parses "def foo(a, &block : Int, self -> Double); end", Def.new("foo", [Arg.new("a")], block_arg: Arg.new("block", restriction: ProcNotation.new(["Int".path, Self.new] of ASTNode, "Double".path)), block_arity: 2) + it_parses "def foo(a, &block : -> Double); end", Def.new("foo", [Arg.new("a")], block_arg: Arg.new("block", restriction: ProcNotation.new(nil, "Double".path)), block_arity: 0) + it_parses "def foo(a, &block : Int -> ); end", Def.new("foo", [Arg.new("a")], block_arg: Arg.new("block", restriction: ProcNotation.new(["Int".path] of ASTNode)), block_arity: 1) + it_parses "def foo(a, &block : self -> self); end", Def.new("foo", [Arg.new("a")], block_arg: Arg.new("block", restriction: ProcNotation.new([Self.new] of ASTNode, Self.new)), block_arity: 1) + it_parses "def foo(a, &block : Foo); end", Def.new("foo", [Arg.new("a")], block_arg: Arg.new("block", restriction: Path.new("Foo")), block_arity: 0) + it_parses "def foo; with a yield; end", Def.new("foo", body: Yield.new(scope: "a".call), block_arity: 1) + it_parses "def foo; with a yield 1; end", Def.new("foo", body: Yield.new([1.int32] of ASTNode, "a".call), block_arity: 1) + it_parses "def foo; a = 1; with a yield a; end", Def.new("foo", body: [Assign.new("a".var, 1.int32), Yield.new(["a".var] of ASTNode, "a".var)] of ASTNode, block_arity: 1) it_parses "def foo(@var); end", Def.new("foo", [Arg.new("var")], [Assign.new("@var".instance_var, "var".var)] of ASTNode) it_parses "def foo(@var); 1; end", Def.new("foo", [Arg.new("var")], [Assign.new("@var".instance_var, "var".var), 1.int32] of ASTNode) it_parses "def foo(@var = 1); 1; end", Def.new("foo", [Arg.new("var", 1.int32)], [Assign.new("@var".instance_var, "var".var), 1.int32] of ASTNode) it_parses "def foo(@@var); end", Def.new("foo", [Arg.new("var")], [Assign.new("@@var".class_var, "var".var)] of ASTNode) it_parses "def foo(@@var); 1; end", Def.new("foo", [Arg.new("var")], [Assign.new("@@var".class_var, "var".var), 1.int32] of ASTNode) it_parses "def foo(@@var = 1); 1; end", Def.new("foo", [Arg.new("var", 1.int32)], [Assign.new("@@var".class_var, "var".var), 1.int32] of ASTNode) - it_parses "def foo(&@block); end", Def.new("foo", body: Assign.new("@block".instance_var, "block".var), block_arg: Arg.new("block"), yields: 0) + it_parses "def foo(&@block); end", Def.new("foo", body: Assign.new("@block".instance_var, "block".var), block_arg: Arg.new("block"), block_arity: 0) # Defs with annotated parameters it_parses "def foo(@[Foo] var); end", Def.new("foo", ["var".arg(annotations: ["Foo".ann])]) it_parses "def foo(@[Foo] outer inner); end", Def.new("foo", ["inner".arg(annotations: ["Foo".ann], external_name: "outer")]) it_parses "def foo(@[Foo] var); end", Def.new("foo", ["var".arg(annotations: ["Foo".ann])]) it_parses "def foo(a, @[Foo] var); end", Def.new("foo", ["a".arg, "var".arg(annotations: ["Foo".ann])]) - it_parses "def foo(a, @[Foo] &block); end", Def.new("foo", ["a".arg], block_arg: "block".arg(annotations: ["Foo".ann]), yields: 0) + it_parses "def foo(a, @[Foo] &block); end", Def.new("foo", ["a".arg], block_arg: "block".arg(annotations: ["Foo".ann]), block_arity: 0) it_parses "def foo(@[Foo] @var); end", Def.new("foo", ["var".arg(annotations: ["Foo".ann])], [Assign.new("@var".instance_var, "var".var)] of ASTNode) it_parses "def foo(@[Foo] var : Int32); end", Def.new("foo", ["var".arg(restriction: "Int32".path, annotations: ["Foo".ann])]) it_parses "def foo(@[Foo] @[Bar] var : Int32); end", Def.new("foo", ["var".arg(restriction: "Int32".path, annotations: ["Foo".ann, "Bar".ann])]) - it_parses "def foo(@[Foo] &@block); end", Def.new("foo", body: Assign.new("@block".instance_var, "block".var), block_arg: "block".arg(annotations: ["Foo".ann]), yields: 0) + it_parses "def foo(@[Foo] &@block); end", Def.new("foo", body: Assign.new("@block".instance_var, "block".var), block_arg: "block".arg(annotations: ["Foo".ann]), block_arity: 0) it_parses "def foo(@[Foo] *args); end", Def.new("foo", args: ["args".arg(annotations: ["Foo".ann])], splat_index: 0) it_parses "def foo(@[Foo] **args); end", Def.new("foo", double_splat: "args".arg(annotations: ["Foo".ann])) it_parses <<-CRYSTAL, Def.new("foo", ["id".arg(restriction: "Int32".path, annotations: ["Foo".ann]), "name".arg(restriction: "String".path, annotations: ["Bar".ann])]) @@ -347,24 +347,24 @@ module Crystal ); end CRYSTAL - it_parses "def foo(\n&block\n); end", Def.new("foo", block_arg: Arg.new("block"), yields: 0) - it_parses "def foo(&block :\n Int ->); end", Def.new("foo", block_arg: Arg.new("block", restriction: ProcNotation.new(["Int".path] of ASTNode)), yields: 1) - it_parses "def foo(&block : Int ->\n); end", Def.new("foo", block_arg: Arg.new("block", restriction: ProcNotation.new(["Int".path] of ASTNode)), yields: 1) + it_parses "def foo(\n&block\n); end", Def.new("foo", block_arg: Arg.new("block"), block_arity: 0) + it_parses "def foo(&block :\n Int ->); end", Def.new("foo", block_arg: Arg.new("block", restriction: ProcNotation.new(["Int".path] of ASTNode)), block_arity: 1) + it_parses "def foo(&block : Int ->\n); end", Def.new("foo", block_arg: Arg.new("block", restriction: ProcNotation.new(["Int".path] of ASTNode)), block_arity: 1) - it_parses "def foo(a, &block : *Int -> ); end", Def.new("foo", [Arg.new("a")], block_arg: Arg.new("block", restriction: ProcNotation.new(["Int".path.splat] of ASTNode)), yields: 1) + it_parses "def foo(a, &block : *Int -> ); end", Def.new("foo", [Arg.new("a")], block_arg: Arg.new("block", restriction: ProcNotation.new(["Int".path.splat] of ASTNode)), block_arity: 1) it_parses "def foo(x, *args, y = 2); 1; end", Def.new("foo", args: ["x".arg, "args".arg, Arg.new("y", default_value: 2.int32)], body: 1.int32, splat_index: 1) it_parses "def foo(x, *args, y = 2, w, z = 3); 1; end", Def.new("foo", args: ["x".arg, "args".arg, Arg.new("y", default_value: 2.int32), "w".arg, Arg.new("z", default_value: 3.int32)], body: 1.int32, splat_index: 1) it_parses "def foo(x, *, y); 1; end", Def.new("foo", args: ["x".arg, "".arg, "y".arg], body: 1.int32, splat_index: 1) assert_syntax_error "def foo(x, *); 1; end", "named parameters must follow bare *" - it_parses "def foo(x, *, y, &); 1; end", Def.new("foo", args: ["x".arg, "".arg, "y".arg], body: 1.int32, splat_index: 1, block_arg: Arg.new(""), yields: 0) + it_parses "def foo(x, *, y, &); 1; end", Def.new("foo", args: ["x".arg, "".arg, "y".arg], body: 1.int32, splat_index: 1, block_arg: Arg.new(""), block_arity: 0) assert_syntax_error "def foo(var = 1 : Int32); end", "the syntax for a parameter with a default value V and type T is `param : T = V`" assert_syntax_error "def foo(var = x : Int); end", "the syntax for a parameter with a default value V and type T is `param : T = V`" it_parses "def foo(**args)\n1\nend", Def.new("foo", body: 1.int32, double_splat: "args".arg) it_parses "def foo(x, **args)\n1\nend", Def.new("foo", body: 1.int32, args: ["x".arg], double_splat: "args".arg) - it_parses "def foo(x, **args, &block)\n1\nend", Def.new("foo", body: 1.int32, args: ["x".arg], double_splat: "args".arg, block_arg: "block".arg, yields: 0) + it_parses "def foo(x, **args, &block)\n1\nend", Def.new("foo", body: 1.int32, args: ["x".arg], double_splat: "args".arg, block_arg: "block".arg, block_arity: 0) it_parses "def foo(**args)\nargs\nend", Def.new("foo", body: "args".var, double_splat: "args".arg) it_parses "def foo(x = 1, **args)\n1\nend", Def.new("foo", body: 1.int32, args: [Arg.new("x", default_value: 1.int32)], double_splat: "args".arg) it_parses "def foo(**args : Foo)\n1\nend", Def.new("foo", body: 1.int32, double_splat: Arg.new("args", restriction: "Foo".path)) @@ -2411,7 +2411,7 @@ module Crystal it "doesn't override yield with macro yield" do parser = Parser.new("def foo; yield 1; {% begin %} yield 1 {% end %}; end") a_def = parser.parse.as(Def) - a_def.yields.should eq(1) + a_def.block_arity.should eq(1) end it "correctly computes line number after `\\{%\n` (#9857)" do diff --git a/src/compiler/crystal/interpreter/multidispatch.cr b/src/compiler/crystal/interpreter/multidispatch.cr index 5e74aee2bbe8..005c67dca47e 100644 --- a/src/compiler/crystal/interpreter/multidispatch.cr +++ b/src/compiler/crystal/interpreter/multidispatch.cr @@ -133,7 +133,7 @@ module Crystal::Repl::Multidispatch a_def.uses_block_arg = true else a_def.block_arg = Arg.new("") - a_def.yields = block.args.size + a_def.block_arity = block.args.size end end diff --git a/src/compiler/crystal/macros/methods.cr b/src/compiler/crystal/macros/methods.cr index aa6a40cf8138..2ecdd2c141c8 100644 --- a/src/compiler/crystal/macros/methods.cr +++ b/src/compiler/crystal/macros/methods.cr @@ -1374,7 +1374,7 @@ module Crystal when "block_arg" interpret_check_args { @block_arg || Nop.new } when "accepts_block?" - interpret_check_args { BoolLiteral.new(@yields != nil) } + interpret_check_args { BoolLiteral.new(@block_arity != nil) } when "return_type" interpret_check_args { @return_type || Nop.new } when "free_vars" diff --git a/src/compiler/crystal/semantic/abstract_def_checker.cr b/src/compiler/crystal/semantic/abstract_def_checker.cr index c2357cff7c65..2a7ccdc05d2a 100644 --- a/src/compiler/crystal/semantic/abstract_def_checker.cr +++ b/src/compiler/crystal/semantic/abstract_def_checker.cr @@ -140,7 +140,7 @@ class Crystal::AbstractDefChecker def implements?(target_type : Type, t1 : Type, m1 : Def, free_vars1, t2 : Type, m2 : Def, free_vars2) return false if m1.abstract? return false unless m1.name == m2.name - return false unless m1.yields == m2.yields + return false unless m1.block_arity == m2.block_arity m1_args, m1_kargs = def_arg_ranges(m1) m2_args, m2_kargs = def_arg_ranges(m2) diff --git a/src/compiler/crystal/semantic/call.cr b/src/compiler/crystal/semantic/call.cr index e3b0dcf47012..314db053c75f 100644 --- a/src/compiler/crystal/semantic/call.cr +++ b/src/compiler/crystal/semantic/call.cr @@ -789,7 +789,7 @@ class Crystal::Call def match_block_arg(match) block_arg = match.def.block_arg return nil, nil unless block_arg - return nil, nil unless match.def.yields || match.def.uses_block_arg? + return nil, nil unless match.def.block_arity || match.def.uses_block_arg? yield_vars = nil block_arg_type = nil diff --git a/src/compiler/crystal/semantic/call_error.cr b/src/compiler/crystal/semantic/call_error.cr index 0271724b5b0a..2984ed5fd4c2 100644 --- a/src/compiler/crystal/semantic/call_error.cr +++ b/src/compiler/crystal/semantic/call_error.cr @@ -405,7 +405,7 @@ class Crystal::Call extra_types : Array(Type)? private def compute_call_error_reason(owner, a_def, arg_types, named_args_types) - if (block && !a_def.yields) || (!block && a_def.yields) + if (block && !a_def.block_arity) || (!block && a_def.block_arity) return BlockMismatch.new end @@ -660,7 +660,7 @@ class Crystal::Call all_arguments_sizes = [] of Int32 min_splat = Int32::MAX defs.each do |a_def| - next if (block && !a_def.yields) || (!block && a_def.yields) + next if (block && !a_def.block_arity) || (!block && a_def.block_arity) min_size, max_size = a_def.min_max_args_sizes if max_size == Int32::MAX @@ -869,7 +869,7 @@ class Crystal::Call printed = true end - if a_def.yields + if a_def.block_arity str << ", " if printed str << '&' if block_arg = a_def.block_arg diff --git a/src/compiler/crystal/semantic/default_arguments.cr b/src/compiler/crystal/semantic/default_arguments.cr index a4f9dcc67e4d..013caab3c3aa 100644 --- a/src/compiler/crystal/semantic/default_arguments.cr +++ b/src/compiler/crystal/semantic/default_arguments.cr @@ -34,7 +34,7 @@ class Crystal::Def end end - retain_body = yields || splat_index || double_splat || assigns_special_var? || macro_def? || args.any? { |arg| arg.default_value && arg.restriction } + retain_body = block_arity || splat_index || double_splat || assigns_special_var? || macro_def? || args.any? { |arg| arg.default_value && arg.restriction } splat_index = self.splat_index double_splat = self.double_splat @@ -94,13 +94,13 @@ class Crystal::Def new_name = name end - expansion = Def.new(new_name, new_args, nil, receiver.clone, block_arg.clone, return_type.clone, macro_def?, yields).at(self) + expansion = Def.new(new_name, new_args, nil, receiver.clone, block_arg.clone, return_type.clone, macro_def?, block_arity).at(self) expansion.args.each { |arg| arg.default_value = nil } expansion.calls_super = calls_super? expansion.calls_initialize = calls_initialize? expansion.calls_previous_def = calls_previous_def? expansion.uses_block_arg = uses_block_arg? - expansion.yields = yields + expansion.block_arity = block_arity expansion.raises = raises? expansion.free_vars = free_vars expansion.annotations = annotations diff --git a/src/compiler/crystal/semantic/method_missing.cr b/src/compiler/crystal/semantic/method_missing.cr index b48af9b6d4a2..54a15355f712 100644 --- a/src/compiler/crystal/semantic/method_missing.cr +++ b/src/compiler/crystal/semantic/method_missing.cr @@ -93,7 +93,7 @@ module Crystal end a_def.body = generated_nodes - a_def.yields = block.try &.args.size + a_def.block_arity = block.try &.args.size end owner = self diff --git a/src/compiler/crystal/semantic/new.cr b/src/compiler/crystal/semantic/new.cr index 91943fab94f9..de8ae55312a0 100644 --- a/src/compiler/crystal/semantic/new.cr +++ b/src/compiler/crystal/semantic/new.cr @@ -73,7 +73,7 @@ module Crystal inherits_from_generic = type.ancestors.any?(GenericClassInstanceType) if is_generic || inherits_from_generic has_default_self_new = self_new_methods.any? do |a_def| - a_def.args.empty? && !a_def.yields + a_def.args.empty? && !a_def.block_arity end # For a generic class type we need to define `new` even @@ -92,7 +92,7 @@ module Crystal initialize_methods.each do |initialize| # If the type has `self.new()`, don't override it - if initialize.args.empty? && !initialize.yields && has_default_self_new + if initialize.args.empty? && !initialize.block_arity && has_default_self_new next end @@ -137,7 +137,7 @@ module Crystal new_def = Def.new("new", def_args, Nop.new).at(self) new_def.splat_index = splat_index new_def.double_splat = double_splat.clone - new_def.yields = yields + new_def.block_arity = block_arity new_def.visibility = visibility new_def.new = true new_def.doc = doc @@ -204,7 +204,7 @@ module Crystal # If the initialize yields, call it with a block # that yields those arguments. - if block_args_count = self.yields + if block_args_count = self.block_arity block_args = Array.new(block_args_count) { |i| Var.new("_arg#{i}") } vars = Array.new(block_args_count) { |i| Var.new("_arg#{i}").at(self).as(ASTNode) } init.block = Block.new(block_args, Yield.new(vars).at(self)).at(self) @@ -289,7 +289,7 @@ module Crystal end expansion = Def.new(name, def_args, Nop.new, splat_index: splat_index).at(self) - expansion.yields = yields + expansion.block_arity = block_arity expansion.visibility = visibility expansion.annotations = annotations diff --git a/src/compiler/crystal/semantic/type_guess_visitor.cr b/src/compiler/crystal/semantic/type_guess_visitor.cr index 64c84ebcb888..4eb13804f64d 100644 --- a/src/compiler/crystal/semantic/type_guess_visitor.cr +++ b/src/compiler/crystal/semantic/type_guess_visitor.cr @@ -769,7 +769,7 @@ module Crystal defs = metaclass.lookup_defs(node.name) defs = defs.select do |a_def| - a_def_has_block = !!a_def.yields + a_def_has_block = !!a_def.block_arity call_has_block = !!(node.block || node.block_arg) next unless a_def_has_block == call_has_block diff --git a/src/compiler/crystal/syntax/ast.cr b/src/compiler/crystal/syntax/ast.cr index c434869d36a9..cf8339ceec85 100644 --- a/src/compiler/crystal/syntax/ast.cr +++ b/src/compiler/crystal/syntax/ast.cr @@ -1056,7 +1056,9 @@ module Crystal property body : ASTNode property block_arg : Arg? property return_type : ASTNode? - property yields : Int32? + # Number of block arguments accepted by this method. + # `nil` if it does not receive a block. + property block_arity : Int32? property name_location : Location? property splat_index : Int32? property doc : String? @@ -1070,7 +1072,7 @@ module Crystal property? assigns_special_var = false property? abstract : Bool - def initialize(@name, @args = [] of Arg, body = nil, @receiver = nil, @block_arg = nil, @return_type = nil, @macro_def = false, @yields = nil, @abstract = false, @splat_index = nil, @double_splat = nil, @free_vars = nil) + def initialize(@name, @args = [] of Arg, body = nil, @receiver = nil, @block_arg = nil, @return_type = nil, @macro_def = false, @block_arity = nil, @abstract = false, @splat_index = nil, @double_splat = nil, @free_vars = nil) @body = Expressions.from body end @@ -1088,7 +1090,7 @@ module Crystal end def clone_without_location - a_def = Def.new(@name, @args.clone, @body.clone, @receiver.clone, @block_arg.clone, @return_type.clone, @macro_def, @yields, @abstract, @splat_index, @double_splat.clone, @free_vars) + a_def = Def.new(@name, @args.clone, @body.clone, @receiver.clone, @block_arg.clone, @return_type.clone, @macro_def, @block_arity, @abstract, @splat_index, @double_splat.clone, @free_vars) a_def.calls_super = calls_super? a_def.calls_initialize = calls_initialize? a_def.calls_previous_def = calls_previous_def? @@ -1099,7 +1101,7 @@ module Crystal a_def end - def_equals_and_hash @name, @args, @body, @receiver, @block_arg, @return_type, @macro_def, @yields, @abstract, @splat_index, @double_splat + def_equals_and_hash @name, @args, @body, @receiver, @block_arg, @return_type, @macro_def, @block_arity, @abstract, @splat_index, @double_splat end class Macro < ASTNode diff --git a/src/compiler/crystal/syntax/parser.cr b/src/compiler/crystal/syntax/parser.cr index 5b772962c98d..df754e7dd7e8 100644 --- a/src/compiler/crystal/syntax/parser.cr +++ b/src/compiler/crystal/syntax/parser.cr @@ -3170,7 +3170,7 @@ module Crystal next_macro_token macro_state, skip_whitespace macro_state = @token.macro_state if macro_state.yields - @yields ||= 0 + @block_arity ||= 0 end skip_whitespace = false @@ -3501,7 +3501,7 @@ module Crystal consume_def_or_macro_name receiver = nil - @yields = nil + @block_arity = nil name_location = @token.location receiver_location = @token.location end_location = token_end_location @@ -3682,7 +3682,7 @@ module Crystal @def_nest -= 1 @doc_enabled = !!@wants_doc - node = Def.new name, params, body, receiver, block_param, return_type, @is_macro_def, @yields, is_abstract, splat_index, double_splat: double_splat, free_vars: free_vars + node = Def.new name, params, body, receiver, block_param, return_type, @is_macro_def, @block_arity, is_abstract, splat_index, double_splat: double_splat, free_vars: free_vars node.name_location = name_location set_visibility node node.end_location = end_location @@ -3723,9 +3723,9 @@ module Crystal def compute_block_arg_yields(block_arg) block_arg_restriction = block_arg.restriction if block_arg_restriction.is_a?(ProcNotation) - @yields = block_arg_restriction.inputs.try(&.size) || 0 + @block_arity = block_arg_restriction.inputs.try(&.size) || 0 else - @yields = 0 + @block_arity = 0 end end @@ -5450,7 +5450,7 @@ module Crystal location = @token.location next_token_skip_space @stop_on_yield += 1 - @yields ||= 1 + @block_arity ||= 1 scope = parse_op_assign @stop_on_yield -= 1 skip_space @@ -5469,9 +5469,9 @@ module Crystal end_location = nil end - yields = (@yields ||= 0) - if args && args.size > yields - @yields = args.size + block_arity = (@block_arity ||= 0) + if args && args.size > block_arity + @block_arity = args.size end Yield.new(args || [] of ASTNode, scope, !!call_args.try(&.has_parentheses)).at(location).at_end(end_location) diff --git a/src/compiler/crystal/tools/context.cr b/src/compiler/crystal/tools/context.cr index 0d7a0a98d55a..7bf49397fd7c 100644 --- a/src/compiler/crystal/tools/context.cr +++ b/src/compiler/crystal/tools/context.cr @@ -175,7 +175,7 @@ module Crystal def visit(node : Def) return false unless contains_target(node) - if @def_with_yield.nil? && !node.yields.nil? + if @def_with_yield.nil? && !node.block_arity.nil? @def_with_yield = node return false end diff --git a/src/compiler/crystal/tools/doc/method.cr b/src/compiler/crystal/tools/doc/method.cr index 44bebd389220..dee37cb6d1b8 100644 --- a/src/compiler/crystal/tools/doc/method.cr +++ b/src/compiler/crystal/tools/doc/method.cr @@ -241,7 +241,7 @@ class Crystal::Doc::Method io << ", " if printed io << '&' arg_to_html block_arg, io, html: html - elsif @def.yields + elsif @def.block_arity io << ", " if printed io << '&' end @@ -314,7 +314,7 @@ class Crystal::Doc::Method end def has_args? - !@def.args.empty? || @def.double_splat || @def.block_arg || @def.yields + !@def.args.empty? || @def.double_splat || @def.block_arg || @def.block_arity end def to_json(builder : JSON::Builder) diff --git a/src/compiler/crystal/tools/doc/to_json.cr b/src/compiler/crystal/tools/doc/to_json.cr index 846fb39d1700..aa1ead27e275 100644 --- a/src/compiler/crystal/tools/doc/to_json.cr +++ b/src/compiler/crystal/tools/doc/to_json.cr @@ -17,7 +17,8 @@ class Crystal::Def builder.field "args", args unless args.empty? builder.field "double_splat", double_splat unless double_splat.nil? builder.field "splat_index", splat_index unless splat_index.nil? - builder.field "yields", yields unless yields.nil? + builder.field "yields", block_arity unless block_arity.nil? + builder.field "block_arity", block_arity unless block_arity.nil? builder.field "block_arg", block_arg unless block_arg.nil? builder.field "return_type", return_type.to_s unless return_type.nil? builder.field "visibility", visibility.to_s diff --git a/src/compiler/crystal/types.cr b/src/compiler/crystal/types.cr index d60c5fbff879..a1cdb71c9d7f 100644 --- a/src/compiler/crystal/types.cr +++ b/src/compiler/crystal/types.cr @@ -843,7 +843,7 @@ module Crystal def : Def do def self.new(a_def : Def) min_size, max_size = a_def.min_max_args_sizes - new min_size, max_size, !!a_def.yields, a_def + new min_size, max_size, !!a_def.block_arity, a_def end end From c8f1af546abe005bb70cdda325b4037da2fb7511 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20M=C3=BCller?= Date: Wed, 14 Dec 2022 12:07:01 +0100 Subject: [PATCH 27/41] Extract internal Regex API for PCRE backend (#12802) --- src/regex.cr | 90 ++++------------------- src/regex/engine.cr | 4 ++ src/regex/lib_pcre.cr | 10 +++ src/regex/match_data.cr | 65 ++++------------- src/regex/pcre.cr | 156 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 199 insertions(+), 126 deletions(-) create mode 100644 src/regex/engine.cr create mode 100644 src/regex/pcre.cr diff --git a/src/regex.cr b/src/regex.cr index b3ccd7bf0bec..136671e2f65c 100644 --- a/src/regex.cr +++ b/src/regex.cr @@ -1,4 +1,5 @@ -require "./regex/*" +require "./regex/engine" +require "./regex/match_data" # A `Regex` represents a regular expression, a pattern that describes the # contents of strings. A `Regex` can determine whether or not a string matches @@ -195,6 +196,8 @@ require "./regex/*" # `Hash` of `String` => `Int32`, and therefore requires named capture groups to have # unique names within a single `Regex`. class Regex + include Regex::Engine + # List of metacharacters that need to be escaped. # # See `Regex.needs_escape?` and `Regex.escape`. @@ -253,28 +256,8 @@ class Regex # options = Regex::Options::IGNORE_CASE | Regex::Options::EXTENDED # Regex.new("dog", options) # => /dog/ix # ``` - def initialize(source : String, @options : Options = Options::None) - # PCRE's pattern must have their null characters escaped - source = source.gsub('\u{0}', "\\0") - @source = source - - @re = LibPCRE.compile(@source, (options | Options::UTF_8 | Options::NO_UTF8_CHECK | Options::DUPNAMES | Options::UCP), out errptr, out erroffset, nil) - raise ArgumentError.new("#{String.new(errptr)} at #{erroffset}") if @re.null? - @extra = LibPCRE.study(@re, LibPCRE::STUDY_JIT_COMPILE, out studyerrptr) - if @extra.null? && studyerrptr - {% unless flag?(:interpreted) %} - LibPCRE.free.call @re.as(Void*) - {% end %} - raise ArgumentError.new("#{String.new(studyerrptr)}") - end - LibPCRE.full_info(@re, nil, LibPCRE::INFO_CAPTURECOUNT, out @captures) - end - - def finalize - LibPCRE.free_study @extra - {% unless flag?(:interpreted) %} - LibPCRE.free.call @re.as(Void*) - {% end %} + def self.new(source : String, options : Options = Options::None) + new(_source: source, _options: options) end # Determines Regex's source validity. If it is, `nil` is returned. @@ -285,15 +268,7 @@ class Regex # Regex.error?("(foo|bar") # => "missing ) at 8" # ``` def self.error?(source) : String? - re = LibPCRE.compile(source, (Options::UTF_8 | Options::NO_UTF8_CHECK | Options::DUPNAMES), out errptr, out erroffset, nil) - if re - {% unless flag?(:interpreted) %} - LibPCRE.free.call re.as(Void*) - {% end %} - nil - else - "#{String.new(errptr)} at #{erroffset}" - end + Engine.error_impl(source) end # Returns `true` if *char* need to be escaped, `false` otherwise. @@ -485,12 +460,10 @@ class Regex # ``` def match(str, pos = 0, options = Regex::Options::None) : MatchData? if byte_index = str.char_index_to_byte_index(pos) - match = match_at_byte_index(str, byte_index, options) + $~ = match_at_byte_index(str, byte_index, options) else - match = nil + $~ = nil end - - $~ = match end # Match at byte index. Matches a regular expression against `String` @@ -504,17 +477,11 @@ class Regex # /(.)(.)/.match_at_byte_index("クリスタル", 3).try &.[2] # => "ス" # ``` def match_at_byte_index(str, byte_index = 0, options = Regex::Options::None) : MatchData? - return ($~ = nil) if byte_index > str.bytesize - - ovector_size = (@captures + 1) * 3 - ovector = Pointer(Int32).malloc(ovector_size) - if internal_matches?(str, byte_index, options, ovector, ovector_size) - match = MatchData.new(self, @re, str, byte_index, ovector, @captures) + if byte_index > str.bytesize + $~ = nil else - match = nil + $~ = match_impl(str, byte_index, options) end - - $~ = match end # Match at character index. It behaves like `#match`, however it returns `Bool` value. @@ -540,14 +507,7 @@ class Regex def matches_at_byte_index?(str, byte_index = 0, options = Regex::Options::None) : Bool return false if byte_index > str.bytesize - internal_matches?(str, byte_index, options, nil, 0) - end - - # Calls `pcre_exec` C function, and handles returning value. - private def internal_matches?(str, byte_index, options, ovector, ovector_size) - ret = LibPCRE.exec(@re, @extra, str, str.bytesize, byte_index, (options | Options::NO_UTF8_CHECK), ovector, ovector_size) - # TODO: when `ret < -1`, it means PCRE error. It should handle correctly. - ret >= 0 + matches_impl(str, byte_index, options) end # Returns a `Hash` where the values are the names of capture groups and the @@ -561,26 +521,7 @@ class Regex # /(.)(?.)(.)(?.)(.)/.name_table # => {4 => "bar", 2 => "foo"} # ``` def name_table : Hash(Int32, String) - LibPCRE.full_info(@re, @extra, LibPCRE::INFO_NAMECOUNT, out name_count) - LibPCRE.full_info(@re, @extra, LibPCRE::INFO_NAMEENTRYSIZE, out name_entry_size) - table_pointer = Pointer(UInt8).null - LibPCRE.full_info(@re, @extra, LibPCRE::INFO_NAMETABLE, pointerof(table_pointer).as(Pointer(Int32))) - name_table = table_pointer.to_slice(name_entry_size*name_count) - - lookup = Hash(Int32, String).new - - name_count.times do |i| - capture_offset = i * name_entry_size - capture_number = ((name_table[capture_offset].to_u16 << 8)).to_i32 | name_table[capture_offset + 1] - - name_offset = capture_offset + 2 - checked = name_table[name_offset, name_entry_size - 3] - name = String.new(checked.to_unsafe) - - lookup[capture_number] = name - end - - lookup + name_table_impl end # Returns the number of (named & non-named) capture groups. @@ -592,8 +533,7 @@ class Regex # /(.)|(.)/.capture_count # => 2 # ``` def capture_count : Int32 - LibPCRE.full_info(@re, @extra, LibPCRE::INFO_CAPTURECOUNT, out capture_count) - capture_count + capture_count_impl end # Convert to `String` in subpattern format. Produces a `String` which can be diff --git a/src/regex/engine.cr b/src/regex/engine.cr new file mode 100644 index 000000000000..ad69e5d034bf --- /dev/null +++ b/src/regex/engine.cr @@ -0,0 +1,4 @@ +require "./pcre" + +# :nodoc: +alias Regex::Engine = PCRE diff --git a/src/regex/lib_pcre.cr b/src/regex/lib_pcre.cr index 2182153870f0..cf32142c5358 100644 --- a/src/regex/lib_pcre.cr +++ b/src/regex/lib_pcre.cr @@ -2,6 +2,16 @@ lib LibPCRE alias Int = LibC::Int + CASELESS = 0x00000001 + MULTILINE = 0x00000002 + DOTALL = 0x00000004 + EXTENDED = 0x00000008 + ANCHORED = 0x00000010 + UTF8 = 0x00000800 + NO_UTF8_CHECK = 0x00002000 + DUPNAMES = 0x00080000 + UCP = 0x20000000 + type Pcre = Void* type PcreExtra = Void* fun compile = pcre_compile(pattern : UInt8*, options : Int, errptr : UInt8**, erroffset : Int*, tableptr : Void*) : Pcre diff --git a/src/regex/match_data.cr b/src/regex/match_data.cr index 085c829073e8..949bce29e603 100644 --- a/src/regex/match_data.cr +++ b/src/regex/match_data.cr @@ -16,6 +16,8 @@ class Regex # starting from `1`, so that `0` can be used to refer to the entire regular # expression without needing to capture it explicitly. struct MatchData + include Engine::MatchData + # Returns the original regular expression. # # ``` @@ -39,10 +41,6 @@ class Regex # ``` getter string : String - # :nodoc: - def initialize(@regex : Regex, @code : LibPCRE::Pcre, @string : String, @pos : Int32, @ovector : Int32*, @group_size : Int32) - end - # Returns the number of elements in this match object. # # ``` @@ -109,10 +107,7 @@ class Regex # ``` def byte_begin(n = 0) : Int32 check_index_out_of_bounds n - n += size if n < 0 - value = @ovector[n * 2] - raise_capture_group_was_not_matched(n) if value < 0 - value + byte_range(n) { |normalized_n| raise_capture_group_was_not_matched(normalized_n) }.begin end # Returns the position of the next byte after the match. @@ -132,10 +127,7 @@ class Regex # ``` def byte_end(n = 0) : Int32 check_index_out_of_bounds n - n += size if n < 0 - value = @ovector[n * 2 + 1] - raise_capture_group_was_not_matched(n) if value < 0 - value + byte_range(n) { |normalized_n| raise_capture_group_was_not_matched(normalized_n) }.end end # Returns the match of the *n*th capture group, or `nil` if there isn't @@ -151,11 +143,8 @@ class Regex def []?(n : Int) : String? return unless valid_group?(n) - n += size if n < 0 - start = @ovector[n * 2] - finish = @ovector[n * 2 + 1] - return if start < 0 - @string.byte_slice(start, finish - start) + range = byte_range(n) { return nil } + @string.byte_slice(range.begin, range.end - range.begin) end # Returns the match of the *n*th capture group, or raises an `IndexError` @@ -167,11 +156,9 @@ class Regex # ``` def [](n : Int) : String check_index_out_of_bounds n - n += size if n < 0 - value = self[n]? - raise_capture_group_was_not_matched n if value.nil? - value + range = byte_range(n) { |normalized_n| raise_capture_group_was_not_matched(normalized_n) } + @string.byte_slice(range.begin, range.end - range.begin) end # Returns the match of the capture group named by *group_name*, or @@ -189,16 +176,7 @@ class Regex # "Crystal".match(/(?Cr).*(?al)/).not_nil!["ok"]? # => "al" # ``` def []?(group_name : String) : String? - max_start = -1 - match = nil - named_capture_number(group_name) do |n| - start = @ovector[n * 2] - if start > max_start - max_start = start - match = self[n]? - end - end - match + fetch_impl(group_name) { nil } end # Returns the match of the capture group named by *group_name*, or @@ -216,14 +194,13 @@ class Regex # "Crystal".match(/(?Cr).*(?al)/).not_nil!["ok"] # => "al" # ``` def [](group_name : String) : String - match = self[group_name]? - unless match - named_capture_number(group_name) do + fetch_impl(group_name) { |exists| + if exists raise KeyError.new("Capture group '#{group_name}' was not matched") + else + raise KeyError.new("Capture group '#{group_name}' does not exist") end - raise KeyError.new("Capture group '#{group_name}' does not exist") - end - match + } end # Returns all matches that are within the given range. @@ -249,20 +226,6 @@ class Regex Array(String).new(count) { |i| self[start + i] } end - private def named_capture_number(group_name) - name_entry_size = LibPCRE.get_stringtable_entries(@code, group_name, out first, out last) - return if name_entry_size < 0 - - while first <= last - capture_number = (first[0].to_u16 << 8) | first[1].to_u16 - yield capture_number - - first += name_entry_size - end - - nil - end - # Returns the part of the original string before the match. If the match # starts at the start of the string, returns the empty string. # diff --git a/src/regex/pcre.cr b/src/regex/pcre.cr new file mode 100644 index 000000000000..ff68509bed9e --- /dev/null +++ b/src/regex/pcre.cr @@ -0,0 +1,156 @@ +require "./lib_pcre" + +# :nodoc: +module Regex::PCRE + private def initialize(*, _source source, _options @options) + # PCRE's pattern must have their null characters escaped + source = source.gsub('\u{0}', "\\0") + @source = source + + @re = LibPCRE.compile(@source, pcre_options(options) | LibPCRE::UTF8 | LibPCRE::NO_UTF8_CHECK | LibPCRE::DUPNAMES | LibPCRE::UCP, out errptr, out erroffset, nil) + raise ArgumentError.new("#{String.new(errptr)} at #{erroffset}") if @re.null? + @extra = LibPCRE.study(@re, LibPCRE::STUDY_JIT_COMPILE, out studyerrptr) + if @extra.null? && studyerrptr + {% unless flag?(:interpreted) %} + LibPCRE.free.call @re.as(Void*) + {% end %} + raise ArgumentError.new("#{String.new(studyerrptr)}") + end + LibPCRE.full_info(@re, nil, LibPCRE::INFO_CAPTURECOUNT, out @captures) + end + + private def pcre_options(options) + flag = 0 + options.each do |option| + flag |= case option + when .ignore_case? then LibPCRE::CASELESS + when .multiline? then LibPCRE::DOTALL | LibPCRE::MULTILINE + when .extended? then LibPCRE::EXTENDED + when .anchored? then LibPCRE::ANCHORED + when .utf_8? then LibPCRE::UTF8 + when .no_utf8_check? then LibPCRE::NO_UTF8_CHECK + when .dupnames? then LibPCRE::DUPNAMES + when .ucp? then LibPCRE::UCP + else + # Unnamed values are explicitly used PCRE options, just pass them through: + option.value + end + end + flag + end + + def finalize + LibPCRE.free_study @extra + {% unless flag?(:interpreted) %} + LibPCRE.free.call @re.as(Void*) + {% end %} + end + + protected def self.error_impl(source) + re = LibPCRE.compile(source, LibPCRE::UTF8 | LibPCRE::NO_UTF8_CHECK | LibPCRE::DUPNAMES, out errptr, out erroffset, nil) + if re + {% unless flag?(:interpreted) %} + LibPCRE.free.call re.as(Void*) + {% end %} + nil + else + "#{String.new(errptr)} at #{erroffset}" + end + end + + private def name_table_impl + LibPCRE.full_info(@re, @extra, LibPCRE::INFO_NAMECOUNT, out name_count) + LibPCRE.full_info(@re, @extra, LibPCRE::INFO_NAMEENTRYSIZE, out name_entry_size) + table_pointer = Pointer(UInt8).null + LibPCRE.full_info(@re, @extra, LibPCRE::INFO_NAMETABLE, pointerof(table_pointer).as(Pointer(Int32))) + name_table = table_pointer.to_slice(name_entry_size*name_count) + + lookup = Hash(Int32, String).new + + name_count.times do |i| + capture_offset = i * name_entry_size + capture_number = ((name_table[capture_offset].to_u16 << 8)).to_i32 | name_table[capture_offset + 1] + + name_offset = capture_offset + 2 + checked = name_table[name_offset, name_entry_size - 3] + name = String.new(checked.to_unsafe) + + lookup[capture_number] = name + end + + lookup + end + + private def capture_count_impl + LibPCRE.full_info(@re, @extra, LibPCRE::INFO_CAPTURECOUNT, out capture_count) + capture_count + end + + private def match_impl(str, byte_index, options) + ovector_size = (@captures + 1) * 3 + ovector = Pointer(Int32).malloc(ovector_size) + if internal_matches?(str, byte_index, options, ovector, ovector_size) + Regex::MatchData.new(self, @re, str, byte_index, ovector, @captures) + end + end + + private def matches_impl(str, byte_index, options) + internal_matches?(str, byte_index, options, nil, 0) + end + + # Calls `pcre_exec` C function, and handles returning value. + private def internal_matches?(str, byte_index, options, ovector, ovector_size) + ret = LibPCRE.exec(@re, @extra, str, str.bytesize, byte_index, pcre_options(options) | LibPCRE::NO_UTF8_CHECK, ovector, ovector_size) + # TODO: when `ret < -1`, it means PCRE error. It should handle correctly. + ret >= 0 + end + + module MatchData + # :nodoc: + def initialize(@regex : ::Regex, @code : LibPCRE::Pcre, @string : String, @pos : Int32, @ovector : Int32*, @group_size : Int32) + end + + private def byte_range(n, &) + n += size if n < 0 + range = Range.new(@ovector[n * 2], @ovector[n * 2 + 1], exclusive: true) + if range.begin < 0 || range.end < 0 + yield n + else + range + end + end + + private def fetch_impl(group_name : String) + max_start = -1 + match = nil + exists = false + each_named_capture_number(group_name) do |n| + exists = true + start = byte_range(n) { nil }.try(&.begin) || next + if start > max_start + max_start = start + match = self[n]? + end + end + if match + match + else + yield exists + end + end + + private def each_named_capture_number(group_name) + name_entry_size = LibPCRE.get_stringtable_entries(@code, group_name, out first, out last) + return if name_entry_size < 0 + + while first <= last + capture_number = (first[0].to_u16 << 8) | first[1].to_u16 + yield capture_number + + first += name_entry_size + end + + nil + end + end +end From ce28902ad5d5b7d1a660146a04158330aafad8e7 Mon Sep 17 00:00:00 2001 From: Mike Robbins Date: Wed, 14 Dec 2022 06:07:33 -0500 Subject: [PATCH 28/41] Automatically cast Int to Float for `{JSON,YAML}::Any#as_f` (#12835) --- spec/std/json/any_spec.cr | 22 +++++++++++++++++ spec/std/json/serializable_spec.cr | 11 +++++++++ spec/std/yaml/any_spec.cr | 24 +++++++++++++++++++ src/json/any.cr | 38 +++++++++++++++++++++++------- src/yaml/any.cr | 38 +++++++++++++++++++++++------- 5 files changed, 117 insertions(+), 16 deletions(-) diff --git a/spec/std/json/any_spec.cr b/spec/std/json/any_spec.cr index 9758bbad0022..6601a53378bd 100644 --- a/spec/std/json/any_spec.cr +++ b/spec/std/json/any_spec.cr @@ -30,16 +30,38 @@ describe JSON::Any do it "gets float32" do JSON.parse("123.45").as_f32.should eq(123.45_f32) + expect_raises(TypeCastError) { JSON.parse("true").as_f32 } JSON.parse("123.45").as_f32?.should eq(123.45_f32) JSON.parse("true").as_f32?.should be_nil end + it "gets float32 from JSON integer (#8618)" do + value = JSON.parse("123").as_f32 + value.should eq(123.0) + value.should be_a(Float32) + + value = JSON.parse("123").as_f32? + value.should eq(123.0) + value.should be_a(Float32) + end + it "gets float64" do JSON.parse("123.45").as_f.should eq(123.45) + expect_raises(TypeCastError) { JSON.parse("true").as_f } JSON.parse("123.45").as_f?.should eq(123.45) JSON.parse("true").as_f?.should be_nil end + it "gets float64 from JSON integer (#8618)" do + value = JSON.parse("123").as_f + value.should eq(123.0) + value.should be_a(Float64) + + value = JSON.parse("123").as_f? + value.should eq(123.0) + value.should be_a(Float64) + end + it "gets string" do JSON.parse(%("hello")).as_s.should eq("hello") JSON.parse(%("hello")).as_s?.should eq("hello") diff --git a/spec/std/json/serializable_spec.cr b/spec/std/json/serializable_spec.cr index 2c2295d3f26b..cceb161c6f12 100644 --- a/spec/std/json/serializable_spec.cr +++ b/spec/std/json/serializable_spec.cr @@ -88,6 +88,12 @@ class JSONAttrWithBool property value : Bool end +class JSONAttrWithFloat + include JSON::Serializable + + property value : Float64 +end + class JSONAttrWithUUID include JSON::Serializable @@ -631,6 +637,11 @@ describe "JSON mapping" do json.value.should be_false end + it "parses JSON integer into a float property (#8618)" do + json = JSONAttrWithFloat.from_json(%({"value": 123})) + json.value.should eq(123.0) + end + it "parses UUID" do uuid = JSONAttrWithUUID.from_json(%({"value": "ba714f86-cac6-42c7-8956-bcf5105e1b81"})) uuid.should be_a(JSONAttrWithUUID) diff --git a/spec/std/yaml/any_spec.cr b/spec/std/yaml/any_spec.cr index e215ba5084e7..c2a030dcc0f4 100644 --- a/spec/std/yaml/any_spec.cr +++ b/spec/std/yaml/any_spec.cr @@ -65,6 +65,8 @@ describe YAML::Any do value.should eq(1.2_f32) value.should be_a(Float32) + expect_raises(TypeCastError) { YAML.parse("true").as_f32 } + value = YAML.parse("1.2").as_f32? value.should eq(1.2_f32) value.should be_a(Float32) @@ -73,11 +75,23 @@ describe YAML::Any do value.should be_nil end + it "gets float32 from JSON integer (#8618)" do + value = YAML.parse("123").as_f32 + value.should eq(123.0) + value.should be_a(Float32) + + value = YAML.parse("123").as_f32? + value.should eq(123.0) + value.should be_a(Float32) + end + it "gets float64" do value = YAML.parse("1.2").as_f value.should eq(1.2) value.should be_a(Float64) + expect_raises(TypeCastError) { YAML.parse("true").as_f } + value = YAML.parse("1.2").as_f? value.should eq(1.2) value.should be_a(Float64) @@ -86,6 +100,16 @@ describe YAML::Any do value.should be_nil end + it "gets float64 from JSON integer (#8618)" do + value = YAML.parse("123").as_f + value.should eq(123.0) + value.should be_a(Float64) + + value = YAML.parse("123").as_f? + value.should eq(123.0) + value.should be_a(Float64) + end + it "gets time" do value = YAML.parse("2010-01-02").as_time value.should eq(Time.utc(2010, 1, 2)) diff --git a/src/json/any.cr b/src/json/any.cr index 59a11b852c5d..d64b7859bc53 100644 --- a/src/json/any.cr +++ b/src/json/any.cr @@ -186,28 +186,50 @@ struct JSON::Any as_i64 if @raw.is_a?(Int64) end - # Checks that the underlying value is `Float`, and returns its value as an `Float64`. + # Checks that the underlying value is `Float` (or `Int`), and returns its value as an `Float64`. # Raises otherwise. def as_f : Float64 - @raw.as(Float64) + case raw = @raw + when Int + raw.to_f + else + raw.as(Float64) + end end - # Checks that the underlying value is `Float`, and returns its value as an `Float64`. + # Checks that the underlying value is `Float` (or `Int`), and returns its value as an `Float64`. # Returns `nil` otherwise. def as_f? : Float64? - @raw.as?(Float64) + case raw = @raw + when Int + raw.to_f + else + raw.as?(Float64) + end end - # Checks that the underlying value is `Float`, and returns its value as an `Float32`. + # Checks that the underlying value is `Float` (or `Int`), and returns its value as an `Float32`. # Raises otherwise. def as_f32 : Float32 - @raw.as(Float).to_f32 + case raw = @raw + when Int + raw.to_f32 + else + raw.as(Float).to_f32 + end end - # Checks that the underlying value is `Float`, and returns its value as an `Float32`. + # Checks that the underlying value is `Float` (or `Int`), and returns its value as an `Float32`. # Returns `nil` otherwise. def as_f32? : Float32? - as_f32 if @raw.is_a?(Float) + case raw = @raw + when Int + raw.to_f32 + when Float + raw.to_f32 + else + nil + end end # Checks that the underlying value is `String`, and returns its value. diff --git a/src/yaml/any.cr b/src/yaml/any.cr index e8e3a82e58b3..2ed8f242999c 100644 --- a/src/yaml/any.cr +++ b/src/yaml/any.cr @@ -198,28 +198,50 @@ struct YAML::Any as_i if @raw.is_a?(Int) end - # Checks that the underlying value is `Float64`, and returns its value. + # Checks that the underlying value is `Float` (or `Int`), and returns its value. # Raises otherwise. def as_f : Float64 - @raw.as(Float64) + case raw = @raw + when Int + raw.to_f + else + raw.as(Float64) + end end - # Checks that the underlying value is `Float64`, and returns its value. + # Checks that the underlying value is `Float` (or `Int`), and returns its value. # Returns `nil` otherwise. def as_f? : Float64? - @raw.as?(Float64) + case raw = @raw + when Int + raw.to_f + else + raw.as?(Float64) + end end - # Checks that the underlying value is `Float`, and returns its value as an `Float32`. + # Checks that the underlying value is `Float` (or `Int`), and returns its value as an `Float32`. # Raises otherwise. def as_f32 : Float32 - @raw.as(Float).to_f32 + case raw = @raw + when Int + raw.to_f32 + else + raw.as(Float).to_f32 + end end - # Checks that the underlying value is `Float`, and returns its value as an `Float32`. + # Checks that the underlying value is `Float` (or `Int`), and returns its value as an `Float32`. # Returns `nil` otherwise. def as_f32? : Float32? - as_f32 if @raw.is_a?(Float) + case raw = @raw + when Int + raw.to_f32 + when Float + raw.to_f32 + else + nil + end end # Checks that the underlying value is `Time`, and returns its value. From dab949e885522c260637bdffbc4015071c8b2f85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20M=C3=BCller?= Date: Thu, 15 Dec 2022 13:25:38 +0100 Subject: [PATCH 29/41] Improve documentation for `Object#to_s` and `#inspect` (#9974) Co-authored-by: Beta Ziliani --- src/array.cr | 6 ++++ src/object.cr | 78 +++++++++++++++++++++++---------------------------- 2 files changed, 41 insertions(+), 43 deletions(-) diff --git a/src/array.cr b/src/array.cr index 51779b62788d..a93f974ab650 100644 --- a/src/array.cr +++ b/src/array.cr @@ -1717,6 +1717,12 @@ class Array(T) self end + # Prints a nicely readable and concise string representation of this array + # to *io*. + # + # The result resembles an array literal but it does not necessarily compile. + # + # Each element is presented using its `#inspect(io)` result to avoid ambiguity. def to_s(io : IO) : Nil executed = exec_recursive(:to_s) do io << '[' diff --git a/src/object.cr b/src/object.cr index 7fe746d3457e..b49159b10fe5 100644 --- a/src/object.cr +++ b/src/object.cr @@ -86,72 +86,64 @@ class Object hash(Crystal::Hasher.new).result end - # Returns a string representation of this object. + # Returns a nicely readable and concise string representation of this object, + # typically intended for users. # - # Descendants must usually **not** override this method. Instead, - # they must override `to_s(io)`, which must append to the given - # IO object. + # This method should usually **not** be overridden. It delegates to + # `#to_s(IO)` which can be overridden for custom implementations. + # + # Also see `#inspect`. def to_s : String String.build do |io| to_s io end end - # Appends a `String` representation of this object - # to the given `IO` object. - # - # An object must never append itself to the io argument, - # as this will in turn call `to_s(io)` on it. - abstract def to_s(io : IO) : Nil - - # Returns a `String` representation of this object suitable - # to be embedded inside other expressions, sometimes providing - # more information about this object. - # - # `#inspect` (and `#inspect(io)`) are the methods used when - # you invoke `#to_s` or `#inspect` on an object that holds - # other objects and wants to show them. For example when you - # invoke `Array#to_s`, `#inspect` will be invoked on each element: + # Prints a nicely readable and concise string representation of this object, + # typically intended for users, to *io*. # + # This method is called when an object is interpolated in a string literal: # ``` - # ary = ["one", "two", "three, etc."] - # ary.inspect # => ["one", "two", "three, etc."] + # "foo #{bar} baz" # calls bar.to_io with the builder for this string # ``` # - # Note that if Array invoked `#to_s` on each of the elements - # above, the output would have been this: - # + # `IO#<<` calls this method to append an object to itself: # ``` - # ary = ["one", "two", "three, etc."] - # # If inspect invoked to_s on each element... - # ary.inspect # => [one, two, three, etc.] + # io << bar # calls bar.to_s(io) # ``` # - # Note that it's not clear how many elements the array has, - # or which are they, because `#to_s` doesn't guarantee that - # the string representation is clearly delimited (in the case - # of `String` the quotes are not shown). + # Thus implementations must not interpolate `self` in a string literal or call + # `io << self` which both would lead to an endless loop. # - # Also note that sometimes the output of `#inspect` will look - # like a Crystal expression that will compile, but this isn't - # always the case, nor is it necessary. Notably, `Reference#inspect` - # and `Struct#inspect` return values that don't compile. + # Also see `#inspect(IO)`. + abstract def to_s(io : IO) : Nil + + # Returns an unambiguous and information-rich string representation of this + # object, typically intended for developers. + # + # This method should usually **not** be overridden. It delegates to + # `#inspect(IO)` which can be overridden for custom implementations. # - # Classes must usually **not** override this method. Instead, - # they must override `inspect(io)`, which must append to the - # given `IO` object. + # Also see `#to_s`. def inspect : String String.build do |io| inspect io end end - # Appends a string representation of this object - # to the given `IO` object. + # Prints to *io* an unambiguous and information-rich string representation of this + # object, typically intended for developers. + # + # It is similar to `#to_s(IO)`, but often provides more information. Ideally, it should + # contain sufficient information to be able to recreate an object with the same value + # (given an identical environment). + # + # For types that don't provide a custom implementation of this method, + # default implementation delegates to `#to_s(IO)`. This said, it is advisable to + # have an appropriate `#inspect` implementation on every type. Default + # implementations are provided by `Struct#inspect` and `Reference#inspect`. # - # Similar to `to_s(io)`, but usually appends more information - # about this object. - # See `#inspect`. + # `::p` and `::p!` use this method to print an object in `STDOUT`. def inspect(io : IO) : Nil to_s io end From 649f0614f09be92253afe6110e6e63a116150d75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20M=C3=BCller?= Date: Thu, 15 Dec 2022 13:25:56 +0100 Subject: [PATCH 30/41] Add custom `message` parameter to `#not_nil!` (#12797) --- spec/std/object_spec.cr | 31 +++++++++++++++++++++++++++++++ src/nil.cr | 10 ++++++++-- src/object.cr | 2 +- 3 files changed, 40 insertions(+), 3 deletions(-) diff --git a/spec/std/object_spec.cr b/spec/std/object_spec.cr index 0775a9c24b89..57c0ff48f703 100644 --- a/spec/std/object_spec.cr +++ b/spec/std/object_spec.cr @@ -513,4 +513,35 @@ describe Object do (x == y).should be_false end end + + describe "#not_nil!" do + it "basic" do + 1.not_nil! + expect_raises(NilAssertionError, "Nil assertion failed") do + nil.not_nil! + end + end + + it "removes Nil type" do + x = TestObject.new.as(TestObject?) + typeof(x.not_nil!).should eq TestObject + x.not_nil!.should be x + end + + it "raises NilAssertionError" do + x = nil.as(TestObject?) + typeof(x.not_nil!).should eq TestObject + expect_raises(NilAssertionError, "Nil assertion failed") do + x.not_nil! + end + end + + it "with message" do + x = TestObject.new + x.not_nil!("custom message").should be x + expect_raises(NilAssertionError, "custom message") do + nil.not_nil!("custom message") + end + end + end end diff --git a/src/nil.cr b/src/nil.cr index a27eff27f8fb..7b97b5f00d3c 100644 --- a/src/nil.cr +++ b/src/nil.cr @@ -103,9 +103,15 @@ struct Nil # Raises `NilAssertionError`. # + # If *message* is given, it is forwarded as error message of `NilAssertionError`. + # # See also: `Object#not_nil!`. - def not_nil! : NoReturn - raise NilAssertionError.new + def not_nil!(message = nil) : NoReturn + if message + raise NilAssertionError.new(message) + else + raise NilAssertionError.new + end end # Returns `self`. diff --git a/src/object.cr b/src/object.cr index b49159b10fe5..608dcf618bda 100644 --- a/src/object.cr +++ b/src/object.cr @@ -220,7 +220,7 @@ class Object # for example using [`if var`](https://crystal-lang.org/reference/syntax_and_semantics/if_var.html). # `not_nil!` is only meant as a last resort when there's no other way to explain this to the compiler. # Either way, consider instead raising a concrete exception with a descriptive message. - def not_nil! + def not_nil!(message = nil) self end From 1788b67e826996aa84f4bfc9e888d567c2db0386 Mon Sep 17 00:00:00 2001 From: Caspian Baska Date: Thu, 15 Dec 2022 20:26:11 +0800 Subject: [PATCH 31/41] Resolve type of free variable on block return type mismatch (#12754) --- spec/compiler/semantic/block_spec.cr | 11 +++++++++++ src/compiler/crystal/semantic/call.cr | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/spec/compiler/semantic/block_spec.cr b/spec/compiler/semantic/block_spec.cr index 021ba2a6738a..8dc82443018b 100644 --- a/spec/compiler/semantic/block_spec.cr +++ b/spec/compiler/semantic/block_spec.cr @@ -1608,4 +1608,15 @@ describe "Block inference" do foo.foo CRYSTAL end + + it "renders expected block return type of a free variable on mismatch" do + assert_error(<<-CR, "expected block to return Int64, not String") + struct Foo + def bar(arg : U, &block : -> U) forall U + end + end + + Foo.new.bar(1_i64) { "hi" } + CR + end end diff --git a/src/compiler/crystal/semantic/call.cr b/src/compiler/crystal/semantic/call.cr index 314db053c75f..ca0f4cfb3cbb 100644 --- a/src/compiler/crystal/semantic/call.cr +++ b/src/compiler/crystal/semantic/call.cr @@ -1041,7 +1041,7 @@ class Crystal::Call when Self match.context.instantiated_type when Crystal::Path - match.context.defining_type.lookup_path(output) + match.context.defining_type.lookup_type_var(output, match.context.free_vars) else output end From c05049de9db682aa37ac744056c8bf1511be843c Mon Sep 17 00:00:00 2001 From: David Keller Date: Thu, 15 Dec 2022 18:55:16 +0100 Subject: [PATCH 32/41] Remove oct/bin floating point literals (#12687) --- spec/compiler/lexer/lexer_spec.cr | 3 +++ src/compiler/crystal/syntax/lexer.cr | 7 +++++++ 2 files changed, 10 insertions(+) diff --git a/spec/compiler/lexer/lexer_spec.cr b/spec/compiler/lexer/lexer_spec.cr index aa9eaf3e06c8..8575f6e1c14b 100644 --- a/spec/compiler/lexer/lexer_spec.cr +++ b/spec/compiler/lexer/lexer_spec.cr @@ -362,6 +362,9 @@ describe "Lexer" do assert_syntax_error "0o200_i8", "0o200 doesn't fit in an Int8" assert_syntax_error "0b10000000_i8", "0b10000000 doesn't fit in an Int8" + assert_syntax_error "0b11_f32", "binary float literal is not supported" + assert_syntax_error "0o73_f64", "octal float literal is not supported" + # 2**31 - 1 it_lexes_i32 [["0x7fffffff", "2147483647"], ["0o17777777777", "2147483647"], ["0b1111111111111111111111111111111", "2147483647"]] it_lexes_i32 [["0x7fffffff_i32", "2147483647"], ["0o17777777777_i32", "2147483647"], ["0b1111111111111111111111111111111_i32", "2147483647"]] diff --git a/src/compiler/crystal/syntax/lexer.cr b/src/compiler/crystal/syntax/lexer.cr index c8f65187a448..333fdadaa452 100644 --- a/src/compiler/crystal/syntax/lexer.cr +++ b/src/compiler/crystal/syntax/lexer.cr @@ -1284,6 +1284,13 @@ module Crystal raise("unexpected '_' in number", @token, (current_pos - start)) if peek_next_char == '_' break unless peek_next_char.in?('0'..'9') when 'i', 'u', 'f' + if current_char == 'f' && base != 10 + case base + when 2 then raise("binary float literal is not supported", @token, (current_pos - start)) + when 8 then raise("octal float literal is not supported", @token, (current_pos - start)) + end + break + end before_suffix_pos = current_pos @token.number_kind = consume_number_suffix next_char From f0003ed02836a123fdbf9f23f91f7f5646d4120e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20M=C3=BCller?= Date: Thu, 15 Dec 2022 18:55:37 +0100 Subject: [PATCH 33/41] Add more specific error message for uninstantiated proc type (#11219) Co-authored-by: Caspian Baska --- spec/compiler/semantic/block_spec.cr | 10 ++++++++++ src/compiler/crystal/semantic/call.cr | 6 +++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/spec/compiler/semantic/block_spec.cr b/spec/compiler/semantic/block_spec.cr index 8dc82443018b..98ab54449665 100644 --- a/spec/compiler/semantic/block_spec.cr +++ b/spec/compiler/semantic/block_spec.cr @@ -676,6 +676,16 @@ describe "Block inference" do "expected block type to be a function type, not Int32" end + it "errors if proc is not instantiated" do + assert_error <<-CR, "can't create an instance of generic class Proc(*T, R) without specifying its type vars" + def capture(&block : Proc) + block + end + + capture { } + CR + end + it "passes #262" do assert_type(%( require "prelude" diff --git a/src/compiler/crystal/semantic/call.cr b/src/compiler/crystal/semantic/call.cr index ca0f4cfb3cbb..a3c16f7c2a33 100644 --- a/src/compiler/crystal/semantic/call.cr +++ b/src/compiler/crystal/semantic/call.cr @@ -836,7 +836,11 @@ class Crystal::Call # is valid too only if Foo is an alias/typedef that refers to a FunctionType block_arg_restriction_type = lookup_node_type(match.context, block_arg_restriction).remove_typedef unless block_arg_restriction_type.is_a?(ProcInstanceType) - block_arg_restriction.raise "expected block type to be a function type, not #{block_arg_restriction_type}" + if block_arg_restriction_type.is_a?(ProcType) + block_arg_restriction.raise "can't create an instance of generic class #{block_arg_restriction_type} without specifying its type vars" + else + block_arg_restriction.raise "expected block type to be a function type, not #{block_arg_restriction_type}" + end return nil, nil end From 174bb009effbb326a40741f8927e6e48692a6133 Mon Sep 17 00:00:00 2001 From: Sijawusz Pur Rahnama Date: Fri, 16 Dec 2022 00:21:50 +0100 Subject: [PATCH 34/41] Add `Indexable#rindex!` method variant (#12759) --- spec/std/indexable_spec.cr | 22 ++++++++++++++++++++++ src/indexable.cr | 14 ++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/spec/std/indexable_spec.cr b/spec/std/indexable_spec.cr index e2071cef0885..f7c39f7ba485 100644 --- a/spec/std/indexable_spec.cr +++ b/spec/std/indexable_spec.cr @@ -133,6 +133,28 @@ describe Indexable do end end + describe "#rindex!" do + it "does rindex with big negative offset" do + indexable = SafeIndexable.new(3) + expect_raises Enumerable::NotFoundError do + indexable.rindex!(0, -100) + end + end + + it "does rindex with big offset" do + indexable = SafeIndexable.new(3) + expect_raises Enumerable::NotFoundError do + indexable.rindex!(0, 100) + end + end + + it "offset type" do + indexable = SafeIndexable.new(3) + indexable.rindex!(1, 2_i64).should eq 1 + indexable.rindex!(1, 2_i64).should be_a(Int64) + end + end + it "does each" do indexable = SafeIndexable.new(3) is = [] of Int32 diff --git a/src/indexable.cr b/src/indexable.cr index 8ff5ddec14f4..8ce454a341ed 100644 --- a/src/indexable.cr +++ b/src/indexable.cr @@ -849,6 +849,13 @@ module Indexable(T) rindex(offset) { |elem| elem == value } end + # :ditto: + # + # Raises `Enumerable::NotFoundError` if *value* is not in `self`. + def rindex!(value, offset = size - 1) + rindex(value, offset) || raise Enumerable::NotFoundError.new + end + # Returns the index of the first object in `self` for which the block # is truthy, starting from the last object, or `nil` if no match # is found. @@ -872,6 +879,13 @@ module Indexable(T) nil end + # :ditto: + # + # Raises `Enumerable::NotFoundError` if no match is found. + def rindex!(offset = size - 1, & : T ->) + rindex(offset) { |e| yield e } || raise Enumerable::NotFoundError.new + end + # Optimized version of `Enumerable#sample` that runs in O(1) time. # # ``` From 7b26311022cb12c120171a0511aadf77b477fa72 Mon Sep 17 00:00:00 2001 From: Quinton Miller Date: Fri, 16 Dec 2022 07:22:10 +0800 Subject: [PATCH 35/41] Enable multithreading specs on Windows CI (#12843) --- spec/std/thread/condition_variable_spec.cr | 2 -- spec/std/thread/mutex_spec.cr | 2 -- spec/std/thread_spec.cr | 2 -- 3 files changed, 6 deletions(-) diff --git a/spec/std/thread/condition_variable_spec.cr b/spec/std/thread/condition_variable_spec.cr index 568f2ea35c31..36e1963794b3 100644 --- a/spec/std/thread/condition_variable_spec.cr +++ b/spec/std/thread/condition_variable_spec.cr @@ -1,5 +1,3 @@ -{% skip_file if flag?(:win32) %} # FIXME: enable after #11647 - {% if flag?(:musl) %} # FIXME: These thread specs occasionally fail on musl/alpine based ci, so # they're disabled for now to reduce noise. diff --git a/spec/std/thread/mutex_spec.cr b/spec/std/thread/mutex_spec.cr index 405c812b0888..d028ffdd8f8a 100644 --- a/spec/std/thread/mutex_spec.cr +++ b/spec/std/thread/mutex_spec.cr @@ -1,5 +1,3 @@ -{% skip_file if flag?(:win32) %} # FIXME: enable after #11647 - {% if flag?(:musl) %} # FIXME: These thread specs occasionally fail on musl/alpine based ci, so # they're disabled for now to reduce noise. diff --git a/spec/std/thread_spec.cr b/spec/std/thread_spec.cr index 7d3e51a92972..599a1968f52f 100644 --- a/spec/std/thread_spec.cr +++ b/spec/std/thread_spec.cr @@ -1,5 +1,3 @@ -{% skip_file if flag?(:win32) %} # FIXME: enable after #11647 - require "spec" {% if flag?(:musl) %} From a5373ade7157f940f21383e22a74656b6d264d89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20M=C3=BCller?= Date: Fri, 16 Dec 2022 12:07:39 +0100 Subject: [PATCH 36/41] Implement `Regex` engine on PCRE2 (#12840) --- spec/std/regex_spec.cr | 20 +++-- src/regex/engine.cr | 13 ++- src/regex/lib_pcre2.cr | 89 +++++++++++++++++++++ src/regex/pcre2.cr | 176 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 290 insertions(+), 8 deletions(-) create mode 100644 src/regex/lib_pcre2.cr create mode 100644 src/regex/pcre2.cr diff --git a/spec/std/regex_spec.cr b/spec/std/regex_spec.cr index c38194bc8785..5935e85060fd 100644 --- a/spec/std/regex_spec.cr +++ b/spec/std/regex_spec.cr @@ -200,12 +200,16 @@ describe "Regex" do /foo/.matches?("foo", options: Regex::Options::ANCHORED).should be_true end - it "matches a large single line string" do - LibPCRE.config LibPCRE::CONFIG_JIT, out jit_enabled - pending! "PCRE JIT mode not available." unless 1 == jit_enabled + it "doesn't crash with a large single line string" do + {% if Regex::Engine.resolve.name == "Regex::PCRE" %} + LibPCRE.config LibPCRE::CONFIG_JIT, out jit_enabled + pending! "PCRE JIT mode not available." unless 1 == jit_enabled + {% end %} str = File.read(datapath("large_single_line_string.txt")) - str.matches?(/^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=)?$/).should be_false + str.matches?(/^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=)?$/) + # We don't care whether this actually matches or not, it's just to make + # sure the engine does not stack overflow with a large string. end end @@ -422,6 +426,12 @@ describe "Regex" do it ".error?" do Regex.error?("(foo|bar)").should be_nil - Regex.error?("(foo|bar").should eq "missing ) at 8" + Regex.error?("(foo|bar").should eq( + if Regex::Engine.to_s == "Regex::PCRE2" + "missing closing parenthesis at 8" + else + "missing ) at 8" + end + ) end end diff --git a/src/regex/engine.cr b/src/regex/engine.cr index ad69e5d034bf..766917f87dd2 100644 --- a/src/regex/engine.cr +++ b/src/regex/engine.cr @@ -1,4 +1,11 @@ -require "./pcre" +{% if flag?(:use_pcre2) || (!flag?(:use_pcre) && !flag?(:win32) && `hash pkg-config 2> /dev/null && pkg-config --silence-errors --modversion libpcre2-8 || printf %s false` != "false") %} + require "./pcre2" -# :nodoc: -alias Regex::Engine = PCRE + # :nodoc: + alias Regex::Engine = PCRE2 +{% else %} + require "./pcre" + + # :nodoc: + alias Regex::Engine = PCRE +{% end %} diff --git a/src/regex/lib_pcre2.cr b/src/regex/lib_pcre2.cr new file mode 100644 index 000000000000..922c492b7e1a --- /dev/null +++ b/src/regex/lib_pcre2.cr @@ -0,0 +1,89 @@ +@[Link("pcre2-8")] +lib LibPCRE2 + alias Int = LibC::Int + + UNSET = ~LibC::SizeT.new(0) + + ANCHORED = 0x80000000 + NO_UTF_CHECK = 0x40000000 + ENDANCHORED = 0x20000000 + + ALLOW_EMPTY_CLASS = 0x00000001 + ALT_BSUX = 0x00000002 + AUTO_CALLOUT = 0x00000004 + CASELESS = 0x00000008 + DOLLAR_ENDONLY = 0x00000010 + DOTALL = 0x00000020 + DUPNAMES = 0x00000040 + EXTENDED = 0x00000080 + FIRSTLINE = 0x00000100 + MATCH_UNSET_BACKREF = 0x00000200 + MULTILINE = 0x00000400 + NEVER_UCP = 0x00000800 + NEVER_UTF = 0x00001000 + NO_AUTO_CAPTURE = 0x00002000 + NO_AUTO_POSSESS = 0x00004000 + NO_DOTSTAR_ANCHOR = 0x00008000 + NO_START_OPTIMIZE = 0x00010000 + UCP = 0x00020000 + UNGREEDY = 0x00040000 + UTF = 0x00080000 + NEVER_BACKSLASH_C = 0x00100000 + ALT_CIRCUMFLEX = 0x00200000 + ALT_VERBNAMES = 0x00400000 + USE_OFFSET_LIMIT = 0x00800000 + EXTENDED_MORE = 0x01000000 + LITERAL = 0x02000000 + MATCH_INVALID_UTF = 0x04000000 + + ERROR_NOMATCH = -1 + + INFO_ALLOPTIONS = 0 + INFO_ARGOPTIONS = 1 + INFO_BACKREFMAX = 2 + INFO_BSR = 3 + INFO_CAPTURECOUNT = 4 + INFO_FIRSTCODEUNIT = 5 + INFO_FIRSTCODETYPE = 6 + INFO_FIRSTBITMAP = 7 + INFO_HASCRORLF = 8 + INFO_JCHANGED = 9 + INFO_JITSIZE = 10 + INFO_LASTCODEUNIT = 11 + INFO_LASTCODETYPE = 12 + INFO_MATCHEMPTY = 13 + INFO_MATCHLIMIT = 14 + INFO_MAXLOOKBEHIND = 15 + INFO_MINLENGTH = 16 + INFO_NAMECOUNT = 17 + INFO_NAMEENTRYSIZE = 18 + INFO_NAMETABLE = 19 + INFO_NEWLINE = 20 + INFO_DEPTHLIMIT = 21 + INFO_RECURSIONLIMIT = 21 # Obsolete synonym + INFO_SIZE = 22 + INFO_HASBACKSLASHC = 23 + INFO_FRAMESIZE = 24 + INFO_HEAPLIMIT = 25 + INFO_EXTRAOPTIONS = 26 + + type Code = Void* + type CompileContext = Void* + type MatchData = Void* + + fun get_error_message = pcre2_get_error_message_8(errorcode : Int, buffer : UInt8*, bufflen : LibC::SizeT) : Int + + fun compile = pcre2_compile_8(pattern : UInt8*, length : LibC::SizeT, options : UInt32, errorcode : LibC::SizeT*, erroroffset : Int*, ccontext : CompileContext*) : Code* + fun code_free = pcre2_code_free_8(code : Code*) : Void + + fun pattern_info = pcre2_pattern_info_8(code : Code*, what : UInt32, where : Void*) : Int + + fun match = pcre2_match_8(code : Code*, subject : UInt8*, length : LibC::SizeT, startoffset : LibC::SizeT, options : UInt32, match_data : MatchData*, mcontext : Void*) : Int + fun match_data_create_from_pattern = pcre2_match_data_create_from_pattern_8(code : Code*, gcontext : Void*) : MatchData* + fun match_data_free = pcre2_match_data_free_8(match_data : MatchData*) : Void + + fun substring_nametable_scan = pcre2_substring_nametable_scan_8(code : Code*, name : UInt8*, first : UInt8*, last : UInt8*) : Int + + fun get_ovector_pointer = pcre2_get_ovector_pointer_8(match_data : MatchData*) : LibC::SizeT* + fun get_ovector_count = pcre2_get_ovector_count_8(match_data : MatchData*) : UInt32 +end diff --git a/src/regex/pcre2.cr b/src/regex/pcre2.cr new file mode 100644 index 000000000000..3eea20280268 --- /dev/null +++ b/src/regex/pcre2.cr @@ -0,0 +1,176 @@ +require "./lib_pcre2" + +# :nodoc: +module Regex::PCRE2 + @re : LibPCRE2::Code* + + # :nodoc: + def initialize(*, _source @source : String, _options @options) + @re = PCRE2.compile(source, pcre2_options(options) | LibPCRE2::UTF | LibPCRE2::NO_UTF_CHECK | LibPCRE2::DUPNAMES | LibPCRE2::UCP) do |error_message| + raise ArgumentError.new(error_message) + end + end + + protected def self.compile(source, options) + if res = LibPCRE2.compile(source, source.bytesize, options, out errorcode, out erroroffset, nil) + res + else + message = String.new(256) do |buffer| + bytesize = LibPCRE2.get_error_message(errorcode, buffer, 256) + {bytesize, 0} + end + yield "#{message} at #{erroroffset}" + end + end + + private def pcre2_options(options) + flag = 0 + options.each do |option| + flag |= case option + when .ignore_case? then LibPCRE2::CASELESS + when .multiline? then LibPCRE2::DOTALL | LibPCRE2::MULTILINE + when .extended? then LibPCRE2::EXTENDED + when .anchored? then LibPCRE2::ANCHORED + when .utf_8? then LibPCRE2::UTF + when .no_utf8_check? then LibPCRE2::NO_UTF_CHECK + when .dupnames? then LibPCRE2::DUPNAMES + when .ucp? then LibPCRE2::UCP + else + raise "unreachable" + end + end + flag + end + + def finalize + {% unless flag?(:interpreted) %} + LibPCRE2.code_free @re + {% end %} + end + + protected def self.error_impl(source) + code = PCRE2.compile(source, LibPCRE2::UTF | LibPCRE2::NO_UTF_CHECK | LibPCRE2::DUPNAMES | LibPCRE2::UCP) do |error_message| + return error_message + end + + LibPCRE2.code_free code + + nil + end + + private def pattern_info(what) + value = uninitialized UInt32 + pattern_info(what, pointerof(value)) + value + end + + private def pattern_info(what, where) + ret = LibPCRE2.pattern_info(@re, what, where) + if ret != 0 + raise "error pattern_info #{what}: #{ret}" + end + end + + private def name_table_impl + lookup = Hash(Int32, String).new + + each_capture_group do |capture_number, name_entry| + lookup[capture_number] = String.new(name_entry.to_unsafe + 2) + end + + lookup + end + + # :nodoc: + def each_capture_group + name_table = uninitialized UInt8* + pattern_info(LibPCRE2::INFO_NAMETABLE, pointerof(name_table)) + + name_entry_size = pattern_info(LibPCRE2::INFO_NAMEENTRYSIZE) + + name_count = pattern_info(LibPCRE2::INFO_NAMECOUNT) + name_count.times do + capture_number = (name_table[0] << 8) | name_table[1] + + yield capture_number, Slice.new(name_table, name_entry_size) + + name_table += name_entry_size + end + end + + private def capture_count_impl + pattern_info(LibPCRE2::INFO_CAPTURECOUNT).to_i32 + end + + private def match_impl(str, byte_index, options) + match_data = match_data(str, byte_index, options) || return + + ovector = LibPCRE2.get_ovector_pointer(match_data) + ovector_count = LibPCRE2.get_ovector_count(match_data) + LibPCRE2.match_data_free(match_data) + + ::Regex::MatchData.new(self, @re, str, byte_index, ovector, ovector_count.to_i32 - 1) + end + + private def matches_impl(str, byte_index, options) + if match_data = match_data(str, byte_index, options) + LibPCRE2.match_data_free(match_data) + true + else + false + end + end + + private def match_data(str, byte_index, options) + match_data = LibPCRE2.match_data_create_from_pattern(@re, nil) + match_count = LibPCRE2.match(@re, str, str.bytesize, byte_index, pcre2_options(options) | LibPCRE2::NO_UTF_CHECK, match_data, nil) + + if match_count < 0 + LibPCRE2.match_data_free(match_data) + case match_count + when LibPCRE2::ERROR_NOMATCH + return + else + raise "error!" + end + end + + match_data + end + + module MatchData + # :nodoc: + def initialize(@regex : Regex, @code : LibPCRE2::Code*, @string : String, @pos : Int32, @ovector : UInt64*, @group_size : Int32) + end + + private def byte_range(n, &) + n += size if n < 0 + range = Range.new(@ovector[n * 2].to_i32!, @ovector[n * 2 + 1].to_i32!, exclusive: true) + if range.begin < 0 || range.end < 0 + yield n + else + range + end + end + + private def fetch_impl(group_name : String) + selected_range = nil + exists = false + @regex.each_capture_group do |number, name_entry| + if name_entry[2, group_name.bytesize] == group_name.to_slice + exists = true + range = byte_range(number) { nil } + if (range && selected_range && range.begin > selected_range.begin) || !selected_range + selected_range = range + end + end + end + + if selected_range + @string.byte_slice(selected_range.begin, selected_range.end - selected_range.begin) + else + yield exists + end + end + end +end From dce9d40222898afb48c6a36f6727abecfda06d57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20M=C3=BCller?= Date: Fri, 16 Dec 2022 18:05:06 +0100 Subject: [PATCH 37/41] Revert "Implement `Regex` engine on PCRE2" (#12850) --- spec/std/regex_spec.cr | 20 ++--- src/regex/engine.cr | 13 +-- src/regex/lib_pcre2.cr | 89 --------------------- src/regex/pcre2.cr | 176 ----------------------------------------- 4 files changed, 8 insertions(+), 290 deletions(-) delete mode 100644 src/regex/lib_pcre2.cr delete mode 100644 src/regex/pcre2.cr diff --git a/spec/std/regex_spec.cr b/spec/std/regex_spec.cr index 5935e85060fd..c38194bc8785 100644 --- a/spec/std/regex_spec.cr +++ b/spec/std/regex_spec.cr @@ -200,16 +200,12 @@ describe "Regex" do /foo/.matches?("foo", options: Regex::Options::ANCHORED).should be_true end - it "doesn't crash with a large single line string" do - {% if Regex::Engine.resolve.name == "Regex::PCRE" %} - LibPCRE.config LibPCRE::CONFIG_JIT, out jit_enabled - pending! "PCRE JIT mode not available." unless 1 == jit_enabled - {% end %} + it "matches a large single line string" do + LibPCRE.config LibPCRE::CONFIG_JIT, out jit_enabled + pending! "PCRE JIT mode not available." unless 1 == jit_enabled str = File.read(datapath("large_single_line_string.txt")) - str.matches?(/^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=)?$/) - # We don't care whether this actually matches or not, it's just to make - # sure the engine does not stack overflow with a large string. + str.matches?(/^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=)?$/).should be_false end end @@ -426,12 +422,6 @@ describe "Regex" do it ".error?" do Regex.error?("(foo|bar)").should be_nil - Regex.error?("(foo|bar").should eq( - if Regex::Engine.to_s == "Regex::PCRE2" - "missing closing parenthesis at 8" - else - "missing ) at 8" - end - ) + Regex.error?("(foo|bar").should eq "missing ) at 8" end end diff --git a/src/regex/engine.cr b/src/regex/engine.cr index 766917f87dd2..ad69e5d034bf 100644 --- a/src/regex/engine.cr +++ b/src/regex/engine.cr @@ -1,11 +1,4 @@ -{% if flag?(:use_pcre2) || (!flag?(:use_pcre) && !flag?(:win32) && `hash pkg-config 2> /dev/null && pkg-config --silence-errors --modversion libpcre2-8 || printf %s false` != "false") %} - require "./pcre2" +require "./pcre" - # :nodoc: - alias Regex::Engine = PCRE2 -{% else %} - require "./pcre" - - # :nodoc: - alias Regex::Engine = PCRE -{% end %} +# :nodoc: +alias Regex::Engine = PCRE diff --git a/src/regex/lib_pcre2.cr b/src/regex/lib_pcre2.cr deleted file mode 100644 index 922c492b7e1a..000000000000 --- a/src/regex/lib_pcre2.cr +++ /dev/null @@ -1,89 +0,0 @@ -@[Link("pcre2-8")] -lib LibPCRE2 - alias Int = LibC::Int - - UNSET = ~LibC::SizeT.new(0) - - ANCHORED = 0x80000000 - NO_UTF_CHECK = 0x40000000 - ENDANCHORED = 0x20000000 - - ALLOW_EMPTY_CLASS = 0x00000001 - ALT_BSUX = 0x00000002 - AUTO_CALLOUT = 0x00000004 - CASELESS = 0x00000008 - DOLLAR_ENDONLY = 0x00000010 - DOTALL = 0x00000020 - DUPNAMES = 0x00000040 - EXTENDED = 0x00000080 - FIRSTLINE = 0x00000100 - MATCH_UNSET_BACKREF = 0x00000200 - MULTILINE = 0x00000400 - NEVER_UCP = 0x00000800 - NEVER_UTF = 0x00001000 - NO_AUTO_CAPTURE = 0x00002000 - NO_AUTO_POSSESS = 0x00004000 - NO_DOTSTAR_ANCHOR = 0x00008000 - NO_START_OPTIMIZE = 0x00010000 - UCP = 0x00020000 - UNGREEDY = 0x00040000 - UTF = 0x00080000 - NEVER_BACKSLASH_C = 0x00100000 - ALT_CIRCUMFLEX = 0x00200000 - ALT_VERBNAMES = 0x00400000 - USE_OFFSET_LIMIT = 0x00800000 - EXTENDED_MORE = 0x01000000 - LITERAL = 0x02000000 - MATCH_INVALID_UTF = 0x04000000 - - ERROR_NOMATCH = -1 - - INFO_ALLOPTIONS = 0 - INFO_ARGOPTIONS = 1 - INFO_BACKREFMAX = 2 - INFO_BSR = 3 - INFO_CAPTURECOUNT = 4 - INFO_FIRSTCODEUNIT = 5 - INFO_FIRSTCODETYPE = 6 - INFO_FIRSTBITMAP = 7 - INFO_HASCRORLF = 8 - INFO_JCHANGED = 9 - INFO_JITSIZE = 10 - INFO_LASTCODEUNIT = 11 - INFO_LASTCODETYPE = 12 - INFO_MATCHEMPTY = 13 - INFO_MATCHLIMIT = 14 - INFO_MAXLOOKBEHIND = 15 - INFO_MINLENGTH = 16 - INFO_NAMECOUNT = 17 - INFO_NAMEENTRYSIZE = 18 - INFO_NAMETABLE = 19 - INFO_NEWLINE = 20 - INFO_DEPTHLIMIT = 21 - INFO_RECURSIONLIMIT = 21 # Obsolete synonym - INFO_SIZE = 22 - INFO_HASBACKSLASHC = 23 - INFO_FRAMESIZE = 24 - INFO_HEAPLIMIT = 25 - INFO_EXTRAOPTIONS = 26 - - type Code = Void* - type CompileContext = Void* - type MatchData = Void* - - fun get_error_message = pcre2_get_error_message_8(errorcode : Int, buffer : UInt8*, bufflen : LibC::SizeT) : Int - - fun compile = pcre2_compile_8(pattern : UInt8*, length : LibC::SizeT, options : UInt32, errorcode : LibC::SizeT*, erroroffset : Int*, ccontext : CompileContext*) : Code* - fun code_free = pcre2_code_free_8(code : Code*) : Void - - fun pattern_info = pcre2_pattern_info_8(code : Code*, what : UInt32, where : Void*) : Int - - fun match = pcre2_match_8(code : Code*, subject : UInt8*, length : LibC::SizeT, startoffset : LibC::SizeT, options : UInt32, match_data : MatchData*, mcontext : Void*) : Int - fun match_data_create_from_pattern = pcre2_match_data_create_from_pattern_8(code : Code*, gcontext : Void*) : MatchData* - fun match_data_free = pcre2_match_data_free_8(match_data : MatchData*) : Void - - fun substring_nametable_scan = pcre2_substring_nametable_scan_8(code : Code*, name : UInt8*, first : UInt8*, last : UInt8*) : Int - - fun get_ovector_pointer = pcre2_get_ovector_pointer_8(match_data : MatchData*) : LibC::SizeT* - fun get_ovector_count = pcre2_get_ovector_count_8(match_data : MatchData*) : UInt32 -end diff --git a/src/regex/pcre2.cr b/src/regex/pcre2.cr deleted file mode 100644 index 3eea20280268..000000000000 --- a/src/regex/pcre2.cr +++ /dev/null @@ -1,176 +0,0 @@ -require "./lib_pcre2" - -# :nodoc: -module Regex::PCRE2 - @re : LibPCRE2::Code* - - # :nodoc: - def initialize(*, _source @source : String, _options @options) - @re = PCRE2.compile(source, pcre2_options(options) | LibPCRE2::UTF | LibPCRE2::NO_UTF_CHECK | LibPCRE2::DUPNAMES | LibPCRE2::UCP) do |error_message| - raise ArgumentError.new(error_message) - end - end - - protected def self.compile(source, options) - if res = LibPCRE2.compile(source, source.bytesize, options, out errorcode, out erroroffset, nil) - res - else - message = String.new(256) do |buffer| - bytesize = LibPCRE2.get_error_message(errorcode, buffer, 256) - {bytesize, 0} - end - yield "#{message} at #{erroroffset}" - end - end - - private def pcre2_options(options) - flag = 0 - options.each do |option| - flag |= case option - when .ignore_case? then LibPCRE2::CASELESS - when .multiline? then LibPCRE2::DOTALL | LibPCRE2::MULTILINE - when .extended? then LibPCRE2::EXTENDED - when .anchored? then LibPCRE2::ANCHORED - when .utf_8? then LibPCRE2::UTF - when .no_utf8_check? then LibPCRE2::NO_UTF_CHECK - when .dupnames? then LibPCRE2::DUPNAMES - when .ucp? then LibPCRE2::UCP - else - raise "unreachable" - end - end - flag - end - - def finalize - {% unless flag?(:interpreted) %} - LibPCRE2.code_free @re - {% end %} - end - - protected def self.error_impl(source) - code = PCRE2.compile(source, LibPCRE2::UTF | LibPCRE2::NO_UTF_CHECK | LibPCRE2::DUPNAMES | LibPCRE2::UCP) do |error_message| - return error_message - end - - LibPCRE2.code_free code - - nil - end - - private def pattern_info(what) - value = uninitialized UInt32 - pattern_info(what, pointerof(value)) - value - end - - private def pattern_info(what, where) - ret = LibPCRE2.pattern_info(@re, what, where) - if ret != 0 - raise "error pattern_info #{what}: #{ret}" - end - end - - private def name_table_impl - lookup = Hash(Int32, String).new - - each_capture_group do |capture_number, name_entry| - lookup[capture_number] = String.new(name_entry.to_unsafe + 2) - end - - lookup - end - - # :nodoc: - def each_capture_group - name_table = uninitialized UInt8* - pattern_info(LibPCRE2::INFO_NAMETABLE, pointerof(name_table)) - - name_entry_size = pattern_info(LibPCRE2::INFO_NAMEENTRYSIZE) - - name_count = pattern_info(LibPCRE2::INFO_NAMECOUNT) - name_count.times do - capture_number = (name_table[0] << 8) | name_table[1] - - yield capture_number, Slice.new(name_table, name_entry_size) - - name_table += name_entry_size - end - end - - private def capture_count_impl - pattern_info(LibPCRE2::INFO_CAPTURECOUNT).to_i32 - end - - private def match_impl(str, byte_index, options) - match_data = match_data(str, byte_index, options) || return - - ovector = LibPCRE2.get_ovector_pointer(match_data) - ovector_count = LibPCRE2.get_ovector_count(match_data) - LibPCRE2.match_data_free(match_data) - - ::Regex::MatchData.new(self, @re, str, byte_index, ovector, ovector_count.to_i32 - 1) - end - - private def matches_impl(str, byte_index, options) - if match_data = match_data(str, byte_index, options) - LibPCRE2.match_data_free(match_data) - true - else - false - end - end - - private def match_data(str, byte_index, options) - match_data = LibPCRE2.match_data_create_from_pattern(@re, nil) - match_count = LibPCRE2.match(@re, str, str.bytesize, byte_index, pcre2_options(options) | LibPCRE2::NO_UTF_CHECK, match_data, nil) - - if match_count < 0 - LibPCRE2.match_data_free(match_data) - case match_count - when LibPCRE2::ERROR_NOMATCH - return - else - raise "error!" - end - end - - match_data - end - - module MatchData - # :nodoc: - def initialize(@regex : Regex, @code : LibPCRE2::Code*, @string : String, @pos : Int32, @ovector : UInt64*, @group_size : Int32) - end - - private def byte_range(n, &) - n += size if n < 0 - range = Range.new(@ovector[n * 2].to_i32!, @ovector[n * 2 + 1].to_i32!, exclusive: true) - if range.begin < 0 || range.end < 0 - yield n - else - range - end - end - - private def fetch_impl(group_name : String) - selected_range = nil - exists = false - @regex.each_capture_group do |number, name_entry| - if name_entry[2, group_name.bytesize] == group_name.to_slice - exists = true - range = byte_range(number) { nil } - if (range && selected_range && range.begin > selected_range.begin) || !selected_range - selected_range = range - end - end - end - - if selected_range - @string.byte_slice(selected_range.begin, selected_range.end - selected_range.begin) - else - yield exists - end - end - end -end From 8825501f1dc9dce963afcf0dfe68f0499786627b Mon Sep 17 00:00:00 2001 From: Beta Ziliani Date: Sat, 17 Dec 2022 10:07:43 -0300 Subject: [PATCH 38/41] Reverting #12405 Compiler: don't always use Array for node dependencies and observers (#12849) --- .../compiler/crystal/zero_one_or_many_spec.cr | 166 ------------------ src/compiler/crystal/codegen/call.cr | 4 +- src/compiler/crystal/interpreter/compiler.cr | 4 +- .../crystal/interpreter/multidispatch.cr | 6 +- src/compiler/crystal/semantic/bindings.cr | 47 ++--- src/compiler/crystal/semantic/call.cr | 73 ++++---- src/compiler/crystal/semantic/call_error.cr | 3 +- .../crystal/semantic/cleanup_transformer.cr | 64 +++---- src/compiler/crystal/semantic/cover.cr | 6 +- src/compiler/crystal/semantic/filters.cr | 2 +- .../crystal/semantic/fix_missing_types.cr | 2 +- src/compiler/crystal/semantic/lib.cr | 7 +- src/compiler/crystal/semantic/main_visitor.cr | 24 ++- src/compiler/crystal/semantic/match.cr | 19 +- .../crystal/semantic/method_lookup.cr | 16 +- src/compiler/crystal/semantic/type_merge.cr | 8 +- src/compiler/crystal/semantic/warnings.cr | 2 +- src/compiler/crystal/tools/context.cr | 12 +- src/compiler/crystal/tools/implementations.cr | 6 +- src/compiler/crystal/zero_one_or_many.cr | 117 ------------ 20 files changed, 165 insertions(+), 423 deletions(-) delete mode 100644 spec/compiler/crystal/zero_one_or_many_spec.cr delete mode 100644 src/compiler/crystal/zero_one_or_many.cr diff --git a/spec/compiler/crystal/zero_one_or_many_spec.cr b/spec/compiler/crystal/zero_one_or_many_spec.cr deleted file mode 100644 index 149a44539324..000000000000 --- a/spec/compiler/crystal/zero_one_or_many_spec.cr +++ /dev/null @@ -1,166 +0,0 @@ -require "spec" -require "../../../src/compiler/crystal/zero_one_or_many" - -describe Crystal::ZeroOneOrMany do - describe "initialize and size" do - it "creates without a value" do - ary = Crystal::ZeroOneOrMany(Int32).new - ary.size.should eq(0) - ary.value.should be_nil - end - - it "creates with a value" do - ary = Crystal::ZeroOneOrMany.new(1) - ary.size.should eq(1) - ary.to_a.should eq([1]) - ary.value.should be_a(Int32) - end - end - - describe "as Indexable" do - it "when there's no value" do - ary = Crystal::ZeroOneOrMany(Int32).new - expect_raises(IndexError) { ary[0] } - expect_raises(IndexError) { ary[1] } - end - - it "when there's a single value" do - ary = Crystal::ZeroOneOrMany.new(1) - ary[0].should eq(1) - expect_raises(IndexError) { ary[1] } - end - - it "when there's two values" do - ary = Crystal::ZeroOneOrMany(Int32).new - ary.concat([1, 2]) - ary[0].should eq(1) - ary[1].should eq(2) - expect_raises(IndexError) { ary[2] } - end - end - - describe "#each" do - it "when there's no value" do - ary = Crystal::ZeroOneOrMany(Int32).new - ary.sum.should eq(0) - end - - it "when there's a single value" do - ary = Crystal::ZeroOneOrMany.new(1) - ary.sum.should eq(1) - end - - it "when there's two values" do - ary = Crystal::ZeroOneOrMany(Int32).new - ary.concat([1, 2]) - ary.sum.should eq(3) - end - end - - describe "#push element" do - it "when there's no value" do - ary = Crystal::ZeroOneOrMany(Int32).new - ary.push 1 - ary.to_a.should eq([1]) - end - - it "when there's a single value" do - ary = Crystal::ZeroOneOrMany.new(1) - ary.push 2 - ary.to_a.should eq([1, 2]) - end - - it "when there's two values" do - ary = Crystal::ZeroOneOrMany(Int32).new - ary.push 1 - ary << 2 - ary.push 3 - ary.to_a.should eq([1, 2, 3]) - end - end - - describe "#+ elements" do - it "when there's no value and elements is empty" do - ary = Crystal::ZeroOneOrMany(Int32).new - ary.concat([] of Int32) - ary.empty?.should be_true - ary.value.should be_nil - end - - it "when there's no value and elements has a single value" do - ary = Crystal::ZeroOneOrMany(Int32).new - ary.concat([1]) - ary.to_a.should eq([1]) - ary.value.should be_a(Int32) - end - - it "when there's no value and elements has more than one value" do - ary = Crystal::ZeroOneOrMany(Int32).new - ary.concat([1, 2]) - ary.to_a.should eq([1, 2]) - ary.value.should be_a(Array(Int32)) - end - - it "when there's a single value" do - ary = Crystal::ZeroOneOrMany.new(1) - ary.concat([2, 3]) - ary.to_a.should eq([1, 2, 3]) - ary.value.should be_a(Array(Int32)) - end - - it "when there's two values" do - ary = Crystal::ZeroOneOrMany(Int32).new - ary.concat([1, 2]) - ary.concat([3, 4]) - ary.to_a.should eq([1, 2, 3, 4]) - ary.value.should be_a(Array(Int32)) - end - end - - describe "#reject" do - it "when there's no value" do - ary = Crystal::ZeroOneOrMany(Int32).new - ary.reject! { true } - ary.empty?.should be_true - ary.value.should be_nil - end - - it "when there's a single value and it matches" do - ary = Crystal::ZeroOneOrMany(Int32).new(1) - ary.reject! { |x| x == 1 } - ary.empty?.should be_true - ary.value.should be_nil - end - - it "when there's a single value and it doesn't match" do - ary = Crystal::ZeroOneOrMany(Int32).new(1) - ary.reject! { |x| x == 2 } - ary.to_a.should eq([1]) - ary.value.should be_a(Int32) - end - - it "when there are three values and none matches" do - ary = Crystal::ZeroOneOrMany(Int32).new - ary.concat([1, 2, 3]) - ary.reject! { |x| x == 4 } - ary.to_a.should eq([1, 2, 3]) - ary.value.should be_a(Array(Int32)) - end - - it "when there are three values and two match" do - ary = Crystal::ZeroOneOrMany(Int32).new - ary.concat([1, 2, 3]) - ary.reject! { |x| x < 3 } - ary.to_a.should eq([3]) - ary.value.should be_a(Int32) - end - - it "when there are three values and all match" do - ary = Crystal::ZeroOneOrMany(Int32).new - ary.concat([1, 2, 3]) - ary.reject! { |x| x < 4 } - ary.empty?.should be_true - ary.value.should be_nil - end - end -end diff --git a/src/compiler/crystal/codegen/call.cr b/src/compiler/crystal/codegen/call.cr index 598204154ef0..2f92d217ff80 100644 --- a/src/compiler/crystal/codegen/call.cr +++ b/src/compiler/crystal/codegen/call.cr @@ -7,7 +7,7 @@ class Crystal::CodeGenVisitor end target_defs = node.target_defs - if target_defs.empty? + unless target_defs node.raise "BUG: no target defs" end @@ -394,7 +394,7 @@ class Crystal::CodeGenVisitor position_at_end current_def_label # Prepare this specific call - call.target_defs = ZeroOneOrMany.new(a_def) + call.target_defs = [a_def] of Def call.obj.try &.set_type(a_def.owner) call.args.zip(a_def.args) do |call_arg, a_def_arg| call_arg.set_type(a_def_arg.type) diff --git a/src/compiler/crystal/interpreter/compiler.cr b/src/compiler/crystal/interpreter/compiler.cr index bb6cadd88fc7..5f480e7d18ab 100644 --- a/src/compiler/crystal/interpreter/compiler.cr +++ b/src/compiler/crystal/interpreter/compiler.cr @@ -1802,7 +1802,7 @@ class Crystal::Repl::Compiler < Crystal::Visitor end target_defs = node.target_defs - if target_defs.empty? + unless target_defs node.raise "BUG: no target defs" end @@ -2458,7 +2458,7 @@ class Crystal::Repl::Compiler < Crystal::Visitor end target_defs = call.target_defs - if target_defs.empty? + unless target_defs call.raise "BUG: no target defs" end diff --git a/src/compiler/crystal/interpreter/multidispatch.cr b/src/compiler/crystal/interpreter/multidispatch.cr index 005c67dca47e..3c22a7dec8a5 100644 --- a/src/compiler/crystal/interpreter/multidispatch.cr +++ b/src/compiler/crystal/interpreter/multidispatch.cr @@ -39,7 +39,7 @@ require "../semantic/main_visitor" # end # ``` module Crystal::Repl::Multidispatch - def self.create_def(context : Context, node : Call, target_defs : ZeroOneOrMany(Def)) + def self.create_def(context : Context, node : Call, target_defs : Array(Def)) if node.block a_def = create_def_uncached(context, node, target_defs) @@ -70,7 +70,7 @@ module Crystal::Repl::Multidispatch a_def end - private def self.create_def_uncached(context : Context, node : Call, target_defs : ZeroOneOrMany(Def)) + private def self.create_def_uncached(context : Context, node : Call, target_defs : Array(Def)) autocast_types = nil # The generated multidispatch method should handle autocasted @@ -200,7 +200,7 @@ module Crystal::Repl::Multidispatch end call = Call.new(call_obj, node.name, call_args) - call.target_defs = ZeroOneOrMany(Def).new(target_def) + call.target_defs = [target_def] call.type = target_def.type if block diff --git a/src/compiler/crystal/semantic/bindings.cr b/src/compiler/crystal/semantic/bindings.cr index 1705b4688f63..bba14d9be92b 100644 --- a/src/compiler/crystal/semantic/bindings.cr +++ b/src/compiler/crystal/semantic/bindings.cr @@ -1,7 +1,7 @@ module Crystal class ASTNode - property dependencies = ZeroOneOrMany(ASTNode).new - property observers = ZeroOneOrMany(ASTNode).new + property! dependencies : Array(ASTNode) + property observers : Array(ASTNode)? property enclosing_call : Call? @dirty = false @@ -107,17 +107,17 @@ module Crystal end def bind_to(node : ASTNode) : Nil - bind(node) do - @dependencies.push node + bind(node) do |dependencies| + dependencies.push node node.add_observer self end end - def bind_to(nodes : Indexable(ASTNode)) : Nil + def bind_to(nodes : Indexable) : Nil return if nodes.empty? - bind do - @dependencies.concat nodes + bind do |dependencies| + dependencies.concat nodes nodes.each &.add_observer self end end @@ -130,7 +130,9 @@ module Crystal raise_frozen_type freeze_type, from_type, from end - yield + dependencies = @dependencies ||= [] of ASTNode + + yield dependencies new_type = type_from_dependencies new_type = map_type(new_type) if new_type @@ -151,28 +153,27 @@ module Crystal Type.merge dependencies end - def unbind_from(nodes : Nil) : Nil + def unbind_from(nodes : Nil) # Nothing to do end - def unbind_from(node : ASTNode) : Nil - @dependencies.reject! &.same?(node) + def unbind_from(node : ASTNode) + @dependencies.try &.reject! &.same?(node) node.remove_observer self end - def unbind_from(nodes : Enumerable(ASTNode)) : Nil - @dependencies.reject! { |dependency| - nodes.any? &.same?(dependency) - } + def unbind_from(nodes : Array(ASTNode)) + @dependencies.try &.reject! { |dep| nodes.any? &.same?(dep) } nodes.each &.remove_observer self end - def add_observer(observer : ASTNode) : Nil - @observers.push observer + def add_observer(observer) + observers = @observers ||= [] of ASTNode + observers.push observer end - def remove_observer(observer : ASTNode) : Nil - @observers.reject! &.same?(observer) + def remove_observer(observer) + @observers.try &.reject! &.same?(observer) end def set_enclosing_call(enclosing_call) @@ -194,9 +195,9 @@ module Crystal end def notify_observers - @observers.each &.update self + @observers.try &.each &.update self @enclosing_call.try &.recalculate - @observers.each &.propagate + @observers.try &.each &.propagate @enclosing_call.try &.propagate end @@ -268,8 +269,8 @@ module Crystal visited = Set(ASTNode).new.compare_by_identity owner_trace << node if node.type?.try &.includes_type?(owner) visited.add node - until node.dependencies.empty? - dependencies = node.dependencies.select { |dep| dep.type? && dep.type.includes_type?(owner) && !visited.includes?(dep) } + while deps = node.dependencies? + dependencies = deps.select { |dep| dep.type? && dep.type.includes_type?(owner) && !visited.includes?(dep) } if dependencies.size > 0 node = dependencies.first nil_reason = node.nil_reason if node.is_a?(MetaTypeVar) diff --git a/src/compiler/crystal/semantic/call.cr b/src/compiler/crystal/semantic/call.cr index a3c16f7c2a33..83fe5f7f88e0 100644 --- a/src/compiler/crystal/semantic/call.cr +++ b/src/compiler/crystal/semantic/call.cr @@ -7,7 +7,7 @@ class Crystal::Call property! scope : Type property with_scope : Type? property! parent_visitor : MainVisitor - property target_defs = ZeroOneOrMany(Def).new + property target_defs : Array(Def)? property expanded : ASTNode? property expanded_macro : Macro? property? uses_with_scope = false @@ -20,14 +20,15 @@ class Crystal::Call end def target_def - case target_defs.size - when 0 - ::raise "Zero target defs for #{self}" - when 1 - target_defs.first - else - ::raise "#{target_defs.size} target defs for #{self}" + if defs = @target_defs + if defs.size == 1 + return defs.first + else + ::raise "#{defs.size} target defs for #{self}" + end end + + ::raise "Zero target defs for #{self}" end def recalculate @@ -77,10 +78,10 @@ class Crystal::Call block = @block - unbind_from target_defs unless target_defs.empty? + unbind_from @target_defs if @target_defs unbind_from block.break if block - @target_defs = ZeroOneOrMany(Def).new + @target_defs = nil if block_arg = @block_arg replace_block_arg_with_block(block_arg) @@ -91,12 +92,11 @@ class Crystal::Call # If @target_defs is set here it means there was a recalculation # fired as a result of a recalculation. We keep the last one. - return unless target_defs.empty? + return if @target_defs - # TODO: optimize zero one or many @target_defs = matches - bind_to matches unless matches.empty? + bind_to matches if matches bind_to block.break if block if (parent_visitor = @parent_visitor) && matches @@ -109,13 +109,13 @@ class Crystal::Call end end - def lookup_matches : ZeroOneOrMany(Def) + def lookup_matches lookup_matches(with_autocast: false) rescue ex : RetryLookupWithLiterals lookup_matches(with_autocast: true) end - def lookup_matches(*, with_autocast = false) : ZeroOneOrMany(Def) + def lookup_matches(*, with_autocast = false) if args.any? { |arg| arg.is_a?(Splat) || arg.is_a?(DoubleSplat) } lookup_matches_with_splat(with_autocast) else @@ -133,7 +133,7 @@ class Crystal::Call end end - def lookup_matches_with_splat(with_autocast) : ZeroOneOrMany(Def) + def lookup_matches_with_splat(with_autocast) # Check if all splat are of tuples arg_types = Array(Type).new(args.size * 2) named_args_types = nil @@ -184,7 +184,7 @@ class Crystal::Call lookup_matches_without_splat arg_types, named_args_types, with_autocast: with_autocast end - def lookup_matches_without_splat(arg_types, named_args_types, with_autocast) : ZeroOneOrMany(Def) + def lookup_matches_without_splat(arg_types, named_args_types, with_autocast) if obj = @obj lookup_matches_in(obj.type, arg_types, named_args_types, with_autocast: with_autocast) elsif name == "super" @@ -198,19 +198,15 @@ class Crystal::Call end end - def lookup_matches_in(owner : AliasType, arg_types, named_args_types, self_type = nil, def_name = self.name, search_in_parents = true, with_autocast = false) : ZeroOneOrMany(Def) + def lookup_matches_in(owner : AliasType, arg_types, named_args_types, self_type = nil, def_name = self.name, search_in_parents = true, with_autocast = false) lookup_matches_in(owner.remove_alias, arg_types, named_args_types, search_in_parents: search_in_parents, with_autocast: with_autocast) end - def lookup_matches_in(owner : UnionType, arg_types, named_args_types, self_type = nil, def_name = self.name, search_in_parents = true, with_autocast = false) : ZeroOneOrMany(Def) - matches = ZeroOneOrMany(Def).new - owner.union_types.each { |type| - matches.concat lookup_matches_in(type, arg_types, named_args_types, search_in_parents: search_in_parents, with_autocast: with_autocast) - } - matches + def lookup_matches_in(owner : UnionType, arg_types, named_args_types, self_type = nil, def_name = self.name, search_in_parents = true, with_autocast = false) + owner.union_types.flat_map { |type| lookup_matches_in(type, arg_types, named_args_types, search_in_parents: search_in_parents, with_autocast: with_autocast) } end - def lookup_matches_in(owner : Program, arg_types, named_args_types, self_type = nil, def_name = self.name, search_in_parents = true, with_autocast = false) : ZeroOneOrMany(Def) + def lookup_matches_in(owner : Program, arg_types, named_args_types, self_type = nil, def_name = self.name, search_in_parents = true, with_autocast = false) lookup_matches_in_type(owner, arg_types, named_args_types, self_type, def_name, search_in_parents: search_in_parents, with_autocast: with_autocast) end @@ -218,26 +214,26 @@ class Crystal::Call lookup_matches_in program, arg_types, named_args_types, search_in_parents: search_in_parents, with_autocast: with_autocast end - def lookup_matches_in(owner : NonGenericModuleType | GenericModuleInstanceType | GenericType, arg_types, named_args_types, self_type = nil, def_name = self.name, search_in_parents = true, with_autocast = false) : ZeroOneOrMany(Def) + def lookup_matches_in(owner : NonGenericModuleType | GenericModuleInstanceType | GenericType, arg_types, named_args_types, self_type = nil, def_name = self.name, search_in_parents = true, with_autocast = false) attach_subclass_observer owner including_types = owner.including_types if including_types lookup_matches_in(including_types, arg_types, named_args_types, search_in_parents: search_in_parents, with_autocast: with_autocast) else - ZeroOneOrMany(Def).new + [] of Def end end - def lookup_matches_in(owner : LibType, arg_types, named_args_types, self_type = nil, def_name = self.name, search_in_parents = true, with_autocast = false) : ZeroOneOrMany(Def) + def lookup_matches_in(owner : LibType, arg_types, named_args_types, self_type = nil, def_name = self.name, search_in_parents = true, with_autocast = false) raise "lib fun call is not supported in dispatch" end - def lookup_matches_in(owner : Type, arg_types, named_args_types, self_type = nil, def_name = self.name, search_in_parents = true, with_autocast = false) : ZeroOneOrMany(Def) + def lookup_matches_in(owner : Type, arg_types, named_args_types, self_type = nil, def_name = self.name, search_in_parents = true, with_autocast = false) lookup_matches_in_type(owner, arg_types, named_args_types, self_type, def_name, search_in_parents: search_in_parents, with_autocast: with_autocast) end - def lookup_matches_with_scope_in(owner, arg_types, named_args_types, with_autocast = false) : ZeroOneOrMany(Def) + def lookup_matches_with_scope_in(owner, arg_types, named_args_types, with_autocast = false) signature = CallSignature.new(name, arg_types, block, named_args_types) matches = lookup_matches_checking_expansion(owner, signature, with_autocast: with_autocast) @@ -255,7 +251,7 @@ class Crystal::Call instantiate signature, matches, owner, self_type: nil, with_autocast: with_autocast end - def lookup_matches_in_type(owner, arg_types, named_args_types, self_type, def_name, search_in_parents, search_in_toplevel = true, with_autocast = false) : ZeroOneOrMany(Def) + def lookup_matches_in_type(owner, arg_types, named_args_types, self_type, def_name, search_in_parents, search_in_toplevel = true, with_autocast = false) signature = CallSignature.new(def_name, arg_types, block, named_args_types) matches = check_tuple_indexer(owner, def_name, args, arg_types) @@ -357,13 +353,12 @@ class Crystal::Call end end - def instantiate(signature, matches, owner, self_type, with_autocast) : ZeroOneOrMany(Def) + def instantiate(signature, matches, owner, self_type, with_autocast) matches.each &.remove_literals if with_autocast block = @block - # TODO: use map - typed_defs = ZeroOneOrMany(Def).new + typed_defs = Array(Def).new(matches.size) matches.each do |match| check_visibility match @@ -458,7 +453,7 @@ class Crystal::Call typed_def.type = return_type if return_type.no_return? || return_type.nil_type? end - def check_tuple_indexer(owner, def_name, args, arg_types) : Matches? + def check_tuple_indexer(owner, def_name, args, arg_types) return unless args.size == 1 case def_name @@ -493,13 +488,13 @@ class Crystal::Call end end - def tuple_indexer_helper(args, arg_types, owner, instance_type, nilable) : Matches? + def tuple_indexer_helper(args, arg_types, owner, instance_type, nilable) index = tuple_indexer_helper_index(args.first, owner, instance_type, nilable) return unless index indexer_def = yield instance_type, index indexer_match = Match.new(indexer_def, arg_types, MatchContext.new(owner, owner)) - Matches.new(ZeroOneOrMany(Match).new(indexer_match), true) + Matches.new([indexer_match] of Match, true) end private def tuple_indexer_helper_index(arg, owner, instance_type, nilable) @@ -579,7 +574,7 @@ class Crystal::Call if index || nilable indexer_def = yield instance_type, (index || -1) indexer_match = Match.new(indexer_def, arg_types, MatchContext.new(owner, owner)) - Matches.new(ZeroOneOrMany(Match).new(indexer_match), true) + Matches.new([indexer_match] of Match, true) else raise "missing key '#{name}' for named tuple #{owner}" end @@ -710,7 +705,7 @@ class Crystal::Call signature = CallSignature.new(previous.name, arg_types, block, named_args_types) context = MatchContext.new(scope, scope, def_free_vars: previous.free_vars) match = Match.new(previous, arg_types, context, named_args_types) - matches = Matches.new(ZeroOneOrMany(Match).new(match), true) + matches = Matches.new([match] of Match, true) unless signature.match(previous_item, context) raise_matches_not_found scope, previous.name, arg_types, named_args_types, matches, with_autocast: with_autocast, number_autocast: !program.has_flag?("no_number_autocast") diff --git a/src/compiler/crystal/semantic/call_error.cr b/src/compiler/crystal/semantic/call_error.cr index 2984ed5fd4c2..875fd5269380 100644 --- a/src/compiler/crystal/semantic/call_error.cr +++ b/src/compiler/crystal/semantic/call_error.cr @@ -643,7 +643,8 @@ class Crystal::Call if obj.is_a?(InstanceVar) scope = self.scope ivar = scope.lookup_instance_var(obj.name) - if ivar.dependencies.size == 1 && ivar.dependencies.first.same?(program.nil_var) + deps = ivar.dependencies? + if deps && deps.size == 1 && deps.first.same?(program.nil_var) similar_name = scope.lookup_similar_instance_var_name(ivar.name) if similar_name msg << colorize(" (#{ivar.name} was never assigned a value, did you mean #{similar_name}?)").yellow.bold diff --git a/src/compiler/crystal/semantic/cleanup_transformer.cr b/src/compiler/crystal/semantic/cleanup_transformer.cr index dd3c4e03bc67..0d7aa41ced5b 100644 --- a/src/compiler/crystal/semantic/cleanup_transformer.cr +++ b/src/compiler/crystal/semantic/cleanup_transformer.cr @@ -562,42 +562,43 @@ module Crystal return exps end - target_defs = node.target_defs - if target_defs.size == 1 - if target_defs.first.is_a?(External) - check_args_are_not_closure node, "can't send closure to C function" - elsif obj_type && obj_type.extern? && node.name.ends_with?('=') - check_args_are_not_closure node, "can't set closure as C #{obj_type.type_desc} member" + if target_defs = node.target_defs + if target_defs.size == 1 + if target_defs[0].is_a?(External) + check_args_are_not_closure node, "can't send closure to C function" + elsif obj_type && obj_type.extern? && node.name.ends_with?('=') + check_args_are_not_closure node, "can't set closure as C #{obj_type.type_desc} member" + end end - end - current_def = @current_def + current_def = @current_def - target_defs.each do |target_def| - if @transformed.add?(target_def) - node.bubbling_exception do - @current_def = target_def - @def_nest_count += 1 - target_def.body = target_def.body.transform(self) - @def_nest_count -= 1 - @current_def = current_def + target_defs.each do |target_def| + if @transformed.add?(target_def) + node.bubbling_exception do + @current_def = target_def + @def_nest_count += 1 + target_def.body = target_def.body.transform(self) + @def_nest_count -= 1 + @current_def = current_def + end end - end - # If the current call targets a method that raises, the method - # where the call happens also raises. - current_def.raises = true if current_def && target_def.raises? - end + # If the current call targets a method that raises, the method + # where the call happens also raises. + current_def.raises = true if current_def && target_def.raises? + end - if target_defs.empty? - exps = [] of ASTNode - if obj = node.obj - exps.push obj + if node.target_defs.not_nil!.empty? + exps = [] of ASTNode + if obj = node.obj + exps.push obj + end + node.args.each { |arg| exps.push arg } + call_exps = Expressions.from exps + call_exps.set_type(exps.last.type?) unless exps.empty? + return call_exps end - node.args.each { |arg| exps.push arg } - call_exps = Expressions.from exps - call_exps.set_type(exps.last.type?) unless exps.empty? - return call_exps end node.replace_splats @@ -1068,7 +1069,10 @@ module Crystal node = super unless node.type? - node.unbind_from node.dependencies + if dependencies = node.dependencies? + node.unbind_from node.dependencies + end + node.bind_to node.expressions end diff --git a/src/compiler/crystal/semantic/cover.cr b/src/compiler/crystal/semantic/cover.cr index 1ae936cba44c..4674d0a12248 100644 --- a/src/compiler/crystal/semantic/cover.cr +++ b/src/compiler/crystal/semantic/cover.cr @@ -4,9 +4,9 @@ require "../types" module Crystal struct Cover getter signature : CallSignature - getter matches : ZeroOneOrMany(Match) + getter matches : Array(Match) - def self.create(signature : CallSignature, matches : ZeroOneOrMany(Match)) + def self.create(signature, matches) if matches matches.empty? ? false : Cover.new(signature, matches) else @@ -14,7 +14,7 @@ module Crystal end end - def initialize(@signature : CallSignature, @matches : ZeroOneOrMany(Match)) + def initialize(@signature, @matches) end def all? diff --git a/src/compiler/crystal/semantic/filters.cr b/src/compiler/crystal/semantic/filters.cr index ebc77d01e675..86938b7e7b77 100644 --- a/src/compiler/crystal/semantic/filters.cr +++ b/src/compiler/crystal/semantic/filters.cr @@ -1,7 +1,7 @@ module Crystal class TypeFilteredNode < ASTNode def initialize(@filter : TypeFilter, @node : ASTNode) - @dependencies = ZeroOneOrMany.new(@node) + @dependencies = [@node] of ASTNode node.add_observer self update(@node) end diff --git a/src/compiler/crystal/semantic/fix_missing_types.cr b/src/compiler/crystal/semantic/fix_missing_types.cr index 07a113c50d20..3a0eb5857d1d 100644 --- a/src/compiler/crystal/semantic/fix_missing_types.cr +++ b/src/compiler/crystal/semantic/fix_missing_types.cr @@ -74,7 +74,7 @@ class Crystal::FixMissingTypes < Crystal::Visitor block.type = @program.no_return end - node.target_defs.each do |target_def| + node.target_defs.try &.each do |target_def| if @fixed.add?(target_def) target_def.type = @program.no_return unless target_def.type? target_def.accept_children self diff --git a/src/compiler/crystal/semantic/lib.cr b/src/compiler/crystal/semantic/lib.cr index f81f63226795..5e0bff6ce3ad 100644 --- a/src/compiler/crystal/semantic/lib.cr +++ b/src/compiler/crystal/semantic/lib.cr @@ -23,10 +23,11 @@ class Crystal::Call obj_type.used = true external.used = true - @target_defs = ZeroOneOrMany(Def).new(external) + untyped_defs = [external] of Def + @target_defs = untyped_defs - self.unbind_from old_target_defs unless old_target_defs.empty? - self.bind_to external + self.unbind_from old_target_defs if old_target_defs + self.bind_to untyped_defs end def check_lib_call_named_args(external) diff --git a/src/compiler/crystal/semantic/main_visitor.cr b/src/compiler/crystal/semantic/main_visitor.cr index 44da862b2a1d..e3c710134e1e 100644 --- a/src/compiler/crystal/semantic/main_visitor.cr +++ b/src/compiler/crystal/semantic/main_visitor.cr @@ -370,7 +370,8 @@ module Crystal var.bind_to(@program.nil_var) var.nil_if_read = false - bind_to_program_nil_var(meta_var) + meta_var.bind_to(@program.nil_var) unless meta_var.dependencies.try &.any? &.same?(@program.nil_var) + node.bind_to(@program.nil_var) end check_mutably_closured meta_var, var @@ -395,10 +396,6 @@ module Crystal end end - private def bind_to_program_nil_var(node) - node.bind_to(@program.nil_var) unless node.dependencies.any? &.same?(@program.nil_var) - end - def visit(node : TypeDeclaration) case var = node.var when Var @@ -600,6 +597,19 @@ module Crystal program.undefined_instance_variable(node, owner, similar_name) end + def first_time_accessing_meta_type_var?(var) + return false if var.uninitialized? + + if var.freeze_type + deps = var.dependencies? + # If no dependencies, it's the case of a global for a regex literal. + # If there are dependencies and it's just one, it's the same var + deps ? deps.size == 1 : false + else + !var.dependencies? + end + end + def visit(node : InstanceVar) var = lookup_instance_var node node.bind_to(var) @@ -1242,7 +1252,7 @@ module Crystal # It can happen that this call is inside an ArrayLiteral or HashLiteral, # was expanded but isn't bound to the expansion because the call (together # with its expansion) was cloned. - if (expanded = node.expanded) && (node.dependencies.empty? || !node.type?) + if (expanded = node.expanded) && (!node.dependencies? || !node.type?) node.bind_to(expanded) end @@ -3246,7 +3256,7 @@ module Crystal def define_special_var(name, value) meta_var, _ = assign_to_meta_var(name) meta_var.bind_to value - bind_to_program_nil_var(meta_var) + meta_var.bind_to program.nil_var unless meta_var.dependencies.any? &.same?(program.nil_var) meta_var.assigned_to = true check_closured meta_var diff --git a/src/compiler/crystal/semantic/match.cr b/src/compiler/crystal/semantic/match.cr index 0e282e497d42..f18572f69291 100644 --- a/src/compiler/crystal/semantic/match.cr +++ b/src/compiler/crystal/semantic/match.cr @@ -130,7 +130,7 @@ module Crystal struct Matches include Enumerable(Match) - property matches : ZeroOneOrMany(Match) + property matches : Array(Match)? property cover : Bool | Cover | Nil property owner : Type? @@ -139,25 +139,32 @@ module Crystal def cover_all? cover = @cover - @success && !@matches.empty? && (cover == true || (cover.is_a?(Cover) && cover.all?)) + matches = @matches + @success && matches && matches.size > 0 && (cover == true || (cover.is_a?(Cover) && cover.all?)) end def empty? - !@success || matches.empty? + return true unless @success + + if matches = @matches + matches.empty? + else + true + end end def each - @success && @matches.each do |match| + @success && @matches.try &.each do |match| yield match end end def size - @matches.size + @matches.try(&.size) || 0 end def [](*args) - Matches.new(@matches.[*args], @cover, @owner, @success) + Matches.new(@matches.try &.[](*args), @cover, @owner, @success) end end end diff --git a/src/compiler/crystal/semantic/method_lookup.cr b/src/compiler/crystal/semantic/method_lookup.cr index c7f1e8087507..9c5e42c12ac0 100644 --- a/src/compiler/crystal/semantic/method_lookup.cr +++ b/src/compiler/crystal/semantic/method_lookup.cr @@ -66,7 +66,7 @@ module Crystal named_args : Array(NamedArgumentType)? class Type - def lookup_matches(signature, owner = self, path_lookup = self, matches_array = ZeroOneOrMany(Match).new, analyze_all = false) + def lookup_matches(signature, owner = self, path_lookup = self, matches_array = nil, analyze_all = false) matches = lookup_matches_without_parents(signature, owner, path_lookup, matches_array, analyze_all: analyze_all) return matches if matches.cover_all? @@ -105,7 +105,7 @@ module Crystal Matches.new(matches_array, cover, owner, false) end - def lookup_matches_without_parents(signature, owner = self, path_lookup = self, matches_array = ZeroOneOrMany(Match).new, analyze_all = false) + def lookup_matches_without_parents(signature, owner = self, path_lookup = self, matches_array = nil, analyze_all = false) if defs = self.defs.try &.[signature.name]? context = MatchContext.new(owner, path_lookup) @@ -129,7 +129,8 @@ module Crystal next if exact_match if match - matches_array.push match + matches_array ||= [] of Match + matches_array << match # If the argument types are compatible with the match's argument types, # we are done. We don't just compare types with ==, there is a special case: @@ -157,7 +158,7 @@ module Crystal Matches.new(matches_array, Cover.create(signature, matches_array), owner) end - def lookup_matches_with_modules(signature, owner = self, path_lookup = self, matches_array = ZeroOneOrMany(Match).new, analyze_all = false) + def lookup_matches_with_modules(signature, owner = self, path_lookup = self, matches_array = nil, analyze_all = false) matches = lookup_matches_without_parents(signature, owner, path_lookup, matches_array, analyze_all: analyze_all) return matches unless matches.empty? @@ -439,7 +440,7 @@ module Crystal if is_new && subtype_matches.empty? other_initializers = subtype_lookup.instance_type.lookup_defs_with_modules("initialize") unless other_initializers.empty? - return Matches.new(ZeroOneOrMany(Match).new, false) + return Matches.new(nil, false) end end @@ -447,7 +448,7 @@ module Crystal # def, we need to copy it to the subclass so that @name, @instance_vars and other # macro vars resolve correctly. if subtype_matches.empty? - new_subtype_matches = ZeroOneOrMany(Match).new + new_subtype_matches = nil base_type_matches.each do |base_type_match| if base_type_match.def.macro_def? @@ -479,6 +480,7 @@ module Crystal changes << Change.new(change_owner, cloned_def) end + new_subtype_matches ||= [] of Match new_subtype_matches.push Match.new(cloned_def, full_subtype_match.arg_types, MatchContext.new(subtype_lookup, full_subtype_match.context.defining_type, full_subtype_match.context.free_vars), full_subtype_match.named_arg_types) end @@ -487,7 +489,7 @@ module Crystal end end - unless new_subtype_matches.empty? + if new_subtype_matches subtype_matches = Matches.new(new_subtype_matches, Cover.create(signature, new_subtype_matches)) end end diff --git a/src/compiler/crystal/semantic/type_merge.cr b/src/compiler/crystal/semantic/type_merge.cr index 9b2fe4e236f5..0d79faee06b1 100644 --- a/src/compiler/crystal/semantic/type_merge.cr +++ b/src/compiler/crystal/semantic/type_merge.cr @@ -2,7 +2,7 @@ require "../program" module Crystal class Program - def type_merge(types : Indexable(Type?)) : Type? + def type_merge(types : Array(Type?)) : Type? case types.size when 0 nil @@ -17,7 +17,7 @@ module Crystal end end - def type_merge(nodes : Indexable(ASTNode)) : Type? + def type_merge(nodes : Array(ASTNode)) : Type? case nodes.size when 0 nil @@ -161,11 +161,11 @@ module Crystal end class Type - def self.merge(nodes : Indexable(ASTNode)) : Type? + def self.merge(nodes : Array(ASTNode)) : Type? nodes.find(&.type?).try &.type.program.type_merge(nodes) end - def self.merge(types : Indexable(Type)) : Type? + def self.merge(types : Array(Type)) : Type? if types.size == 0 nil else diff --git a/src/compiler/crystal/semantic/warnings.cr b/src/compiler/crystal/semantic/warnings.cr index 437632bc9953..0dae71c5cd06 100644 --- a/src/compiler/crystal/semantic/warnings.cr +++ b/src/compiler/crystal/semantic/warnings.cr @@ -26,7 +26,7 @@ module Crystal return unless @warnings.level.all? return if compiler_expanded_call(node) - node.target_defs.each do |target_def| + node.target_defs.try &.each do |target_def| check_deprecation(target_def, node, @deprecated_methods_detected) end end diff --git a/src/compiler/crystal/tools/context.cr b/src/compiler/crystal/tools/context.cr index 7bf49397fd7c..5bc6dd43e0e9 100644 --- a/src/compiler/crystal/tools/context.cr +++ b/src/compiler/crystal/tools/context.cr @@ -77,11 +77,13 @@ module Crystal def visit(node : Call) return false if node.obj.nil? && node.name == "raise" - node.target_defs.each do |typed_def| - typed_def.accept(self) - next unless @context_visitor.def_with_yield.not_nil!.location == typed_def.location - @context_visitor.inside_typed_def do - typed_def.accept(@context_visitor) + node.target_defs.try do |defs| + defs.each do |typed_def| + typed_def.accept(self) + next unless @context_visitor.def_with_yield.not_nil!.location == typed_def.location + @context_visitor.inside_typed_def do + typed_def.accept(@context_visitor) + end end end true diff --git a/src/compiler/crystal/tools/implementations.cr b/src/compiler/crystal/tools/implementations.cr index 4e49f18c4712..f4f3a390f0eb 100644 --- a/src/compiler/crystal/tools/implementations.cr +++ b/src/compiler/crystal/tools/implementations.cr @@ -109,8 +109,10 @@ module Crystal def visit(node : Call) return contains_target(node) unless node.location && @target_location.between?(node.name_location, node.name_end_location) - node.target_defs.each do |target_def| - @locations << target_def.location.not_nil! + if target_defs = node.target_defs + target_defs.each do |target_def| + @locations << target_def.location.not_nil! + end end end diff --git a/src/compiler/crystal/zero_one_or_many.cr b/src/compiler/crystal/zero_one_or_many.cr deleted file mode 100644 index 80c1557e09cb..000000000000 --- a/src/compiler/crystal/zero_one_or_many.cr +++ /dev/null @@ -1,117 +0,0 @@ -# An Array(T)-like type that's optimized for the case of -# frequently having zero or one elements. -struct Crystal::ZeroOneOrMany(T) - include Indexable(T) - - getter value : Nil | T | Array(T) - - def initialize - @value = nil - end - - def initialize(@value : T) - end - - def unsafe_fetch(index : Int) - value = @value - case value - in Nil - raise IndexError.new("Called ZeroOneOrMany#unsafe_fetch but value is nil") - in T - if index != 0 - raise IndexError.new("Called ZeroOneOrMany#unsafe_fetch with index != 0 but value is not an array") - end - - value - in Array(T) - value.unsafe_fetch(index) - end - end - - def each(& : T ->) - value = @value - case value - in Nil - # Nothing to do - in T - yield value - in Array(T) - value.each do |element| - yield element - end - end - end - - def size : Int32 - value = @value - case value - in Nil - 0 - in T - 1 - in Array(T) - value.size - end - end - - def <<(element : T) : self - push(element) - end - - def push(element : T) : self - value = @value - case value - in Nil - @value = element - in T - @value = [value, element] of T - in Array(T) - value.push element - end - self - end - - def concat(elements : Indexable(T)) : self - value = @value - case value - in Nil - case elements.size - when 0 - # Nothing to do - when 1 - @value = elements.first - else - @value = elements.map(&.as(T)).to_a - end - in T - new_value = Array(T).new(elements.size + 1) - new_value.push(value) - new_value.concat(elements) - @value = new_value - in Array(T) - value.concat(elements) - end - self - end - - def reject!(&block : T -> _) : self - value = @value - case value - in Nil - # Nothing to do - in T - if yield value - @value = nil - end - in Array(T) - value.reject! { |element| yield element } - case value.size - when 0 - @value = nil - when 1 - @value = value.first - end - end - self - end -end From 49ab4252b2e778400d70c1747a7154be417c117d Mon Sep 17 00:00:00 2001 From: Pete Brumm Date: Sat, 17 Dec 2022 07:09:39 -0600 Subject: [PATCH 39/41] Fix: Read `UInt` in zip directory header (#12822) Co-authored-by: Sijawusz Pur Rahnama --- spec/std/compress/zip/zip_file_spec.cr | 17 +++++++++++++++++ src/compress/zip/file.cr | 8 ++++---- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/spec/std/compress/zip/zip_file_spec.cr b/spec/std/compress/zip/zip_file_spec.cr index 89a1a7ad1606..24deddb5cb06 100644 --- a/spec/std/compress/zip/zip_file_spec.cr +++ b/spec/std/compress/zip/zip_file_spec.cr @@ -127,6 +127,23 @@ describe Compress::Zip do end end + it "writes over int16 files to make sure we can parse" do + io = IO::Memory.new + + Compress::Zip::Writer.open(io) do |zip| + 0_u16.upto(UInt16::MAX - 1).each do |index| + zip.add Compress::Zip::Writer::Entry.new("foo_#{index}.txt", comment: "some comment"), + "contents of foo" + end + end + + io.rewind + + Compress::Zip::File.open(io) do |zip| + zip.entries.size.should eq(UInt16::MAX) + end + end + typeof(Compress::Zip::File.new("file.zip")) typeof(Compress::Zip::File.open("file.zip") { }) end diff --git a/src/compress/zip/file.cr b/src/compress/zip/file.cr index ba2c133231c9..166626a0b73f 100644 --- a/src/compress/zip/file.cr +++ b/src/compress/zip/file.cr @@ -121,10 +121,10 @@ class Compress::Zip::File raise Error.new("Expected end of central directory header signature, not 0x#{signature.to_s(16)}") end - read Int16 # number of this disk - read Int16 # disk start - read Int16 # number of entries in disk - entries_size = read Int16 # number of total entries + read UInt16 # number of this disk + read UInt16 # disk start + read UInt16 # number of entries in disk + entries_size = read UInt16 # number of total entries read UInt32 # size of the central directory directory_offset = read UInt32 # offset of central directory comment_length = read UInt16 # comment length From f4ae5f033b379fb96c00b04e344e6c17994b50e7 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sat, 17 Dec 2022 13:03:49 -0300 Subject: [PATCH 40/41] Add lib functions earlier so that they are visible in top-level macros (#12848) --- spec/compiler/semantic/lib_spec.cr | 14 ++++++++++++++ src/compiler/crystal/semantic/top_level_visitor.cr | 9 ++++++++- .../crystal/semantic/type_declaration_visitor.cr | 8 ++++---- 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/spec/compiler/semantic/lib_spec.cr b/spec/compiler/semantic/lib_spec.cr index 70c2a7b11819..1ee3c7aa99c6 100644 --- a/spec/compiler/semantic/lib_spec.cr +++ b/spec/compiler/semantic/lib_spec.cr @@ -962,4 +962,18 @@ describe "Semantic: lib" do ), "passing Void return value of lib fun call has no effect" end + + it "can list lib functions at the top level (#12395)" do + assert_type(%( + lib LibFoo + fun foo + end + + {% if LibFoo.methods.size == 1 %} + true + {% else %} + 1 + {% end %} + )) { bool } + end end diff --git a/src/compiler/crystal/semantic/top_level_visitor.cr b/src/compiler/crystal/semantic/top_level_visitor.cr index a3e4fbdd4a2c..dea95f0f9494 100644 --- a/src/compiler/crystal/semantic/top_level_visitor.cr +++ b/src/compiler/crystal/semantic/top_level_visitor.cr @@ -914,7 +914,12 @@ class Crystal::TopLevelVisitor < Crystal::SemanticVisitor annotations = read_annotations - external = External.new(node.name, ([] of Arg), node.body, node.real_name).at(node) + # We'll resolve the external args types later, in TypeDeclarationVisitor + external_args = node.args.map do |arg| + Arg.new(arg.name).at(arg.location) + end + + external = External.new(node.name, external_args, node.body, node.real_name).at(node) call_convention = nil process_def_annotations(external, annotations) do |annotation_type, ann| @@ -947,6 +952,8 @@ class Crystal::TopLevelVisitor < Crystal::SemanticVisitor external.fun_def = node node.external = external + current_type.add_def(external) + false end diff --git a/src/compiler/crystal/semantic/type_declaration_visitor.cr b/src/compiler/crystal/semantic/type_declaration_visitor.cr index db557ee6d634..bcb3a63e6eb6 100644 --- a/src/compiler/crystal/semantic/type_declaration_visitor.cr +++ b/src/compiler/crystal/semantic/type_declaration_visitor.cr @@ -107,14 +107,16 @@ class Crystal::TypeDeclarationVisitor < Crystal::SemanticVisitor def visit(node : FunDef) external = node.external - node.args.each do |arg| + node.args.each_with_index do |arg, index| restriction = arg.restriction.not_nil! arg_type = lookup_type(restriction) arg_type = check_allowed_in_lib(restriction, arg_type) if arg_type.remove_typedef.void? restriction.raise "can't use Void as parameter type" end - external.args << Arg.new(arg.name, type: arg_type).at(arg.location) + + # The external args were added in TopLevelVisitor + external.args[index].type = arg_type end node_return_type = node.return_type @@ -131,8 +133,6 @@ class Crystal::TypeDeclarationVisitor < Crystal::SemanticVisitor old_external = add_external external old_external.dead = true if old_external - current_type.add_def(external) - if current_type.is_a?(Program) key = DefInstanceKey.new external.object_id, external.args.map(&.type), nil, nil program.add_def_instance key, external From adfc4ce39f28773905ecadf429e172881d5afdac Mon Sep 17 00:00:00 2001 From: Mike Robbins Date: Tue, 20 Dec 2022 06:27:30 -0500 Subject: [PATCH 41/41] Fix `HTTP::Client` certificate validation error on FQDN (host with trailing dot) (#12778) Co-authored-by: Quinton Miller --- spec/manual/https_client_spec.cr | 4 ++++ spec/std/uri_spec.cr | 4 ++++ src/http/client.cr | 2 +- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/spec/manual/https_client_spec.cr b/spec/manual/https_client_spec.cr index 3780a088cbc2..5c1dc11199b2 100644 --- a/spec/manual/https_client_spec.cr +++ b/spec/manual/https_client_spec.cr @@ -7,6 +7,10 @@ describe "https requests" do HTTP::Client.get("https://google.com") end + it "can fetch from google.com. FQDN with trailing dot (#12777)" do + HTTP::Client.get("https://google.com.") + end + it "can close request before consuming body" do HTTP::Client.get("https://crystal-lang.org") do break diff --git a/spec/std/uri_spec.cr b/spec/std/uri_spec.cr index 5e8bb02f7835..96aea2e16989 100644 --- a/spec/std/uri_spec.cr +++ b/spec/std/uri_spec.cr @@ -68,6 +68,10 @@ describe "URI" do assert_uri("http://[::1]:81/", scheme: "http", host: "[::1]", port: 81, path: "/") assert_uri("http://192.0.2.16:81/", scheme: "http", host: "192.0.2.16", port: 81, path: "/") + # preserves fully-qualified host with trailing dot + assert_uri("https://example.com./", scheme: "https", host: "example.com.", path: "/") + assert_uri("https://example.com.:8443/", scheme: "https", host: "example.com.", port: 8443, path: "/") + # port it { URI.parse("http://192.168.0.2:/foo").should eq URI.new(scheme: "http", host: "192.168.0.2", path: "/foo") } diff --git a/src/http/client.cr b/src/http/client.cr index 80f3506b60a3..eeb6300f4f5d 100644 --- a/src/http/client.cr +++ b/src/http/client.cr @@ -801,7 +801,7 @@ class HTTP::Client if tls = @tls tcp_socket = io begin - io = OpenSSL::SSL::Socket::Client.new(tcp_socket, context: tls, sync_close: true, hostname: @host) + io = OpenSSL::SSL::Socket::Client.new(tcp_socket, context: tls, sync_close: true, hostname: @host.rchop('.')) rescue exc # don't leak the TCP socket when the SSL connection failed tcp_socket.close