diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3430db1de7d..b732b6ba2c7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -113,6 +113,7 @@ jobs: cargo install --locked --debug --path ./forc-plugins/forc-fmt cargo install --locked --debug --path ./forc-plugins/forc-lsp cargo install --locked --debug --path ./forc-plugins/forc-client + cargo install --locked --debug --path ./forc-plugins/forc-debug cargo install --locked --debug --path ./forc-plugins/forc-doc cargo install --locked --debug --path ./forc-plugins/forc-tx cargo install --locked --debug --path ./forc-plugins/forc-crypto @@ -401,10 +402,14 @@ jobs: uses: dtolnay/rust-toolchain@master with: toolchain: ${{ env.RUST_VERSION }} + - name: Install fuel-core for tests + uses: baptiste0928/cargo-install@v2 + with: + crate: fuel-core-bin + version: "0.21" - uses: Swatinem/rust-cache@v2 - name: Run tests run: cargo test --locked --release - cargo-unused-deps-check: runs-on: ubuntu-latest steps: @@ -471,6 +476,7 @@ jobs: ./.github/workflows/scripts/verify_tag.sh ${{ github.ref_name }} forc/Cargo.toml ./.github/workflows/scripts/verify_tag.sh ${{ github.ref_name }} forc-pkg/Cargo.toml ./.github/workflows/scripts/verify_tag.sh ${{ github.ref_name }} forc-plugins/forc-client/Cargo.toml + ./.github/workflows/scripts/verify_tag.sh ${{ github.ref_name }} forc-plugins/forc-debug/Cargo.toml ./.github/workflows/scripts/verify_tag.sh ${{ github.ref_name }} forc-plugins/forc-doc/Cargo.toml ./.github/workflows/scripts/verify_tag.sh ${{ github.ref_name }} forc-plugins/forc-fmt/Cargo.toml ./.github/workflows/scripts/verify_tag.sh ${{ github.ref_name }} forc-plugins/forc-lsp/Cargo.toml @@ -739,13 +745,13 @@ jobs: - name: Strip release binaries x86_64-linux-gnu if: matrix.job.target == 'x86_64-unknown-linux-gnu' run: | - for BINARY in forc forc-fmt forc-lsp forc-deploy forc-run forc-doc forc-crypto forc-tx forc-submit; do + for BINARY in forc forc-fmt forc-lsp forc-debug forc-deploy forc-run forc-doc forc-crypto forc-tx forc-submit; do strip "target/${{ matrix.job.target }}/release/$BINARY" done - name: Strip release binaries aarch64-linux-gnu if: matrix.job.target == 'aarch64-unknown-linux-gnu' run: | - for BINARY in forc forc-fmt forc-lsp forc-deploy forc-run forc-doc forc-crypto forc-tx forc-submit; do + for BINARY in forc forc-fmt forc-lsp forc-debug forc-deploy forc-run forc-doc forc-crypto forc-tx forc-submit; do docker run --rm -v \ "$PWD/target:/target:Z" \ ghcr.io/cross-rs/${{ matrix.job.target }}:main \ @@ -755,7 +761,7 @@ jobs: - name: Strip release binaries mac if: matrix.job.os == 'macos-latest' run: | - for BINARY in forc forc-fmt forc-lsp forc-deploy forc-run forc-doc forc-crypto forc-tx forc-submit; do + for BINARY in forc forc-fmt forc-lsp forc-debug forc-deploy forc-run forc-doc forc-crypto forc-tx forc-submit; do strip -x "target/${{ matrix.job.target }}/release/$BINARY" done @@ -769,7 +775,7 @@ jobs: ZIP_FILE_NAME=forc-binaries-${{ env.PLATFORM_NAME }}_${{ env.ARCH }}.tar.gz echo "ZIP_FILE_NAME=$ZIP_FILE_NAME" >> $GITHUB_ENV mkdir -pv ./forc-binaries - for BINARY in forc forc-fmt forc-lsp forc-deploy forc-run forc-doc forc-crypto forc-tx forc-submit; do + for BINARY in forc forc-fmt forc-lsp forc-debug forc-deploy forc-run forc-doc forc-crypto forc-tx forc-submit; do cp "target/${{ matrix.job.target }}/release/$BINARY" ./forc-binaries done tar -czvf $ZIP_FILE_NAME ./forc-binaries diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index c1f83a27c3c..ad352609cf9 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -12,4 +12,5 @@ jobs: test: uses: FuelLabs/github-actions/.github/workflows/mdbook-docs.yml@master with: - docs-src-path: 'docs/book/src' \ No newline at end of file + docs-src-path: 'docs/book/src' + spellcheck-config-path: 'docs/book/.spellcheck.yml' \ No newline at end of file diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index dc8b6dc0c3f..fa2ac16b866 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -28,6 +28,7 @@ jobs: cargo install --locked --debug --path ./forc-plugins/forc-fmt cargo install --locked --debug --path ./forc-plugins/forc-lsp cargo install --locked --debug --path ./forc-plugins/forc-client + cargo install --locked --debug --path ./forc-plugins/forc-debug cargo install --locked --debug --path ./forc-plugins/forc-doc cargo install --locked --debug --path ./forc-plugins/forc-crypto cargo install --locked --debug forc-explore diff --git a/.gitignore b/.gitignore index 5a70f037099..d241d3df6af 100644 --- a/.gitignore +++ b/.gitignore @@ -19,5 +19,11 @@ out # Benchmarks data output directory benchmarks/ +# Generated client schema +**/schema.graphql + # .DS_Store -.DS_Store \ No newline at end of file +.DS_Store + +# Generated files in test directories +sway-lsp/tests/**/Forc.lock diff --git a/Cargo.lock b/Cargo.lock index 10e2139d624..26f49a97043 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -33,7 +33,7 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "cipher", "cpufeatures", ] @@ -44,7 +44,7 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "getrandom 0.2.11", "once_cell", "version_check", @@ -325,7 +325,7 @@ checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" dependencies = [ "addr2line", "cc", - "cfg-if", + "cfg-if 1.0.0", "libc", "miniz_oxide", "object", @@ -477,7 +477,7 @@ dependencies = [ "arrayref", "arrayvec 0.7.4", "cc", - "cfg-if", + "cfg-if 1.0.0", "constant_time_eq 0.3.0", ] @@ -584,6 +584,12 @@ dependencies = [ "libc", ] +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + [[package]] name = "cfg-if" version = "1.0.0" @@ -768,6 +774,17 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" +[[package]] +name = "clipboard-win" +version = "4.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7191c27c2357d9b7ef96baac1773290d4ca63b24205b82a3fd8a0637afcf0362" +dependencies = [ + "error-code", + "str-buf", + "winapi", +] + [[package]] name = "cobs" version = "0.2.3" @@ -1012,7 +1029,7 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", ] [[package]] @@ -1063,7 +1080,7 @@ version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "crossbeam-utils", ] @@ -1073,7 +1090,7 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "crossbeam-epoch", "crossbeam-utils", ] @@ -1085,7 +1102,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" dependencies = [ "autocfg", - "cfg-if", + "cfg-if 1.0.0", "crossbeam-utils", "memoffset 0.9.0", "scopeguard", @@ -1097,7 +1114,7 @@ version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", ] [[package]] @@ -1183,7 +1200,7 @@ version = "4.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e89b8c6a2e4b1f45971ad09761aafb85514a84744b67a95e32c3cc1352d1f65c" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "cpufeatures", "curve25519-dalek-derive", "digest 0.10.7", @@ -1320,7 +1337,7 @@ version = "5.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "hashbrown 0.14.3", "lock_api", "once_cell", @@ -1420,6 +1437,12 @@ version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" +[[package]] +name = "difference" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" + [[package]] name = "digest" version = "0.9.0" @@ -1482,7 +1505,7 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "dirs-sys-next", ] @@ -1628,9 +1651,15 @@ version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", ] +[[package]] +name = "endian-type" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" + [[package]] name = "entities" version = "1.0.1" @@ -1679,6 +1708,38 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "error-chain" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" +dependencies = [ + "backtrace", + "version_check", +] + +[[package]] +name = "error-code" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64f18991e7bf11e7ffee451b5318b5c1a73c52d0d0ada6e5a3017c8c1ced6a21" +dependencies = [ + "libc", + "str-buf", +] + +[[package]] +name = "escargot" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "768064bd3a0e2bedcba91dc87ace90beea91acc41b6a01a3ca8e9aa8827461bf" +dependencies = [ + "log", + "once_cell", + "serde", + "serde_json", +] + [[package]] name = "eth-keystore" version = "0.5.0" @@ -1786,13 +1847,23 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +[[package]] +name = "fd-lock" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0010f02effd88c702318c5dde0463206be67495d0b4d906ba7c0a8f166cc7f06" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "fd-lock" version = "4.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b93f7a0db71c99f68398f80653ed05afb0b00e062e1a20c7ff849c4edfabbbcc" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "rustix", "windows-sys 0.52.0", ] @@ -1840,7 +1911,7 @@ version = "0.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "libc", "redox_syscall 0.4.1", "windows-sys 0.52.0", @@ -1973,6 +2044,24 @@ dependencies = [ "tracing", ] +[[package]] +name = "forc-debug" +version = "0.48.1" +dependencies = [ + "anyhow", + "clap 3.2.25", + "escargot", + "fuel-core-client", + "fuel-types", + "fuel-vm", + "portpicker", + "rexpect", + "serde_json", + "shellfish", + "thiserror", + "tokio", +] + [[package]] name = "forc-doc" version = "0.48.1" @@ -2110,7 +2199,7 @@ dependencies = [ "anyhow", "clap 3.2.25", "dirs 3.0.2", - "fd-lock", + "fd-lock 4.0.1", "forc-tracing", "fuel-tx", "hex", @@ -2806,7 +2895,7 @@ version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877e94aff08e743b651baaea359664321055749b398adff8740a7399af7796e7" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", ] [[package]] @@ -2826,7 +2915,7 @@ version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "libc", "wasi 0.9.0+wasi-snapshot-preview1", ] @@ -2837,7 +2926,7 @@ version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "libc", "wasi 0.11.0+wasi-snapshot-preview1", ] @@ -3478,7 +3567,7 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", ] [[package]] @@ -3506,7 +3595,7 @@ checksum = "9b74065805db266ba2c6edbd670b23c4714824a955628472b2e46cc9f3a869cb" dependencies = [ "async-trait", "bytes", - "cfg-if", + "cfg-if 1.0.0", "common-multipart-rfc7578", "dirs 4.0.0", "futures", @@ -3589,7 +3678,7 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f01b677d82ef7a676aa37e099defd83a28e15687112cafdd112d60236b6115b" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "ecdsa", "elliptic-curve", "once_cell", @@ -4169,6 +4258,40 @@ dependencies = [ "tempfile", ] +[[package]] +name = "nibble_vec" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" +dependencies = [ + "smallvec", +] + +[[package]] +name = "nix" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c722bee1037d430d0f8e687bbdbf222f27cc6e4e68d5caf630857bb2b6dbdce" +dependencies = [ + "bitflags 1.3.2", + "cc", + "cfg-if 0.1.10", + "libc", + "void", +] + +[[package]] +name = "nix" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa9b4819da1bc61c0ea48b63b7bc8604064dd43013e7cc325df098d49cd7c18a" +dependencies = [ + "bitflags 1.3.2", + "cc", + "cfg-if 1.0.0", + "libc", +] + [[package]] name = "normpath" version = "1.1.1" @@ -4416,7 +4539,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b8419dc8cc6d866deb801274bba2e6f8f6108c1bb7fcc10ee5ab864931dbb45" dependencies = [ "bitflags 2.4.1", - "cfg-if", + "cfg-if 1.0.0", "foreign-types", "libc", "once_cell", @@ -4555,7 +4678,7 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "instant", "libc", "redox_syscall 0.2.16", @@ -4569,7 +4692,7 @@ version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "libc", "redox_syscall 0.4.1", "smallvec", @@ -5117,6 +5240,16 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" +[[package]] +name = "radix_trie" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" +dependencies = [ + "endian-type", + "nibble_vec", +] + [[package]] name = "rand" version = "0.8.5" @@ -5358,6 +5491,18 @@ dependencies = [ "substrate-bn", ] +[[package]] +name = "rexpect" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "862c0149d91461fab225ddd06a5af919913f5c57405ed4f27d2466d6cc877186" +dependencies = [ + "error-chain", + "nix 0.14.1", + "regex", + "tempfile", +] + [[package]] name = "rfc6979" version = "0.4.0" @@ -5597,6 +5742,30 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" +[[package]] +name = "rustyline" +version = "8.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbd4eaf7a7738f76c98e4f0395253ae853be3eb018f7b0bb57fe1b6c17e31874" +dependencies = [ + "bitflags 1.3.2", + "cfg-if 1.0.0", + "clipboard-win", + "dirs-next", + "fd-lock 2.0.0", + "libc", + "log", + "memchr", + "nix 0.20.0", + "radix_trie", + "scopeguard", + "smallvec", + "unicode-segmentation", + "unicode-width", + "utf8parse", + "winapi", +] + [[package]] name = "ryu" version = "1.0.15" @@ -5935,7 +6104,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" dependencies = [ "block-buffer 0.9.0", - "cfg-if", + "cfg-if 1.0.0", "cpufeatures", "digest 0.9.0", "opaque-debug", @@ -5947,7 +6116,7 @@ version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "cpufeatures", "digest 0.10.7", ] @@ -5977,6 +6146,19 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" +[[package]] +name = "shellfish" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c66e6fc7a668d89939ee241fb342f401f8c11a80da8fb72fd14681f8d47440b" +dependencies = [ + "async-trait", + "cfg-if 1.0.0", + "rustyline", + "tokio", + "yansi", +] + [[package]] name = "shlex" version = "1.2.0" @@ -6135,6 +6317,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "str-buf" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e08d8363704e6c71fc928674353e6b7c23dcea9d82d7012c8faf2a3a025f8d0" + [[package]] name = "str_indices" version = "0.4.3" @@ -6334,7 +6522,7 @@ dependencies = [ "criterion", "dashmap", "dirs 4.0.0", - "fd-lock", + "fd-lock 4.0.1", "forc-pkg", "forc-tracing", "forc-util", @@ -6431,6 +6619,7 @@ name = "swayfmt" version = "0.48.1" dependencies = [ "anyhow", + "difference", "forc-tracing", "paste", "prettydiff 0.6.4", @@ -6528,7 +6717,7 @@ version = "0.29.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd727fc423c2060f6c92d9534cef765c65a6ed3f428a03d7def74a8c4348e666" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "core-foundation-sys", "libc", "ntapi", @@ -6607,7 +6796,7 @@ version = "3.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "fastrand", "redox_syscall 0.4.1", "rustix", @@ -6774,7 +6963,7 @@ version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "once_cell", ] @@ -7451,7 +7640,7 @@ version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "serde", "serde_json", "wasm-bindgen-macro", @@ -7478,7 +7667,7 @@ version = "0.4.39" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac36a15a220124ac510204aec1c3e5db8a22ab06fd6706d881dc6149f8ed9a12" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "js-sys", "wasm-bindgen", "web-sys", @@ -7815,7 +8004,7 @@ version = "0.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "windows-sys 0.48.0", ] diff --git a/Cargo.toml b/Cargo.toml index 5586e0c2f41..e2d936ef430 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ members = [ "forc-pkg", "forc-plugins/forc-client", "forc-plugins/forc-crypto", + "forc-plugins/forc-debug", "forc-plugins/forc-doc", "forc-plugins/forc-fmt", "forc-plugins/forc-lsp", diff --git a/docs/book/.spellcheck.yml b/docs/book/.spellcheck.yml new file mode 100644 index 00000000000..8030b91c02a --- /dev/null +++ b/docs/book/.spellcheck.yml @@ -0,0 +1,28 @@ +matrix: + - name: SPCheck + aspell: + lang: en + dictionary: + encoding: utf-8 + wordlists: + - docs/book/spell-check-custom-words.txt + pipeline: + - pyspelling.filters.context: + context_visible_first: true + escapes: \\[\\`~] + delimiters: + # Ignore all code blocks + - open: '(?s)^(?P *`{3,}\s*(\w+\s*,?\s*)+.*?)$' + close: '^( *`{3,})$' + - pyspelling.filters.markdown: + markdown_extensions: + - pymdownx.superfences: + - pyspelling.filters.html: + comments: false + ignores: + - code + - pre + sources: + - '**/book/src/*.md' + - '**/book/src/**/*.md' + default_encoding: utf-8 diff --git a/docs/book/spell-check-custom-words.txt b/docs/book/spell-check-custom-words.txt new file mode 100644 index 00000000000..52bc4fc8749 --- /dev/null +++ b/docs/book/spell-check-custom-words.txt @@ -0,0 +1,187 @@ +ABI +ABIs +ASM +IDE +IDEs +LSP +namespace +ALU +APIs +JSON +BrowserStack +CLI +Deserialization +deserializing +DApp +subcurrency +Subcurrency +intrinsics +Intrinsics +workspace +workspaces +Workspaces +neovim +EVM +EVM's +EOA +ERC +Ethereum +Ethereum's +FVM +FuelVM +Fuelup +Github +GraphQL +Infura +JSON +LSP +Merkle +PoA +PoS +PoW +RPC +SDK +SDK's +SDKs +SauceLabs +Sepolia +Structs +Sway +TAI +TODO +TypeScript +UTF +UTXO +UTXOs +Utils +VM +VSCode +abigen +args +async +backend +backtraces +blockchain +blockchain's +bytecode +codespace +codespaces +config +cryptographic +customizable +customizations +dapp +dev +dropdown +enum +enums +env +forc +frontend +fuelup +fullstack +graphQL +graphql +http +https +js +localhost +mainnet +mempool +merkle +monorepo +monorepos +natively +npm +nvm +onboarding +params +pnpm +prerelease +queryable +quickstart +relayer +relayers +repo +repos +runnable +stateful +struct +structs +struct's +testnet +testnets +toolchain +toolchains +urql +validator +validators +superABI +superABIs +SuperABIs +supertraits +compositional +typeclass +turbofish +DSL +TOML +IPFS +Bitwise +Bitwise +runtime +runtimes +formatter +deployable +Utils +ETH +initializer +initializers +destructuring +instantiation +VMs +superset +CEI +pre +entrancy +interoperable +blockchains +keccak +SHA +UI +backtrace +Collateralized +collateralized +submodule +DEX +TypeChain +inlines +inlining +MiB +FuelVM's +deterministically +CLI +VS +GraphViz +DOT +DCA +AST +GitHub +decrypt +subcommand +subcommands +Subcommands +supertrait +supertraits +Supertraits +incrementor +monomorphization +Booleans +boolean +Orchestrator +orchestrator +growable +arity +tuple's +unary +SRC +DEX \ No newline at end of file diff --git a/docs/book/src/SUMMARY.md b/docs/book/src/SUMMARY.md index 195908d9d8e..655b0947318 100644 --- a/docs/book/src/SUMMARY.md +++ b/docs/book/src/SUMMARY.md @@ -10,7 +10,7 @@ - [Examples](./examples/index.md) - [Counter](./examples/counter.md) - [Subcurrency](./examples/subcurrency.md) - - [FizzBuzz](./examples/fizzbuzz.md) + - [`FizzBuzz`](./examples/fizzbuzz.md) - [Wallet Smart Contract](./examples/wallet_smart_contract.md) - [Program Types](./sway-program-types/index.md) - [Contracts](./sway-program-types/smart_contracts.md) @@ -69,27 +69,28 @@ - [Workspaces](./forc/workspaces.md) - [Dependencies](./forc/dependencies.md) - [Commands](./forc/commands/index.md) - - [forc addr2line](./forc/commands/forc_addr2line.md) - - [forc build](./forc/commands/forc_build.md) - - [forc check](./forc/commands/forc_check.md) - - [forc clean](./forc/commands/forc_clean.md) - - [forc completions](./forc/commands/forc_completions.md) - - [forc contract-id](./forc/commands/forc_contract-id.md) - - [forc init](./forc/commands/forc_init.md) - - [forc new](./forc/commands/forc_new.md) - - [forc parse-bytecode](./forc/commands/forc_parse-bytecode.md) - - [forc plugins](./forc/commands/forc_plugins.md) - - [forc predicate-root](./forc/commands/forc_predicate-root.md) - - [forc test](./forc/commands/forc_test.md) - - [forc update](./forc/commands/forc_update.md) - - [forc template](./forc/commands/forc_template.md) + - [`forc addr2line`](./forc/commands/forc_addr2line.md) + - [`forc build`](./forc/commands/forc_build.md) + - [`forc check`](./forc/commands/forc_check.md) + - [`forc clean`](./forc/commands/forc_clean.md) + - [`forc completions`](./forc/commands/forc_completions.md) + - [`forc contract-id`](./forc/commands/forc_contract-id.md) + - [`forc init`](./forc/commands/forc_init.md) + - [`forc new`](./forc/commands/forc_new.md) + - [`forc parse-bytecode`](./forc/commands/forc_parse-bytecode.md) + - [`forc plugins`](./forc/commands/forc_plugins.md) + - [`forc predicate-root`](./forc/commands/forc_predicate-root.md) + - [`forc test`](./forc/commands/forc_test.md) + - [`forc update`](./forc/commands/forc_update.md) + - [`forc template`](./forc/commands/forc_template.md) - [Plugins](./forc/plugins/index.md) - - [forc client](./forc/plugins/forc_client/index.md) - - [forc deploy](./forc/plugins/forc_client/forc_deploy.md) - - [forc run](./forc/plugins/forc_client/forc_run.md) - - [forc submit](./forc/plugins/forc_client/forc_submit.md) - - [forc crypto](./forc/plugins/forc_crypto.md) - - [forc doc](./forc/plugins/forc_doc.md) - - [forc explore](./forc/plugins/forc_explore.md) - - [forc fmt](./forc/plugins/forc_fmt.md) - - [forc lsp](./forc/plugins/forc_lsp.md) + - [`forc client`](./forc/plugins/forc_client/index.md) + - [`forc deploy`](./forc/plugins/forc_client/forc_deploy.md) + - [`forc run`](./forc/plugins/forc_client/forc_run.md) + - [`forc submit`](./forc/plugins/forc_client/forc_submit.md) + - [`forc crypto`](./forc/plugins/forc_crypto.md) + - [`forc debug`](./forc/plugins/forc_debug.md) + - [`forc doc`](./forc/plugins/forc_doc.md) + - [`forc explore`](./forc/plugins/forc_explore.md) + - [`forc fmt`](./forc/plugins/forc_fmt.md) + - [`forc lsp`](./forc/plugins/forc_lsp.md) diff --git a/docs/book/src/advanced/assembly.md b/docs/book/src/advanced/assembly.md index 313191d0f01..3bad77ecab6 100644 --- a/docs/book/src/advanced/assembly.md +++ b/docs/book/src/advanced/assembly.md @@ -1,10 +1,10 @@ # Inline Assembly in Sway -While many users will never have to touch assembly language while writing sway code, it is a powerful tool that enables many advanced use-cases (ie: optimizations, building libraries, etc). +While many users will never have to touch assembly language while writing sway code, it is a powerful tool that enables many advanced use-cases (e.g., optimizations, building libraries, etc). ## ASM Block -In Sway, the way we use assembly inline is to declare an asm block like this: +In Sway, the way we use assembly inline is to declare an `asm` block like this: ```sway asm() {...} diff --git a/docs/book/src/advanced/associated_types.md b/docs/book/src/advanced/associated_types.md index e9606225943..1828026ebb4 100644 --- a/docs/book/src/advanced/associated_types.md +++ b/docs/book/src/advanced/associated_types.md @@ -61,7 +61,7 @@ In this example, `get_value` is a trait method that returns an associated type ` ## Use Cases Associated types are particularly useful in scenarios where you want to define traits that work with different -types of data structures or abstractions, allowing the implementor to specify the concrete types. Some common use cases include: +types of data structures or abstractions, allowing the implementer to specify the concrete types. Some common use cases include: - Collections: Traits for generic collections that allow users to specify the type of elements. - Iterator Patterns: Traits for implementing iterators with varying element types. diff --git a/docs/book/src/advanced/traits.md b/docs/book/src/advanced/traits.md index 08a039ae851..ed87d3b3732 100644 --- a/docs/book/src/advanced/traits.md +++ b/docs/book/src/advanced/traits.md @@ -20,7 +20,7 @@ We have just declared a trait called `Compare`. After the name of the trait, the ## Implementing a Trait -Ok, so I know that numbers can be equal. I want to implement my `Compare` trait for `u64`. Let's take a look at how that is done: +The example below implements a `Compare` trait for `u64` to check if two numbers are equal. Let's take a look at how that is done: ```sway impl Compare for u64 { @@ -34,8 +34,8 @@ The above snippet declares all of the methods in the trait `Compare` for the typ ## Supertraits -When using multiple traits, scenarios often come up where one trait may require functionality from another trait. This is where supertraits come in as they allow you to require a trait when implementing another -trait (ie. a trait with a trait). A good example of this is the `Ord` trait of the `core` library of Sway. The `Ord` trait requires the `Eq` trait, so `Eq` is kept as a separate trait as one may decide to implement `Eq` +When using multiple traits, scenarios often come up where one trait may require functionality from another trait. This is where supertraits come in as they allow you to require a trait when implementing another trait, i.e., a trait with a trait. +A good example of this is the `Ord` trait of the `core` library of Sway. The `Ord` trait requires the `Eq` trait, so `Eq` is kept as a separate trait as one may decide to implement `Eq` without implementing other parts of the `Ord` trait. ```sway diff --git a/docs/book/src/basics/blockchain_types.md b/docs/book/src/basics/blockchain_types.md index 86b0ff799b3..e91312eda34 100644 --- a/docs/book/src/basics/blockchain_types.md +++ b/docs/book/src/basics/blockchain_types.md @@ -54,7 +54,7 @@ let forty_two: b256 = my_contract_id.into(); -The `Identity` type is an enum that allows for the handling of both `Address` and `ContractId` types. This is useful in cases where either type is accepted, e.g. receiving funds from an identified sender, but not caring if the sender is an address or a contract. +The `Identity` type is an enum that allows for the handling of both `Address` and `ContractId` types. This is useful in cases where either type is accepted, e.g., receiving funds from an identified sender, but not caring if the sender is an address or a contract. An `Identity` is implemented as follows. diff --git a/docs/book/src/basics/built_in_types.md b/docs/book/src/basics/built_in_types.md index f7e500589b2..f42bb1525bc 100644 --- a/docs/book/src/basics/built_in_types.md +++ b/docs/book/src/basics/built_in_types.md @@ -52,7 +52,7 @@ computation gets reverted automatically by FuelVM. additional overflow/underflow checks inserted, which generally results in somewhat higher gas consumption. -The same does not happen with 256-bit operations, including `b256`, which uses specialized operations and are as performant as possible. +The same does not happen with 256-bit operations, including `b256`, which uses specialized operations and are as efficient as possible. ## Boolean Type @@ -82,7 +82,7 @@ In Sway, string literals are stored as variable length string slices. Which mean let my_string: str = "fuel"; ``` -String slices, because they contain pointers have limited usage. They cannot be used is constants, storages, configurables, nor as main function argument or returns. +String slices, because they contain pointers, have limited usage. They cannot be used in constants, storage, configurable constants, nor as main function arguments or returns. For these cases one must use string arrays, as described below. @@ -174,7 +174,7 @@ let x = [1, 2, 3, 4, 5]; Arrays are allocated on the stack since their size is known. An array's size is _always_ static, i.e. it cannot change. An array of five elements cannot become an array of six elements. -Arrays can be iterated over, unlike tuples. An array's type is written as the type the array contains followed by the number of elements, semicolon-separated and within square brackets, e.g. `[u64; 5]`. To access an element in an array, use the _array indexing syntax_, i.e. square brackets. +Arrays can be iterated over, unlike tuples. An array's type is written as the type the array contains followed by the number of elements, semicolon-separated and within square brackets, e.g., `[u64; 5]`. To access an element in an array, use the _array indexing syntax_, i.e. square brackets. Array elements can also be mutated if the underlying array is declared as mutable: diff --git a/docs/book/src/basics/constants.md b/docs/book/src/basics/constants.md index be52dcec323..d5d8a48413b 100644 --- a/docs/book/src/basics/constants.md +++ b/docs/book/src/basics/constants.md @@ -46,7 +46,7 @@ The identifier is the name of the constant used in the path. The type is the typ definition has to implement. -You can _define_ an associated const directly in the interface surface of a trait: +You can _define_ an associated `const` directly in the interface surface of a trait: ```sway script; diff --git a/docs/book/src/basics/index.md b/docs/book/src/basics/index.md index cc2aaffa186..8f0511b9e8b 100644 --- a/docs/book/src/basics/index.md +++ b/docs/book/src/basics/index.md @@ -1,6 +1,6 @@ # Sway Language basics -Sway is a programming language designed for the FuelVM. It is a statically typed, compiled language with type inference and traits. Sway aims to make smart contract development safer and more performant through the use of strong static analysis and compiler feedback. +Sway is a programming language designed for the FuelVM. It is a statically typed, compiled language with type inference and traits. Sway aims to make smart contract development safer and more efficient through the use of strong static analysis and compiler feedback. Get started with the basics of Sway: diff --git a/docs/book/src/basics/methods_and_associated_functions.md b/docs/book/src/basics/methods_and_associated_functions.md index f21ed9a7b49..b25443a8dda 100644 --- a/docs/book/src/basics/methods_and_associated_functions.md +++ b/docs/book/src/basics/methods_and_associated_functions.md @@ -6,7 +6,7 @@ Methods are similar to functions in that we declare them with the `fn` keyword a _Associated functions_ are very similar to _methods_, in that they are also defined in the context of a struct or enum, but they do not actually use any of the data in the struct and as a result do not take _self_ as a parameter. Associated functions could be standalone functions, but they are included in a specific type for organizational or semantic reasons. -To declare methods and associated functions for a struct or enum, use an _impl block_. Here, `impl` stands for implementation. +To declare methods and associated functions for a struct or enum, use an `impl` block. Here, `impl` is short for implementation. ```sway diff --git a/docs/book/src/basics/structs_tuples_and_enums.md b/docs/book/src/basics/structs_tuples_and_enums.md index d28973dbd5b..4ec75211eb1 100644 --- a/docs/book/src/basics/structs_tuples_and_enums.md +++ b/docs/book/src/basics/structs_tuples_and_enums.md @@ -19,7 +19,7 @@ In order to instantiate the struct we use _struct instantiation syntax_, which i There are three ways to instantiate the struct. -- Hardcoding values for the fields +- Hard coding values for the fields - Passing in variables with names different than the struct fields - Using a shorthand notation via variables that are the same as the field names diff --git a/docs/book/src/blockchain-development/access_control.md b/docs/book/src/blockchain-development/access_control.md index 279a2b12d1c..786258ad63b 100644 --- a/docs/book/src/blockchain-development/access_control.md +++ b/docs/book/src/blockchain-development/access_control.md @@ -27,9 +27,9 @@ The `msg_sender` function works as follows: ## Contract Ownership -Many contracts require some form of ownership for access control. The [SRC-5 Ownership Standard](https://github.com/FuelLabs/sway-standards/tree/master/standards/src_5) has been defined to provide a interoperable interface for ownership within contracts. +Many contracts require some form of ownership for access control. The [SRC-5 Ownership Standard](https://github.com/FuelLabs/sway-standards/tree/master/standards/src5-ownership) has been defined to provide a interoperable interface for ownership within contracts. -To accomplish this, use the [Ownerhsip Library](https://github.com/FuelLabs/sway-libs/tree/master/libs/ownership) to keep track of the owner. This allows setting and revoking ownership using the variants `Some(..)` and `None` respectively. This is better, safer, and more readable than using the `Identity` type directly where revoking ownership has to be done using some magic value such as `std::constants::ZERO_B256` or otherwise. +To accomplish this, use the [Ownership Library](https://github.com/FuelLabs/sway-libs/tree/master/libs/ownership) to keep track of the owner. This allows setting and revoking ownership using the variants `Some(..)` and `None` respectively. This is better, safer, and more readable than using the `Identity` type directly where revoking ownership has to be done using some magic value such as `std::constants::ZERO_B256` or otherwise. - The following is an example of how to properly lock a function such that only the owner may call a function: diff --git a/docs/book/src/blockchain-development/calling_contracts.md b/docs/book/src/blockchain-development/calling_contracts.md index f49a83ddfff..cb9dbbe42f3 100644 --- a/docs/book/src/blockchain-development/calling_contracts.md +++ b/docs/book/src/blockchain-development/calling_contracts.md @@ -2,7 +2,7 @@ Smart contracts can be _called_ by other contracts or scripts. In the FuelVM, this is done primarily with the [`call`](https://fuellabs.github.io/fuel-specs/master/vm/instruction_set#call-call-contract) instruction. -Sway provides a nice way to manage callable interfaces with its `abi` system. The Fuel ABI specification can be found [here](https://fuellabs.github.io/fuel-specs/master/protocol/abi). +Sway provides a nice way to manage callable interfaces with its ABI system. The Fuel ABI specification can be found [here](https://fuellabs.github.io/fuel-specs/master/protocol/abi). ## Example @@ -78,7 +78,7 @@ fn main() { A common attack vector for smart contracts is [re-entrancy](https://docs.soliditylang.org/en/v0.8.4/security-considerations.html#re-entrancy). Similar to the EVM, the FuelVM allows for re-entrancy. -A _stateless_ re-entrancy guard is included in the [sway-libs](https://github.com/FuelLabs/sway-libs) library. The guard will panic (revert) at run time if re-entrancy is detected. +A _stateless_ re-entrancy guard is included in the [`sway-libs`](https://github.com/FuelLabs/sway-libs) library. The guard will panic (revert) at run time if re-entrancy is detected. ```sway contract; @@ -152,9 +152,9 @@ In case there is a storage read after an interaction, the CEI analyzer will issu In addition to storage reads and writes after an interaction, the CEI analyzer reports analogous warnings about: -- balance tree updates, i.e. balance tree reads with subsequent writes, which may be produced by the `tr` and `tro` asm instructions or library functions using them under the hood; +- balance tree updates, i.e. balance tree reads with subsequent writes, which may be produced by the `tr` and `tro` ASM instructions or library functions using them under the hood; - balance trees reads with `bal` instruction; -- changes to the output messages that can be produced by the `__smo` intrinsic function or the `smo` asm instruction. +- changes to the output messages that can be produced by the `__smo` intrinsic function or the `smo` ASM instruction. ## Differences from the EVM diff --git a/docs/book/src/blockchain-development/native_assets.md b/docs/book/src/blockchain-development/native_assets.md index 7d8c8deab3a..d16a3d67947 100644 --- a/docs/book/src/blockchain-development/native_assets.md +++ b/docs/book/src/blockchain-development/native_assets.md @@ -15,7 +15,7 @@ However, unlike the EVM, the process for sending _any_ native asset is the same. All contracts in Fuel can mint and burn their own native token. Contracts can also receive and transfer any native asset including their own. Internal balances of all native assets pushed through calls or minted by the contract are tracked by the FuelVM and can be queried at any point using the balance_of function from the `std` library. Therefore, there is no need for any manual accounting of the contract's balances using persistent storage. -The `std` library provides handy methods for accessing Fuel's native assset operations. +The `std` library provides handy methods for accessing Fuel's native asset operations. In this example, we show a basic liquidity pool contract minting its own native asset LP token. diff --git a/docs/book/src/common-collections/index.md b/docs/book/src/common-collections/index.md index 707c5fc4f19..d173d0ba8e0 100644 --- a/docs/book/src/common-collections/index.md +++ b/docs/book/src/common-collections/index.md @@ -4,12 +4,12 @@ Sway’s standard library includes a number of very useful data structures calle A vector on the heap allows you to store a variable number of values next to each other. -A storage vector is similar to a vector on the heap but uses persistent storage. +A `StorageVec` is similar to a vector on the heap but uses persistent storage. -A storage map allows you to associate a value with a particular key. +A `StorageMap` allows you to associate a value with a particular key. -We’ll discuss how to create and update vectors, storage vectors, and storage maps, as well as what makes each special. +We’ll discuss how to create and update a vector, `StorageVec`, and `StorageMap`, as well as what makes each special. - [Vectors on the Heap](./vec.md) -- [Storage Vectors](./storage_vec.md) -- [Storage Maps](./storage_map.md) +- [`StorageVec`](./storage_vec.md) +- [`StorageMap`](./storage_map.md) diff --git a/docs/book/src/common-collections/storage_vec.md b/docs/book/src/common-collections/storage_vec.md index 54124808afd..f8a12ca6e96 100644 --- a/docs/book/src/common-collections/storage_vec.md +++ b/docs/book/src/common-collections/storage_vec.md @@ -10,9 +10,9 @@ In order to use `StorageVec`, you must first import `StorageVec` as follows: Another major difference between `Vec` and `StorageVec` is that `StorageVec` can only be used in a contract because only contracts are allowed to access persistent storage. -## Creating a New Storage Vector +## Creating a New `StorageVec` -To create a new empty storage vector, we have to declare the vector in a `storage` block as follows: +To create a new empty `StorageVec`, we have to declare the vector in a `storage` block as follows: ```sway {{#include ../../../../examples/storage_vec/src/main.sw:storage_vec_decl}} @@ -20,17 +20,17 @@ To create a new empty storage vector, we have to declare the vector in a `storag Just like any other storage variable, two things are required when declaring a `StorageVec`: a type annotation and an initializer. The initializer is just an empty struct of type `StorageVec` because `StorageVec` itself is an empty struct! Everything that is interesting about `StorageVec` is implemented in its methods. -Storage vectors, just like `Vec`, are implemented using generics which means that the `StorageVec` type provided by the standard library can hold any type. When we create a storage vector to hold a specific type, we can specify the type within angle brackets. In the example above, we’ve told the Sway compiler that the `StorageVec` in `v` will hold elements of the `u64` type. +Storage vectors, just like `Vec`, are implemented using generics which means that the `StorageVec` type provided by the standard library can hold any type. When we create a `StorageVec` to hold a specific type, we can specify the type within angle brackets. In the example above, we’ve told the Sway compiler that the `StorageVec` in `v` will hold elements of the `u64` type. -## Updating a Storage Vector +## Updating a `StorageVec` -To add elements to a storage vector, we can use the `push` method, as shown below: +To add elements to a `StorageVec`, we can use the `push` method, as shown below: ```sway {{#include ../../../../examples/storage_vec/src/main.sw:storage_vec_push}} ``` -Note two details here. First, in order to use `push`, we need to first access the vector using the `storage` keyword. Second, because `push` requires accessing storage, a `storage` annotation is required on the ABI function that calls `push`. While it may seem that `#[storage(write)]` should be enough here, the `read` annotation is also required because each call to `push` requires _reading_ (and then updating) the length of the storage vector which is also stored in persistent storage. +Note two details here. First, in order to use `push`, we need to first access the vector using the `storage` keyword. Second, because `push` requires accessing storage, a `storage` annotation is required on the ABI function that calls `push`. While it may seem that `#[storage(write)]` should be enough here, the `read` annotation is also required because each call to `push` requires _reading_ (and then updating) the length of the `StorageVec` which is also stored in persistent storage. > **Note** > The storage annotation is also required for any private function defined in the contract that tries to push into the vector. @@ -69,13 +69,13 @@ Storage vectors, just like `Vec`, can only store values that are the same typ {{#include ../../../../examples/storage_vec/src/main.sw:storage_vec_multiple_types_enum}} ``` -Then we can declare a storage vector in a `storage` block to hold that enum and so, ultimately, holds different types: +Then we can declare a `StorageVec` in a `storage` block to hold that enum and so, ultimately, holds different types: ```sway {{#include ../../../../examples/storage_vec/src/main.sw:storage_vec_multiple_types_decl}} ``` -We can now push different enum variants to the storage vector as follows: +We can now push different enum variants to the `StorageVec` as follows: ```sway {{#include ../../../../examples/storage_vec/src/main.sw:storage_vec_multiple_types_fn}} @@ -83,7 +83,7 @@ We can now push different enum variants to the storage vector as follows: Now that we’ve discussed some of the most common ways to use storage vectors, be sure to review the API documentation for all the many useful methods defined on `StorageVec` by the standard library. For now, these can be found in the [source code for `StorageVec`](https://github.com/FuelLabs/sway/blob/master/sway-lib-std/src/storage.sw). For example, in addition to `push`, a `pop` method removes and returns the last element, a `remove` method removes and returns the element at some chosen index within the vector, an `insert` method inserts an element at some chosen index within the vector, etc. -## Nested Storage Vecs +## Nested Storage Vectors It is possible to nest storage vectors as follows: diff --git a/docs/book/src/examples/fizzbuzz.md b/docs/book/src/examples/fizzbuzz.md index 65274ac06cd..3d0bfe98ef7 100644 --- a/docs/book/src/examples/fizzbuzz.md +++ b/docs/book/src/examples/fizzbuzz.md @@ -1,6 +1,6 @@ -# FizzBuzz +# `FizzBuzz` -This example is not the traditional [FizzBuzz](https://en.wikipedia.org/wiki/Fizz_buzz#Programming); instead it is the smart contract version! A script can call the `fizzbuzz` ABI method of this contract with some `u64` value and receive back its fizzbuzzability as an `enum`. +This example is not the traditional [`FizzBuzz`](https://en.wikipedia.org/wiki/Fizz_buzz#Programming); instead it is the smart contract version! A script can call the `fizzbuzz` ABI method of this contract with some `u64` value and receive back the result as an `enum`. The format for custom structs and enums such as `FizzBuzzResult` will be automatically included in the ABI JSON so that off-chain code can handle the encoded form of the returned data. diff --git a/docs/book/src/examples/index.md b/docs/book/src/examples/index.md index 48a4e24e76d..91d82ca6f03 100644 --- a/docs/book/src/examples/index.md +++ b/docs/book/src/examples/index.md @@ -4,5 +4,5 @@ Some basic example contracts to see how Sway and Forc work. - [Counter](./counter.md) - [Subcurrency](./subcurrency.md) -- [FizzBuzz](./fizzbuzz.md) +- [`FizzBuzz`](./fizzbuzz.md) - [Wallet Smart Contract](./wallet_smart_contract.md) diff --git a/docs/book/src/forc/commands/forc_addr2line.md b/docs/book/src/forc/commands/forc_addr2line.md index 9da1be10b3f..cd1835e1c49 100644 --- a/docs/book/src/forc/commands/forc_addr2line.md +++ b/docs/book/src/forc/commands/forc_addr2line.md @@ -1 +1 @@ -# forc addr2line +# `forc addr2line` diff --git a/docs/book/src/forc/commands/forc_build.md b/docs/book/src/forc/commands/forc_build.md index f7285039bd7..950396b5f28 100644 --- a/docs/book/src/forc/commands/forc_build.md +++ b/docs/book/src/forc/commands/forc_build.md @@ -1 +1 @@ -# forc build +# `forc build` diff --git a/docs/book/src/forc/commands/forc_check.md b/docs/book/src/forc/commands/forc_check.md index 49f24c6bc70..88bd1757b61 100644 --- a/docs/book/src/forc/commands/forc_check.md +++ b/docs/book/src/forc/commands/forc_check.md @@ -1 +1 @@ -# forc check +# `forc check` diff --git a/docs/book/src/forc/commands/forc_clean.md b/docs/book/src/forc/commands/forc_clean.md index bbe1d3db337..5ce65cb93c3 100644 --- a/docs/book/src/forc/commands/forc_clean.md +++ b/docs/book/src/forc/commands/forc_clean.md @@ -1 +1 @@ -# forc clean +# `forc clean` diff --git a/docs/book/src/forc/commands/forc_completions.md b/docs/book/src/forc/commands/forc_completions.md index 91ae73c0015..309573d4870 100644 --- a/docs/book/src/forc/commands/forc_completions.md +++ b/docs/book/src/forc/commands/forc_completions.md @@ -1 +1 @@ -# forc completions +# `forc completions` diff --git a/docs/book/src/forc/commands/forc_contract-id.md b/docs/book/src/forc/commands/forc_contract-id.md index f6c47b041e2..81c0f140465 100644 --- a/docs/book/src/forc/commands/forc_contract-id.md +++ b/docs/book/src/forc/commands/forc_contract-id.md @@ -1 +1 @@ -# forc contract-id +# `forc contract-id` diff --git a/docs/book/src/forc/commands/forc_init.md b/docs/book/src/forc/commands/forc_init.md index 9931411557d..0a5ba539d84 100644 --- a/docs/book/src/forc/commands/forc_init.md +++ b/docs/book/src/forc/commands/forc_init.md @@ -1 +1 @@ -# forc init +# `forc init` diff --git a/docs/book/src/forc/commands/forc_new.md b/docs/book/src/forc/commands/forc_new.md index 0e6e939318c..58fd660c119 100644 --- a/docs/book/src/forc/commands/forc_new.md +++ b/docs/book/src/forc/commands/forc_new.md @@ -1 +1 @@ -# forc new +# `forc new` diff --git a/docs/book/src/forc/commands/forc_parse-bytecode.md b/docs/book/src/forc/commands/forc_parse-bytecode.md index 79d718cc9f7..80c67edb461 100644 --- a/docs/book/src/forc/commands/forc_parse-bytecode.md +++ b/docs/book/src/forc/commands/forc_parse-bytecode.md @@ -1 +1 @@ -# forc parse-bytecode +# `forc parse-bytecode` diff --git a/docs/book/src/forc/commands/forc_plugins.md b/docs/book/src/forc/commands/forc_plugins.md index a6cfa94c56e..bc0e7aa6c00 100644 --- a/docs/book/src/forc/commands/forc_plugins.md +++ b/docs/book/src/forc/commands/forc_plugins.md @@ -1 +1 @@ -# forc plugins +# `forc plugins` diff --git a/docs/book/src/forc/commands/forc_predicate-root.md b/docs/book/src/forc/commands/forc_predicate-root.md index 863450ebb50..9ed1d7ca56d 100644 --- a/docs/book/src/forc/commands/forc_predicate-root.md +++ b/docs/book/src/forc/commands/forc_predicate-root.md @@ -1 +1 @@ -# forc predicate-root +# `forc predicate-root` diff --git a/docs/book/src/forc/commands/forc_template.md b/docs/book/src/forc/commands/forc_template.md index a6cd711e7aa..4bc677ff08c 100644 --- a/docs/book/src/forc/commands/forc_template.md +++ b/docs/book/src/forc/commands/forc_template.md @@ -1 +1 @@ -# forc template +# `forc template` diff --git a/docs/book/src/forc/commands/forc_test.md b/docs/book/src/forc/commands/forc_test.md index 85254c0002e..7c4ce2723b7 100644 --- a/docs/book/src/forc/commands/forc_test.md +++ b/docs/book/src/forc/commands/forc_test.md @@ -1 +1 @@ -# forc test +# `forc test` diff --git a/docs/book/src/forc/commands/forc_update.md b/docs/book/src/forc/commands/forc_update.md index 0dcb013a3d0..6805c316eed 100644 --- a/docs/book/src/forc/commands/forc_update.md +++ b/docs/book/src/forc/commands/forc_update.md @@ -1 +1 @@ -# forc update +# `forc update` diff --git a/docs/book/src/forc/dependencies.md b/docs/book/src/forc/dependencies.md index b495b320e2e..12222f0cba8 100644 --- a/docs/book/src/forc/dependencies.md +++ b/docs/book/src/forc/dependencies.md @@ -1,6 +1,6 @@ # Dependencies -Forc has a dependency management system which can pull packages using git and ipfs. This allows users to build and share Forc libraries. +Forc has a dependency management system which can pull packages using git and `ipfs`. This allows users to build and share Forc libraries. ## Adding a dependency @@ -24,7 +24,7 @@ Depending on a local library using `path`: custom_lib = { path = "../custom_lib" } ``` -For `ipfs` sources, `forc` will fetch the specified `cid` using either a local ipfs node or a public gateway. `forc` automatically tries to connect to local `ipfs` node and if it fails, fallbacks to using `https://ipfs.io/`, as a gateway. +For `ipfs` sources, `forc` will fetch the specified `cid` using either a local `ipfs` node or a public gateway. `forc` automatically tries to connect to local `ipfs` node. If it fails, it defaults to using `https://ipfs.io/` as a gateway. The following example adds a dependency with an `ipfs` source. diff --git a/docs/book/src/forc/index.md b/docs/book/src/forc/index.md index b53d75e65a0..2f1c992037e 100644 --- a/docs/book/src/forc/index.md +++ b/docs/book/src/forc/index.md @@ -2,6 +2,6 @@ Forc stands for Fuel Orchestrator. Forc provides a variety of tools and commands for developers working with the Fuel ecosystem, such as scaffolding a new project, formatting, running scripts, deploying contracts, testing contracts, and more. If you're coming from a Rust background, forc is similar to cargo. -If you are new to Forc, see the [Forc Project](../introduction/forc_project.md) introduction section. +If you are new to Forc, see the [Forc Project](https://docs.fuel.network/docs/sway/introduction/forc_project/) introduction section. For a comprehensive overview of the Forc CLI commands, see the [Commands](./commands/index.md) section. diff --git a/docs/book/src/forc/manifest_reference.md b/docs/book/src/forc/manifest_reference.md index be543ccef50..126ee75e5ca 100644 --- a/docs/book/src/forc/manifest_reference.md +++ b/docs/book/src/forc/manifest_reference.md @@ -31,8 +31,8 @@ An example `Forc.toml` is shown below. Under `[project]` the following fields ar Also for the following fields, a default value is provided so omitting them is allowed: -* `entry` - (default : _main.sw_) -* `implicit-std` - (default : _true_) +* `entry` - (default : `main.sw` ) +* `implicit-std` - (default : `true` ) ```toml [project] @@ -69,15 +69,15 @@ The `[build-profile]` tables provide a way to customize compiler settings such a The following fields can be provided for a build-profile: * `print-ast` - Whether to print out the generated AST or not, defaults to false. -* `print-dca-graph` - Whether to print out the computed DCA graph (in GraphViz DOT format), defaults to false. -* `print-dca-graph-url-format` - The URL format to be used in the generated DOT file, an example for vscode would be: "vscode://file/{path}:{line}:{col}" +* `print-dca-graph` - Whether to print out the computed Dead Code Analysis (DCA) graph (in GraphViz DOT format), defaults to false. +* `print-dca-graph-url-format` - The URL format to be used in the generated DOT file, an example for VS Code would be: `vscode://file/{path}:{line}:{col}`. * `print-ir` - Whether to compile to bytecode (false) or to print out the generated IR (true), defaults to false. * `print-finalized-asm` - Whether to compile to bytecode (false) or to print out the generated ASM (true), defaults to false. * `print-intermediate-asm` - Whether to compile to bytecode (false) or to print out the generated ASM (true), defaults to false. * `terse` - Terse mode. Limited warning and error output, defaults to false. * `time_phases` - Whether to output the time elapsed over each part of the compilation process, defaults to false. -* `include_tests` - Whether or not to include test functions in parsing, type-checking and codegen, this is set to true by invocations like `forc test`, defaults to false. -* `json_abi_with_callpaths` - Whether to json abi with callpaths instead of names for struct and enums, defaults to false. +* `include_tests` - Whether or not to include test functions in parsing, type-checking, and code generation. This is set to true by invocations like `forc test`, but defaults to false. +* `json_abi_with_callpaths` - Whether to generate a JSON ABI with `callpaths` instead of names for structs and enums, defaults to false. This option can help prevent conflicting struct or enum definitions by using the full path instead of the name. * `error_on_warnings` - Whether to treat errors as warnings, defaults to false. There are two default `[build-profile]` available with every manifest file. These are `debug` and `release` profiles. If you want to override these profiles, you can provide them explicitly in the manifest file like the following example: @@ -105,7 +105,7 @@ terse = true Since `release` and `debug` implicitly included in every manifest file, you can use them by just passing `--release` or by not passing anything (debug is default). For using a user defined build profile there is `--build-profile ` option available to the relevant commands. (For an example see [forc-build](../forc/commands/forc_build.md)) -Note that providing the corresponding cli options (like `--finalized-asm`) will override the selected build profile. For example if you pass both `--release` and `--finalized-asm`, release build profile is omitted and resulting build profile would have a structure like the following: +Note that providing the corresponding CLI options (like `--finalized-asm`) will override the selected build profile. For example if you pass both `--release` and `--finalized-asm`, release build profile is omitted and resulting build profile would have a structure like the following: ```toml print-ast = false @@ -122,7 +122,7 @@ experimental-private-modules = false ## The `[patch]` section -The [patch] section of `Forc.toml` can be used to override dependencies with other copies. The example provided below patches source with master branch of the same repo. +The [patch] section of `Forc.toml` can be used to override dependencies with other copies. The example provided below patches `https://github.com/fuellabs/sway` with the `test` branch of the same repo. ```toml [project] diff --git a/docs/book/src/forc/plugins/forc_client/forc_deploy.md b/docs/book/src/forc/plugins/forc_client/forc_deploy.md index 5fd00d93425..96a3b57a5a5 100644 --- a/docs/book/src/forc/plugins/forc_client/forc_deploy.md +++ b/docs/book/src/forc/plugins/forc_client/forc_deploy.md @@ -1 +1 @@ -# forc deploy +# `forc deploy` diff --git a/docs/book/src/forc/plugins/forc_client/forc_run.md b/docs/book/src/forc/plugins/forc_client/forc_run.md index 7d6f88c8db5..008a92b9593 100644 --- a/docs/book/src/forc/plugins/forc_client/forc_run.md +++ b/docs/book/src/forc/plugins/forc_client/forc_run.md @@ -1 +1 @@ -# forc run +# `forc run` diff --git a/docs/book/src/forc/plugins/forc_client/forc_submit.md b/docs/book/src/forc/plugins/forc_client/forc_submit.md index ad314de43e7..54e1e5f6c5e 100644 --- a/docs/book/src/forc/plugins/forc_client/forc_submit.md +++ b/docs/book/src/forc/plugins/forc_client/forc_submit.md @@ -1 +1 @@ -# forc submit +# `forc submit` diff --git a/docs/book/src/forc/plugins/forc_client/index.md b/docs/book/src/forc/plugins/forc_client/index.md index 2ecce8826b6..ce9ca27d64e 100644 --- a/docs/book/src/forc/plugins/forc_client/index.md +++ b/docs/book/src/forc/plugins/forc_client/index.md @@ -1,4 +1,4 @@ -# forc-client +# `forc-client` Forc plugin for interacting with a Fuel node. @@ -18,7 +18,7 @@ By default `fuel-core` runs without UTXO validation, this allows you to send inv If you want to run `fuel-core` with UTXO validation, you can pass `--utxo-validation` to `fuel-core run`. -To install `forc-wallet` please refer to `forc-wallet`'s [github repo](https://github.com/FuelLabs/forc-wallet#forc-wallet). +To install `forc-wallet` please refer to `forc-wallet`'s [GitHub repo](https://github.com/FuelLabs/forc-wallet#forc-wallet). 1. Construct the transaction by using either `forc deploy` or `forc run`. To do so simply run `forc deploy` or `forc run` with your desired parameters. For a list of parameters please refer to the [forc-deploy](./forc_deploy.md) or [forc-run](./forc_run.md) section of the book. Once you run either command you will be asked the address of the wallet you are going to be signing with. After the address is given the transaction will be generated and you will be given a transaction ID. At this point CLI will actively wait for you to insert the signature. 2. Take the transaction ID generated in the first step and sign it with `forc wallet sign --account tx-id `. This will generate a signature. @@ -62,7 +62,7 @@ To interact with the latest testnet, use the `--testnet` flag. When this flag is forc-deploy --testnet ``` -It is also possible to pass the exact node url while using `forc-deploy` or `forc-run` which can be done using `--node-url` flag. +It is also possible to pass the exact node URL while using `forc-deploy` or `forc-run` which can be done using `--node-url` flag. ```sh forc-deploy --node-url https://beta-3.fuel.network diff --git a/docs/book/src/forc/plugins/forc_crypto.md b/docs/book/src/forc/plugins/forc_crypto.md index 7c5aa994161..c0789970b64 100644 --- a/docs/book/src/forc/plugins/forc_crypto.md +++ b/docs/book/src/forc/plugins/forc_crypto.md @@ -1 +1 @@ -# forc crypto +# `forc crypto` diff --git a/docs/book/src/forc/plugins/forc_debug.md b/docs/book/src/forc/plugins/forc_debug.md new file mode 100644 index 00000000000..4d2a40becc0 --- /dev/null +++ b/docs/book/src/forc/plugins/forc_debug.md @@ -0,0 +1 @@ +# forc debug diff --git a/docs/book/src/forc/plugins/forc_doc.md b/docs/book/src/forc/plugins/forc_doc.md index 2f664873b33..e554a752e6d 100644 --- a/docs/book/src/forc/plugins/forc_doc.md +++ b/docs/book/src/forc/plugins/forc_doc.md @@ -1 +1 @@ -# forc doc +# `forc doc` diff --git a/docs/book/src/forc/plugins/forc_explore.md b/docs/book/src/forc/plugins/forc_explore.md index c8d4ca5ed5b..b86cb623cdf 100644 --- a/docs/book/src/forc/plugins/forc_explore.md +++ b/docs/book/src/forc/plugins/forc_explore.md @@ -1 +1 @@ -# forc explore +# `forc explore` diff --git a/docs/book/src/forc/plugins/forc_fmt.md b/docs/book/src/forc/plugins/forc_fmt.md index f49f6b7877b..667a7c1b635 100644 --- a/docs/book/src/forc/plugins/forc_fmt.md +++ b/docs/book/src/forc/plugins/forc_fmt.md @@ -1 +1 @@ -# forc fmt +# `forc fmt` diff --git a/docs/book/src/forc/plugins/forc_lsp.md b/docs/book/src/forc/plugins/forc_lsp.md index 624d69001f7..b5a93f24f24 100644 --- a/docs/book/src/forc/plugins/forc_lsp.md +++ b/docs/book/src/forc/plugins/forc_lsp.md @@ -1 +1 @@ -# forc lsp +# `forc lsp` diff --git a/docs/book/src/introduction/standard_library.md b/docs/book/src/introduction/standard_library.md index 81a46e3d0d7..1c5c101f93b 100644 --- a/docs/book/src/introduction/standard_library.md +++ b/docs/book/src/introduction/standard_library.md @@ -7,7 +7,7 @@ Similar to Rust, Sway comes with its own standard library. The Sway Standard Library is the foundation of portable Sway software, a set of minimal shared abstractions for the broader Sway ecosystem. It offers core types, like `Result` and `Option`, library-defined operations on language primitives, native asset management, blockchain contextual operations, access control, storage management, and support for types from other VMs, among many other things. -The entire Sway standard library is a Forc project called `std`, and is available directly here: (navigate to the appropriate tagged release if the latest `master` is not compatible). For the latest `std` documentation see: . +The entire Sway standard library is a Forc project called `std`, and is available directly [here](https://github.com/FuelLabs/sway/tree/master/sway-lib-std). Navigate to the appropriate tagged release if the latest `master` is not compatible. You can find the latest `std` documentation [here](https://fuellabs.github.io/sway/master/std/). ## Using the Standard Library diff --git a/docs/book/src/lsp/installation.md b/docs/book/src/lsp/installation.md index da121453361..381bf90a72b 100644 --- a/docs/book/src/lsp/installation.md +++ b/docs/book/src/lsp/installation.md @@ -1,6 +1,6 @@ # Installation -The Sway language server is contained in the [forc-lsp](../forc/plugins/forc_lsp.md) binary, which is installed as part of the [Fuel toolchain](../introduction/fuel_toolchain.md). Once installed, it can be used with a variety of IDEs. It must be installed for any of the IDE plugins to work. +The Sway language server is contained in the [`forc-lsp`](../forc/plugins/forc_lsp.md) binary, which is installed as part of the [Fuel toolchain](../introduction/fuel_toolchain.md). Once installed, it can be used with a variety of IDEs. It must be installed for any of the IDE plugins to work. > **Note**: There is no need to manually run `forc-lsp` (the plugin will automatically start it), however both `forc` and `forc-lsp` must be in your `$PATH`. To check if `forc` is in your `$PATH`, type `forc --help` in your terminal. diff --git a/docs/book/src/reference/compiler_intrinsics.md b/docs/book/src/reference/compiler_intrinsics.md index ebeda03ac06..5ae7bb7fba3 100644 --- a/docs/book/src/reference/compiler_intrinsics.md +++ b/docs/book/src/reference/compiler_intrinsics.md @@ -106,7 +106,7 @@ ___ __gtf(index: u64, tx_field_id: u64) -> T ``` -**Description:** Returns transaction field with ID `tx_field_id` at index `index`, if applicable. This is a wrapper around FuelVM's [`gtf` instruction](https://fuellabs.github.io/fuel-specs/master/vm/instruction_set#gtf-get-transaction-fields). The resuting field is cast to `T`. +**Description:** Returns transaction field with ID `tx_field_id` at index `index`, if applicable. This is a wrapper around FuelVM's [`gtf` instruction](https://fuellabs.github.io/fuel-specs/master/vm/instruction_set#gtf-get-transaction-fields). The resulting field is cast to `T`. **Constraints:** None. diff --git a/docs/book/src/reference/contributing_to_sway.md b/docs/book/src/reference/contributing_to_sway.md index 97edb5f16d6..844a31b0b3d 100644 --- a/docs/book/src/reference/contributing_to_sway.md +++ b/docs/book/src/reference/contributing_to_sway.md @@ -28,7 +28,7 @@ First, open a new terminal and start `fuel-core` with: fuel-core ``` -Then open a second terminal, cd into the `sway` repo and run: +Then open a second terminal, `cd` into the `sway` repo and run: ```sh cargo run --bin test @@ -49,7 +49,7 @@ There are many ways in which you may contribute to the Sway project, some of whi - Reporting bugs - Adding documentation to the Sway book -- Adding new features or bugfixes for which there is already an open issue +- Adding new features or bug fixes for which there is already an open issue - Making feature requests Check out our [Help Wanted](https://github.com/FuelLabs/sway/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22), [Sway Book](https://github.com/FuelLabs/sway/issues?q=is%3Aopen+is%3Aissue+label%3A%22The+Sway+Book%22) or [Good First Issue](https://github.com/FuelLabs/sway/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22) issues to find a suitable task. diff --git a/docs/book/src/reference/keywords.md b/docs/book/src/reference/keywords.md index 449b5e82a60..57dd52f4b11 100644 --- a/docs/book/src/reference/keywords.md +++ b/docs/book/src/reference/keywords.md @@ -4,15 +4,15 @@ The following list contains keywords that are reserved for current or future use by the Sway language. As such, they cannot be used as identifiers. Identifiers are names of functions, variables, parameters, modules, constants, attributes, types or -traits, ect. +traits, etc. ## Keywords Currently in Use The following is a list of keywords currently in use, with their functionality described. -- `as` - rename items in `use` statements, eg `use type::a as alias_name` -- [`abi`](../sway-program-types/smart_contracts.md#the-abi-declaration) - defines a smart contract ABI in a syntactcally similar way to traits +- `as` - rename items in `use` statements, e.g., `use type::a as alias_name` +- [`abi`](../sway-program-types/smart_contracts.md#the-abi-declaration) - defines a smart contract ABI in a syntactically similar way to traits - [`break`](../basics/control_flow.md#break-and-continue) - exit a loop immediately - [`const`](../basics/constants.md) - define constant items - [`continue`](../basics/control_flow.md#break-and-continue) - continue to the next loop iteration @@ -23,7 +23,7 @@ functionality described. - [`if`](../basics/control_flow.md#if-expressions) - branch based on the result of a conditional expression - `impl` - implement inherent or trait functionality - `let` - bind a variable -- [`match`](../basics/control_flow.md#match-expressions) - exhaustfully match a value to patterns +- [`match`](../basics/control_flow.md#match-expressions) - exhaustively match a value to patterns - `mod` - define a module - `mut` - denote mutability in references, or pattern bindings - `pub` - denote public visibility of Sway data structures, traits, or modules @@ -78,7 +78,7 @@ Keywords associated with defining the type of Sway program to compile ### Attribute Keywords -Keywords associated with defining the funcitonallity of attributes +Keywords associated with defining the functionality of attributes - [`allow`](./attributes.md#allow) - overrides checks that would otherwise result in errors or warnings - [`doc`](./attributes.md#doc) - specifies documentation diff --git a/docs/book/src/reference/rust_differences.md b/docs/book/src/reference/rust_differences.md index 3943a9258f8..c9505814b20 100644 --- a/docs/book/src/reference/rust_differences.md +++ b/docs/book/src/reference/rust_differences.md @@ -19,7 +19,7 @@ enum Foo { } ``` -In Sway, enums are simplified. Enums variants must all specify exactly one type. This type represents their interior data. This is actually isomorphic to what Rust offers, just with a different syntax. I'll now rewrite the above enum but with Sway syntax: +In Sway, enums are simplified. Enums variants must all specify exactly one type. This type represents their interior data. This is actually isomorphic to what Rust offers, but with a different syntax. You can see the above enum but with Sway syntax below: ```sway // This is equivalent Sway syntax for the above Rust enum. diff --git a/docs/book/src/sway-program-types/index.md b/docs/book/src/sway-program-types/index.md index 18c97dae3a3..fdd076181da 100644 --- a/docs/book/src/sway-program-types/index.md +++ b/docs/book/src/sway-program-types/index.md @@ -7,7 +7,7 @@ A Sway program itself has a type: it is either a _contract_, a _predicate_, a _s Every Sway file _must_ begin with a declaration of what type of program it is. A project can have many libraries within it, but only one contract, script, or predicate. Scripts and predicates require `main` functions to serve as entry points, while contracts instead publish an ABI. This chapter will go into detail about all of these various types of programs and what purposes they serve. -Contracts are used primarily for protocols or systems that operate within a fixed set of rules. A good example would be a staking contract or a decentralized exchange. +Contracts are used primarily for protocols or systems that operate within a fixed set of rules. A good example would be a staking contract or a decentralized exchange (also called a DEX). Scripts are used for complex on-chain interactions that won't persist. An example of this may be using a DEX and Lender to create a leveraged position (borrow, swap, re-collateralize, borrow) which is a complex transaction that would usually take multiple steps. diff --git a/docs/book/src/sway-program-types/libraries.md b/docs/book/src/sway-program-types/libraries.md index 37c30554d05..e2a1c312eba 100644 --- a/docs/book/src/sway-program-types/libraries.md +++ b/docs/book/src/sway-program-types/libraries.md @@ -34,7 +34,7 @@ library; use ::revert::revert; ``` -- The `enum` definition which starts with the keyword `pub` to indicate that this `Option` is publically available _outside_ the `option` library: +- The `enum` definition which starts with the keyword `pub` to indicate that this `Option` is publicly available _outside_ the `option` library: ```sway pub enum Option { @@ -91,7 +91,7 @@ mod vm; // .. Other deps ``` -with other libraries contained in the `src` folder, like the vm library (inside of `src/vm.sw`): +with other libraries contained in the `src` folder, like the `vm` library (inside of `src/vm.sw`): ```sway library; @@ -100,7 +100,7 @@ mod evm; // ... ``` -and it's own sub-library evm located in `src/vm/evm.sw`: +and it's own sub-library `evm` located in `src/vm/evm.sw`: ```sway library; diff --git a/docs/book/src/sway-program-types/predicates.md b/docs/book/src/sway-program-types/predicates.md index 4fa7f0c9d11..7229aae22e9 100644 --- a/docs/book/src/sway-program-types/predicates.md +++ b/docs/book/src/sway-program-types/predicates.md @@ -13,6 +13,6 @@ fn main() -> bool { ## Debugging Predicates -Because they don't have any side effects (they are _pure_), predicates cannot create receipts. Therefore, they cannot have logging or create a stack backtrace. This means that there is no native way to debug them aside from using a single-stepping debugger (which is a [work-in-progress](https://github.com/FuelLabs/fuel-debugger/pull/1)). +Because they don't have any side effects (they are _pure_), predicates cannot create receipts. Therefore, they cannot have logging or create a stack backtrace. This means that there is no native way to debug them aside from using a single-stepping debugger. As a workaround, the predicate can be written, tested, and debugged first as a `script`, and then changed back into a `predicate`. diff --git a/docs/book/src/sway-program-types/scripts.md b/docs/book/src/sway-program-types/scripts.md index 9ee4d421566..4f1c051a940 100644 --- a/docs/book/src/sway-program-types/scripts.md +++ b/docs/book/src/sway-program-types/scripts.md @@ -10,9 +10,9 @@ This example script calls a contract: {{#include ../../../../examples/wallet_contract_caller_script/src/main.sw}} ``` -Scripts, similar to predicates, rely on a `main()` function as an entry point. You can call other functions defined in a script from the `main()` function or call another contract via an [abi cast](./smart_contracts.md#calling-a-smart-contract-from-a-script). +Scripts, similar to predicates, rely on a `main()` function as an entry point. You can call other functions defined in a script from the `main()` function or call another contract via an [ABI cast](./smart_contracts.md#calling-a-smart-contract-from-a-script). -An example use case for a script would be a router that trades funds through multiple DEXes to get the price for the input asset, or a script to re-adjust a Collateralized Debt Position via a flashloan. +An example use case for a script would be a router that trades funds through multiple decentralized exchanges to get the price for the input asset, or a script to re-adjust a Collateralized Debt Position via a flash loan. ## Scripts and the SDKs diff --git a/docs/book/src/sway-program-types/smart_contracts.md b/docs/book/src/sway-program-types/smart_contracts.md index ff5458ba1c1..4ab98187d99 100644 --- a/docs/book/src/sway-program-types/smart_contracts.md +++ b/docs/book/src/sway-program-types/smart_contracts.md @@ -12,9 +12,9 @@ The interface of a smart contract, also just called a contract, must be defined As with any Sway program, the program starts with a declaration of what [program type](./index.md) it is. A contract must also either define or import an [ABI declaration](#the-abi-declaration) and implement it. - + It is considered good practice to define your ABI in a separate library and import it into your contract. This allows callers of your contract to simply import the ABI directly and use it in their scripts to call your contract. - + Let's take a look at an ABI declaration in a library: @@ -85,7 +85,7 @@ Now that we have defined our interface and implemented it for our contract, we n {{#include ../../../../examples/wallet_contract_caller_script/src/main.sw}} ``` -The main new concept is the _abi cast_: `abi(AbiName, contract_address)`. This returns a `ContractCaller` type which can be used to call contracts. The methods of the ABI become the methods available on this contract caller: `send_funds` and `receive_funds`. We then directly call the contract ABI method as if it was just a regular method. You also have the option of specifying the following special parameters inside curly braces right before the main list of parameters: +The main new concept is the `abi cast`: `abi(AbiName, contract_address)`. This returns a `ContractCaller` type which can be used to call contracts. The methods of the ABI become the methods available on this contract caller: `send_funds` and `receive_funds`. We then directly call the contract ABI method as if it was just a regular method. You also have the option of specifying the following special parameters inside curly braces right before the main list of parameters: 1. `gas`: a `u64` that represents the gas being forwarded to the contract when it is called. 2. `coins`: a `u64` that represents how many coins are being forwarded with this call. diff --git a/docs/book/src/testing/testing-with-rust.md b/docs/book/src/testing/testing-with-rust.md index 20008bf9a96..260ab849ae6 100644 --- a/docs/book/src/testing/testing-with-rust.md +++ b/docs/book/src/testing/testing-with-rust.md @@ -9,11 +9,10 @@ and our Rust code we can add integration testing. To add Rust integration testing to a Forc project we can use [the `sway-test-rs` cargo generate template](https://github.com/FuelLabs/sway/tree/master/templates/sway-test-rs). -This template makes it easy for Sway devs to add the boilerplate required when +This template makes it easier for Sway developers to add the boilerplate required when setting up their Rust integration testing. -Let's add a Rust integration test to [the fresh project we created in the -introduction](../introduction/forc_project.md). +Let's add a Rust integration test to [the fresh project we created in the introduction](../introduction/forc_project.md). ### 1. Enter the project @@ -36,8 +35,8 @@ template. Let's make sure we have the `cargo generate` command installed! cargo install cargo-generate ``` -> _**Note**: You can learn more about cargo generate by visiting [its -> repository](https://github.com/cargo-generate/cargo-generate)._ +> _**Note**: You can learn more about cargo generate by visiting the +> [cargo-generate repository](https://github.com/cargo-generate/cargo-generate)._ ### 3. Generate the test harness @@ -91,8 +90,7 @@ We have two new files! ### 4. Build the forc project Before running the tests, we need to build our contract so that the necessary -ABI, storage and bytecode artifacts are available. We can do so with `forc -build`: +ABI, storage and bytecode artifacts are available. We can do so with `forc build`: ```console $ forc build @@ -178,7 +176,7 @@ following: ```rust,ignore use fuels::{prelude::*, types::ContractId}; -// Load abi from json +// Load ABI from JSON abigen!(TestContract, "out/debug/my-fuel-project-abi.json"); async fn get_contract_instance() -> (TestContract, ContractId) { diff --git a/docs/reference/src/documentation/misc/workarounds/predicates.md b/docs/reference/src/documentation/misc/workarounds/predicates.md index b44ea9ed059..78de4de6be4 100644 --- a/docs/reference/src/documentation/misc/workarounds/predicates.md +++ b/docs/reference/src/documentation/misc/workarounds/predicates.md @@ -2,6 +2,6 @@ A [predicate](../../language/program-types/predicate.md) does not have any side effects because it is pure and thus it cannot create [receipts](https://github.com/FuelLabs/fuel-specs/blob/master/src/abi/receipts.md). -Since there are no receipts they cannot use logging nor create a stack backtrace for debugging. This means that there is no way to debug them aside from using a single-stepping [debugger](https://github.com/FuelLabs/fuel-debugger). +Since there are no receipts they cannot use logging nor create a stack backtrace for debugging. This means that there is no way to debug them aside from using a single-stepping [debugger](../../../../../../forc-plugins/forc-debug/README.md). As a workaround, the predicate can be written, tested, and debugged first as a [`script`](../../language/program-types/script.md), and then changed back into a `predicate`. diff --git a/examples/asm_return_tuple_pointer/src/main.sw b/examples/asm_return_tuple_pointer/src/main.sw index 6b23104fa77..339b993508c 100644 --- a/examples/asm_return_tuple_pointer/src/main.sw +++ b/examples/asm_return_tuple_pointer/src/main.sw @@ -3,10 +3,10 @@ script; fn adder(a: u64, b: u64, c: u64) -> (u64, u64) { let empty_tuple = (0u64, 0u64); asm(output: empty_tuple, r1: a, r2: b, r3: c, r4, r5) { - add r4 r1 r2; // add a & b and put the result in r4 - add r5 r2 r3; // add b & c and put the result in r5 - sw output r4 i0; // store the word in r4 in output + 0 words - sw output r5 i1; // store the word in r5 in output + 1 word + add r4 r1 r2; // add a & b and put the result in r4 + add r5 r2 r3; // add b & c and put the result in r5 + sw output r4 i0; // store the word in r4 in output + 0 words + sw output r5 i1; // store the word in r5 in output + 1 word output: (u64, u64) // return both values } } diff --git a/forc-plugins/forc-crypto/Cargo.toml b/forc-plugins/forc-crypto/Cargo.toml index 59833f62416..7807b13956b 100644 --- a/forc-plugins/forc-crypto/Cargo.toml +++ b/forc-plugins/forc-crypto/Cargo.toml @@ -8,7 +8,6 @@ homepage.workspace = true license.workspace = true repository.workspace = true - [dependencies] anyhow = "1.0.75" async-trait = "0.1.58" diff --git a/forc-plugins/forc-debug/Cargo.toml b/forc-plugins/forc-debug/Cargo.toml new file mode 100644 index 00000000000..ace71f2502a --- /dev/null +++ b/forc-plugins/forc-debug/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "forc-debug" +version = "0.48.1" +description = "Supports debugging Sway code via CLI and DAP server." +authors.workspace = true +edition.workspace = true +homepage.workspace = true +license.workspace = true +repository.workspace = true + +[dependencies] +clap = { version = "3.1", features = ["env", "derive"] } +fuel-core-client = { version = "0.21" } +fuel-types = { version = "0.43", features = ["serde"] } +fuel-vm = { version = "0.43", features = ["serde"] } +serde_json = "1.0" +shellfish = { version = "0.6.0", features = ["rustyline", "async", "tokio"] } +thiserror = "1.0" +tokio = { version = "1.19", features = ["net", "io-util", "macros", "rt-multi-thread"] } + +[dev-dependencies] +anyhow = "1.0" # Used by the examples only +escargot = "0.5.7" +portpicker = "0.1.1" +rexpect = "0.4" diff --git a/forc-plugins/forc-debug/README.md b/forc-plugins/forc-debug/README.md new file mode 100755 index 00000000000..186d2b0fcb8 --- /dev/null +++ b/forc-plugins/forc-debug/README.md @@ -0,0 +1,7 @@ +# forc-debug + +Debugger attachable to FuelVM over a GraphQL API. + +## Testing + +The automated tests assume that `fuel-core` binary is installed. diff --git a/forc-plugins/forc-debug/docs/walkthrough.md b/forc-plugins/forc-debug/docs/walkthrough.md new file mode 100755 index 00000000000..bb8e7fc25a3 --- /dev/null +++ b/forc-plugins/forc-debug/docs/walkthrough.md @@ -0,0 +1,284 @@ +# An example project + +First, we need a project to debug, so create a new project using + +```bash +forc new --script dbg_example && cd dbg_example +``` + +And then add some content to `src/main.sw`, for example: + +```sway +script; + +use std::logging::log; + +fn factorial(n: u64) -> u64 { + let mut result = 1; + let mut counter = 0; + while counter < n { + counter = counter + 1; + result = result * counter; + } + return result; +} + +fn main() { + log::(factorial(5)); // 120 +} +``` + +## Building and bytecode output + +Now we are ready to build the thing. + +```bash +forc build +``` + +After this the resulting binary should be located at `out/debug/dbg_example.bin`. Because we are interested in the resulting bytecode, we can read that with: + +```bash +forc parse-bytecode out/debug/dbg_example.bin +``` + +Which should give us something like + +```text + + half-word byte op raw notes + 0 0 JI { imm: 4 } 90 00 00 04 jump to byte 16 + 1 4 NOOP 47 00 00 00 + 2 8 InvalidOpcode 00 00 00 00 data section offset lo (0) + 3 12 InvalidOpcode 00 00 00 44 data section offset hi (68) + 4 16 LW { ra: 63, rb: 12, imm: 1 } 5d fc c0 01 + 5 20 ADD { ra: 63, rb: 63, rc: 12 } 10 ff f3 00 + 6 24 MOVE { ra: 18, rb: 1 } 1a 48 10 00 + 7 28 MOVE { ra: 17, rb: 0 } 1a 44 00 00 + 8 32 LW { ra: 16, rb: 63, imm: 0 } 5d 43 f0 00 + 9 36 LT { ra: 16, rb: 17, rc: 16 } 16 41 14 00 + 10 40 JNZI { ra: 16, imm: 13 } 73 40 00 0d conditionally jump to byte 52 + 11 44 LOG { ra: 18, rb: 0, rc: 0, rd: 0 } 33 48 00 00 + 12 48 RET { ra: 0 } 24 00 00 00 + 13 52 ADD { ra: 17, rb: 17, rc: 1 } 10 45 10 40 + 14 56 MUL { ra: 18, rb: 18, rc: 17 } 1b 49 24 40 + 15 60 JI { imm: 8 } 90 00 00 08 jump to byte 32 + 16 64 NOOP 47 00 00 00 + 17 68 InvalidOpcode 00 00 00 00 + 18 72 InvalidOpcode 00 00 00 05 +``` + +We can recognize the `while` loop by the conditional jumps `JNZI`. The condition just before the first jump can be identified by `LT` instruction (for `<`). Some notable instructions that are generated only once in our code include `MUL` for multiplication and `LOG {.., 0, 0, 0}` from the `log` function. + +## Setting up the debugging + +We can start up the debug infrastructure. On a new terminal session run `fuel-core run --db-type in-memory --debug`; we need to have that running because it actually executes the program. Now we can fire up the debugger itself: `forc-debug`. Now +if everything is set up correctly, you shoould see the debugger prompt (`>>`). You can use `help` command to list available commands. + +Now we would like to inspect the program while it's running. To do this, we first need to send the script to the executor, i.e. `fuel-core`. To do so, we need a *transaction specification*, `tx.json`. It looks something like this: + +```json +{ + "Script": { + "script_gas_limit": 1000000, + "script": [], + "script_data": [], + "policies": { + "bits": "GasPrice", + "values": [0,0,0,0] + }, + "inputs": [ + { + "CoinSigned": { + "utxo_id": { + "tx_id": "c49d65de61cf04588a764b557d25cc6c6b4bc0d7429227e2a21e61c213b3a3e2", + "output_index": 18 + }, + "owner": "f1e92c42b90934aa6372e30bc568a326f6e66a1a0288595e6e3fbd392a4f3e6e", + "amount": 10599410012256088338, + "asset_id": "2cafad611543e0265d89f1c2b60d9ebf5d56ad7e23d9827d6b522fd4d6e44bc3", + "tx_pointer": { + "block_height": 0, + "tx_index": 0 + }, + "witness_index": 0, + "maturity": 0, + "predicate_gas_used": null, + "predicate": null, + "predicate_data": null + } + } + ], + "outputs": [], + "witnesses": [ + { + "data": [ + 156,254,34,102,65,96,133,170,254,105,147,35,196,199,179,133,132,240,208,149,11,46,30,96,44,91,121,195,145,184,159,235,117,82,135,41,84,154,102,61,61,16,99,123,58,173,75,226,219,139,62,33,41,176,16,18,132,178,8,125,130,169,32,108 + ] + } + ], + "receipts_root": "0000000000000000000000000000000000000000000000000000000000000000" + } +} +``` + +However, the key `script` should contain the actual bytecode to execute, i.e. the contents of `out/debug/dbg_example.bin` as a JSON array. The following command can be used to generate it: + +```bash +python3 -c 'print(list(open("out/debug/dbg_example.bin", "rb").read()))' +``` + +So now we replace the script array with the result, and save it as `tx.json`. + +## Using the debugger + +Now we can actually execute the script: + +```text +>> start_tx tx.json + +Receipt: Log { id: 0000000000000000000000000000000000000000000000000000000000000000, ra: 120, rb: 0, rc: 0, rd: 0, pc: 10380, is: 10336 } +Receipt: Return { id: 0000000000000000000000000000000000000000000000000000000000000000, val: 0, pc: 10384, is: 10336 } +Receipt: ScriptResult { result: Success, gas_used: 60 } +Terminated +``` + +Looking at the first output line, we can see that it logged `ra: 120` which is the correct return value for `factorial(5)`. It also tells us that the exection terminated without hitting any breakpoints. That's unsurprising, because we haven't set up any. We can do so with `breakpoint` command: + +```text +>> breakpoint 0 + +>> start_tx tx.json + +Receipt: ScriptResult { result: Success, gas_used: 0 } +Stopped on breakpoint at address 0 of contract 0x0000000000000000000000000000000000000000000000000000000000000000 + +``` + +Now we have stopped execution at the breakpoint on entry (address `0`). We can now inspect the initial state of the VM. + +```text +>> register ggas + +reg[0x9] = 1000000 # ggas + +>> memory 0x10 0x8 + + 000010: e9 5c 58 86 c8 87 26 dd +``` + +However, that's not too interesting either, so let's just execute until the end, and then reset the vm to remove the breakpoints. + +```text +>> continue + +Receipt: Log { id: 0000000000000000000000000000000000000000000000000000000000000000, ra: 120, rb: 0, rc: 0, rd: 0, pc: 10380, is: 10336 } +Receipt: Return { id: 0000000000000000000000000000000000000000000000000000000000000000, val: 0, pc: 10384, is: 10336 } +Terminated + +>> reset + +``` + +Next, we will setup a breakpoint to check the state on each iteration of the `while` loop. For instance, if we'd like to see what numbers get multiplied together, we could set up a breakpoint before the operation. The bytecode has only a single `MUL` instruction: + +```text + half-word byte op raw notes + 14 56 MUL { ra: 18, rb: 18, rc: 17 } 1b 49 24 40 +``` + +We can set a breakpoint on its address, at halfword-offset `14`. + +```text +>>> breakpoint 14 + +>> start_tx tx.json + +Receipt: ScriptResult { result: Success, gas_used: 9 } +Stopped on breakpoint at address 56 of contract 0x0000000000000000000000000000000000000000000000000000000000000000 + +``` + +Now we can inspect the inputs tu multiply. Looking at [the specification](https://github.com/FuelLabs/fuel-specs/blob/master/src/fuel-vm/instruction-set.md#mul-multiply) tells us that the instruction `MUL { ra: 18, rb: 18, rc: 17 }` means `reg[18] = reg[18] * reg[17]`. So inpecting the inputs tells us that + +```text +>> r 18 17 + +reg[0x12] = 1 # reg18 +reg[0x11] = 1 # reg17 +``` + +So on the first round the numbers are `1` and `1`, so we can continue to the next iteration: + +```text +>> c + +Stopped on breakpoint at address 56 of contract 0x0000000000000000000000000000000000000000000000000000000000000000 + +>> r 18 17 + +reg[0x12] = 1 # reg18 +reg[0x11] = 2 # reg17 + +``` + +And the next one: + +```text +>> c + +Stopped on breakpoint at address 56 of contract 0x0000000000000000000000000000000000000000000000000000000000000000 + +>> r 18 17 + +reg[0x12] = 2 # reg18 +reg[0x11] = 3 # reg17 +``` + +And fourth one: + +```text +>> c + +Stopped on breakpoint at address 56 of contract 0x0000000000000000000000000000000000000000000000000000000000000000 + +>> r 18 17 + +reg[0x12] = 6 # reg18 +reg[0x11] = 4 # reg17 + +``` + +And round 5: + +```text +>> c + +Stopped on breakpoint at address 56 of contract 0x0000000000000000000000000000000000000000000000000000000000000000 + +>> r 18 17 + +reg[0x12] = 24 # reg18 +reg[0x11] = 5 # reg17 + +``` + +At this point we can look at the values + +17 | 18 +---|---- +1 | 1 +2 | 1 +3 | 2 +4 | 6 +5 | 24 + +From this we can clearly see that the left side, register `17` is the `counter` variable, and register `18` is `result`. Now the counter equals the given factorial function argument `5`, and the loop terminates. So when we continue, the program finishes without encountering any more breakpoints: + +```text +>> c + +Receipt: Log { id: 0000000000000000000000000000000000000000000000000000000000000000, ra: 120, rb: 0, rc: 0, rd: 0, pc: 10380, is: 10336 } +Receipt: Return { id: 0000000000000000000000000000000000000000000000000000000000000000, val: 0, pc: 10384, is: 10336 } +Terminated +``` diff --git a/forc-plugins/forc-debug/examples/client_usage.rs b/forc-plugins/forc-debug/examples/client_usage.rs new file mode 100755 index 00000000000..90ed87e0184 --- /dev/null +++ b/forc-plugins/forc-debug/examples/client_usage.rs @@ -0,0 +1,41 @@ +use forc_debug::{ContractId, FuelClient, Transaction}; + +#[tokio::main] +async fn main() { + run_example().await.expect("Running example failed"); +} + +async fn run_example() -> Result<(), anyhow::Error> { + let client = FuelClient::new("http://localhost:4000/graphql")?; + + let session_id = client.start_session().await?; + + client + .set_breakpoint(&session_id, ContractId::zeroed(), 0) + .await?; + + let tx: Transaction = + serde_json::from_str(include_str!("example_tx.json")).expect("Invalid transaction JSON"); + let status = client.start_tx(&session_id, &tx).await?; + assert!(status.breakpoint.is_some()); + + let value = client.register(&session_id, 12).await?; + println!("reg[12] = {}", value); + + let mem = client.memory(&session_id, 0x10, 0x20).await?; + println!("mem[0x10..0x30] = {:?}", mem); + + client.set_single_stepping(&session_id, true).await?; + + let status = client.continue_tx(&session_id).await?; + assert!(status.breakpoint.is_some()); + + client.set_single_stepping(&session_id, false).await?; + + let status = client.continue_tx(&session_id).await?; + assert!(status.breakpoint.is_none()); + + client.end_session(&session_id).await?; + + Ok(()) +} diff --git a/forc-plugins/forc-debug/examples/example_tx.json b/forc-plugins/forc-debug/examples/example_tx.json new file mode 100755 index 00000000000..607c5b059f5 --- /dev/null +++ b/forc-plugins/forc-debug/examples/example_tx.json @@ -0,0 +1,187 @@ +{ + "Script": { + "script_gas_limit": 1000000, + "script": [ + 144, + 0, + 0, + 4, + 71, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 68, + 93, + 252, + 192, + 1, + 16, + 255, + 243, + 0, + 26, + 72, + 16, + 0, + 26, + 68, + 0, + 0, + 93, + 67, + 240, + 0, + 22, + 65, + 20, + 0, + 115, + 64, + 0, + 13, + 51, + 72, + 0, + 0, + 36, + 0, + 0, + 0, + 16, + 69, + 16, + 64, + 27, + 73, + 36, + 64, + 144, + 0, + 0, + 8, + 71, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 5 + ], + "script_data": [], + "policies": { + "bits": "GasPrice", + "values": [ + 0, + 0, + 0, + 0 + ] + }, + "inputs": [ + { + "CoinSigned": { + "utxo_id": { + "tx_id": "c49d65de61cf04588a764b557d25cc6c6b4bc0d7429227e2a21e61c213b3a3e2", + "output_index": 18 + }, + "owner": "f1e92c42b90934aa6372e30bc568a326f6e66a1a0288595e6e3fbd392a4f3e6e", + "amount": 10599410012256088338, + "asset_id": "2cafad611543e0265d89f1c2b60d9ebf5d56ad7e23d9827d6b522fd4d6e44bc3", + "tx_pointer": { + "block_height": 0, + "tx_index": 0 + }, + "witness_index": 0, + "maturity": 0, + "predicate_gas_used": null, + "predicate": null, + "predicate_data": null + } + } + ], + "outputs": [], + "witnesses": [ + { + "data": [ + 156, + 254, + 34, + 102, + 65, + 96, + 133, + 170, + 254, + 105, + 147, + 35, + 196, + 199, + 179, + 133, + 132, + 240, + 208, + 149, + 11, + 46, + 30, + 96, + 44, + 91, + 121, + 195, + 145, + 184, + 159, + 235, + 117, + 82, + 135, + 41, + 84, + 154, + 102, + 61, + 61, + 16, + 99, + 123, + 58, + 173, + 75, + 226, + 219, + 139, + 62, + 33, + 41, + 176, + 16, + 18, + 132, + 178, + 8, + 125, + 130, + 169, + 32, + 108 + ] + } + ], + "receipts_root": "0000000000000000000000000000000000000000000000000000000000000000" + } +} \ No newline at end of file diff --git a/forc-plugins/forc-debug/src/lib.rs b/forc-plugins/forc-debug/src/lib.rs new file mode 100755 index 00000000000..7ff2becbf13 --- /dev/null +++ b/forc-plugins/forc-debug/src/lib.rs @@ -0,0 +1,5 @@ +pub mod names; + +// Re-exports +pub use fuel_core_client::client::{schema::RunResult, FuelClient}; +pub use fuel_vm::prelude::{ContractId, Transaction}; diff --git a/forc-plugins/forc-debug/src/main.rs b/forc-plugins/forc-debug/src/main.rs new file mode 100644 index 00000000000..57b9335242e --- /dev/null +++ b/forc-plugins/forc-debug/src/main.rs @@ -0,0 +1,275 @@ +use clap::Parser; +use forc_debug::names::register_name; +use shellfish::async_fn; +use shellfish::{Command as ShCommand, Shell}; +use std::error::Error; + +use forc_debug::{names, ContractId, FuelClient, RunResult, Transaction}; +use fuel_vm::consts::{VM_MAX_RAM, VM_REGISTER_COUNT, WORD_SIZE}; + +#[derive(Parser, Debug)] +pub struct Opt { + #[clap(default_value = "http://127.0.0.1:4000/graphql")] + pub api_url: String, +} + +#[tokio::main] +async fn main() -> Result<(), Box> { + let config = Opt::parse(); + + let mut shell = Shell::new_async( + State { + client: FuelClient::new(&config.api_url)?, + session_id: String::new(), // Placeholder + }, + ">> ", + ); + + macro_rules! command { + ($f:ident, $help:literal, $names:expr) => { + for c in $names { + shell.commands.insert( + c, + ShCommand::new_async($help.to_string(), async_fn!(State, $f)), + ); + } + }; + } + + command!( + cmd_start_tx, + "path/to/tx.json -- start a new transaction", + ["n", "tx", "new_tx", "start_tx"] + ); + command!( + cmd_reset, + "-- reset, removing breakpoints and other state", + ["reset"] + ); + command!( + cmd_continue, + "-- run until next breakpoint or termination", + ["c", "continue"] + ); + command!( + cmd_step, + "[on|off] -- turn single-stepping on or off", + ["s", "step"] + ); + command!( + cmd_breakpoint, + "[contract_id] offset -- set a breakpoint", + ["b", "breakpoint"] + ); + command!( + cmd_registers, + "[regname ...] -- dump registers", + ["r", "reg", "register", "registers"] + ); + command!(cmd_memory, "[offset] limit -- dump memory", ["m", "memory"]); + + let session_id = shell.state.client.start_session().await?; + shell.state.session_id = session_id.clone(); + shell.run_async().await?; + shell.state.client.end_session(&session_id).await?; + Ok(()) +} + +struct State { + client: FuelClient, + session_id: String, +} + +#[derive(Debug, thiserror::Error)] +enum ArgError { + #[error("Invalid argument")] + Invalid, + #[error("Not enough arguments")] + NotEnough, + #[error("Too many arguments")] + TooMany, +} + +fn pretty_print_run_result(rr: &RunResult) { + for receipt in rr.receipts() { + println!("Receipt: {:?}", receipt); + } + if let Some(bp) = &rr.breakpoint { + println!( + "Stopped on breakpoint at address {} of contract {}", + bp.pc.0, bp.contract + ); + } else { + println!("Terminated"); + } +} + +async fn cmd_start_tx(state: &mut State, mut args: Vec) -> Result<(), Box> { + args.remove(0); + let path_to_tx_json = args.pop().ok_or_else(|| Box::new(ArgError::NotEnough))?; + if !args.is_empty() { + return Err(Box::new(ArgError::TooMany)); + } + + let tx_json = std::fs::read(path_to_tx_json)?; + let tx: Transaction = serde_json::from_slice(&tx_json).unwrap(); + let status = state.client.start_tx(&state.session_id, &tx).await?; + pretty_print_run_result(&status); + + Ok(()) +} + +async fn cmd_reset(state: &mut State, mut args: Vec) -> Result<(), Box> { + args.remove(0); + if !args.is_empty() { + return Err(Box::new(ArgError::TooMany)); + } + + let _ = state.client.reset(&state.session_id).await?; + + Ok(()) +} + +async fn cmd_continue(state: &mut State, mut args: Vec) -> Result<(), Box> { + args.remove(0); + if !args.is_empty() { + return Err(Box::new(ArgError::TooMany)); + } + + let status = state.client.continue_tx(&state.session_id).await?; + pretty_print_run_result(&status); + + Ok(()) +} + +async fn cmd_step(state: &mut State, mut args: Vec) -> Result<(), Box> { + args.remove(0); + if args.len() > 1 { + return Err(Box::new(ArgError::TooMany)); + } + + state + .client + .set_single_stepping( + &state.session_id, + args.first() + .map(|v| !["off", "no", "disable"].contains(&v.as_str())) + .unwrap_or(true), + ) + .await?; + Ok(()) +} + +async fn cmd_breakpoint(state: &mut State, mut args: Vec) -> Result<(), Box> { + args.remove(0); + let offset = args.pop().ok_or_else(|| Box::new(ArgError::NotEnough))?; + let contract_id = args.pop(); + + if !args.is_empty() { + return Err(Box::new(ArgError::TooMany)); + } + + let offset = if let Some(offset) = parse_int(&offset) { + offset as u64 + } else { + return Err(Box::new(ArgError::Invalid)); + }; + + let contract = if let Some(contract_id) = contract_id { + if let Ok(contract_id) = contract_id.parse::() { + contract_id + } else { + return Err(Box::new(ArgError::Invalid)); + } + } else { + ContractId::zeroed() // Current script + }; + + state + .client + .set_breakpoint(&state.session_id, contract, offset) + .await?; + + Ok(()) +} + +async fn cmd_registers(state: &mut State, mut args: Vec) -> Result<(), Box> { + args.remove(0); + + if args.is_empty() { + for r in 0..VM_REGISTER_COUNT { + let value = state.client.register(&state.session_id, r as u32).await?; + println!("reg[{:#x}] = {:<8} # {}", r, value, register_name(r)); + } + } else { + for arg in &args { + if let Some(v) = parse_int(arg) { + if v < VM_REGISTER_COUNT { + let value = state.client.register(&state.session_id, v as u32).await?; + println!("reg[{:#02x}] = {:<8} # {}", v, value, register_name(v)); + } else { + println!("Register index too large {}", v); + return Ok(()); + } + } else if let Some(index) = names::register_index(arg) { + let value = state + .client + .register(&state.session_id, index as u32) + .await?; + println!("reg[{:#02x}] = {:<8} # {}", index, value, arg); + } else { + println!("Unknown register name {}", arg); + return Ok(()); + } + } + } + + Ok(()) +} + +async fn cmd_memory(state: &mut State, mut args: Vec) -> Result<(), Box> { + args.remove(0); + + let limit = args + .pop() + .map(|a| parse_int(&a).ok_or(ArgError::Invalid)) + .transpose()? + .unwrap_or(WORD_SIZE * (VM_MAX_RAM as usize)); + + let offset = args + .pop() + .map(|a| parse_int(&a).ok_or(ArgError::Invalid)) + .transpose()? + .unwrap_or(0); + + if !args.is_empty() { + return Err(Box::new(ArgError::TooMany)); + } + + let mem = state + .client + .memory(&state.session_id, offset as u32, limit as u32) + .await?; + + for (i, chunk) in mem.chunks(WORD_SIZE).enumerate() { + print!(" {:06x}:", offset + i * WORD_SIZE); + for byte in chunk { + print!(" {:02x}", byte); + } + println!(); + } + + Ok(()) +} + +fn parse_int(s: &str) -> Option { + let (s, radix) = if let Some(stripped) = s.strip_prefix("0x") { + (stripped, 16) + } else { + (s, 10) + }; + + let s = s.replace('_', ""); + + usize::from_str_radix(&s, radix).ok() +} diff --git a/forc-plugins/forc-debug/src/names.rs b/forc-plugins/forc-debug/src/names.rs new file mode 100755 index 00000000000..91e8833cbf4 --- /dev/null +++ b/forc-plugins/forc-debug/src/names.rs @@ -0,0 +1,16 @@ +pub const REGISTERS: [&str; 16] = [ + "zero", "one", "of", "pc", "ssp", "sp", "fp", "hp", "err", "ggas", "cgas", "bal", "is", "ret", + "retl", "flag", +]; + +pub fn register_name(index: usize) -> String { + if index < REGISTERS.len() { + REGISTERS[index].to_owned() + } else { + format!("reg{index}") + } +} + +pub fn register_index(name: &str) -> Option { + REGISTERS.iter().position(|&n| n == name) +} diff --git a/forc-plugins/forc-debug/tests/cli_integration.rs b/forc-plugins/forc-debug/tests/cli_integration.rs new file mode 100755 index 00000000000..77e6e8df0c5 --- /dev/null +++ b/forc-plugins/forc-debug/tests/cli_integration.rs @@ -0,0 +1,60 @@ +#![deny(unused_must_use)] + +use escargot::CargoBuild; +use rexpect::session::spawn_command; +use std::process::Command; + +#[test] +fn test_cli() { + let port = portpicker::pick_unused_port().expect("No ports free"); + + let mut fuel_core = Command::new("fuel-core") + .arg("run") + .arg("--debug") + .arg("--db-type") + .arg("in-memory") + .arg("--port") + .arg(port.to_string()) + .spawn() + .expect("Failed to start fuel-core"); + + let mut run_cmd = CargoBuild::new() + .bin("forc-debug") + .current_release() + .current_target() + .run() + .unwrap() + .command(); + + dbg!(&run_cmd); + + run_cmd.arg(format!("http://127.0.0.1:{}/graphql", port)); + + let mut cmd = spawn_command(run_cmd, Some(2000)).unwrap(); + + cmd.exp_regex(r"^>> ").unwrap(); + cmd.send_line("reg 0").unwrap(); + cmd.exp_regex(r"reg\[0x0\] = 0\s+# zero").unwrap(); + cmd.send_line("reg 1").unwrap(); + cmd.exp_regex(r"reg\[0x1\] = 1\s+# one").unwrap(); + cmd.send_line("breakpoint 0").unwrap(); + cmd.exp_regex(r">> ").unwrap(); + cmd.send_line("start_tx examples/example_tx.json").unwrap(); + cmd.exp_regex(r"Stopped on breakpoint at address 0 of contract 0x0{64}") + .unwrap(); + cmd.send_line("step on").unwrap(); + cmd.exp_regex(r">> ").unwrap(); + cmd.send_line("continue").unwrap(); + cmd.exp_regex(r"Stopped on breakpoint at address 16 of contract 0x0{64}") + .unwrap(); + cmd.send_line("step off").unwrap(); + cmd.exp_regex(r">> ").unwrap(); + cmd.send_line("continue").unwrap(); + cmd.exp_regex(r"Receipt: Return").unwrap(); + cmd.send_line("reset").unwrap(); + cmd.send_line("start_tx examples/example_tx.json").unwrap(); + cmd.exp_regex(r"Receipt: Return").unwrap(); + cmd.send_line(r"exit").unwrap(); + + fuel_core.kill().expect("Couldn't kill fuel-core"); +} diff --git a/forc-test/src/execute.rs b/forc-test/src/execute.rs new file mode 100644 index 00000000000..417bfdbfdba --- /dev/null +++ b/forc-test/src/execute.rs @@ -0,0 +1,178 @@ +use crate::setup::TestSetup; +use crate::TestResult; +use crate::TEST_METADATA_SEED; +use forc_pkg::PkgTestEntry; +use fuel_tx::{self as tx, output::contract::Contract, Chargeable, Finalizable}; +use fuel_vm::error::InterpreterError; +use fuel_vm::{ + self as vm, + checked_transaction::builder::TransactionBuilderExt, + interpreter::{Interpreter, NotSupportedEcal}, + prelude::{Instruction, SecretKey}, + storage::MemoryStorage, +}; +use rand::{Rng, SeedableRng}; + +/// An interface for executing a test within a VM [Interpreter] instance. +#[derive(Debug)] +pub struct TestExecutor { + pub interpreter: Interpreter, + tx_builder: tx::TransactionBuilder, + test_entry: PkgTestEntry, + name: String, +} + +impl TestExecutor { + pub fn new( + bytecode: &[u8], + test_offset: u32, + test_setup: TestSetup, + test_entry: &PkgTestEntry, + name: String, + ) -> Self { + let storage = test_setup.storage().clone(); + + // Patch the bytecode to jump to the relevant test. + let bytecode = patch_test_bytecode(bytecode, test_offset).into_owned(); + + // Create a transaction to execute the test function. + let script_input_data = vec![]; + let rng = &mut rand::rngs::StdRng::seed_from_u64(TEST_METADATA_SEED); + + // Prepare the transaction metadata. + let secret_key = SecretKey::random(rng); + let utxo_id = rng.gen(); + let amount = 1; + let maturity = 1.into(); + let asset_id = rng.gen(); + let tx_pointer = rng.gen(); + + let mut tx_builder = tx::TransactionBuilder::script(bytecode, script_input_data) + .add_unsigned_coin_input( + secret_key, + utxo_id, + amount, + asset_id, + tx_pointer, + 0u32.into(), + ) + .maturity(maturity) + .clone(); + + let mut output_index = 1; + // Insert contract ids into tx input + for contract_id in test_setup.contract_ids() { + tx_builder + .add_input(tx::Input::contract( + tx::UtxoId::new(tx::Bytes32::zeroed(), 0), + tx::Bytes32::zeroed(), + tx::Bytes32::zeroed(), + tx::TxPointer::new(0u32.into(), 0), + contract_id, + )) + .add_output(tx::Output::Contract(Contract { + input_index: output_index, + balance_root: fuel_tx::Bytes32::zeroed(), + state_root: tx::Bytes32::zeroed(), + })); + output_index += 1; + } + let consensus_params = tx_builder.get_params().clone(); + + // Temporarily finalize to calculate `script_gas_limit` + let tmp_tx = tx_builder.clone().finalize(); + // Get `max_gas` used by everything except the script execution. Add `1` because of rounding. + let max_gas = + tmp_tx.max_gas(consensus_params.gas_costs(), consensus_params.fee_params()) + 1; + // Increase `script_gas_limit` to the maximum allowed value. + tx_builder.script_gas_limit(consensus_params.tx_params().max_gas_per_tx - max_gas); + + TestExecutor { + interpreter: Interpreter::with_storage(storage, consensus_params.into()), + tx_builder, + test_entry: test_entry.clone(), + name, + } + } + + pub fn execute(&mut self) -> anyhow::Result { + let block_height = (u32::MAX >> 1).into(); + let start = std::time::Instant::now(); + let transition = self + .interpreter + .transact(self.tx_builder.finalize_checked(block_height)) + .map_err(|err: InterpreterError<_>| anyhow::anyhow!(err))?; + let duration = start.elapsed(); + let state = *transition.state(); + let receipts = transition.receipts().to_vec(); + + let gas_used = *receipts + .iter() + .find_map(|receipt| match receipt { + tx::Receipt::ScriptResult { gas_used, .. } => Some(gas_used), + _ => None, + }) + .ok_or_else(|| anyhow::anyhow!("missing used gas information from test execution"))?; + + // Only retain `Log` and `LogData` receipts. + let logs = receipts + .into_iter() + .filter(|receipt| { + matches!(receipt, tx::Receipt::Log { .. }) + || matches!(receipt, tx::Receipt::LogData { .. }) + }) + .collect(); + + let span = self.test_entry.span.clone(); + let file_path = self.test_entry.file_path.clone(); + let condition = self.test_entry.pass_condition.clone(); + let name = self.name.clone(); + Ok(TestResult { + name, + file_path, + duration, + span, + state, + condition, + logs, + gas_used, + }) + } +} + +/// Given some bytecode and an instruction offset for some test's desired entry point, patch the +/// bytecode with a `JI` (jump) instruction to jump to the desired test. +/// +/// We want to splice in the `JI` only after the initial data section setup is complete, and only +/// if the entry point doesn't begin exactly after the data section setup. +/// +/// The following is how the beginning of the bytecode is laid out: +/// +/// ```ignore +/// [0] ji i4 ; Jumps to the data section setup. +/// [1] noop +/// [2] DATA_SECTION_OFFSET[0..32] +/// [3] DATA_SECTION_OFFSET[32..64] +/// [4] lw $ds $is 1 ; The data section setup, i.e. where the first ji lands. +/// [5] add $$ds $$ds $is +/// [6] ; This is where we want to jump from to our test code! +/// ``` +fn patch_test_bytecode(bytecode: &[u8], test_offset: u32) -> std::borrow::Cow<[u8]> { + // TODO: Standardize this or add metadata to bytecode. + const PROGRAM_START_INST_OFFSET: u32 = 6; + const PROGRAM_START_BYTE_OFFSET: usize = PROGRAM_START_INST_OFFSET as usize * Instruction::SIZE; + + // If our desired entry point is the program start, no need to jump. + if test_offset == PROGRAM_START_INST_OFFSET { + return std::borrow::Cow::Borrowed(bytecode); + } + + // Create the jump instruction and splice it into the bytecode. + let ji = vm::fuel_asm::op::ji(test_offset); + let ji_bytes = ji.to_bytes(); + let start = PROGRAM_START_BYTE_OFFSET; + let end = start + ji_bytes.len(); + let mut patched = bytecode.to_vec(); + patched.splice(start..end, ji_bytes); + std::borrow::Cow::Owned(patched) +} diff --git a/forc-test/src/lib.rs b/forc-test/src/lib.rs index efe8b51815a..0aa8efe0734 100644 --- a/forc-test/src/lib.rs +++ b/forc-test/src/lib.rs @@ -1,8 +1,15 @@ +pub mod execute; +pub mod setup; + +use crate::execute::TestExecutor; +use crate::setup::{ + ContractDeploymentSetup, ContractTestSetup, DeploymentSetup, ScriptTestSetup, TestSetup, +}; use forc_pkg as pkg; use fuel_abi_types::error_codes::ErrorSignal; use fuel_tx as tx; use fuel_vm::checked_transaction::builder::TransactionBuilderExt; -use fuel_vm::{self as vm, fuel_asm, prelude::Instruction}; +use fuel_vm::{self as vm}; use pkg::TestPassCondition; use pkg::{Built, BuiltPackage}; use rand::{Rng, SeedableRng}; @@ -10,8 +17,6 @@ use rayon::prelude::*; use std::{collections::HashMap, fs, path::PathBuf, sync::Arc}; use sway_core::BuildTarget; use sway_types::Span; -use tx::output::contract::Contract; -use tx::{Chargeable, Finalizable}; use vm::prelude::SecretKey; /// The result of a `forc test` invocation. @@ -117,31 +122,6 @@ pub enum PackageWithDeploymentToTest { Contract(ContractToTest), } -/// Required test setup for package types that requires a deployment. -#[derive(Debug)] -enum DeploymentSetup { - Script(ScriptTestSetup), - Contract(ContractTestSetup), -} - -impl DeploymentSetup { - /// Returns the storage for this test setup - fn storage(&self) -> &vm::storage::MemoryStorage { - match self { - DeploymentSetup::Script(script_setup) => &script_setup.storage, - DeploymentSetup::Contract(contract_setup) => &contract_setup.storage, - } - } - - /// Return the root contract id if this is a contract setup. - fn root_contract_id(&self) -> Option { - match self { - DeploymentSetup::Script(_) => None, - DeploymentSetup::Contract(contract_setup) => Some(contract_setup.root_contract_id), - } - } -} - /// The set of options provided to the `test` function. #[derive(Default, Clone)] pub struct Opts { @@ -176,69 +156,6 @@ pub struct TestPrintOpts { pub print_logs: bool, } -/// The storage and the contract id (if a contract is being tested) for a test. -#[derive(Debug)] -enum TestSetup { - WithDeployment(DeploymentSetup), - WithoutDeployment(vm::storage::MemoryStorage), -} - -impl TestSetup { - /// Returns the storage for this test setup - fn storage(&self) -> &vm::storage::MemoryStorage { - match self { - TestSetup::WithDeployment(deployment_setup) => deployment_setup.storage(), - TestSetup::WithoutDeployment(storage) => storage, - } - } - - /// Produces an iterator yielding contract ids of contract dependencies for this test setup. - fn contract_dependency_ids(&self) -> impl Iterator + '_ { - match self { - TestSetup::WithDeployment(deployment_setup) => match deployment_setup { - DeploymentSetup::Script(script_setup) => { - script_setup.contract_dependency_ids.iter() - } - DeploymentSetup::Contract(contract_setup) => { - contract_setup.contract_dependency_ids.iter() - } - }, - TestSetup::WithoutDeployment(_) => [].iter(), - } - } - - /// Return the root contract id if this is a contract setup. - fn root_contract_id(&self) -> Option { - match self { - TestSetup::WithDeployment(deployment_setup) => deployment_setup.root_contract_id(), - TestSetup::WithoutDeployment(_) => None, - } - } - - /// Produces an iterator yielding all contract ids required to be included in the transaction - /// for this test setup. - fn contract_ids(&self) -> impl Iterator + '_ { - self.contract_dependency_ids() - .cloned() - .chain(self.root_contract_id()) - } -} - -/// The data collected to test a contract. -#[derive(Debug)] -struct ContractTestSetup { - storage: vm::storage::MemoryStorage, - contract_dependency_ids: Vec, - root_contract_id: tx::ContractId, -} - -/// The data collected to test a script. -#[derive(Debug)] -struct ScriptTestSetup { - storage: vm::storage::MemoryStorage, - contract_dependency_ids: Vec, -} - impl TestedPackage { pub fn tests_passed(&self) -> bool { self.tests.iter().all(|test| test.passed()) @@ -412,64 +329,42 @@ impl<'a> PackageTests { .bytecode .entries .par_iter() - .filter_map(|entry| entry.kind.test().map(|test| (entry, test))) - .filter(|(entry, _)| { - // If a test filter is specified, only the tests containing the filter phrase in - // their name are going to be executed. - match &test_filter { - Some(filter) => filter.filter(&entry.finalized.fn_name), - None => true, + .filter_map(|entry| { + if let Some(test_entry) = entry.kind.test() { + // If a test filter is specified, only the tests containing the filter phrase in + // their name are going to be executed. + let name = entry.finalized.fn_name.clone(); + if let Some(filter) = test_filter { + if !filter.filter(&name) { + return None; + } + } + return Some((entry, test_entry)); } + None }) .map(|(entry, test_entry)| { + // Execute the test and return the result. let offset = u32::try_from(entry.finalized.imm) .expect("test instruction offset out of range"); let name = entry.finalized.fn_name.clone(); let test_setup = self.setup()?; - let (state, duration, receipts) = - exec_test(&pkg_with_tests.bytecode.bytes, offset, test_setup); - - let gas_used = *receipts - .iter() - .find_map(|receipt| match receipt { - tx::Receipt::ScriptResult { gas_used, .. } => Some(gas_used), - _ => None, - }) - .ok_or_else(|| { - anyhow::anyhow!("missing used gas information from test execution") - })?; - - // Only retain `Log` and `LogData` receipts. - let logs = receipts - .into_iter() - .filter(|receipt| { - matches!(receipt, fuel_tx::Receipt::Log { .. }) - || matches!(receipt, fuel_tx::Receipt::LogData { .. }) - }) - .collect(); - - let span = test_entry.span.clone(); - let file_path = test_entry.file_path.clone(); - let condition = test_entry.pass_condition.clone(); - Ok(TestResult { + TestExecutor::new( + &pkg_with_tests.bytecode.bytes, + offset, + test_setup, + test_entry, name, - file_path, - duration, - span, - state, - condition, - logs, - gas_used, - }) + ) + .execute() }) .collect::>() })?; - let tested_pkg = TestedPackage { + Ok(TestedPackage { built: Box::new(pkg_with_tests.clone()), tests, - }; - Ok(tested_pkg) + }) } /// Setup the storage for a test and returns a contract id for testing contracts. @@ -528,7 +423,7 @@ impl TestResult { } } - /// Return the revert code for this `TestResult` if the test is reverted. + /// Return the revert code for this [TestResult] if the test is reverted. pub fn revert_code(&self) -> Option { match self.state { vm::state::ProgramState::Revert(revert_code) => Some(revert_code), @@ -536,7 +431,7 @@ impl TestResult { } } - /// Return a `ErrorSignal` for this `TestResult` if the test is failed to pass. + /// Return an [ErrorSignal] for this [TestResult] if the test is failed to pass. pub fn error_signal(&self) -> anyhow::Result { let revert_code = self.revert_code().ok_or_else(|| { anyhow::anyhow!("there is no revert code to convert to `ErrorSignal`") @@ -544,7 +439,7 @@ impl TestResult { ErrorSignal::try_from_revert_code(revert_code).map_err(|e| anyhow::anyhow!(e)) } - /// Return `TestDetails` from the span of the function declaring this test. + /// Return [TestDetails] from the span of the function declaring this test. pub fn details(&self) -> anyhow::Result { let span_start = self.span.start(); let file_str = fs::read_to_string(&*self.file_path)?; @@ -658,9 +553,6 @@ pub fn build(opts: Opts) -> anyhow::Result { BuiltTests::from_built(built, &member_contract_dependencies) } -/// Result of preparing a deployment transaction setup for a contract. -type ContractDeploymentSetup = (tx::ContractId, vm::checked_transaction::Checked); - /// Deploys the provided contract and returns an interpreter instance ready to be used in test /// executions with deployed contract. fn deployment_transaction( @@ -722,123 +614,6 @@ fn run_tests( } } -/// Given some bytecode and an instruction offset for some test's desired entry point, patch the -/// bytecode with a `JI` (jump) instruction to jump to the desired test. -/// -/// We want to splice in the `JI` only after the initial data section setup is complete, and only -/// if the entry point doesn't begin exactly after the data section setup. -/// -/// The following is how the beginning of the bytecode is laid out: -/// -/// ```ignore -/// [0] ji i4 ; Jumps to the data section setup. -/// [1] noop -/// [2] DATA_SECTION_OFFSET[0..32] -/// [3] DATA_SECTION_OFFSET[32..64] -/// [4] lw $ds $is 1 ; The data section setup, i.e. where the first ji lands. -/// [5] add $$ds $$ds $is -/// [6] ; This is where we want to jump from to our test code! -/// ``` -fn patch_test_bytecode(bytecode: &[u8], test_offset: u32) -> std::borrow::Cow<[u8]> { - // TODO: Standardize this or add metadata to bytecode. - const PROGRAM_START_INST_OFFSET: u32 = 6; - const PROGRAM_START_BYTE_OFFSET: usize = PROGRAM_START_INST_OFFSET as usize * Instruction::SIZE; - - // If our desired entry point is the program start, no need to jump. - if test_offset == PROGRAM_START_INST_OFFSET { - return std::borrow::Cow::Borrowed(bytecode); - } - - // Create the jump instruction and splice it into the bytecode. - let ji = fuel_asm::op::ji(test_offset); - let ji_bytes = ji.to_bytes(); - let start = PROGRAM_START_BYTE_OFFSET; - let end = start + ji_bytes.len(); - let mut patched = bytecode.to_vec(); - patched.splice(start..end, ji_bytes); - std::borrow::Cow::Owned(patched) -} - -// Execute the test whose entry point is at the given instruction offset as if it were a script. -fn exec_test( - bytecode: &[u8], - test_offset: u32, - test_setup: TestSetup, -) -> ( - vm::state::ProgramState, - std::time::Duration, - Vec, -) { - let storage = test_setup.storage().clone(); - - // Patch the bytecode to jump to the relevant test. - let bytecode = patch_test_bytecode(bytecode, test_offset).into_owned(); - - // Create a transaction to execute the test function. - let script_input_data = vec![]; - let rng = &mut rand::rngs::StdRng::seed_from_u64(TEST_METADATA_SEED); - - // Prepare the transaction metadata. - let secret_key = SecretKey::random(rng); - let utxo_id = rng.gen(); - let amount = 1; - let maturity = 1.into(); - let asset_id = rng.gen(); - let tx_pointer = rng.gen(); - let block_height = (u32::MAX >> 1).into(); - - let mut tb = tx::TransactionBuilder::script(bytecode, script_input_data) - .add_unsigned_coin_input( - secret_key, - utxo_id, - amount, - asset_id, - tx_pointer, - 0u32.into(), - ) - .maturity(maturity) - .clone(); - let mut output_index = 1; - // Insert contract ids into tx input - for contract_id in test_setup.contract_ids() { - tb.add_input(tx::Input::contract( - tx::UtxoId::new(tx::Bytes32::zeroed(), 0), - tx::Bytes32::zeroed(), - tx::Bytes32::zeroed(), - tx::TxPointer::new(0u32.into(), 0), - contract_id, - )) - .add_output(tx::Output::Contract(Contract { - input_index: output_index, - balance_root: fuel_tx::Bytes32::zeroed(), - state_root: tx::Bytes32::zeroed(), - })); - output_index += 1; - } - let consensus_params = tb.get_params().clone(); - - // Temporarily finalize to calculate `script_gas_limit` - let tmp_tx = tb.clone().finalize(); - // Get `max_gas` used by everything except the script execution. Add `1` because of rounding. - let max_gas = tmp_tx.max_gas(consensus_params.gas_costs(), consensus_params.fee_params()) + 1; - // Increase `script_gas_limit` to the maximum allowed value. - tb.script_gas_limit(consensus_params.tx_params().max_gas_per_tx - max_gas); - - let tx = tb.finalize_checked(block_height); - - let mut interpreter: vm::prelude::Interpreter<_, _, vm::interpreter::NotSupportedEcal> = - vm::interpreter::Interpreter::with_storage(storage, consensus_params.into()); - - // Execute and return the result. - let start = std::time::Instant::now(); - let transition = interpreter.transact(tx).unwrap(); - let duration = start.elapsed(); - let state = *transition.state(); - let receipts = transition.receipts().to_vec(); - - (state, duration, receipts) -} - #[cfg(test)] mod tests { use std::path::PathBuf; diff --git a/forc-test/src/setup.rs b/forc-test/src/setup.rs new file mode 100644 index 00000000000..8d213a46ad2 --- /dev/null +++ b/forc-test/src/setup.rs @@ -0,0 +1,93 @@ +use fuel_tx as tx; +use fuel_vm::{self as vm}; + +/// Result of preparing a deployment transaction setup for a contract. +pub type ContractDeploymentSetup = (tx::ContractId, vm::checked_transaction::Checked); + +/// Required test setup for package types that requires a deployment. +#[derive(Debug)] +pub enum DeploymentSetup { + Script(ScriptTestSetup), + Contract(ContractTestSetup), +} + +impl DeploymentSetup { + /// Returns the storage for this test setup + fn storage(&self) -> &vm::storage::MemoryStorage { + match self { + DeploymentSetup::Script(script_setup) => &script_setup.storage, + DeploymentSetup::Contract(contract_setup) => &contract_setup.storage, + } + } + + /// Return the root contract id if this is a contract setup. + fn root_contract_id(&self) -> Option { + match self { + DeploymentSetup::Script(_) => None, + DeploymentSetup::Contract(contract_setup) => Some(contract_setup.root_contract_id), + } + } +} + +/// The storage and the contract id (if a contract is being tested) for a test. +#[derive(Debug)] +pub enum TestSetup { + WithDeployment(DeploymentSetup), + WithoutDeployment(vm::storage::MemoryStorage), +} + +impl TestSetup { + /// Returns the storage for this test setup + pub fn storage(&self) -> &vm::storage::MemoryStorage { + match self { + TestSetup::WithDeployment(deployment_setup) => deployment_setup.storage(), + TestSetup::WithoutDeployment(storage) => storage, + } + } + + /// Produces an iterator yielding contract ids of contract dependencies for this test setup. + pub fn contract_dependency_ids(&self) -> impl Iterator + '_ { + match self { + TestSetup::WithDeployment(deployment_setup) => match deployment_setup { + DeploymentSetup::Script(script_setup) => { + script_setup.contract_dependency_ids.iter() + } + DeploymentSetup::Contract(contract_setup) => { + contract_setup.contract_dependency_ids.iter() + } + }, + TestSetup::WithoutDeployment(_) => [].iter(), + } + } + + /// Return the root contract id if this is a contract setup. + pub fn root_contract_id(&self) -> Option { + match self { + TestSetup::WithDeployment(deployment_setup) => deployment_setup.root_contract_id(), + TestSetup::WithoutDeployment(_) => None, + } + } + + /// Produces an iterator yielding all contract ids required to be included in the transaction + /// for this test setup. + pub fn contract_ids(&self) -> impl Iterator + '_ { + self.contract_dependency_ids() + .cloned() + .chain(self.root_contract_id()) + } +} + +/// The data collected to test a contract. +#[derive(Debug)] +pub struct ContractTestSetup { + pub storage: vm::storage::MemoryStorage, + pub contract_dependency_ids: Vec, + pub root_contract_id: tx::ContractId, +} + +/// The data collected to test a script. +#[derive(Debug)] +pub struct ScriptTestSetup { + pub storage: vm::storage::MemoryStorage, + pub contract_dependency_ids: Vec, +} diff --git a/sway-core/src/semantic_analysis/namespace/items.rs b/sway-core/src/semantic_analysis/namespace/items.rs index 2fb38266c6b..43bfb038c5b 100644 --- a/sway-core/src/semantic_analysis/namespace/items.rs +++ b/sway-core/src/semantic_analysis/namespace/items.rs @@ -28,6 +28,7 @@ pub(crate) enum GlobImport { } pub(super) type SymbolMap = im::OrdMap; +// The final `bool` field of `UseSynonyms` is true if the `Vec` path is absolute. pub(super) type UseSynonyms = im::HashMap, GlobImport, ty::TyDecl, bool)>; pub(super) type UseAliases = im::HashMap; diff --git a/sway-core/src/semantic_analysis/namespace/root.rs b/sway-core/src/semantic_analysis/namespace/root.rs index bc23539627c..2724cae9e69 100644 --- a/sway-core/src/semantic_analysis/namespace/root.rs +++ b/sway-core/src/semantic_analysis/namespace/root.rs @@ -342,8 +342,19 @@ impl Root { match module.use_synonyms.get(symbol) { Some((_, _, decl @ ty::TyDecl::EnumVariantDecl { .. }, _)) => Ok(decl.clone()), Some((src_path, _, _, _)) if mod_path != src_path => { - // TODO: check that the symbol import is public? - self.resolve_symbol(handler, engines, src_path, true_symbol, self_type) + // If the symbol is imported, before resolving to it, + // we need to check if there is a local symbol withing the module with + // the same name, and if yes resolve to the local symbol, because it + // shadows the import. + // Note that we can have two situations here: + // - glob-import, in which case the local symbol simply shadows the glob-imported one. + // - non-glob import, in which case we will already have a name clash reported + // as an error, but still have to resolve to the local module symbol + // if it exists. + match module.symbols.get(true_symbol) { + Some(decl) => Ok(decl.clone()), + None => self.resolve_symbol(handler, engines, src_path, true_symbol, self_type), + } } _ => module .check_symbol(true_symbol) diff --git a/sway-core/src/semantic_analysis/type_check_context.rs b/sway-core/src/semantic_analysis/type_check_context.rs index 3756507aa88..abf74aa39e7 100644 --- a/sway-core/src/semantic_analysis/type_check_context.rs +++ b/sway-core/src/semantic_analysis/type_check_context.rs @@ -606,7 +606,7 @@ impl<'a> TypeCheckContext<'a> { /// /// The `mod_path` is significant here as we assume the resolution is done within the /// context of the module pointed to by `mod_path` and will only check the call path prefixes - /// and the symbol's own visibility + /// and the symbol's own visibility. pub(crate) fn resolve_call_path_with_visibility_check_and_modpath( &self, handler: &Handler, diff --git a/sway-error/src/parser_error.rs b/sway-error/src/parser_error.rs index 4d712a9610b..adf5d78fee2 100644 --- a/sway-error/src/parser_error.rs +++ b/sway-error/src/parser_error.rs @@ -94,6 +94,8 @@ pub enum ParseErrorKind { UnexpectedTokenAfterSliceType, #[error("Expected a path type.")] ExpectedPathType, + #[error("Expected ':'. Enum variants must be in the form `Variant: ()`, `Variant: `, or `Variant: (, ..., )`. E.g., `Foo: (), or `Bar: (bool, u32)`.")] + MissingColonInEnumTypeField, } #[derive(Debug, Error, Clone, PartialEq, Eq, Hash)] diff --git a/sway-lib-core/src/never.sw b/sway-lib-core/src/never.sw index 5c9760b6e1d..ae35a4b0002 100644 --- a/sway-lib-core/src/never.sw +++ b/sway-lib-core/src/never.sw @@ -42,7 +42,7 @@ use ::ops::{Eq, Not, Ord}; /// ``` /// /// Regardless of the type of `x`, the return block of type `Never` will always coerce into `x` type. -/// +/// /// # Examples /// /// ```sway @@ -57,8 +57,7 @@ pub enum Never {} impl Not for Never { fn not(self) -> Self { - match self { - } + match self {} } } diff --git a/sway-lib-core/src/ops.sw b/sway-lib-core/src/ops.sw index 7ba7e5b4676..2517d8d37e5 100644 --- a/sway-lib-core/src/ops.sw +++ b/sway-lib-core/src/ops.sw @@ -34,7 +34,7 @@ pub trait Add { /// let struct1 = MyStruct { val: 1 }; /// let struct2 = MyStruct { val: 2 }; /// let result_struct = struct1 + struct2; - /// assert(result_struct.val == 3); + /// assert(result_struct.val == 3); /// } /// ``` fn add(self, other: Self) -> Self; @@ -122,7 +122,7 @@ pub trait Subtract { /// let struct1 = MyStruct { val: 3 }; /// let struct2 = MyStruct { val: 1 }; /// let result_struct = struct1 - struct2; - /// assert(result_struct.val == 2); + /// assert(result_struct.val == 2); /// } /// ``` fn subtract(self, other: Self) -> Self; @@ -192,7 +192,7 @@ pub trait Multiply { /// let struct1 = MyStruct { val: 3 }; /// let struct2 = MyStruct { val: 2 }; /// let result_struct = struct1 * struct2; - /// assert(result_struct.val == 6); + /// assert(result_struct.val == 6); /// } /// ``` fn multiply(self, other: Self) -> Self; @@ -280,7 +280,7 @@ pub trait Divide { /// let struct1 = MyStruct { val: 10 }; /// let struct2 = MyStruct { val: 2 }; /// let result_struct = struct1 / struct2; - /// assert(result_struct.val == 5); + /// assert(result_struct.val == 5); /// } /// ``` fn divide(self, other: Self) -> Self; @@ -352,7 +352,7 @@ pub trait Mod { /// let struct1 = MyStruct { val: 10 }; /// let struct2 = MyStruct { val: 2 }; /// let result_struct = struct1 % struct2; - /// assert(result_struct.val == 0); + /// assert(result_struct.val == 0); /// } /// ``` fn modulo(self, other: Self) -> Self; @@ -414,7 +414,7 @@ pub trait Not { /// fn foo() { /// let struct = MyStruct { val: true }; /// let result_struct = !struct; - /// assert(!result_struct.val); + /// assert(!result_struct.val); /// } /// ``` fn not(self) -> Self; @@ -494,7 +494,7 @@ pub trait Eq { /// let struct1 = MyStruct { val: 2 }; /// let struct2 = MyStruct { val: 2 }; /// let result = struct1 == struct2; - /// assert(result); + /// assert(result); /// } /// ``` fn eq(self, other: Self) -> bool; @@ -530,7 +530,7 @@ pub trait Eq { /// let struct1 = MyStruct { val: 10 }; /// let struct2 = MyStruct { val: 2 }; /// let result = struct1 != struct2; - /// assert(result); + /// assert(result); /// } /// ``` fn neq(self, other: Self) -> bool { @@ -615,7 +615,7 @@ pub trait Ord { /// let struct1 = MyStruct { val: 10 }; /// let struct2 = MyStruct { val: 2 }; /// let result = struct1 > struct2; - /// assert(result); + /// assert(result); /// } /// ``` fn gt(self, other: Self) -> bool; @@ -647,7 +647,7 @@ pub trait Ord { /// let struct1 = MyStruct { val: 10 }; /// let struct2 = MyStruct { val: 2 }; /// let result = struct1 < struct2; - /// assert(!result); + /// assert(!result); /// } /// ``` fn lt(self, other: Self) -> bool; @@ -741,7 +741,7 @@ pub trait BitwiseAnd { /// let struct1 = MyStruct { val: 10 }; /// let struct2 = MyStruct { val: 11 }; /// let result_struct = struct1 & struct2; - /// assert(result_struct.val == 10); + /// assert(result_struct.val == 10); /// } /// ``` fn binary_and(self, other: Self) -> Self; @@ -815,7 +815,7 @@ pub trait BitwiseOr { /// let struct1 = MyStruct { val: 10 }; /// let struct2 = MyStruct { val: 11 }; /// let result_struct = struct1 | struct2; - /// assert(result_struct.val == 11); + /// assert(result_struct.val == 11); /// } /// ``` fn binary_or(self, other: Self) -> Self; @@ -889,7 +889,7 @@ pub trait BitwiseXor { /// let struct1 = MyStruct { val: 10 }; /// let struct2 = MyStruct { val: 11 }; /// let result_struct = struct1 ^ struct2; - /// assert(result_struct.val == 1); + /// assert(result_struct.val == 1); /// } /// ``` fn binary_xor(self, other: Self) -> Self; @@ -903,7 +903,7 @@ impl BitwiseXor for u256 { impl BitwiseXor for b256 { fn binary_xor(self, other: Self) -> Self { - __xor(self, other) + __xor(self, other) } } @@ -973,13 +973,12 @@ trait OrdEq: Ord + Eq { /// let struct1 = MyStruct { val: 10 }; /// let struct2 = MyStruct { val: 10 }; /// let result = struct1 >= struct2; - /// assert(result); + /// assert(result); /// } /// ``` fn ge(self, other: Self) -> bool { self.gt(other) || self.eq(other) } - /// Evaluates if one value of the same type is less or equal to than another. /// /// # Additional Information @@ -1019,7 +1018,7 @@ trait OrdEq: Ord + Eq { /// let struct1 = MyStruct { val: 10 }; /// let struct2 = MyStruct { val: 10 }; /// let result = struct1 <= struct2; - /// assert(result); + /// assert(result); /// } /// ``` fn le(self, other: Self) -> bool { @@ -1065,7 +1064,7 @@ pub trait Shift { /// fn foo() { /// let struct1 = MyStruct { val: 10 }; /// let result_struct = struct1 << 3; - /// assert(result_struct.val == 80); + /// assert(result_struct.val == 80); /// } /// ``` fn lsh(self, other: u64) -> Self; @@ -1099,7 +1098,7 @@ pub trait Shift { /// fn foo() { /// let struct1 = MyStruct { val: 10 }; /// let result_struct = struct1 >> 1; - /// assert(result_struct.val == 5); + /// assert(result_struct.val == 5); /// } /// ``` fn rsh(self, other: u64) -> Self; @@ -1168,12 +1167,16 @@ impl Shift for u8 { /// Build a single b256 value from a tuple of 4 u64 values. fn compose(words: (u64, u64, u64, u64)) -> b256 { - asm(r1: words) { r1: b256 } + asm(r1: words) { + r1: b256 + } } /// Get a tuple of 4 u64 values from a single b256 value. fn decompose(val: b256) -> (u64, u64, u64, u64) { - asm(r1: val) { r1: (u64, u64, u64, u64) } + asm(r1: val) { + r1: (u64, u64, u64, u64) + } } #[test] @@ -1215,4 +1218,4 @@ impl Eq for str { } } } -} \ No newline at end of file +} diff --git a/sway-lib-core/src/prelude.sw b/sway-lib-core/src/prelude.sw index 00fb4e4c420..588d2e2a101 100644 --- a/sway-lib-core/src/prelude.sw +++ b/sway-lib-core/src/prelude.sw @@ -10,4 +10,4 @@ use ::raw_slice::*; use ::never::*; use ::ops::*; use ::storage::*; -use ::str::*; \ No newline at end of file +use ::str::*; diff --git a/sway-lib-core/src/primitive_conversions.sw b/sway-lib-core/src/primitive_conversions.sw index 9f05cbc0731..3160a5eca02 100644 --- a/sway-lib-core/src/primitive_conversions.sw +++ b/sway-lib-core/src/primitive_conversions.sw @@ -287,7 +287,9 @@ fn assert(condition: bool) { fn test_u64_as_u256() { let val = 2; let result = val.as_u256(); - assert(result == 0x0000000000000000000000000000000000000000000000000000000000000002u256); + assert( + result == 0x0000000000000000000000000000000000000000000000000000000000000002u256, + ); } #[test] @@ -301,7 +303,9 @@ fn test_u32_as_u64() { fn test_u32_as_u256() { let val = 2u32; let result = val.as_u256(); - assert(result == 0x0000000000000000000000000000000000000000000000000000000000000002u256); + assert( + result == 0x0000000000000000000000000000000000000000000000000000000000000002u256, + ); } #[test] @@ -322,7 +326,9 @@ fn test_u16_as_u32() { fn test_u16_as_u256() { let val = 2u16; let result = val.as_u256(); - assert(result == 0x0000000000000000000000000000000000000000000000000000000000000002u256); + assert( + result == 0x0000000000000000000000000000000000000000000000000000000000000002u256, + ); } #[test] @@ -350,12 +356,16 @@ fn test_u8_as_u16() { fn test_u8_as_u256() { let val = 2u8; let result = val.as_u256(); - assert(result == 0x0000000000000000000000000000000000000000000000000000000000000002u256); + assert( + result == 0x0000000000000000000000000000000000000000000000000000000000000002u256, + ); } #[test] fn test_b256_as_u256() { let val = 0x0000000000000000000000000000000000000000000000000000000000000002; let result = val.as_u256(); - assert(result == 0x0000000000000000000000000000000000000000000000000000000000000002u256); + assert( + result == 0x0000000000000000000000000000000000000000000000000000000000000002u256, + ); } diff --git a/sway-lib-core/src/primitives.sw b/sway-lib-core/src/primitives.sw index 6c3b1548b36..5f118f6a1ef 100644 --- a/sway-lib-core/src/primitives.sw +++ b/sway-lib-core/src/primitives.sw @@ -3,7 +3,7 @@ library; impl u256 { /// The smallest value that can be represented by this integer type. /// - /// # Returns + /// # Returns /// /// * [u256] - The smallest `u256` value. /// @@ -60,7 +60,7 @@ impl u256 { impl u64 { /// The smallest value that can be represented by this integer type. /// - /// # Returns + /// # Returns /// /// * [u64] - The smallest `u64` value. /// @@ -234,7 +234,7 @@ impl u8 { /// # Returns /// /// * [u8] - The smallest `u8` value. - /// + /// /// # Examples /// /// ```sway @@ -271,7 +271,7 @@ impl u8 { /// # Returns /// /// * [u64] - The number of bits for a `u8`. - /// + /// /// # Examples /// /// ```sway @@ -291,9 +291,9 @@ impl b256 { /// # Returns /// /// * [b256] - The smallest `b256` value. - /// + /// /// # Examples - /// + /// /// ```sway /// use std::constants::ZERO_B256; /// @@ -336,7 +336,7 @@ impl b256 { /// ```sway /// fn foo() { /// let bits == b256::bits(); - /// assert(bits == 256); + /// assert(bits == 256); /// } /// ``` pub fn bits() -> u64 { diff --git a/sway-lib-core/src/raw_ptr.sw b/sway-lib-core/src/raw_ptr.sw index b18fe4096d7..5b8d487fc8d 100644 --- a/sway-lib-core/src/raw_ptr.sw +++ b/sway-lib-core/src/raw_ptr.sw @@ -4,39 +4,41 @@ impl raw_ptr { /// Returns `true` if the pointer is null. /// /// # Returns - /// + /// /// * [bool] - `true` if the pointer is null, otherwise `false`. /// /// # Examples /// /// ```sway /// use std::alloc::alloc; - /// + /// /// fn foo() { /// let ptr = alloc::(2); /// assert(!ptr.is_null()); /// } /// ``` pub fn is_null(self) -> bool { - let null_ptr = asm() { zero: raw_ptr }; + let null_ptr = asm() { + zero: raw_ptr + }; __eq(self, null_ptr) } /// Calculates the offset from the pointer. /// /// # Arguments - /// + /// /// * `count`: [u64] - The number of `size_of` bytes to increase by. /// /// # Returns - /// + /// /// * [raw_ptr] - The pointer to the offset memory location. /// /// # Examples - /// + /// /// ```sway /// use std::alloc::alloc; - /// + /// /// fn foo() { /// let ptr = alloc::(2); /// let offset_ptr = ptr.add::(1); @@ -50,18 +52,18 @@ impl raw_ptr { /// Calculates the offset from the pointer. /// /// # Arguments - /// + /// /// * `count`: [u64] - The number of `size_of` bytes to decrease by. /// /// # Returns - /// + /// /// * [raw_ptr] - The pointer to the offset memory location. /// /// # Examples - /// + /// /// ```sway /// use std::alloc::alloc; - /// + /// /// fn foo() { /// let ptr = alloc::(2); /// let offset_ptr = ptr.add::(1); @@ -83,7 +85,7 @@ impl raw_ptr { /// /// ```sway /// use std::alloc::alloc; - /// + /// /// fn foo() { /// let ptr = alloc::(1); /// ptr.write(5); @@ -92,7 +94,9 @@ impl raw_ptr { /// ``` pub fn read(self) -> T { if __is_reference_type::() { - asm(ptr: self) { ptr: T } + asm(ptr: self) { + ptr: T + } } else if __eq(__size_of::(), 1) { asm(ptr: self, val) { lb val ptr i0; @@ -107,17 +111,17 @@ impl raw_ptr { } /// Copies `count * size_of` bytes from `self` to `dst`. - /// + /// /// # Arguments /// /// * `dst`: [raw_ptr] - Pointer to the location in memory to copy the bytes to. /// * `count`: [u64] - The number of `size_of` bytes to copy. - /// + /// /// # Examples /// /// ```sway /// use std::alloc::alloc; - /// + /// /// fn foo() { /// let ptr_1 = alloc::(1); /// let ptr_2 = alloc::(1); @@ -136,12 +140,12 @@ impl raw_ptr { /// Writes the given value to the address. /// /// # Arguments - /// + /// /// * `val`: [T] - The value to write to memory. /// /// ```sway /// use std::alloc::alloc; - /// + /// /// fn foo() { /// let ptr = alloc::(1); /// ptr.write(5); @@ -174,7 +178,7 @@ impl raw_ptr { /// /// ```sway /// use std::alloc::alloc; - /// + /// /// fn foo() { /// let ptr = alloc::(1); /// ptr.write_byte(5u8); @@ -182,7 +186,9 @@ impl raw_ptr { /// } /// ``` pub fn write_byte(self, val: u8) { - let val_ptr = asm(r1: val) { r1: raw_ptr }; + let val_ptr = asm(r1: val) { + r1: raw_ptr + }; asm(ptr: self, val: val_ptr) { sb ptr val i0; }; @@ -198,7 +204,7 @@ impl raw_ptr { /// /// ```sway /// use std::alloc::alloc; - /// + /// /// fn foo() { /// let ptr = alloc::(1); /// ptr.write_byte(5u8); @@ -213,17 +219,17 @@ impl raw_ptr { } /// Copies `count` bytes from `self` to `dst`. - /// + /// /// # Arguments /// /// * `dst`: [raw_ptr] - Pointer to the location in memory to copy the bytes to. /// * `count`: [u64] - The number of bytes to copy. - /// + /// /// # Examples /// /// ```sway /// use std::alloc::alloc; - /// + /// /// fn foo() { /// let ptr_1 = alloc::(1); /// let ptr_2 = alloc::(1); @@ -241,18 +247,18 @@ impl raw_ptr { /// Add a `u64` offset to a `raw_ptr`. /// /// # Arguments - /// + /// /// * `count`: [u64] - The number of `u64` bytes to increase by. /// /// # Returns - /// + /// /// * [raw_ptr] - The pointer to the offset memory location. /// /// # Examples - /// + /// /// ```sway /// use std::alloc::alloc; - /// + /// /// fn foo() { /// let ptr = alloc::(2); /// let offset_ptr_1 = ptr.add::(1); @@ -270,18 +276,18 @@ impl raw_ptr { /// Subtract a `u64` offset from a `raw_ptr`. /// /// # Arguments - /// + /// /// * `count`: [u64] - The number of `u64` bytes to decrease by. /// /// # Returns - /// + /// /// * [raw_ptr] - The pointer to the offset memory location. /// /// # Examples - /// + /// /// ```sway /// use std::alloc::alloc; - /// + /// /// fn foo() { /// let ptr = alloc::(2); /// let offset_ptr = ptr.add::(1); diff --git a/sway-lib-core/src/raw_slice.sw b/sway-lib-core/src/raw_slice.sw index eddd6906f19..13bd0affb90 100644 --- a/sway-lib-core/src/raw_slice.sw +++ b/sway-lib-core/src/raw_slice.sw @@ -28,7 +28,7 @@ pub trait AsRawSlice { /// /// fn foo() { /// let my_type = MyType { - /// ptr: alloc_bytes(0), + /// ptr: alloc_bytes(0), /// len: 0 /// } /// let slice = my_type.as_raw_slice(); @@ -49,7 +49,9 @@ pub trait AsRawSlice { /// /// * [raw_slice] - The newly created `raw_slice`. fn from_parts(parts: (raw_ptr, u64)) -> raw_slice { - asm(ptr: parts) { ptr: raw_slice } + asm(ptr: parts) { + ptr: raw_slice + } } /// Returns a pointer and length from a `raw_slice`. @@ -62,7 +64,9 @@ fn from_parts(parts: (raw_ptr, u64)) -> raw_slice { /// /// * [(raw_ptr, u64)] - A tuple of the location in memory of the original `raw_slice` and its length. fn into_parts(slice: raw_slice) -> (raw_ptr, u64) { - asm(ptr: slice) { ptr: (raw_ptr, u64) } + asm(ptr: slice) { + ptr: (raw_ptr, u64) + } } impl raw_slice { @@ -81,7 +85,7 @@ impl raw_slice { /// /// ```sway /// use std::alloc::alloc; - /// + /// /// fn foo() { /// let ptr = alloc::(1); /// let slice = raw_slice::from_parts::(ptr, 1); @@ -102,7 +106,7 @@ impl raw_slice { /// /// ```sway /// use std::alloc::alloc; - /// + /// /// fn foo() { /// let ptr = alloc::(1); /// let slice = raw_slice::from_parts::(ptr, 1); @@ -115,7 +119,7 @@ impl raw_slice { } /// Returns the number of elements in the slice. - /// + /// /// # Returns /// /// * [u64] - The length of the slice based on `size_of::`. @@ -124,7 +128,7 @@ impl raw_slice { /// /// ```sway /// use std::alloc::alloc; - /// + /// /// fn foo() { /// let ptr = alloc::(1); /// let slice = raw_slice::from_parts::(ptr, 1); @@ -134,9 +138,9 @@ impl raw_slice { pub fn len(self) -> u64 { __div(into_parts(self).1, __size_of::()) } - + /// Returns the number of elements in the slice when the elements are bytes. - /// + /// /// # Returns /// /// * [u64] - The number of bytes in the `raw_slice`. @@ -145,7 +149,7 @@ impl raw_slice { /// /// ```sway /// use std::alloc::alloc; - /// + /// /// fn foo() { /// let ptr = alloc::(1); /// let slice = raw_slice::from_parts::(ptr, 1); diff --git a/sway-lib-core/src/storage.sw b/sway-lib-core/src/storage.sw index 959a7f1dfa6..c9997493939 100644 --- a/sway-lib-core/src/storage.sw +++ b/sway-lib-core/src/storage.sw @@ -3,9 +3,9 @@ library; /// Describes a location in storage. /// /// # Additional Information -/// +/// /// The location in storage is specified by the `b256` key of a particular storage slot and an -/// offset, in words, from the start of the storage slot at `key`. The parameter `T` is the type of +/// offset, in words, from the start of the storage slot at `key`. The parameter `T` is the type of /// the data to be read from or written to at `offset`. /// `field_id` is a unique identifier for the storage field being referred to, it is different even /// for multiple zero sized fields that might live at the same location but diff --git a/sway-lib-core/src/str.sw b/sway-lib-core/src/str.sw index f2ec476766c..27ddb173682 100644 --- a/sway-lib-core/src/str.sw +++ b/sway-lib-core/src/str.sw @@ -3,13 +3,17 @@ library; impl str { /// Return a `raw_ptr` to the beginning of the string slice on the heap pub fn as_ptr(self) -> raw_ptr { - let (ptr, _) = asm(s: self) { s: (raw_ptr, u64) }; + let (ptr, _) = asm(s: self) { + s: (raw_ptr, u64) + }; ptr } /// Return the length of the string slice in bytes pub fn len(self) -> u64 { - let (_, len) = asm(s: self) { s: (raw_ptr, u64) }; + let (_, len) = asm(s: self) { + s: (raw_ptr, u64) + }; len } } @@ -18,7 +22,7 @@ pub fn from_str_array(s: S) -> str { __assert_is_str_array::(); let str_size = __size_of_str_array::(); let src = __addr_of(s); - + let ptr = asm(size: __size_of::(), dest, src: src) { aloc size; move dest hp; @@ -26,5 +30,7 @@ pub fn from_str_array(s: S) -> str { dest: raw_ptr }; - asm(s: (ptr, str_size)) { s: str } + asm(s: (ptr, str_size)) { + s: str + } } diff --git a/sway-parse/src/item/mod.rs b/sway-parse/src/item/mod.rs index 379a97596ac..71f2fa14f97 100644 --- a/sway-parse/src/item/mod.rs +++ b/sway-parse/src/item/mod.rs @@ -1,8 +1,8 @@ use crate::{Parse, ParseResult, ParseToEnd, Parser, ParserConsumed}; use sway_ast::keywords::{ - AbiToken, ClassToken, ConfigurableToken, ConstToken, EnumToken, FnToken, ImplToken, ModToken, - MutToken, OpenAngleBracketToken, RefToken, SelfToken, SemicolonToken, StorageToken, + AbiToken, ClassToken, ColonToken, ConfigurableToken, ConstToken, EnumToken, FnToken, ImplToken, + ModToken, MutToken, OpenAngleBracketToken, RefToken, SelfToken, SemicolonToken, StorageToken, StructToken, TraitToken, TypeToken, UseToken, WhereToken, }; use sway_ast::{ @@ -96,7 +96,11 @@ impl Parse for TypeField { fn parse(parser: &mut Parser) -> ParseResult { Ok(TypeField { name: parser.parse()?, - colon_token: parser.parse()?, + colon_token: if parser.peek::().is_some() { + parser.parse() + } else { + Err(parser.emit_error(ParseErrorKind::MissingColonInEnumTypeField)) + }?, ty: parser.parse()?, }) } diff --git a/swayfmt/Cargo.toml b/swayfmt/Cargo.toml index aa75bbd743c..784b2c00c4a 100644 --- a/swayfmt/Cargo.toml +++ b/swayfmt/Cargo.toml @@ -24,6 +24,7 @@ thiserror = "1.0.30" toml = { version = "0.7", features = ["parse"] } [dev-dependencies] +difference = "2.0.0" paste = "1.0" prettydiff = "0.6" test-macros = { path = "test_macros" } diff --git a/swayfmt/src/utils/language/attribute.rs b/swayfmt/src/utils/language/attribute.rs index 58e0fc6dfbc..daa6b7e5611 100644 --- a/swayfmt/src/utils/language/attribute.rs +++ b/swayfmt/src/utils/language/attribute.rs @@ -15,7 +15,7 @@ use sway_types::{ Spanned, }; -impl Format for Annotated { +impl Format for Annotated { fn format( &self, formatted_code: &mut FormattedCode, @@ -24,11 +24,17 @@ impl Format for Annotated { // format each `Attribute` let mut start = None; for attr in &self.attribute_list { + if let Some(start) = start { + // Write any comments that may have been defined in between the + // attributes and the value + write_comments(formatted_code, start..attr.span().start(), formatter)?; + if !formatted_code.ends_with(NEW_LINE) { + write!(formatted_code, "{}", NEW_LINE)?; + } + } formatter.write_indent_into_buffer(formatted_code)?; attr.format(formatted_code, formatter)?; - if start.is_none() { - start = Some(attr.span().end()); - } + start = Some(attr.span().end()); } if let Some(start) = start { // Write any comments that may have been defined in between the diff --git a/swayfmt/src/utils/language/expr/asm_block.rs b/swayfmt/src/utils/language/expr/asm_block.rs index a0c63a863ea..19f92f5ea68 100644 --- a/swayfmt/src/utils/language/expr/asm_block.rs +++ b/swayfmt/src/utils/language/expr/asm_block.rs @@ -61,21 +61,54 @@ fn format_asm_block( formatter.with_shape( formatter.shape.with_default_code_line(), |formatter| -> Result<(), FormatterError> { - AsmBlock::open_parenthesis(formatted_code, formatter)?; + formatter + .shape + .code_line + .update_line_style(LineStyle::Normal); + + let mut inline_arguments = FormattedCode::new(); asm_block .registers .get() - .format(formatted_code, formatter)?; - AsmBlock::close_parenthesis(formatted_code, formatter)?; + .format(&mut inline_arguments, formatter)?; + + formatter.shape.code_line.update_expr_new_line(false); + formatter + .shape + .code_line + .update_expr_kind(shape::ExprKind::Function); + if inline_arguments.len() > formatter.shape.width_heuristics.fn_call_width { + formatter + .shape + .code_line + .update_line_style(LineStyle::Multiline); + AsmBlock::open_parenthesis(formatted_code, formatter)?; + formatter.indent(); + asm_block + .registers + .get() + .format(formatted_code, formatter)?; + formatter.unindent(); + write!(formatted_code, "{}", formatter.indent_to_str()?)?; + AsmBlock::close_parenthesis(formatted_code, formatter)?; + } else { + AsmBlock::open_parenthesis(formatted_code, formatter)?; + write!(formatted_code, "{}", inline_arguments)?; + AsmBlock::close_parenthesis(formatted_code, formatter)?; + } + + formatter + .shape + .code_line + .update_line_style(LineStyle::Multiline); + AsmBlock::open_curly_brace(formatted_code, formatter)?; + asm_block.contents.get().format(formatted_code, formatter)?; + AsmBlock::close_curly_brace(formatted_code, formatter)?; Ok(()) }, )?; - AsmBlock::open_curly_brace(formatted_code, formatter)?; - asm_block.contents.get().format(formatted_code, formatter)?; - AsmBlock::close_curly_brace(formatted_code, formatter)?; - Ok(()) } @@ -156,7 +189,7 @@ impl Format for Instruction { formatted_code: &mut FormattedCode, _formatter: &mut Formatter, ) -> Result<(), FormatterError> { - write!(formatted_code, "{:<4}", &self.op_code_ident().as_str())?; + write!(formatted_code, "{}", &self.op_code_ident().as_str())?; for arg in self.register_arg_idents() { write!(formatted_code, " {}", arg.as_str())? } diff --git a/swayfmt/src/utils/language/expr/mod.rs b/swayfmt/src/utils/language/expr/mod.rs index 010bdb9d714..69114cf9cfa 100644 --- a/swayfmt/src/utils/language/expr/mod.rs +++ b/swayfmt/src/utils/language/expr/mod.rs @@ -29,6 +29,64 @@ pub(crate) mod struct_field; #[cfg(test)] mod tests; +#[inline] +fn two_parts_expr( + lhs: &Expr, + operator: &str, + rhs: &Expr, + formatted_code: &mut FormattedCode, + formatter: &mut Formatter, +) -> Result<(), FormatterError> { + let mut rhs_code = FormattedCode::new(); + rhs.format(&mut rhs_code, formatter)?; + + if !formatter.shape.code_line.expr_new_line + && rhs_code.len() > formatter.shape.width_heuristics.collection_width + { + // Right hand side is too long to fit in a single line, and + // the current expr is not being rendered multiline at the + // expr level, then add an indentation to the following + // expression and generate the code + formatter.with_shape( + formatter + .shape + .with_code_line_from(LineStyle::Multiline, ExprKind::Undetermined), + |formatter| -> Result<(), FormatterError> { + formatter.shape.code_line.update_expr_new_line(true); + + lhs.format(formatted_code, formatter)?; + formatter.indent(); + write!( + formatted_code, + "\n{}{} ", + formatter.indent_to_str()?, + operator, + )?; + rhs.format(formatted_code, formatter)?; + formatter.unindent(); + Ok(()) + }, + )?; + } else { + lhs.format(formatted_code, formatter)?; + match formatter.shape.code_line.line_style { + LineStyle::Multiline => { + write!( + formatted_code, + "\n{}{} ", + formatter.indent_to_str()?, + operator, + )?; + } + _ => { + write!(formatted_code, " {} ", operator,)?; + } + } + write!(formatted_code, "{}", rhs_code)?; + } + Ok(()) +} + impl Format for Expr { fn format( &self, @@ -107,9 +165,15 @@ impl Format for Expr { )?; } Self::Parens(expr) => { + if formatter.shape.code_line.expr_new_line { + formatter.indent(); + } Self::open_parenthesis(formatted_code, formatter)?; expr.get().format(formatted_code, formatter)?; Self::close_parenthesis(formatted_code, formatter)?; + if formatter.shape.code_line.expr_new_line { + formatter.unindent(); + } } Self::Block(code_block) => { if !code_block.get().statements.is_empty() @@ -544,46 +608,26 @@ impl Format for Expr { double_ampersand_token, rhs, } => { - lhs.format(formatted_code, formatter)?; - match formatter.shape.code_line.line_style { - LineStyle::Multiline => { - write!( - formatted_code, - "\n{}{} ", - formatter.indent_to_str()?, - double_ampersand_token.span().as_str() - )?; - } - _ => { - write!( - formatted_code, - " {} ", - double_ampersand_token.span().as_str() - )?; - } - } - rhs.format(formatted_code, formatter)?; + two_parts_expr( + lhs, + double_ampersand_token.span().as_str(), + rhs, + formatted_code, + formatter, + )?; } Self::LogicalOr { lhs, double_pipe_token, rhs, } => { - lhs.format(formatted_code, formatter)?; - match formatter.shape.code_line.line_style { - LineStyle::Multiline => { - write!( - formatted_code, - "\n{}{} ", - formatter.indent_to_str()?, - double_pipe_token.span().as_str() - )?; - } - _ => { - write!(formatted_code, " {} ", double_pipe_token.span().as_str())?; - } - } - rhs.format(formatted_code, formatter)?; + two_parts_expr( + lhs, + double_pipe_token.span().as_str(), + rhs, + formatted_code, + formatter, + )?; } Self::Reassignment { assignable, @@ -703,6 +747,12 @@ fn same_line_if_only_argument(expr: &Expr) -> bool { matches!( expr, Expr::Struct { path: _, fields: _ } + | Expr::Tuple(_) + | Expr::Parens(_) + | Expr::Not { + bang_token: _, + expr: _ + } | Expr::Path(_) | Expr::FuncApp { func: _, args: _ } | Expr::Match { diff --git a/swayfmt/src/utils/language/statement.rs b/swayfmt/src/utils/language/statement.rs index e10ca03dd29..3c4ccf9b856 100644 --- a/swayfmt/src/utils/language/statement.rs +++ b/swayfmt/src/utils/language/statement.rs @@ -85,10 +85,35 @@ fn format_statement( remove_arguments_from_expr(expr.clone()).format(&mut temp_expr, formatter)?; if temp_expr.len() > formatter.shape.width_heuristics.chain_width { - formatter.shape.code_line.expr_new_line = true; + let update_expr_new_line = if !matches!( + expr, + Expr::MethodCall { .. } + | Expr::FuncApp { func: _, args: _ } + | Expr::If(_) + | Expr::While { + while_token: _, + condition: _, + block: _ + } + ) { + // Method calls, If, While should not tamper with the + // expr_new_line because that would be inherited for all + // statements. That should be applied at the lowest level + // possible (ideally at the expression level) + formatter.shape.code_line.expr_new_line + } else if formatter.shape.code_line.expr_new_line { + // already enabled + true + } else { + formatter.shape.code_line.update_expr_new_line(true); + false + }; // reformat the expression adding a break expr.format(formatted_code, formatter)?; - formatter.shape.code_line.expr_new_line = false; + formatter + .shape + .code_line + .update_expr_new_line(update_expr_new_line); } else { expr.format(formatted_code, formatter)?; } diff --git a/swayfmt/src/utils/map/newline.rs b/swayfmt/src/utils/map/newline.rs index 54403426c01..daab22b6c9d 100644 --- a/swayfmt/src/utils/map/newline.rs +++ b/swayfmt/src/utils/map/newline.rs @@ -1,12 +1,6 @@ use anyhow::Result; -use ropey::Rope; -use std::{ - collections::BTreeMap, - iter::{Enumerate, Peekable}, - path::PathBuf, - str::Chars, - sync::Arc, -}; +use ropey::{str_utils::byte_to_char_idx, Rope}; +use std::{collections::BTreeMap, path::PathBuf, sync::Arc}; use sway_ast::Module; use sway_types::SourceEngine; @@ -45,59 +39,50 @@ fn is_new_line_in_rope(rope: &Rope, index: usize) -> bool { true } -/// Checks if there is a new line (Operating system specific) next in the iter of characters -#[inline] -fn is_new_line_next_in_iter( - input_iter: &mut Peekable>>, - new_line: &[char], -) -> bool { - let total = new_line.len(); - for (p, char_new_line) in new_line.iter().enumerate() { - if input_iter.peek().map(|x| x.1) == Some(*char_new_line) { - if total != p + 1 { - input_iter.next(); - } - } else { - return false; - } - } - - true -} - /// Search for newline sequences in the unformatted code and collect ByteSpan -> NewlineSequence for the input source fn newline_map_from_src(unformatted_input: &str) -> Result { let mut newline_map = BTreeMap::new(); // Iterate over the unformatted source code to find NewlineSequences - let mut input_iter = unformatted_input.chars().enumerate().peekable(); + let mut input_iter = unformatted_input.chars().peekable(); let mut current_sequence_length = 0; let mut in_sequence = false; let mut sequence_start = 0; - let os_new_line = NEW_LINE.chars().collect::>(); - while let Some((char_index, char)) = input_iter.next() { - let next_char = input_iter.peek().map(|input| input.1); - if matches!(char, '/' | ';' | '}') - && is_new_line_next_in_iter(&mut input_iter, &os_new_line) - { + let mut bytes_offset = 0; + while let Some(char) = input_iter.next() { + // Keep of byte offset for each char, it is used for indexing the + // unformatted input (to replace the newline sequences with correct + // amount of newlines). The code downstream deal with a vector of bytes + // and not utf-8 chars. + let char_index = bytes_offset; + bytes_offset += char.len_utf8(); + + let next_char = input_iter.peek(); + let is_new_line = unformatted_input + .get(char_index..char_index + NEW_LINE.len()) + .map(|c| c == NEW_LINE) + .unwrap_or(false); + let is_new_line_next = unformatted_input + .get(char_index + 1..char_index + 1 + NEW_LINE.len()) + .map(|c| c == NEW_LINE) + .unwrap_or(false); + + if matches!(char, ';' | '}') && is_new_line_next { if !in_sequence { - sequence_start = char_index + os_new_line.len(); + sequence_start = char_index + NEW_LINE.len(); in_sequence = true; } - } else if os_new_line.ends_with(&[char]) && in_sequence { + } else if is_new_line && in_sequence { current_sequence_length += 1; } - if (Some('}') == next_char || Some('(') == next_char) && in_sequence { + if (Some(&'}') == next_char || Some(&'(') == next_char) && in_sequence { // If we are in a sequence and find `}`, abort the sequence current_sequence_length = 0; in_sequence = false; } - if next_char == Some(' ') || next_char == Some('\t') { + if next_char == Some(&' ') || next_char == Some(&'\t') { continue; } - if !is_new_line_next_in_iter(&mut input_iter, &os_new_line) - && current_sequence_length > 0 - && in_sequence - { + if !is_new_line_next && current_sequence_length > 0 && in_sequence { // Next char is not a newline so this is the end of the sequence let byte_span = ByteSpan { start: sequence_start, @@ -341,6 +326,8 @@ fn insert_after_span( let mut len = sequence_string.len() as i64; let mut src_rope = Rope::from_str(formatted_code); + let at = byte_to_char_idx(formatted_code, at); + // Remove the previous sequence_length, that will be replaced in the next statement let mut remove_until = at; for i in at..at + newline_sequence.sequence_length { @@ -349,26 +336,33 @@ fn insert_after_span( } remove_until = i; } - if remove_until > at { + let removed = if remove_until > at { src_rope .try_remove(at..remove_until) .map_err(|_| FormatterError::NewlineSequenceError)?; - len -= (remove_until - at) as i64; - } + let removed = (remove_until - at) as i64; + len -= removed; + removed + } else { + 0 + }; // Do never insert the newline sequence between two alphanumeric characters - if !src_rope + let is_token = src_rope .get_char(at) .map(is_alphanumeric) .unwrap_or_default() - || !src_rope + && src_rope .get_char(at + 1) .map(is_alphanumeric) - .unwrap_or_default() - { + .unwrap_or_default(); + + if !is_token { src_rope .try_insert(at, &sequence_string) .map_err(|_| FormatterError::NewlineSequenceError)?; + } else { + len = -removed; } formatted_code.clear(); diff --git a/swayfmt/test_macros/src/lib.rs b/swayfmt/test_macros/src/lib.rs index da65c227868..faa93230e8e 100644 --- a/swayfmt/test_macros/src/lib.rs +++ b/swayfmt/test_macros/src/lib.rs @@ -122,20 +122,16 @@ macro_rules! assert_eq_pretty { let got = &$got; let expected = &$expected; if got != expected { - panic!( - " -printed outputs differ! -expected: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -{} -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -got: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -{} -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -", - expected, got - ); + use difference::{Changeset, Difference}; + let changeset = Changeset::new(expected, got, "\n"); + for diff in changeset.diffs { + match diff { + Difference::Same(s) => println!("{}", s), + Difference::Add(s) => println!("\x1b[32m+{}\x1b[0m", s), // Green color for additions + Difference::Rem(s) => println!("\x1b[31m-{}\x1b[0m", s), // Red color for removals + } + } + panic!("printed outputs differ!"); } }; } diff --git a/swayfmt/tests/mod.rs b/swayfmt/tests/mod.rs index 1039e977251..e6c0faa3ffe 100644 --- a/swayfmt/tests/mod.rs +++ b/swayfmt/tests/mod.rs @@ -1429,7 +1429,7 @@ fn foo() { fn foo() { asm(r1: self, r2: other, r3, r4) { addi r3 zero i32; - meq r4 r1 r2 r3; + meq r4 r1 r2 r3; r4: bool } } @@ -2310,3 +2310,380 @@ fn test_function() -> bool { "#, ); } + +#[test] +fn long_doc_break_new_line() { + check( + r#"library; + +/// Allocates zeroed memory on the heap. +/// +/// # Additional Information +/// +/// In the FuelVM, the heap begins at `VM_MAX_RAM` and grows downward. +/// The heap pointer(`$hp`) always points to the first allocated byte. +/// +/// Initially the heap will look like this: +/// ``` +/// ▾$hp +/// ... 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | +/// ▴VM_MAX_RAM +/// ``` +/// After allocating with `let ptr = alloc::(1)`: +/// ``` +/// ▾$hp +/// ... 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | +/// ▴ptr ▴VM_MAX_RAM +/// ``` +/// After writing with `sw(ptr, u64::max())`: +/// ``` +/// ▾$hp +/// ... 00 00 00 00 00 00 00 00 FF FF FF FF FF FF FF FF | +/// ▴ptr ▴VM_MAX_RAM +/// ``` +/// For more information, see the Fuel Spec for [VM Initialization](https://fuellabs.github.io/fuel-specs/master/vm#vm-initialization) +/// and the VM Instruction Set for [Memory Allocation](https://fuellabs.github.io/fuel-specs/master/vm/instruction_set.html#aloc-allocate-memory). +/// +/// # Arguments +/// +/// * `count`: [u64] - The number of `size_of` bytes to allocate onto the heap. +/// +/// # Returns +/// +/// * [raw_ptr] - The pointer to the newly allocated memory. +/// +/// # Examples +/// +/// ```sway +/// use std::alloc::alloc; +/// +/// fn foo() { +/// let ptr = alloc::(2); +/// assert(!ptr.is_null()); +/// } +/// ``` +/// Reallocates the given area of memory. +/// +/// # Arguments +/// +/// * `ptr`: [raw_ptr] - The pointer to the area of memory to reallocate. +/// * `count`: [u64] - The number of `size_of` bytes kept when reallocating. These are not set to 0. +/// * `new_count`: [u64] - The number of new `size_of` bytes to allocate. These are set to 0. +/// +/// # Returns +/// +/// * [raw_ptr] - The pointer to the newly reallocated memory. +/// +/// # Examples +/// +/// ```sway +/// use std::alloc::{alloc, realloc}; +/// +/// fn foo() { +/// let ptr = alloc::(1); +/// ptr.write(5); +/// let reallocated_ptr = realloc::(ptr, 1, 2); +/// assert(reallocated_ptr.read::() == 5); +/// } +/// ``` +pub fn realloc(ptr: raw_ptr, count: u64, new_count: u64) -> raw_ptr { + if new_count > count { + let new_ptr = alloc::(new_count); + if count > 0 { + ptr.copy_to::(new_ptr, count); + } + new_ptr + } else { + ptr + } +} + +/// Allocates zeroed memory on the heap in individual bytes. +pub fn alloc_bytes(count: u64) -> raw_ptr { + asm(size: count, ptr) { + aloc size; + move ptr hp; + ptr: raw_ptr + } +} + + "#, + r#"library; + +/// Allocates zeroed memory on the heap. +/// +/// # Additional Information +/// +/// In the FuelVM, the heap begins at `VM_MAX_RAM` and grows downward. +/// The heap pointer(`$hp`) always points to the first allocated byte. +/// +/// Initially the heap will look like this: +/// ``` +/// ▾$hp +/// ... 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | +/// ▴VM_MAX_RAM +/// ``` +/// After allocating with `let ptr = alloc::(1)`: +/// ``` +/// ▾$hp +/// ... 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | +/// ▴ptr ▴VM_MAX_RAM +/// ``` +/// After writing with `sw(ptr, u64::max())`: +/// ``` +/// ▾$hp +/// ... 00 00 00 00 00 00 00 00 FF FF FF FF FF FF FF FF | +/// ▴ptr ▴VM_MAX_RAM +/// ``` +/// For more information, see the Fuel Spec for [VM Initialization](https://fuellabs.github.io/fuel-specs/master/vm#vm-initialization) +/// and the VM Instruction Set for [Memory Allocation](https://fuellabs.github.io/fuel-specs/master/vm/instruction_set.html#aloc-allocate-memory). +/// +/// # Arguments +/// +/// * `count`: [u64] - The number of `size_of` bytes to allocate onto the heap. +/// +/// # Returns +/// +/// * [raw_ptr] - The pointer to the newly allocated memory. +/// +/// # Examples +/// +/// ```sway +/// use std::alloc::alloc; +/// +/// fn foo() { +/// let ptr = alloc::(2); +/// assert(!ptr.is_null()); +/// } +/// ``` +/// Reallocates the given area of memory. +/// +/// # Arguments +/// +/// * `ptr`: [raw_ptr] - The pointer to the area of memory to reallocate. +/// * `count`: [u64] - The number of `size_of` bytes kept when reallocating. These are not set to 0. +/// * `new_count`: [u64] - The number of new `size_of` bytes to allocate. These are set to 0. +/// +/// # Returns +/// +/// * [raw_ptr] - The pointer to the newly reallocated memory. +/// +/// # Examples +/// +/// ```sway +/// use std::alloc::{alloc, realloc}; +/// +/// fn foo() { +/// let ptr = alloc::(1); +/// ptr.write(5); +/// let reallocated_ptr = realloc::(ptr, 1, 2); +/// assert(reallocated_ptr.read::() == 5); +/// } +/// ``` +pub fn realloc(ptr: raw_ptr, count: u64, new_count: u64) -> raw_ptr { + if new_count > count { + let new_ptr = alloc::(new_count); + if count > 0 { + ptr.copy_to::(new_ptr, count); + } + new_ptr + } else { + ptr + } +} + +/// Allocates zeroed memory on the heap in individual bytes. +pub fn alloc_bytes(count: u64) -> raw_ptr { + asm(size: count, ptr) { + aloc size; + move ptr hp; + ptr: raw_ptr + } +} +"#, + ) +} + +#[test] +fn broken_doc_comment() { + check( + r#"library; + /// line 1 + /// line 2 + // line 3 + /// line 4 + // line 5 + /// line 6 + fn test() { + } + "#, + r#"library; +/// line 1 +/// line 2 +// line 3 +/// line 4 +// line 5 +/// line 6 +fn test() {} +"#, + ); +} + +#[test] +fn asm_block_v2() { + check( + r#"library; + + pub fn transfer(self, asset_id: AssetId, amount: u64) { + // maintain a manual index as we only have `while` loops in sway atm: + let mut index = 0; + + asm(input: input) { + input: u256 + } + + // If an output of type `OutputVariable` is found, check if its `amount` is + // zero. As one cannot transfer zero coins to an output without a panic, a + // variable output with a value of zero is by definition unused. + let number_of_outputs = output_count(); + while index < number_of_outputs { + if let Output::Variable = output_type(index) { + if output_amount(index) == 0 { + asm(r1: self.value, r2: index, r3: amount, r4: asset_id.value.with_long_long_name) { + tro r1 r2 r3 r4; + }; + return; + } + } + index += 1; + } + + revert(FAILED_TRANSFER_TO_ADDRESS_SIGNAL); + } + "#, + r#"library; + +pub fn transfer(self, asset_id: AssetId, amount: u64) { + // maintain a manual index as we only have `while` loops in sway atm: + let mut index = 0; + + asm(input: input) { + input: u256 + } + + // If an output of type `OutputVariable` is found, check if its `amount` is + // zero. As one cannot transfer zero coins to an output without a panic, a + // variable output with a value of zero is by definition unused. + let number_of_outputs = output_count(); + while index < number_of_outputs { + if let Output::Variable = output_type(index) { + if output_amount(index) == 0 { + asm( + r1: self.value, + r2: index, + r3: amount, + r4: asset_id.value.with_long_long_name, + ) { + tro r1 r2 r3 r4; + }; + return; + } + } + index += 1; + } + + revert(FAILED_TRANSFER_TO_ADDRESS_SIGNAL); +} +"#, + ); +} + +#[test] +fn long_expr_assign() { + check( + r#"library; + +fn foo() { + let x = self.a > other.a || (self.a == other.a && (self.b > other.b || (self.b == other.b && (self.c > other.c || (self.c == other.c && self.d > other.d))))); +} + "#, + r#"library; + +fn foo() { + let x = self.a > other.a + || (self.a == other.a + && (self.b > other.b + || (self.b == other.b + && (self.c > other.c + || (self.c == other.c + && self.d > other.d))))); +} +"#, + ); +} + +#[test] +fn long_expr_return() { + check( + r#"library; + +fn foo() { + self.a > other.a || (self.a == other.a && (self.b > other.b || (self.b == other.b && (self.c > other.c || (self.c == other.c && self.d > other.d))))); +} + "#, + r#"library; + +fn foo() { + self.a > other.a + || (self.a == other.a + && (self.b > other.b + || (self.b == other.b + && (self.c > other.c + || (self.c == other.c + && self.d > other.d))))); +} +"#, + ); +} + +#[test] +fn long_expr_single_arg() { + check( + r#"library; +fn test() { + Self::from(( + self.b * other.c + result_b_d.upper + overflow_of_b_to_a_3 + overflow_of_b_to_a_2 + overflow_of_b_to_a_1 + overflow_of_b_to_a_0, + b, + c, + result_d_d.lower, + )) +} + "#, + r#"library; +fn test() { + Self::from(( + self.b * other.c + result_b_d.upper + overflow_of_b_to_a_3 + overflow_of_b_to_a_2 + overflow_of_b_to_a_1 + overflow_of_b_to_a_0, + b, + c, + result_d_d.lower, + )) +} +"#, + ); +} + +#[test] +fn single_argument_not() { + check( + r#"library; +fn test() { + assert(!(U256::from((0, 0, 0, 1)) > U256::from((0, u64::max(), 0, 0)))); +} + "#, + r#"library; +fn test() { + assert(!(U256::from((0, 0, 0, 1)) > U256::from((0, u64::max(), 0, 0)))); +} +"#, + ); +} diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/enum_rust_like/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_fail/enum_rust_like/Forc.lock new file mode 100644 index 00000000000..cab182f4fba --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/enum_rust_like/Forc.lock @@ -0,0 +1,8 @@ +[[package]] +name = "core" +source = "path+from-root-EC2C1FC903667D61" + +[[package]] +name = "enum_rust_like" +source = "member" +dependencies = ["core"] diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/enum_rust_like/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_fail/enum_rust_like/Forc.toml new file mode 100644 index 00000000000..5ed5fb9d796 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/enum_rust_like/Forc.toml @@ -0,0 +1,8 @@ +[project] +authors = ["Fuel Labs "] +entry = "main.sw" +license = "Apache-2.0" +name = "enum_rust_like" + +[dependencies] +core = { path = "../../../../../../sway-lib-core" } diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/enum_rust_like/json_abi_oracle.json b/test/src/e2e_vm_tests/test_programs/should_fail/enum_rust_like/json_abi_oracle.json new file mode 100644 index 00000000000..0637a088a01 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/enum_rust_like/json_abi_oracle.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/enum_rust_like/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_fail/enum_rust_like/src/main.sw new file mode 100644 index 00000000000..97c124500f2 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/enum_rust_like/src/main.sw @@ -0,0 +1,18 @@ +script; + +// A developer familiar with Rust might be tempted to define enum variants as `Foo` or `Bar(u32)`. +// This file tests the error message that informs the developer of the correct syntax. + +enum Enum1 { + Ok, // Illegal + Err, // Also illegal, but shadowed by previous error +} + +enum Enum2 { + F(u32), // Illegal + G(u32, u32), // Also illegal, but shadowed by previous error +} + +fn main() { +} + diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/enum_rust_like/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/enum_rust_like/test.toml new file mode 100644 index 00000000000..c7ee5328308 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/enum_rust_like/test.toml @@ -0,0 +1,11 @@ +category = "fail" + +# check: $()error +# check: enum_rust_like/src/main.sw:7:5 +# check: $()Ok, // Illegal +# nextln: $()Expected ':'. Enum variants must be in the form `Variant: ()`, `Variant: `, or `Variant: (, ..., )`. E.g., `Foo: (), or `Bar: (bool, u32)`. + +# check: $()error +# check: enum_rust_like/src/main.sw:12:4 +# check: $()F(u32), // Illegal +# nextln: $()Expected ':'. Enum variants must be in the form `Variant: ()`, `Variant: `, or `Variant: (, ..., )`. E.g., `Foo: (), or `Bar: (bool, u32)`. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/resolve_local_items_that_shadow_imports/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_fail/resolve_local_items_that_shadow_imports/Forc.lock new file mode 100644 index 00000000000..e892375730d --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/resolve_local_items_that_shadow_imports/Forc.lock @@ -0,0 +1,13 @@ +[[package]] +name = "core" +source = "path+from-root-8AA4CB15A1A05A28" + +[[package]] +name = "resolve_local_items_that_shadow_imports" +source = "member" +dependencies = ["std"] + +[[package]] +name = "std" +source = "path+from-root-8AA4CB15A1A05A28" +dependencies = ["core"] diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/resolve_local_items_that_shadow_imports/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_fail/resolve_local_items_that_shadow_imports/Forc.toml new file mode 100644 index 00000000000..f95c902d4a0 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/resolve_local_items_that_shadow_imports/Forc.toml @@ -0,0 +1,8 @@ +[project] +name = "resolve_local_items_that_shadow_imports" +authors = ["Fuel Labs "] +entry = "main.sw" +license = "Apache-2.0" + +[dependencies] +std = { path = "../../../../../../sway-lib-std" } diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/resolve_local_items_that_shadow_imports/json_abi_oracle.json b/test/src/e2e_vm_tests/test_programs/should_fail/resolve_local_items_that_shadow_imports/json_abi_oracle.json new file mode 100644 index 00000000000..fad72a08f17 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/resolve_local_items_that_shadow_imports/json_abi_oracle.json @@ -0,0 +1,25 @@ +{ + "configurables": [], + "functions": [ + { + "attributes": null, + "inputs": [], + "name": "main", + "output": { + "name": "", + "type": 0, + "typeArguments": null + } + } + ], + "loggedTypes": [], + "messagesTypes": [], + "types": [ + { + "components": [], + "type": "()", + "typeId": 0, + "typeParameters": null + } + ] +} \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/resolve_local_items_that_shadow_imports/src/lib.sw b/test/src/e2e_vm_tests/test_programs/should_fail/resolve_local_items_that_shadow_imports/src/lib.sw new file mode 100644 index 00000000000..99d6561e5dc --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/resolve_local_items_that_shadow_imports/src/lib.sw @@ -0,0 +1,19 @@ +library; + +pub enum Enum { + A: (), +} + +pub struct Struct { + x: u64, +} + +pub struct PubStruct { + x: u64, +} + +pub struct GenericStruct { + x: T, +} + +pub const X: u64 = 0u64; \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/resolve_local_items_that_shadow_imports/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_fail/resolve_local_items_that_shadow_imports/src/main.sw new file mode 100644 index 00000000000..54084ba4a7f --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/resolve_local_items_that_shadow_imports/src/main.sw @@ -0,0 +1,72 @@ +// This test proves that https://github.com/FuelLabs/sway/issues/5383 is fixed. + +script; + +mod lib; + +use lib::Enum; +use lib::Struct; +use lib::PubStruct; +use lib::GenericStruct; + +enum Enum { + A: (), + B: (), +} + +struct Struct { + x: u64, + y: u64, +} + +impl Struct { + fn use_me(self) { + poke(self.x); + poke(self.y); + } +} + +pub struct PubStruct { + x: u64, + y: u64, +} + +impl PubStruct { + fn use_me(self) { + poke(self.x); + poke(self.y); + } +} + +struct GenericStruct { + x: T, + y: u64, +} + +impl GenericStruct { + fn use_me(self) { + poke(self.x); + poke(self.y); + } +} + +pub const X: bool = true; + +fn main() { + // We must get errors for defining items multiple times, + // but that should be all errors. + // We shouldn't have any errors below, because the + // below items will resolve to local ones. + let _ = Struct { x: 0, y: 0 }; + let _ = PubStruct { x: 0, y: 0 }; + let _ = GenericStruct { x: 0, y: 0 }; + let _ = Enum::B; + let _: bool = X; + + Struct { x: 0, y: 0 }.use_me(); + PubStruct { x: 0, y: 0 }.use_me(); + GenericStruct { x: 0, y: 0 }.use_me(); + poke(Enum::A); +} + +fn poke(_x: T) { } \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/resolve_local_items_that_shadow_imports/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/resolve_local_items_that_shadow_imports/test.toml new file mode 100644 index 00000000000..db1fa2336de --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/resolve_local_items_that_shadow_imports/test.toml @@ -0,0 +1,19 @@ +category = "fail" + +#check: $()error +#check: $()enum Enum { +#nextln: $()Name "Enum" is defined multiple times. + +#check: $()error +#check: $()struct Struct { +#nextln: $()Name "Struct" is defined multiple times. + +#check: $()error +#check: $()pub struct PubStruct { +#nextln: $()Name "PubStruct" is defined multiple times. + +#check: $()error +#check: $()struct GenericStruct { +#nextln: $()Name "GenericStruct" is defined multiple times. + +#not: $()error \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/diverging_exprs/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/diverging_exprs/src/main.sw index a4086e6a92a..bd769dd78ac 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/diverging_exprs/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/diverging_exprs/src/main.sw @@ -1,10 +1,5 @@ script; -fn revert() -> T { - let code = 1u64; - __revert(code) -} - fn diverge_in_let_body() -> u64 { let _x: bool = { return 5; diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/generic_type_inference/json_abi_oracle.json b/test/src/e2e_vm_tests/test_programs/should_pass/language/generic_type_inference/json_abi_oracle.json index bf820505563..fad72a08f17 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/generic_type_inference/json_abi_oracle.json +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/generic_type_inference/json_abi_oracle.json @@ -12,24 +12,7 @@ } } ], - "loggedTypes": [ - { - "logId": 0, - "loggedType": { - "name": "", - "type": 1, - "typeArguments": null - } - }, - { - "logId": 1, - "loggedType": { - "name": "", - "type": 1, - "typeArguments": null - } - } - ], + "loggedTypes": [], "messagesTypes": [], "types": [ { @@ -37,12 +20,6 @@ "type": "()", "typeId": 0, "typeParameters": null - }, - { - "components": null, - "type": "u64", - "typeId": 1, - "typeParameters": null } ] } \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/generic_type_inference/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/generic_type_inference/src/main.sw index 37dfa4ec423..47aa55365e0 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/generic_type_inference/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/generic_type_inference/src/main.sw @@ -8,6 +8,12 @@ struct CustomType { name: str, } +impl CustomType { + fn use_me(self) { + poke(self.name); + } +} + enum MyResult { Ok: T, Err: E, @@ -56,12 +62,15 @@ fn test_try_from() { } fn main() { - sell_product(); + let _ = sell_product(); simple_vec_test(); complex_vec_test(); simple_option_generics_test(); test_assert_eq_u64(); test_try_from(); + + // Suppress DCA warnings. + CustomType { name: "" }.use_me(); } fn sell_product() -> MyResult { @@ -73,3 +82,5 @@ fn sell_product() -> MyResult { return MyResult::Ok(false); } + +fn poke(_x: T) { } \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/resolve_local_items_that_shadow_imports/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_pass/resolve_local_items_that_shadow_imports/Forc.lock new file mode 100644 index 00000000000..e892375730d --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/resolve_local_items_that_shadow_imports/Forc.lock @@ -0,0 +1,13 @@ +[[package]] +name = "core" +source = "path+from-root-8AA4CB15A1A05A28" + +[[package]] +name = "resolve_local_items_that_shadow_imports" +source = "member" +dependencies = ["std"] + +[[package]] +name = "std" +source = "path+from-root-8AA4CB15A1A05A28" +dependencies = ["core"] diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/resolve_local_items_that_shadow_imports/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_pass/resolve_local_items_that_shadow_imports/Forc.toml new file mode 100644 index 00000000000..f95c902d4a0 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/resolve_local_items_that_shadow_imports/Forc.toml @@ -0,0 +1,8 @@ +[project] +name = "resolve_local_items_that_shadow_imports" +authors = ["Fuel Labs "] +entry = "main.sw" +license = "Apache-2.0" + +[dependencies] +std = { path = "../../../../../../sway-lib-std" } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/resolve_local_items_that_shadow_imports/json_abi_oracle.json b/test/src/e2e_vm_tests/test_programs/should_pass/resolve_local_items_that_shadow_imports/json_abi_oracle.json new file mode 100644 index 00000000000..fad72a08f17 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/resolve_local_items_that_shadow_imports/json_abi_oracle.json @@ -0,0 +1,25 @@ +{ + "configurables": [], + "functions": [ + { + "attributes": null, + "inputs": [], + "name": "main", + "output": { + "name": "", + "type": 0, + "typeArguments": null + } + } + ], + "loggedTypes": [], + "messagesTypes": [], + "types": [ + { + "components": [], + "type": "()", + "typeId": 0, + "typeParameters": null + } + ] +} \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/resolve_local_items_that_shadow_imports/src/lib.sw b/test/src/e2e_vm_tests/test_programs/should_pass/resolve_local_items_that_shadow_imports/src/lib.sw new file mode 100644 index 00000000000..99d6561e5dc --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/resolve_local_items_that_shadow_imports/src/lib.sw @@ -0,0 +1,19 @@ +library; + +pub enum Enum { + A: (), +} + +pub struct Struct { + x: u64, +} + +pub struct PubStruct { + x: u64, +} + +pub struct GenericStruct { + x: T, +} + +pub const X: u64 = 0u64; \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/resolve_local_items_that_shadow_imports/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/resolve_local_items_that_shadow_imports/src/main.sw new file mode 100644 index 00000000000..34a65682237 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/resolve_local_items_that_shadow_imports/src/main.sw @@ -0,0 +1,79 @@ +// This test proves that https://github.com/FuelLabs/sway/issues/5383 is fixed. + +script; + +mod lib; + +use lib::*; + +enum Enum { + A: (), + B: (), +} + +struct Struct { + x: u64, + y: u64, +} + +impl Struct { + fn use_me(self) { + poke(self.x); + poke(self.y); + } +} + +pub struct PubStruct { + x: u64, + y: u64, +} + +impl PubStruct { + fn use_me(self) { + poke(self.x); + poke(self.y); + } +} + +struct GenericStruct { + x: T, + y: u64, +} + +impl GenericStruct { + fn use_me(self) { + poke(self.x); + poke(self.y); + } +} + +pub const X: bool = true; + +fn access_struct(s: Struct) { + poke(s.y); +} + +fn access_enum(e: Enum) { + match e { + Enum::B => poke(e), + _ => (), + }; +} + +fn main() { + let s = Struct { x: 0, y: 0 }; + let _ = PubStruct { x: 0, y: 0 }; + let _ = GenericStruct { x: 0, y: 0 }; + let e = Enum::B; + let _: bool = X; + + access_struct(s); + access_enum(e); + + Struct { x: 0, y: 0 }.use_me(); + PubStruct { x: 0, y: 0 }.use_me(); + GenericStruct { x: 0, y: 0 }.use_me(); + poke(Enum::A); +} + +fn poke(_x: T) { } \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/resolve_local_items_that_shadow_imports/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/resolve_local_items_that_shadow_imports/test.toml new file mode 100644 index 00000000000..75744eb5e72 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/resolve_local_items_that_shadow_imports/test.toml @@ -0,0 +1,23 @@ +category = "compile" +validate_abi = true +expected_warnings = 6 + +#check: $()warning +#check: $()pub enum Enum { +#nextln: $()This enum is never used. + +#check: $()warning +#check: $()pub struct Struct { +#nextln: $()This struct is never used. + +#check: $()warning +#check: $()pub struct PubStruct { +#nextln $()This struct is never used. + +#check: $()warning +#check: $()pub struct GenericStruct { +#nextln: $()This struct is never used. + +#check: $()warning +#check: $()pub const X: u64 = 0u64; +#nextln: $()This declaration is never used. \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/multiple_impl/json_abi_oracle.json b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/multiple_impl/json_abi_oracle.json index 2a4d517fd13..3c8224f070c 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/multiple_impl/json_abi_oracle.json +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/multiple_impl/json_abi_oracle.json @@ -20,14 +20,6 @@ "type": 1, "typeArguments": null } - }, - { - "logId": 1, - "loggedType": { - "name": "", - "type": 1, - "typeArguments": null - } } ], "messagesTypes": [], diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/multiple_impl/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/multiple_impl/src/main.sw index 040a5130417..74d5af7395f 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/multiple_impl/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/multiple_impl/src/main.sw @@ -20,5 +20,3 @@ impl TestContr for Contract { bar(); } } - -fn main() {} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/multiple_impl/src/testlib2.sw b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/multiple_impl/src/testlib2.sw index c80101627f9..d47a4156cd0 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/multiple_impl/src/testlib2.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/multiple_impl/src/testlib2.sw @@ -1,5 +1,6 @@ library; pub fn bar() { - log(2); + log(2); // This log should never be logged. + assert(false); // This function should never be called. }