diff --git a/Cargo.lock b/Cargo.lock index 0e59d47f63..9cd5ec4232 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -51,9 +51,9 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstream" -version = "0.6.19" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" +checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192" dependencies = [ "anstyle", "anstyle-parse", @@ -81,29 +81,29 @@ dependencies = [ [[package]] name = "anstyle-query" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" +checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] name = "anstyle-wincon" -version = "3.0.9" +version = "3.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" +checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] name = "anyhow" -version = "1.0.98" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" +checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100" [[package]] name = "ascii_tree" @@ -124,9 +124,9 @@ dependencies = [ [[package]] name = "async-channel" -version = "2.3.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" +checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" dependencies = [ "concurrent-queue", "event-listener-strategy", @@ -163,9 +163,9 @@ dependencies = [ [[package]] name = "async-fs" -version = "2.1.2" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebcd09b382f40fcd159c2d695175b2ae620ffa5f3bd6f664131efff4e8b9e04a" +checksum = "09f7e37c0ed80b2a977691c47dae8625cfb21e205827106c64f7c588766b2e50" dependencies = [ "async-lock", "blocking", @@ -178,7 +178,7 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" dependencies = [ - "async-channel 2.3.1", + "async-channel 2.5.0", "async-executor", "async-io", "async-lock", @@ -189,9 +189,9 @@ dependencies = [ [[package]] name = "async-io" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1237c0ae75a0f3765f58910ff9cdd0a12eeb39ab2f4c7de23262f337f0aacbb3" +checksum = "19634d6336019ef220f09fd31168ce5c184b295cbf80345437cc36094ef223ca" dependencies = [ "async-lock", "cfg-if", @@ -200,19 +200,18 @@ dependencies = [ "futures-lite", "parking", "polling", - "rustix 1.0.7", + "rustix 1.0.8", "slab", - "tracing", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] name = "async-lock" -version = "3.4.0" +version = "3.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" +checksum = "5fd03604047cee9b6ce9de9f70c6cd540a0520c813cbd49bae61f33ab80ed1dc" dependencies = [ - "event-listener 5.4.0", + "event-listener 5.4.1", "event-listener-strategy", "pin-project-lite", ] @@ -228,7 +227,7 @@ dependencies = [ "rustix 0.38.44", "serde", "serde_json", - "thiserror 2.0.12", + "thiserror", "tokio", "tower-layer", "tower-service", @@ -249,28 +248,27 @@ dependencies = [ [[package]] name = "async-process" -version = "2.3.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cde3f4e40e6021d7acffc90095cbd6dc54cb593903d1de5832f435eb274b85dc" +checksum = "65daa13722ad51e6ab1a1b9c01299142bc75135b337923cfa10e79bbbd669f00" dependencies = [ - "async-channel 2.3.1", + "async-channel 2.5.0", "async-io", "async-lock", "async-signal", "async-task", "blocking", "cfg-if", - "event-listener 5.4.0", + "event-listener 5.4.1", "futures-lite", - "rustix 1.0.7", - "tracing", + "rustix 1.0.8", ] [[package]] name = "async-signal" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7605a4e50d4b06df3898d5a70bf5fde51ed9059b0434b73105193bc27acce0d" +checksum = "f567af260ef69e1d52c2b560ce0ea230763e6fbb9214a85d768760a920e3e3c1" dependencies = [ "async-io", "async-lock", @@ -278,17 +276,17 @@ dependencies = [ "cfg-if", "futures-core", "futures-io", - "rustix 1.0.7", + "rustix 1.0.8", "signal-hook-registry", "slab", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] name = "async-std" -version = "1.13.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "730294c1c08c2e0f85759590518f6333f0d5a0a766a27d519c1b244c3dfd8a24" +checksum = "2c8e079a4ab67ae52b7403632e4618815d6db36d2a010cfe41b02c1b1578f93b" dependencies = [ "async-channel 1.9.0", "async-global-executor", @@ -372,11 +370,11 @@ dependencies = [ [[package]] name = "blocking" -version = "1.6.1" +version = "1.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" +checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" dependencies = [ - "async-channel 2.3.1", + "async-channel 2.5.0", "async-task", "futures-io", "futures-lite", @@ -385,15 +383,15 @@ dependencies = [ [[package]] name = "boxcar" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26c4925bc979b677330a8c7fe7a8c94af2dbb4a2d37b4a20a80d884400f46baa" +checksum = "36f64beae40a84da1b4b26ff2761a5b895c12adc41dc25aaee1c4f2bbfe97a6e" [[package]] name = "bumpalo" -version = "3.18.1" +version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db76d6187cd04dff33004d8e6c9cc4e05cd330500379d2394209271b4aeee" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "bytes" @@ -403,9 +401,9 @@ checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "camino" -version = "1.1.10" +version = "1.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0da45bc31171d8d6960122e222a67740df867c1dd53b4d51caa297084c185cab" +checksum = "5d07aa9a93b00c76f71bc35d598bed923f6d4f3a9ca5c24b7737ae1a292841c0" [[package]] name = "cast" @@ -415,9 +413,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.27" +version = "1.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc" +checksum = "2352e5597e9c544d5e6d9c95190d5d27738ade584fa8db0a16e130e5c2b5296e" dependencies = [ "shlex", ] @@ -457,9 +455,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.40" +version = "4.5.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f" +checksum = "1fc0e74a703892159f5ae7d3aac52c8e6c392f5ae5f359c70b5881d60aaac318" dependencies = [ "clap_builder", "clap_derive", @@ -467,9 +465,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.40" +version = "4.5.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e" +checksum = "b3e7f4214277f3c7aa526a59dd3fbe306a370daee1f8b7b8c987069cd8e888a8" dependencies = [ "anstream", "anstyle", @@ -479,14 +477,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.40" +version = "4.5.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2c7947ae4cc3d851207c1adb5b5e260ff0cca11446b1d6d1423788e442257ce" +checksum = "14cb31bb0a7d536caef2639baa7fad459e15c3144efefa6dbd1c84562c4739f6" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.105", ] [[package]] @@ -622,9 +620,9 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crunchy" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] name = "crypto-common" @@ -667,7 +665,7 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.105", ] [[package]] @@ -698,7 +696,7 @@ dependencies = [ "glob", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.105", ] [[package]] @@ -709,7 +707,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.105", ] [[package]] @@ -764,9 +762,9 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] name = "event-listener" -version = "5.4.0" +version = "5.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae" +checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" dependencies = [ "concurrent-queue", "parking", @@ -779,7 +777,7 @@ version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" dependencies = [ - "event-listener 5.4.0", + "event-listener 5.4.1", "pin-project-lite", ] @@ -793,12 +791,15 @@ checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" name = "fe" version = "0.26.0" dependencies = [ + "camino", "clap", "dir-test", + "fe-common", "fe-driver", + "fe-resolver", "fe-test-utils", "tracing", - "tracing-subscriber", + "url", ] [[package]] @@ -809,7 +810,6 @@ dependencies = [ "criterion", "fe-common", "fe-driver", - "fe-hir-analysis", "fe-parser", "tracing", "url", @@ -824,14 +824,13 @@ dependencies = [ "fe-parser", "ordermap", "paste", + "petgraph", "radix_immutable", "rust-embed", "rustc-hash 2.1.1", "salsa", - "serde", "serde-semver", - "smol_str 0.1.24", - "thiserror 1.0.69", + "smol_str", "toml", "tracing", "url", @@ -842,15 +841,12 @@ name = "fe-driver" version = "0.26.0" dependencies = [ "camino", - "clap", "codespan-reporting", "fe-common", "fe-hir", "fe-hir-analysis", "fe-resolver", - "fe-test-utils", "salsa", - "smol_str 0.1.24", "tracing", "url", ] @@ -870,9 +866,7 @@ dependencies = [ "paste", "rustc-hash 2.1.1", "salsa", - "smallvec 2.0.0-alpha.11", "thin-vec", - "tracing", "url", ] @@ -901,7 +895,6 @@ dependencies = [ "salsa", "smallvec 1.15.1", "smallvec 2.0.0-alpha.11", - "smol_str 0.2.2", "thin-vec", "tracing", "url", @@ -925,19 +918,15 @@ dependencies = [ "fe-hir", "fe-hir-analysis", "fe-parser", - "fe-resolver", "fe-test-utils", "futures", "futures-batch", "glob", - "patricia_tree", "rustc-hash 2.1.1", "salsa", - "serde", "serde_json", "tempfile", "tokio", - "tokio-macros", "tower", "tracing", "tracing-subscriber", @@ -955,7 +944,6 @@ dependencies = [ "lazy_static", "logos", "rowan", - "rustc-hash 2.1.1", "smallvec 2.0.0-alpha.11", "tracing", "unwrap-infallible", @@ -968,11 +956,11 @@ name = "fe-resolver" version = "0.26.0" dependencies = [ "camino", - "fe-common", + "dir-test", + "fe-test-utils", "glob", - "serde", - "serde-semver", - "smol_str 0.1.24", + "indexmap", + "petgraph", "toml", "tracing", "url", @@ -982,7 +970,6 @@ dependencies = [ name = "fe-test-utils" version = "0.1.0" dependencies = [ - "camino", "insta", "tracing", "tracing-subscriber", @@ -994,18 +981,21 @@ dependencies = [ name = "fe-uitest" version = "0.26.0" dependencies = [ - "camino", "dir-test", "fe-common", "fe-driver", - "fe-hir", "fe-hir-analysis", "fe-test-utils", - "tracing", "url", "wasm-bindgen-test", ] +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + [[package]] name = "fnv" version = "1.0.7" @@ -1088,9 +1078,9 @@ checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-lite" -version = "2.6.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532" +checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" dependencies = [ "fastrand", "futures-core", @@ -1107,7 +1097,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.105", ] [[package]] @@ -1176,9 +1166,9 @@ checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "glob" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" [[package]] name = "gloo-timers" @@ -1210,9 +1200,9 @@ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "hashbrown" -version = "0.15.4" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "allocator-api2", "equivalent", @@ -1225,7 +1215,7 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" dependencies = [ - "hashbrown 0.15.4", + "hashbrown 0.15.5", ] [[package]] @@ -1355,12 +1345,12 @@ checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed" [[package]] name = "indexmap" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" dependencies = [ "equivalent", - "hashbrown 0.15.4", + "hashbrown 0.15.5", ] [[package]] @@ -1373,6 +1363,17 @@ dependencies = [ "similar", ] +[[package]] +name = "io-uring" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4" +dependencies = [ + "bitflags 2.9.1", + "cfg-if", + "libc", +] + [[package]] name = "is-terminal" version = "0.4.16" @@ -1441,9 +1442,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.174" +version = "0.2.175" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" +checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" [[package]] name = "linux-raw-sys" @@ -1484,18 +1485,18 @@ dependencies = [ [[package]] name = "logos" -version = "0.15.0" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab6f536c1af4c7cc81edf73da1f8029896e7e1e16a219ef09b184e76a296f3db" +checksum = "ff472f899b4ec2d99161c51f60ff7075eeb3097069a36050d8037a6325eb8154" dependencies = [ "logos-derive", ] [[package]] name = "logos-codegen" -version = "0.15.0" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "189bbfd0b61330abea797e5e9276408f2edbe4f822d7ad08685d67419aafb34e" +checksum = "192a3a2b90b0c05b27a0b2c43eecdb7c415e29243acc3f89cc8247a5b693045c" dependencies = [ "beef", "fnv", @@ -1504,14 +1505,14 @@ dependencies = [ "quote", "regex-syntax 0.8.5", "rustc_version", - "syn 2.0.104", + "syn 2.0.105", ] [[package]] name = "logos-derive" -version = "0.15.0" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebfe8e1a19049ddbfccbd14ac834b215e11b85b90bab0c2dba7c7b92fb5d5cba" +checksum = "605d9697bcd5ef3a42d38efc51541aa3d6a4a25f7ab6d1ed0da5ac632a26b470" dependencies = [ "logos-codegen", ] @@ -1650,9 +1651,9 @@ checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" [[package]] name = "ordermap" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d31b8b7a99f71bdff4235faf9ce9eada0ad3562c8fbeb7d607d9f41a6ec569d" +checksum = "6d6bff06e4a5dc6416bead102d3e63c480dd852ffbb278bf8cfeb4966b329609" dependencies = [ "indexmap", ] @@ -1698,21 +1699,24 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" -[[package]] -name = "patricia_tree" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edb45b6331bbdbb54c9a29413703e892ab94f83a31e4a546c778495a91e7fbca" -dependencies = [ - "bitflags 2.9.1", -] - [[package]] name = "percent-encoding" version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +[[package]] +name = "petgraph" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54acf3a685220b533e437e264e4d932cfbdc4cc7ec0cd232ed73c08d03b8a7ca" +dependencies = [ + "fixedbitset", + "hashbrown 0.15.5", + "indexmap", + "serde", +] + [[package]] name = "pin-project-lite" version = "0.2.16" @@ -1766,17 +1770,16 @@ dependencies = [ [[package]] name = "polling" -version = "3.8.0" +version = "3.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b53a684391ad002dd6a596ceb6c74fd004fdce75f4be2e3f615068abbea5fd50" +checksum = "b5bd19146350fe804f7cb2669c851c03d69da628803dab0d98018142aaa5d829" dependencies = [ "cfg-if", "concurrent-queue", "hermit-abi", "pin-project-lite", - "rustix 1.0.7", - "tracing", - "windows-sys 0.59.0", + "rustix 1.0.8", + "windows-sys 0.60.2", ] [[package]] @@ -1796,9 +1799,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.95" +version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +checksum = "d61789d7719defeb74ea5fe81f2fdfdbd28a803847077cecce2ff14e1472f6f1" dependencies = [ "unicode-ident", ] @@ -1821,16 +1824,16 @@ checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] name = "radix_immutable" version = "0.0.1" -source = "git+https://github.com/micahscopes/radix_immutable.git#cc9825ea75f73a58f41225836510daefab40b1c6" +source = "git+https://github.com/micahscopes/radix_immutable.git#514098f05404713bd3bf8ed0dbbca12ec8a8590b" dependencies = [ "once_cell", ] [[package]] name = "rayon" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" dependencies = [ "either", "rayon-core", @@ -1838,9 +1841,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.12.1" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" dependencies = [ "crossbeam-deque", "crossbeam-utils", @@ -1848,9 +1851,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.13" +version = "0.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6" +checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" dependencies = [ "bitflags 2.9.1", ] @@ -1931,7 +1934,7 @@ dependencies = [ "proc-macro2", "quote", "rust-embed-utils", - "syn 2.0.104", + "syn 2.0.105", "walkdir", ] @@ -1947,9 +1950,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.25" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f" +checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" [[package]] name = "rustc-hash" @@ -1987,22 +1990,22 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" +checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" dependencies = [ "bitflags 2.9.1", "errno", "libc", "linux-raw-sys 0.9.4", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] name = "rustversion" -version = "1.0.21" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "ryu" @@ -2019,7 +2022,7 @@ dependencies = [ "boxcar", "crossbeam-queue", "dashmap", - "hashbrown 0.15.4", + "hashbrown 0.15.5", "hashlink", "indexmap", "parking_lot", @@ -2048,7 +2051,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.105", "synstructure", ] @@ -2119,14 +2122,14 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.105", ] [[package]] name = "serde_json" -version = "1.0.140" +version = "1.0.142" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +checksum = "030fedb782600dcbd6f02d479bf0d817ac3bb40d644745b769d6a96bc3afc5a7" dependencies = [ "itoa", "memchr", @@ -2142,7 +2145,7 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.105", ] [[package]] @@ -2182,9 +2185,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" -version = "1.4.5" +version = "1.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" +checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" dependencies = [ "libc", ] @@ -2197,9 +2200,9 @@ checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" [[package]] name = "slab" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" [[package]] name = "smallvec" @@ -2219,7 +2222,7 @@ version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a33bd3e260892199c3ccfc487c88b2da2265080acb316cd920da72fdfd7c599f" dependencies = [ - "async-channel 2.3.1", + "async-channel 2.5.0", "async-executor", "async-fs", "async-io", @@ -2239,23 +2242,14 @@ dependencies = [ "serde", ] -[[package]] -name = "smol_str" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd538fb6910ac1099850255cf94a94df6551fbdd602454387d0adb2d1ca6dead" -dependencies = [ - "serde", -] - [[package]] name = "socket2" -version = "0.5.10" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -2283,9 +2277,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.104" +version = "2.0.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" +checksum = "7bc3fcb250e53458e712715cf74285c1f889686520d79294a9ef3bd7aa1fc619" dependencies = [ "proc-macro2", "quote", @@ -2300,7 +2294,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.105", ] [[package]] @@ -2312,7 +2306,7 @@ dependencies = [ "fastrand", "getrandom", "once_cell", - "rustix 1.0.7", + "rustix 1.0.8", "windows-sys 0.59.0", ] @@ -2339,42 +2333,22 @@ checksum = "144f754d318415ac792f9d69fc87abbbfc043ce2ef041c60f16ad828f638717d" [[package]] name = "thiserror" -version = "1.0.69" +version = "2.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +checksum = "0b0949c3a6c842cbde3f1686d6eea5a010516deb7085f79db747562d4102f41e" dependencies = [ - "thiserror-impl 1.0.69", -] - -[[package]] -name = "thiserror" -version = "2.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" -dependencies = [ - "thiserror-impl 2.0.12", + "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.69" +version = "2.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +checksum = "cc5b44b4ab9c2fdd0e0512e6bece8388e214c0749f5862b114cc5b7a25daf227" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", -] - -[[package]] -name = "thiserror-impl" -version = "2.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.104", + "syn 2.0.105", ] [[package]] @@ -2408,21 +2382,23 @@ dependencies = [ [[package]] name = "tokio" -version = "1.45.1" +version = "1.47.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779" +checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" dependencies = [ "backtrace", "bytes", + "io-uring", "libc", "mio", "parking_lot", "pin-project-lite", "signal-hook-registry", + "slab", "socket2", "tokio-macros", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -2433,7 +2409,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.105", ] [[package]] @@ -2518,7 +2494,7 @@ checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.105", ] [[package]] @@ -2695,7 +2671,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.105", "wasm-bindgen-shared", ] @@ -2730,7 +2706,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.105", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2765,7 +2741,7 @@ checksum = "17d5042cc5fa009658f9a7333ef24291b1291a25b6382dd68862a7f3b969f69b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.105", ] [[package]] @@ -2809,6 +2785,12 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + [[package]] name = "windows-sys" version = "0.52.0" @@ -2833,7 +2815,7 @@ version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ - "windows-targets 0.53.2", + "windows-targets 0.53.3", ] [[package]] @@ -2854,10 +2836,11 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.53.2" +version = "0.53.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef" +checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" dependencies = [ + "windows-link", "windows_aarch64_gnullvm 0.53.0", "windows_aarch64_msvc 0.53.0", "windows_i686_gnu 0.53.0", @@ -2966,9 +2949,9 @@ checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" [[package]] name = "winnow" -version = "0.7.11" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74c7b26e3480b707944fc872477815d29a8e429d2f93a1ce000f5fa84a15cbcd" +checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95" dependencies = [ "memchr", ] @@ -3008,7 +2991,7 @@ checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.105", "synstructure", ] @@ -3029,7 +3012,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.105", "synstructure", ] @@ -3046,9 +3029,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.2" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" +checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" dependencies = [ "yoke", "zerofrom", @@ -3063,5 +3046,5 @@ checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.105", ] diff --git a/Cargo.toml b/Cargo.toml index 1813c5bd14..d053b68d5f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,8 @@ tracing = "0.1.41" tracing-subscriber = { version = "0.3.19", features = ["env-filter"] } tracing-tree = "0.4.0" wasm-bindgen-test = "0.3" +semver = "1.0.26" +petgraph = "0.8" [profile.dev] # Set to 0 to make the build faster and debugging more difficult. diff --git a/crates/bench/Cargo.toml b/crates/bench/Cargo.toml index dc8eac80fc..4ff786c029 100644 --- a/crates/bench/Cargo.toml +++ b/crates/bench/Cargo.toml @@ -8,7 +8,6 @@ criterion = "0.5" walkdir = "2.4" [dependencies] -hir-analysis.workspace = true driver.workspace = true parser.workspace = true common.workspace = true diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index 3a407bc0fc..e340b0348b 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -17,9 +17,8 @@ serde-semver.workspace = true smol_str.workspace = true rust-embed = { version = "8.5.0", features = ["debug-embed"] } toml = "0.8.8" -serde = { version = "1.0", features = ["derive"] } -thiserror = "1.0" tracing.workspace = true +petgraph.workspace = true parser.workspace = true url.workspace = true diff --git a/crates/common/src/config.rs b/crates/common/src/config.rs index 4871c7b3d6..ef2d9dd4bc 100644 --- a/crates/common/src/config.rs +++ b/crates/common/src/config.rs @@ -5,7 +5,7 @@ use smol_str::SmolStr; use toml::Value; use url::Url; -use crate::{ingot::Version, urlext::UrlExt}; +use crate::{graph::EdgeWeight, ingot::Version, urlext::UrlExt}; #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Config { @@ -55,8 +55,12 @@ impl Config { .get("dependencies") .and_then(|value| value.as_table()) { - diagnostics.push(ConfigDiagnostic::DependenciesNotYetSupported); for (alias, value) in table { + // Validate dependency alias + if !is_valid_name(alias) { + diagnostics.push(ConfigDiagnostic::InvalidDependencyAlias(alias.into())); + } + match value { Value::String(path) => { dependencies.push(Dependency::path(alias.into(), Utf8PathBuf::from(path))); @@ -64,15 +68,20 @@ impl Config { Value::Table(table) => { let path = table.get("path").and_then(|value| value.as_str()); if let Some(path) = path { - let mut parameters = DependencyParameters::default(); + let mut arguments = DependencyArguments::default(); if let Some(name) = table.get("name").and_then(|value| value.as_str()) { - parameters.name = Some(SmolStr::new(name)); + if is_valid_name(name) { + arguments.name = Some(SmolStr::new(name)); + } else { + diagnostics + .push(ConfigDiagnostic::InvalidDependencyName(name.into())); + } } if let Some(version) = table.get("version").and_then(|value| value.as_str()) { if let Ok(version) = version.parse() { - parameters.version = Some(version); + arguments.version = Some(version); } else { diagnostics .push(ConfigDiagnostic::InvalidVersion(version.into())); @@ -81,7 +90,7 @@ impl Config { dependencies.push(Dependency::path_with_arguments( alias.into(), Utf8PathBuf::from(path), - parameters, + arguments, )); } else { diagnostics.push(ConfigDiagnostic::MissingDependencyPath { @@ -106,10 +115,24 @@ impl Config { }) } - pub fn based_dependencies(&self, base_url: &Url) -> Vec { + pub fn forward_edges(&self, base_url: &Url) -> Vec<(Url, EdgeWeight)> { self.dependencies .iter() - .map(|dependency| dependency.based(base_url)) + .map(|dependency| { + let (url, alias, arguments) = match &dependency.description { + DependencyDescription::Path(path) => ( + base_url.join_directory(path).unwrap(), + dependency.alias.clone(), + DependencyArguments::default(), + ), + DependencyDescription::PathWithParameters { path, parameters } => ( + base_url.join_directory(path).unwrap(), + dependency.alias.clone(), + parameters.clone(), + ), + }; + (url, EdgeWeight { alias, arguments }) + }) .collect() } @@ -139,7 +162,7 @@ pub enum DependencyDescription { Path(Utf8PathBuf), PathWithParameters { path: Utf8PathBuf, - parameters: DependencyParameters, + parameters: DependencyArguments, }, } @@ -160,42 +183,17 @@ impl Dependency { pub fn path_with_arguments( alias: SmolStr, path: Utf8PathBuf, - parameters: DependencyParameters, + parameters: DependencyArguments, ) -> Self { Self { alias, description: DependencyDescription::PathWithParameters { path, parameters }, } } - - pub fn based(&self, base_url: &Url) -> BasedDependency { - match &self.description { - DependencyDescription::Path(path) => BasedDependency { - alias: self.alias.clone(), - parameters: DependencyParameters::default(), - url: base_url.join_directory(path).unwrap(), - }, - DependencyDescription::PathWithParameters { - path, - parameters: _, - } => BasedDependency { - alias: self.alias.clone(), - parameters: DependencyParameters::default(), - url: base_url.join_directory(path).unwrap(), - }, - } - } -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct BasedDependency { - pub alias: SmolStr, - pub parameters: DependencyParameters, - pub url: Url, } #[derive(Debug, Default, Clone, PartialEq, Eq, Hash)] -pub struct DependencyParameters { +pub struct DependencyArguments { pub name: Option, pub version: Option, } @@ -220,32 +218,29 @@ pub enum ConfigDiagnostic { found: SmolStr, expected: Option, }, - /// TODO: push diagnostics for fields that should not exist - // UnrecognizedField(SmolStr), - DependenciesNotYetSupported, } impl Display for ConfigDiagnostic { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Self::MissingIngotMetadata => write!(f, "missing ingot metadata"), - Self::MissingName => write!(f, "missing ingot name"), - Self::MissingVersion => write!(f, "missing ingot version"), - Self::InvalidName(name) => write!(f, "invalid ingot name \"{name}\""), - Self::InvalidVersion(version) => write!(f, "invalid ingot version \"{version}\""), + Self::MissingIngotMetadata => write!(f, "Missing ingot metadata"), + Self::MissingName => write!(f, "Missing ingot name"), + Self::MissingVersion => write!(f, "Missing ingot version"), + Self::InvalidName(name) => write!(f, "Invalid ingot name \"{name}\""), + Self::InvalidVersion(version) => write!(f, "Invalid ingot version \"{version}\""), Self::InvalidDependencyAlias(alias) => { - write!(f, "invalid dependency alias \"{alias}\"") + write!(f, "Invalid dependency alias \"{alias}\"") } Self::InvalidDependencyName(name) => { - write!(f, "invalid dependency name \"{name}\"") + write!(f, "Invalid dependency name \"{name}\"") } Self::InvalidDependencyVersion(version) => { - write!(f, "invalid dependency version \"{version}\"") + write!(f, "Invalid dependency version \"{version}\"") } - Self::InvalidTomlSyntax(err) => write!(f, "invalid TOML syntax: {err}"), + Self::InvalidTomlSyntax(err) => write!(f, "Invalid TOML syntax: {err}"), Self::MissingDependencyPath { alias, description } => write!( f, - "the dependency \"{alias}\" is missing a path argument \"{description}\"" + "The dependency \"{alias}\" is missing a path argument \"{description}\"" ), Self::UnexpectedTomlData { field, @@ -255,19 +250,18 @@ impl Display for ConfigDiagnostic { if let Some(expected) = expected { write!( f, - "expected a {expected} in field {field}, but found a {found}" + "Expected a {expected} in field {field}, but found a {found}" ) } else { - write!(f, "unexpected field {field}") + write!(f, "Unexpected field {field}") } } - Self::DependenciesNotYetSupported => write!(f, "dependencies are not yet supported"), } } } fn is_valid_name_char(c: char) -> bool { - c.is_alphanumeric() || c == '-' + c.is_alphanumeric() || c == '_' } fn is_valid_name(s: &str) -> bool { diff --git a/crates/common/src/file/workspace.rs b/crates/common/src/file/workspace.rs index 00eca6e59f..21f936214b 100644 --- a/crates/common/src/file/workspace.rs +++ b/crates/common/src/file/workspace.rs @@ -21,7 +21,7 @@ pub struct Workspace { #[salsa::tracked] impl Workspace { pub fn default(db: &dyn InputDb) -> Self { - Workspace::new(db, Trie::new(), IndexMap::new()) + Workspace::new(db, Trie::new(), IndexMap::default()) } pub(crate) fn set( &self, diff --git a/crates/common/src/graph.rs b/crates/common/src/graph.rs new file mode 100644 index 0000000000..49a68e9e7c --- /dev/null +++ b/crates/common/src/graph.rs @@ -0,0 +1,245 @@ +use petgraph::graph::{DiGraph, NodeIndex}; +use petgraph::visit::{Dfs, EdgeRef}; +use salsa::Setter; +use smol_str::SmolStr; +use std::collections::HashMap; +use url::Url; + +use crate::{config::DependencyArguments, InputDb}; + +#[salsa::input] +#[derive(Debug)] +pub struct Graph { + graph: DiGraph, + node_map: HashMap, +} + +#[derive(Debug, Clone)] +pub struct JoinEdge { + pub origin: Url, + pub destination: Url, + pub weight: EdgeWeight, +} + +#[derive(Clone, Debug)] +pub struct EdgeWeight { + pub alias: SmolStr, + pub arguments: DependencyArguments, +} + +#[salsa::tracked] +impl Graph { + pub fn default(db: &dyn InputDb) -> Self { + Graph::new(db, DiGraph::new(), HashMap::new()) + } + + pub fn contains_url(&self, db: &dyn InputDb, url: &Url) -> bool { + self.node_map(db).contains_key(url) + } + + /// Returns a subgraph containing all cyclic nodes and all nodes that lead to cycles. + /// + /// This method identifies strongly connected components (SCCs) in the graph and returns + /// a subgraph that includes: + /// - All nodes that are part of multi-node cycles + /// - All nodes that have paths leading to cyclic nodes + /// + /// Returns an empty graph if no cycles are detected. + pub fn cyclic_subgraph(&self, db: &dyn InputDb) -> DiGraph { + use petgraph::algo::tarjan_scc; + use std::collections::VecDeque; + + let graph = self.graph(db); + let sccs = tarjan_scc(&graph); + + // Find actual cyclic nodes (multi-node SCCs only) + let mut cyclic_nodes = std::collections::HashSet::new(); + for scc in sccs { + if scc.len() > 1 { + // Multi-node SCC = actual cycle + for node_idx in scc { + cyclic_nodes.insert(node_idx); + } + } + } + + // If no cycles, return empty graph + if cyclic_nodes.is_empty() { + return DiGraph::new(); + } + + // Use reverse DFS from cyclic nodes to find all predecessors + let mut nodes_to_include = cyclic_nodes.clone(); + let mut visited = std::collections::HashSet::new(); + let mut queue = VecDeque::new(); + + // Start from all cyclic nodes and work backwards + for &cyclic_node in &cyclic_nodes { + queue.push_back(cyclic_node); + } + + while let Some(current) = queue.pop_front() { + if visited.contains(¤t) { + continue; + } + visited.insert(current); + nodes_to_include.insert(current); + + // Add all predecessors (nodes that point to current) + for pred in graph.node_indices() { + if graph.find_edge(pred, current).is_some() && !visited.contains(&pred) { + queue.push_back(pred); + } + } + } + + // Build subgraph with all nodes that lead to cycles + let mut subgraph = DiGraph::new(); + let mut node_map = HashMap::new(); + + // Add nodes + for &node_idx in &nodes_to_include { + let url = &graph[node_idx]; + let new_idx = subgraph.add_node(url.clone()); + node_map.insert(node_idx, new_idx); + } + + // Add edges between included nodes + for edge in graph.edge_references() { + if let (Some(&from_new), Some(&to_new)) = + (node_map.get(&edge.source()), node_map.get(&edge.target())) + { + subgraph.add_edge(from_new, to_new, edge.weight().clone()); + } + } + + subgraph + } + + pub fn join_graph( + &self, + db: &mut dyn InputDb, + other: DiGraph, + join_edges: Vec, + ) { + let current_graph = self.graph(db); + let combined_graph = join_graphs(¤t_graph, &other, &join_edges); + + // Build new node map + let mut new_node_map = HashMap::new(); + for node_idx in combined_graph.node_indices() { + let url = &combined_graph[node_idx]; + new_node_map.insert(url.clone(), node_idx); + } + + self.set_graph(db).to(combined_graph); + self.set_node_map(db).to(new_node_map); + } + + pub fn dependency_urls(&self, db: &dyn InputDb, url: &Url) -> Vec { + let node_map = self.node_map(db); + let graph = self.graph(db); + + if let Some(&root) = node_map.get(url) { + let mut dfs = Dfs::new(&graph, root); + let mut visited = Vec::new(); + + while let Some(node) = dfs.next(&graph) { + if node != root { + visited.push(graph[node].clone()); + } + } + + visited + } else { + Vec::new() + } + } + + pub fn add_node(&self, db: &mut dyn InputDb, url: Url) -> NodeIndex { + let mut graph = self.graph(db); + let mut node_map = self.node_map(db); + + if let Some(&existing_idx) = node_map.get(&url) { + return existing_idx; + } + + let new_idx = graph.add_node(url.clone()); + node_map.insert(url, new_idx); + + self.set_graph(db).to(graph); + self.set_node_map(db).to(node_map); + + new_idx + } + + pub fn add_edge(&self, db: &mut dyn InputDb, from: &Url, to: &Url, weight: EdgeWeight) -> bool { + let node_map = self.node_map(db); + + if let (Some(&from_idx), Some(&to_idx)) = (node_map.get(from), node_map.get(to)) { + let mut graph = self.graph(db); + graph.add_edge(from_idx, to_idx, weight); + self.set_graph(db).to(graph); + true + } else { + false + } + } +} + +fn join_graphs( + graph1: &DiGraph, + graph2: &DiGraph, + join_edges: &[JoinEdge], +) -> DiGraph { + let mut combined = DiGraph::new(); + let mut node_map = HashMap::::new(); + + // Insert graph1 nodes + for idx in graph1.node_indices() { + let url = &graph1[idx]; + if !node_map.contains_key(url) { + let new_idx = combined.add_node(url.clone()); + node_map.insert(url.clone(), new_idx); + } + } + + // Insert graph2 nodes (avoid duplicates) + for idx in graph2.node_indices() { + let url = &graph2[idx]; + if !node_map.contains_key(url) { + let new_idx = combined.add_node(url.clone()); + node_map.insert(url.clone(), new_idx); + } + } + + // Insert graph1 edges + for edge in graph1.edge_references() { + let source_url = &graph1[edge.source()]; + let target_url = &graph1[edge.target()]; + if let (Some(&from), Some(&to)) = (node_map.get(source_url), node_map.get(target_url)) { + combined.add_edge(from, to, edge.weight().clone()); + } + } + + // Insert graph2 edges + for edge in graph2.edge_references() { + let source_url = &graph2[edge.source()]; + let target_url = &graph2[edge.target()]; + if let (Some(&from), Some(&to)) = (node_map.get(source_url), node_map.get(target_url)) { + combined.add_edge(from, to, edge.weight().clone()); + } + } + + // Insert join edges (connecting graph1 and graph2) + for join_edge in join_edges { + if let (Some(&from), Some(&to)) = ( + node_map.get(&join_edge.origin), + node_map.get(&join_edge.destination), + ) { + combined.add_edge(from, to, join_edge.weight.clone()); + } + } + + combined +} diff --git a/crates/common/src/ingot.rs b/crates/common/src/ingot.rs index 3da48ec044..fc65a7b6fb 100644 --- a/crates/common/src/ingot.rs +++ b/crates/common/src/ingot.rs @@ -132,9 +132,9 @@ impl<'db> Ingot<'db> { let base_url = self.base(db); let mut deps = match self.config(db) { Some(config) => config - .based_dependencies(&base_url) + .forward_edges(&base_url) .into_iter() - .map(|based_dependency| (based_dependency.alias, based_dependency.url)) + .map(|(url, weight)| (weight.alias, url)) .collect(), None => vec![], }; diff --git a/crates/common/src/lib.rs b/crates/common/src/lib.rs index 73fcaf91fe..3e49fcfd81 100644 --- a/crates/common/src/lib.rs +++ b/crates/common/src/lib.rs @@ -2,16 +2,20 @@ pub mod config; pub mod core; pub mod diagnostics; pub mod file; +pub mod graph; pub mod indexmap; pub mod ingot; +pub mod tree; pub mod urlext; use file::Workspace; +use graph::Graph; #[salsa::db] // Each database must implement InputDb explicitly with its own storage mechanism pub trait InputDb: salsa::Database { fn workspace(&self) -> Workspace; + fn graph(&self) -> Graph; } #[doc(hidden)] @@ -27,6 +31,9 @@ macro_rules! impl_input_db { fn workspace(&self) -> $crate::file::Workspace { self.index.clone().expect("Workspace not initialized") } + fn graph(&self) -> $crate::graph::Graph { + self.graph.clone().expect("Graph not initialized") + } } }; } @@ -46,9 +53,12 @@ macro_rules! impl_db_default { let mut db = Self { storage: salsa::Storage::default(), index: None, + graph: None, }; let index = $crate::file::Workspace::default(&db); db.index = Some(index); + let graph = $crate::graph::Graph::default(&db); + db.graph = Some(graph); $crate::core::HasBuiltinCore::initialize_builtin_core(&mut db); db } @@ -65,6 +75,7 @@ macro_rules! define_input_db { pub struct $db_name { storage: salsa::Storage, index: Option<$crate::file::Workspace>, + graph: Option<$crate::graph::Graph>, } #[salsa::db] diff --git a/crates/common/src/tree.rs b/crates/common/src/tree.rs new file mode 100644 index 0000000000..0a223dc03c --- /dev/null +++ b/crates/common/src/tree.rs @@ -0,0 +1,169 @@ +use petgraph::graph::{DiGraph, NodeIndex}; +use std::collections::{HashMap, HashSet}; +use url::Url; + +use crate::{config::Config, graph::EdgeWeight}; + +#[derive(Clone)] +enum TreePrefix { + Root, + Fork(String), + Last(String), +} + +impl TreePrefix { + fn new_prefix(&self) -> String { + match self { + TreePrefix::Root => "".to_string(), + TreePrefix::Fork(p) => format!("{p}├── "), + TreePrefix::Last(p) => format!("{p}└── "), + } + } + + fn child_indent(&self) -> String { + match self { + TreePrefix::Root => "".to_string(), + TreePrefix::Fork(p) => format!("{p}│ "), + TreePrefix::Last(p) => format!("{p} "), + } + } +} + +pub fn display_dependency_tree( + graph: &DiGraph, + root_url: &Url, + configs: &HashMap, +) -> String { + let mut output = String::new(); + + let cycle_nodes = find_cycle_nodes(graph); + + if let Some(root_idx) = graph.node_indices().find(|i| graph[*i] == *root_url) { + let context = TreeContext { + graph, + configs, + cycle_nodes: &cycle_nodes, + }; + let mut seen = HashSet::new(); + print_node_with_alias( + &context, + root_idx, + TreePrefix::Root, + &mut output, + &mut seen, + None, + ); + } else { + output.push_str("[error: root node not found]\n"); + } + + output +} + +struct TreeContext<'a> { + graph: &'a DiGraph, + configs: &'a HashMap, + cycle_nodes: &'a HashSet, +} + +fn print_node_with_alias( + context: &TreeContext, + node: NodeIndex, + prefix: TreePrefix, + output: &mut String, + seen: &mut HashSet, + alias: Option<&str>, +) { + let ingot_path = &context.graph[node]; + + // Build the label with alias support + let base_label = if let Some(config) = context.configs.get(ingot_path) { + let ingot_name = config.metadata.name.as_deref().unwrap_or("null"); + let version = config + .metadata + .version + .as_ref() + .map(ToString::to_string) + .unwrap_or_else(|| "null".to_string()); + + // Show "ingot_name as alias" if alias differs from ingot name + match alias { + Some(alias_str) if alias_str != ingot_name => { + format!("➖ {} as {} v{}", ingot_name, alias_str, version) + } + _ => format!("➖ {} v{}", ingot_name, version), + } + } else { + "➖ [invalid fe.toml]".to_string() + }; + + let is_in_cycle = context.cycle_nodes.contains(&node); + let will_close_cycle = seen.contains(&node); + + let mut label = base_label; + if will_close_cycle { + label = format!("{label} 🔄 [cycle]"); + } + + if is_in_cycle { + output.push_str(&format!("{}{}\n", prefix.new_prefix(), red(&label))); + } else { + output.push_str(&format!("{}{}\n", prefix.new_prefix(), label)); + } + + if will_close_cycle { + return; + } + + seen.insert(node); + + // Process children with alias information from edges + use petgraph::visit::EdgeRef; + let children: Vec<_> = context + .graph + .edges_directed(node, petgraph::Direction::Outgoing) + .collect(); + + for (i, edge) in children.iter().enumerate() { + let child_prefix = if i == children.len() - 1 { + TreePrefix::Last(prefix.child_indent()) + } else { + TreePrefix::Fork(prefix.child_indent()) + }; + + print_node_with_alias( + context, + edge.target(), + child_prefix, + output, + seen, + Some(&edge.weight().alias), + ); + } + + seen.remove(&node); +} + +fn red(s: &str) -> String { + format!("\x1b[31m{s}\x1b[0m") +} + +fn find_cycle_nodes(graph: &DiGraph) -> HashSet { + use petgraph::algo::kosaraju_scc; + + let mut cycles = HashSet::new(); + for scc in kosaraju_scc(graph) { + if scc.len() > 1 { + cycles.extend(scc); + } else { + let node = scc[0]; + if graph + .neighbors_directed(node, petgraph::Direction::Outgoing) + .any(|n| n == node) + { + cycles.insert(node); // self-loop + } + } + } + cycles +} diff --git a/crates/driver/Cargo.toml b/crates/driver/Cargo.toml index 27e2c07407..7d43ccb813 100644 --- a/crates/driver/Cargo.toml +++ b/crates/driver/Cargo.toml @@ -13,7 +13,6 @@ doctest = false [dependencies] camino.workspace = true -clap.workspace = true codespan-reporting.workspace = true salsa.workspace = true @@ -22,6 +21,4 @@ hir.workspace = true hir-analysis.workspace = true resolver.workspace = true url.workspace = true -smol_str.workspace = true -test-utils.workspace = true tracing.workspace = true diff --git a/crates/driver/src/lib.rs b/crates/driver/src/lib.rs index d2dbbb5f6a..dadbe52257 100644 --- a/crates/driver/src/lib.rs +++ b/crates/driver/src/lib.rs @@ -4,232 +4,111 @@ pub mod db; pub mod diagnostics; pub mod files; -use camino::Utf8PathBuf; -use common::core::HasBuiltinCore; -use common::ingot::IngotBaseUrl; +use std::{collections::HashMap, mem::take}; -use common::InputDb; +use common::{ + graph::{EdgeWeight, JoinEdge}, + tree::display_dependency_tree, + InputDb, +}; pub use db::DriverDataBase; -use clap::{Parser, Subcommand}; +use common::config::Config; use hir::hir_def::TopLevelMod; use resolver::{ - ingot::{source_files::SourceFiles, Ingot, IngotResolver}, - Resolver, + files::{FilesResolutionDiagnostic, FilesResolutionError, FilesResolver, FilesResource}, + graph::{DiGraph, GraphResolutionHandler, GraphResolver, GraphResolverImpl}, + ResolutionHandler, Resolver, }; use url::Url; -pub fn run(opts: &Options) { - match &opts.command { - Command::Build => eprintln!("`fe build` doesn't work at the moment"), - Command::Check { path, core } => { - let mut db = DriverDataBase::default(); - let mut ingot_resolver = IngotResolver::default(); - - let core_url = if let Some(core_path) = core { - if !core_path.exists() { - eprintln!("the core path `{core_path}` does not exist"); - std::process::exit(1) - } +pub type IngotGraphResolver<'a> = GraphResolverImpl, EdgeWeight>; - let core_url = match core_path.canonicalize_utf8() { - Ok(canonical_path) => { - if canonical_path.is_file() { - Url::from_file_path(canonical_path) - .expect("unable to create file url from directory path ") - } else { - Url::from_directory_path(canonical_path) - .expect("unable to create directory url from canonical path") - } - } - Err(err) => { - eprintln!("failed to canonicalize path `{core_path}`: {err}"); - std::process::exit(1) - } - }; - - match ingot_resolver.resolve(&core_url) { - Ok(Ingot::Folder { - config, - source_files: - Some(SourceFiles { - root: Some(_root), - files, - }), - }) => { - let core_base_url = Url::parse("core-ingot:///").unwrap(); - let diagnostics = ingot_resolver.take_diagnostics(); - if !diagnostics.is_empty() { - eprintln!("an error was encountered while resolving `{core_path}`"); - for diagnostic in diagnostics { - eprintln!("{diagnostic}"); - } - std::process::exit(1) - } - let index = db.workspace(); - if let Some(config) = config { - let config_url = config.url; - index.touch_ingot(&mut db, &core_base_url, Some(config.content)); - let config = core_base_url - .ingot(&db) - .expect("core ingot should exist") - .config(&db) - .expect("core ingot config should exist"); - if let Some(diagnostics) = config.formatted_diagnostics() { - eprintln!( - "there are issues with the core fe.toml file {config_url}" - ); - eprintln!("{diagnostics}"); - std::process::exit(1) - } - } else { - index.touch_ingot(&mut db, &core_base_url, None); - }; - for (file_url, content) in files { - let rebased_file_url = core_base_url - .join(&file_url.path()[core_url.path().len()..]) - .unwrap(); - index.touch(&mut db, rebased_file_url, Some(content)); - } - core_base_url - } - Ok(Ingot::SingleFile { .. }) => { - eprintln!("standalone core ingot not supported"); - std::process::exit(1) - } - Ok(_) => { - eprintln!("an error was encountered while resolving `{core_path}`"); - for diagnostic in ingot_resolver.take_diagnostics() { - eprintln!("{diagnostic}"); - } - std::process::exit(1) - } - Err(error) => { - eprintln!("an error was encountered while resolving `{core_path}`"); - eprintln!("{error}"); - std::process::exit(1) - } - } - } else { - db.builtin_core().base(&db) - }; +pub fn ingot_graph_resolver<'a>() -> IngotGraphResolver<'a> { + let files_resolver = FilesResolver::new() + .with_required_file("fe.toml") + .with_required_directory("src") + .with_required_file("src/lib.fe") + .with_pattern("src/**/*.fe"); + GraphResolverImpl::new(files_resolver) +} - if !path.exists() { - eprintln!("the path `{path}` does not exist"); - std::process::exit(1) - } +pub fn init_ingot(db: &mut DriverDataBase, ingot_url: &Url) -> Vec { + tracing::trace!(target: "resolver", "Starting workspace ingot resolution for: {}", ingot_url); + let mut diagnostics: Vec = { + let mut handler = InputHandler::from_db(db, ingot_url.clone()); + let mut ingot_graph_resolver = ingot_graph_resolver(); - let path_url = match path.canonicalize_utf8() { - Ok(canonical_path) => { - if canonical_path.is_file() { - Url::from_file_path(canonical_path) - .expect("unable to create file url from directory path ") - } else { - Url::from_directory_path(canonical_path) - .expect("unable to create directory url from canonical path") - } - } - Err(err) => { - eprintln!("failed to canonicalize path `{path}`: {err}"); - std::process::exit(1) - } - }; + // Root ingot resolution should never fail since directory existence is validated earlier. + // If it fails, it indicates a bug in the resolver or an unexpected system condition. + if let Err(err) = ingot_graph_resolver.graph_resolve(&mut handler, ingot_url) { + panic!("Unexpected failure resolving root ingot at {}: {:?}. This indicates a bug in the resolver since directory existence is validated before calling init_ingot.", ingot_url, err); + } - let local_url = match ingot_resolver.resolve(&path_url) { - Ok(Ingot::Folder { - config, - source_files: - Some(SourceFiles { - root: Some(_root), - files, - }), - }) => { - let base_url = Url::from_directory_path(path.canonicalize_utf8().unwrap()) - .expect("failed to parse base URL"); - - let diagnostics = ingot_resolver.take_diagnostics(); - if !diagnostics.is_empty() { - eprintln!("an error was encountered while resolving `{path}`"); - for diagnostic in diagnostics { - eprintln!("{diagnostic}"); - } - std::process::exit(1) - } - let index = db.workspace(); - if let Some(config) = config { - let config_url = config.url; - index.touch_ingot(&mut db, &base_url, Some(config.content)); - let config = base_url - .ingot(&db) - .expect("local ingot should exist") - .config(&db) - .expect("local ingot config should exist"); - if let Some(diagnostics) = config.formatted_diagnostics() { - eprintln!("there are issues with the local fe.toml file {config_url}",); - eprintln!("{diagnostics}"); - std::process::exit(1) - } - } else { - index.touch_ingot(&mut db, &base_url, None); - }; + // Collect diagnostics from all sources + let mut all_diagnostics = Vec::new(); - for (file_url, content) in files { - index.touch(&mut db, file_url, Some(content)); - } - base_url - } - Ok(Ingot::SingleFile { url, content }) => { - db.workspace().touch(&mut db, url.clone(), Some(content)); - url - } - Ok(_) => { - for diagnostic in ingot_resolver.take_diagnostics() { - eprintln!("{diagnostic}"); - } - std::process::exit(1) - } - Err(error) => { - eprintln!("{error}: {path}"); - std::process::exit(1) - } - }; + // Add handler diagnostics (missing fe.toml) + all_diagnostics.extend(handler.diagnostics); - let core_source_diags = - db.run_on_ingot(core_url.ingot(&db).expect("core ingot should exist")); - if !core_source_diags.is_empty() { - eprintln!("errors in {core_url}\n"); - core_source_diags.emit(&db); - std::process::exit(1); - } + // Add graph resolver diagnostics (unresolvable dependencies) + all_diagnostics.extend(ingot_graph_resolver.take_diagnostics().into_iter().map( + |diagnostic| IngotInitDiagnostics::UnresolvableIngotDependency { + target: diagnostic.0, + error: diagnostic.1, + }, + )); - let local_source_diags = db.run_on_ingot(local_url.ingot(&db).unwrap()); - if !local_source_diags.is_empty() { - eprintln!("errors in {local_url}\n"); - local_source_diags.emit(&db); - std::process::exit(1); + // Add files resolver diagnostics (file errors) + all_diagnostics.extend( + ingot_graph_resolver + .node_resolver + .take_diagnostics() + .into_iter() + .map(|diagnostic| IngotInitDiagnostics::FileError { diagnostic }), + ); + + all_diagnostics + }; + + // Check for cycles after graph resolution (now that handler is dropped) + let cyclic_subgraph = db.graph().cyclic_subgraph(db); + + // Add cycle diagnostics - single comprehensive diagnostic if any cycles exist + if cyclic_subgraph.node_count() > 0 { + // Get configs for all nodes in the cyclic subgraph + let mut configs = HashMap::new(); + for node_idx in cyclic_subgraph.node_indices() { + let url = &cyclic_subgraph[node_idx]; + if let Some(ingot) = db.workspace().containing_ingot(db, url.clone()) { + if let Some(config) = ingot.config(db) { + configs.insert(url.clone(), config); + } } } - Command::New => eprintln!("`fe new` doesn't work at the moment"), + + // The root ingot should be part of any detected cycles since we're analyzing its dependencies + if !cyclic_subgraph + .node_indices() + .any(|idx| cyclic_subgraph[idx] == *ingot_url) + { + panic!("Root ingot {} not found in cyclic subgraph. This indicates a bug in cycle detection logic.", ingot_url); + } + let tree_root = ingot_url.clone(); + + // Generate the tree display string + let tree_display = display_dependency_tree(&cyclic_subgraph, &tree_root, &configs); + + diagnostics.push(IngotInitDiagnostics::IngotDependencyCycle { tree_display }); } -} -#[derive(Debug, Clone, Parser)] -#[command(version, about, long_about = None)] -pub struct Options { - #[command(subcommand)] - pub command: Command, -} + if diagnostics.is_empty() { + tracing::trace!(target: "resolver", "Ingot resolution completed successfully for: {}", ingot_url); + } else { + tracing::warn!(target: "resolver", "Ingot resolution completed with {} diagnostics for: {}", diagnostics.len(), ingot_url); + } -#[derive(Debug, Clone, Subcommand)] -pub enum Command { - Build, - Check { - // #[clap(default_value_t = find_project_root().unwrap_or(Utf8PathBuf::from(".")))] - path: Utf8PathBuf, - #[arg(short, long)] - core: Option, - }, - New, + diagnostics } fn _dump_scope_graph(db: &DriverDataBase, top_mod: TopLevelMod) -> String { @@ -239,3 +118,186 @@ fn _dump_scope_graph(db: &DriverDataBase, top_mod: TopLevelMod) -> String { } // Maybe the driver should eventually only support WASI? + +#[derive(Debug)] +pub enum IngotInitDiagnostics { + UnresolvableIngotDependency { + target: Url, + error: FilesResolutionError, + }, + IngotDependencyCycle { + tree_display: String, + }, + FileError { + diagnostic: FilesResolutionDiagnostic, + }, + MissingFeToml { + ingot_url: Url, + }, + InvalidToml { + ingot_url: Url, + error: String, + }, + ConfigValidation { + ingot_url: Url, + diagnostic: common::config::ConfigDiagnostic, + }, + MissingRootFile { + ingot_url: Url, + is_main_ingot: bool, + }, +} + +impl std::fmt::Display for IngotInitDiagnostics { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + IngotInitDiagnostics::UnresolvableIngotDependency { target, error } => { + write!(f, "Failed to resolve ingot dependency '{target}': {error}") + } + IngotInitDiagnostics::IngotDependencyCycle { tree_display } => { + write!( + f, + "Detected cycle(s) in ingot dependencies:\n\n{}", + tree_display + ) + } + IngotInitDiagnostics::FileError { diagnostic } => { + write!(f, "File resolution error: {diagnostic}") + } + IngotInitDiagnostics::MissingFeToml { ingot_url } => { + write!(f, "Missing fe.toml file in ingot: {ingot_url}") + } + IngotInitDiagnostics::InvalidToml { ingot_url, error } => { + write!(f, "Invalid fe.toml file in ingot {ingot_url}: {error}") + } + IngotInitDiagnostics::ConfigValidation { + ingot_url, + diagnostic, + } => { + write!(f, "Config validation error at {ingot_url}: {diagnostic}") + } + IngotInitDiagnostics::MissingRootFile { + ingot_url, + is_main_ingot, + } => { + if *is_main_ingot { + write!( + f, + "Missing root file (src/lib.fe) in current ingot: {ingot_url}" + ) + } else { + write!( + f, + "Missing root file (src/lib.fe) in dependency: {ingot_url}" + ) + } + } + } + } +} + +pub struct InputHandler<'a> { + pub db: &'a mut dyn InputDb, + pub join_edges: Vec, + pub diagnostics: Vec, + pub main_ingot_url: Url, +} + +impl<'a> InputHandler<'a> { + pub fn from_db(db: &'a mut dyn InputDb, main_ingot_url: Url) -> Self { + Self { + db, + join_edges: vec![], + diagnostics: vec![], + main_ingot_url, + } + } +} + +impl<'a> ResolutionHandler for InputHandler<'a> { + type Item = Vec<(Url, EdgeWeight)>; + + fn handle_resolution(&mut self, ingot_url: &Url, resource: FilesResource) -> Self::Item { + let mut config = None; + + for file in resource.files { + if file.path.ends_with("fe.toml") { + self.db.workspace().touch( + self.db, + Url::from_file_path(file.path).unwrap(), + Some(file.content.clone()), + ); + config = Some(file.content); + } else { + self.db.workspace().touch( + self.db, + Url::from_file_path(file.path).unwrap(), + Some(file.content), + ); + } + } + + if let Some(content) = config { + let config = match Config::parse(&content) { + Ok(config) => config, + Err(err) => { + // Add invalid config as a diagnostic + tracing::error!(target: "resolver", "Failed to parse fe.toml: {:?}", err); + self.diagnostics.push(IngotInitDiagnostics::InvalidToml { + ingot_url: ingot_url.clone(), + error: err.to_string(), + }); + return vec![]; + } + }; + + // Check for config validation diagnostics (invalid names, versions, etc.) + for diagnostic in &config.diagnostics { + self.diagnostics + .push(IngotInitDiagnostics::ConfigValidation { + ingot_url: ingot_url.clone(), + diagnostic: diagnostic.clone(), + }); + } + + // Missing src/lib.fe file is now handled by the FilesResolver diagnostics + + // let weights: HashSet = self.db.graph().node_weights().cloned().collect(); + + config + .forward_edges(ingot_url) + .into_iter() + .filter_map(|(url, weight)| { + if self.db.graph().contains_url(self.db, &url) { + self.join_edges.push(JoinEdge { + origin: ingot_url.clone(), + destination: url, + weight, + }); + None + } else { + Some((url, weight)) + } + }) + .collect() + } else { + // No fe.toml file found - this will be reported by the FilesResolver as a diagnostic + vec![] + } + } +} + +impl<'a> GraphResolutionHandler> for InputHandler<'a> { + type Item = (); + + fn handle_graph_resolution( + &mut self, + _ingot_url: &Url, + graph: DiGraph, + ) -> Self::Item { + // Update the graph singleton in the database with the resolved graph + self.db + .graph() + .join_graph(self.db, graph, take(&mut self.join_edges)); + } +} diff --git a/crates/fe/Cargo.toml b/crates/fe/Cargo.toml index 4d9b12a539..9f801218c2 100644 --- a/crates/fe/Cargo.toml +++ b/crates/fe/Cargo.toml @@ -7,7 +7,10 @@ edition = "2021" clap.workspace = true driver.workspace = true tracing.workspace = true -tracing-subscriber.workspace = true +common.workspace = true +resolver.workspace = true +camino.workspace = true +url.workspace = true [dev-dependencies] dir-test.workspace = true diff --git a/crates/fe/src/check.rs b/crates/fe/src/check.rs new file mode 100644 index 0000000000..f568183304 --- /dev/null +++ b/crates/fe/src/check.rs @@ -0,0 +1,188 @@ +use camino::Utf8PathBuf; +use common::InputDb; +use driver::DriverDataBase; +use url::Url; + +pub fn check(path: &Utf8PathBuf) { + let mut db = DriverDataBase::default(); + + // Determine if we're dealing with a single file or an ingot directory + let has_errors = if path.is_file() && path.extension() == Some("fe") { + check_single_file(&mut db, path) + } else if path.is_dir() { + check_ingot(&mut db, path) + } else { + eprintln!("❌ Error: Path must be either a .fe file or a directory containing fe.toml"); + std::process::exit(1); + }; + + if has_errors { + std::process::exit(1); + } +} + +fn check_single_file(db: &mut DriverDataBase, file_path: &Utf8PathBuf) -> bool { + // Create a file URL for the single .fe file + let file_url = match Url::from_file_path(file_path.canonicalize_utf8().unwrap()) { + Ok(url) => url, + Err(_) => { + eprintln!("❌ Error: Invalid file path: {file_path}"); + return true; + } + }; + + // Read the file content + let content = match std::fs::read_to_string(file_path) { + Ok(content) => content, + Err(err) => { + eprintln!("Error reading file {file_path}: {err}"); + return true; + } + }; + + // Add the file to the workspace + db.workspace().touch(db, file_url.clone(), Some(content)); + + // Try to get the file and check it for errors + if let Some(file) = db.workspace().get(db, &file_url) { + let top_mod = db.top_mod(file); + let diags = db.run_on_top_mod(top_mod); + if !diags.is_empty() { + eprintln!("errors in {file_url}"); + eprintln!(); + diags.emit(db); + return true; + } + } else { + eprintln!("❌ Error: Could not process file {file_path}"); + return true; + } + + false +} + +fn check_ingot(db: &mut DriverDataBase, dir_path: &Utf8PathBuf) -> bool { + let canonical_path = match dir_path.canonicalize_utf8() { + Ok(path) => path, + Err(_) => { + eprintln!( + "Error: Invalid or non-existent directory path: {}", + dir_path + ); + eprintln!(" Make sure the directory exists and is accessible"); + return true; + } + }; + + let ingot_url = match Url::from_directory_path(canonical_path.as_str()) { + Ok(url) => url, + Err(_) => { + eprintln!("❌ Error: Invalid directory path: {}", dir_path); + return true; + } + }; + let init_diagnostics = driver::init_ingot(db, &ingot_url); + + // Handle workspace setup diagnostics if any + if !init_diagnostics.is_empty() { + let mut has_init_issues = false; + for diagnostic in &init_diagnostics { + eprintln!("❌ {diagnostic}"); + has_init_issues = true; + } + if has_init_issues { + return true; + } + } + + let Some(ingot) = db.workspace().containing_ingot(db, ingot_url.clone()) else { + // Check if the issue is a missing fe.toml file + let config_url = match ingot_url.join("fe.toml") { + Ok(url) => url, + Err(_) => { + eprintln!("❌ Error: Invalid ingot directory path"); + return true; + } + }; + + if db.workspace().get(db, &config_url).is_none() { + eprintln!("❌ Error: No fe.toml file found in the root directory"); + eprintln!(" Expected fe.toml at: {}", config_url); + eprintln!( + " Make sure you're in an fe project directory or create a fe.toml file" + ); + } else { + eprintln!("❌ Error: Could not resolve ingot from directory"); + } + return true; + }; + + // Check if the ingot has source files before trying to analyze + if ingot.root_file(db).is_err() { + eprintln!( + "source files resolution error: `src` folder does not exist in the ingot directory" + ); + return true; + } + + let diags = db.run_on_ingot(ingot); + let mut has_errors = false; + + if !diags.is_empty() { + diags.emit(db); + has_errors = true; + } + + // Collect all dependencies with errors + let mut dependency_errors = Vec::new(); + for dependency_url in db.graph().dependency_urls(db, &ingot_url) { + let Some(ingot) = db.workspace().containing_ingot(db, dependency_url.clone()) else { + // Skip dependencies that can't be resolved + continue; + }; + let diags = db.run_on_ingot(ingot); + if !diags.is_empty() { + dependency_errors.push((dependency_url, diags)); + } + } + + // Print dependency errors if any exist + if !dependency_errors.is_empty() { + has_errors = true; + if dependency_errors.len() == 1 { + eprintln!("❌ Error in downstream ingot"); + } else { + eprintln!("❌ Errors in downstream ingots"); + } + + for (dependency_url, diags) in dependency_errors { + print_dependency_info(db, &dependency_url); + diags.emit(db); + } + } + + has_errors +} + +fn print_dependency_info(db: &DriverDataBase, dependency_url: &Url) { + eprintln!(); + + // Get the ingot for this dependency URL to access its config + if let Some(ingot) = db.workspace().containing_ingot(db, dependency_url.clone()) { + if let Some(config) = ingot.config(db) { + let name = config.metadata.name.as_deref().unwrap_or("unknown"); + if let Some(version) = &config.metadata.version { + eprintln!("➖ {name} (version: {version})"); + } else { + eprintln!("➖ {name}"); + } + } else { + eprintln!("➖ Unknown dependency"); + } + } else { + eprintln!("➖ Unknown dependency"); + } + + eprintln!("🔗 {dependency_url}"); + eprintln!(); +} diff --git a/crates/fe/src/main.rs b/crates/fe/src/main.rs index 54eaaf25ac..df3e7a0d4f 100644 --- a/crates/fe/src/main.rs +++ b/crates/fe/src/main.rs @@ -1,16 +1,46 @@ -use clap::Parser; -use driver::Options; +mod check; +mod tree; -fn main() { - let opts = Options::parse(); +use camino::Utf8PathBuf; +use check::check; +use clap::{Parser, Subcommand}; + +#[derive(Debug, Clone, Parser)] +#[command(version, about, long_about = None)] +pub struct Options { + #[command(subcommand)] + pub command: Command, +} - // Initialize tracing with ERROR as default level - tracing_subscriber::fmt() - .with_env_filter( - tracing_subscriber::EnvFilter::try_from_default_env() - .unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("error")), - ) - .init(); +#[derive(Debug, Clone, Subcommand)] +pub enum Command { + Build, + Check { + // #[clap(default_value_t = find_project_root().unwrap_or(Utf8PathBuf::from(".")))] + path: Utf8PathBuf, + #[arg(short, long)] + core: Option, + }, + Tree { + path: Utf8PathBuf, + }, + New, +} - driver::run(&opts); +fn main() { + let opts = Options::parse(); + run(&opts); +} +pub fn run(opts: &Options) { + match &opts.command { + Command::Build => eprintln!("`fe build` doesn't work at the moment"), + Command::Check { path, core: _ } => { + //: TODO readd custom core + check(path); + } + Command::Tree { path } => { + tree::print_tree(path); + } + Command::New => eprintln!("`fe new` doesn't work at the moment"), + } } diff --git a/crates/fe/src/tree.rs b/crates/fe/src/tree.rs new file mode 100644 index 0000000000..2279efbac1 --- /dev/null +++ b/crates/fe/src/tree.rs @@ -0,0 +1,135 @@ +use std::collections::HashMap; + +use camino::Utf8PathBuf; +use common::{config::Config, graph::EdgeWeight, tree::display_dependency_tree}; +use resolver::{ + files::{FilesResolver, FilesResource}, + graph::{DiGraph, GraphResolutionHandler, GraphResolver}, + ResolutionHandler, Resolver, +}; +use url::Url; + +pub fn print_tree(path: &Utf8PathBuf) { + let mut resolver = tree_resolver(); + let mut handler = TreeHandler::default(); + + let canonical_path = match path.canonicalize_utf8() { + Ok(path) => path, + Err(_) => { + eprintln!("Error: Invalid or non-existent directory path: {}", path); + return; + } + }; + + let ingot_url = match Url::from_directory_path(canonical_path.as_str()) { + Ok(url) => url, + Err(_) => { + eprintln!("Error: Invalid directory path: {}", path); + return; + } + }; + + match resolver.graph_resolve(&mut handler, &ingot_url) { + Ok(tree_output) => { + // Print graph resolver diagnostics (unresolvable nodes) + for unresolvable_node in resolver.take_diagnostics() { + eprintln!( + "❌ Failed to resolve ingot dependency '{}': {}", + unresolvable_node.0, unresolvable_node.1 + ); + } + + // Print files resolver diagnostics + for diagnostic in resolver.node_resolver.take_diagnostics() { + eprintln!("❌ File resolution error: {}", diagnostic); + } + + // Print the tree + print!("{}", tree_output); + } + Err(err) => { + // Print diagnostics even on failure + for unresolvable_node in resolver.take_diagnostics() { + eprintln!( + "❌ Failed to resolve ingot dependency '{}': {}", + unresolvable_node.0, unresolvable_node.1 + ); + } + + for diagnostic in resolver.node_resolver.take_diagnostics() { + eprintln!("❌ File resolution error: {}", diagnostic); + } + + println!("❌ Failed to resolve dependency tree: {err}"); + } + } +} + +pub type IngotGraphResolver = + resolver::graph::GraphResolverImpl; + +pub fn tree_resolver() -> IngotGraphResolver { + let files_resolver = FilesResolver::new().with_required_file("fe.toml"); + resolver::graph::GraphResolverImpl::new(files_resolver) +} + +#[derive(Default)] +pub struct TreeHandler { + pub configs: HashMap, +} + +impl ResolutionHandler for TreeHandler { + type Item = Vec<(Url, EdgeWeight)>; + + fn handle_resolution(&mut self, ingot_url: &Url, resource: FilesResource) -> Self::Item { + tracing::trace!(target: "resolver", "Handling ingot resolution for: {}", ingot_url); + + // Look for fe.toml file + if let Some(config_file) = resource + .files + .iter() + .find(|f| f.path.file_name() == Some("fe.toml")) + { + match Config::parse(&config_file.content) { + Ok(config) => { + tracing::trace!(target: "resolver", "Successfully parsed config for ingot: {}", ingot_url); + + // Report config validation diagnostics + for diagnostic in &config.diagnostics { + eprintln!( + "❌ Config validation error at {}: {}", + ingot_url, diagnostic + ); + } + + self.configs.insert(ingot_url.clone(), config.clone()); + let dependencies = config.forward_edges(ingot_url); + for (url, _) in &dependencies { + tracing::trace!(target: "resolver", "Found dependency: {} -> {}", ingot_url, url); + } + dependencies + } + Err(err) => { + tracing::warn!(target: "resolver", "Failed to parse config for ingot {}: {}", ingot_url, err); + eprintln!("❌ Invalid fe.toml file at {}: {}", ingot_url, err); + vec![] + } + } + } else { + // This case should not happen since we require fe.toml, but handle it gracefully + vec![] + } + } +} + +impl GraphResolutionHandler> for TreeHandler { + type Item = String; + + fn handle_graph_resolution( + &mut self, + ingot_url: &Url, + graph: DiGraph, + ) -> Self::Item { + display_dependency_tree(&graph, ingot_url, &self.configs) + } +} diff --git a/crates/fe/tests/cli_output.rs b/crates/fe/tests/cli_output.rs index e22d2eb857..ebac9fc18a 100644 --- a/crates/fe/tests/cli_output.rs +++ b/crates/fe/tests/cli_output.rs @@ -16,8 +16,18 @@ fn normalize_output(output: &str) -> String { output.replace(&project_root.to_string_lossy().to_string(), "") } -// Helper function to run fe binary +// Helper function to run fe check fn run_fe_check(path: &str) -> (String, i32) { + run_fe_command("check", path) +} + +// Helper function to run fe tree +fn run_fe_tree(path: &str) -> (String, i32) { + run_fe_command("tree", path) +} + +// Helper function to run fe binary with specified subcommand +fn run_fe_command(subcommand: &str, path: &str) -> (String, i32) { // Build the fe binary let cargo_exe = std::env::var("CARGO").unwrap_or_else(|_| "cargo".to_string()); let output = Command::new(&cargo_exe) @@ -32,7 +42,7 @@ fn run_fe_check(path: &str) -> (String, i32) { ); } - // Run fe check + // Run fe subcommand let fe_binary = std::env::current_exe() .expect("Failed to get current exe") .parent() @@ -42,10 +52,10 @@ fn run_fe_check(path: &str) -> (String, i32) { .join("fe"); let output = Command::new(&fe_binary) - .args(["check", path]) + .args([subcommand, path]) .env("NO_COLOR", "1") // Disable color output for consistent snapshots across environments .output() - .expect("Failed to run fe check"); + .unwrap_or_else(|_| panic!("Failed to run fe {}", subcommand)); // Combine stdout and stderr for snapshot let mut full_output = String::new(); @@ -94,3 +104,20 @@ fn test_cli_ingot(fixture: Fixture<&str>) { let snapshot_path = ingot_dir.join(ingot_name); snap_test!(output, snapshot_path.to_str().unwrap()); } + +#[dir_test( + dir: "$CARGO_MANIFEST_DIR/tests/fixtures/cli_output/ingots", + glob: "**/fe.toml", +)] +fn test_tree_output(fixture: Fixture<&str>) { + let ingot_dir = std::path::Path::new(fixture.path()) + .parent() + .expect("fe.toml should have parent"); + + let (output, _) = run_fe_tree(ingot_dir.to_str().unwrap()); + + // Use the ingot directory name for the snapshot with _tree suffix + let ingot_name = ingot_dir.file_name().unwrap().to_str().unwrap(); + let snapshot_path = ingot_dir.join(format!("{}_tree", ingot_name)); + snap_test!(output, snapshot_path.to_str().unwrap()); +} diff --git a/crates/fe/tests/fixtures/cli_output/ingots/basic/basic.snap b/crates/fe/tests/fixtures/cli_output/ingots/basic/basic.snap new file mode 100644 index 0000000000..9dd54baa06 --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/basic/basic.snap @@ -0,0 +1,5 @@ +--- +source: crates/fe/tests/cli_output.rs +expression: output +--- +=== EXIT CODE: 0 === diff --git a/crates/fe/tests/fixtures/cli_output/ingots/basic/basic_tree.snap b/crates/fe/tests/fixtures/cli_output/ingots/basic/basic_tree.snap new file mode 100644 index 0000000000..bd3e2c22c0 --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/basic/basic_tree.snap @@ -0,0 +1,8 @@ +--- +source: crates/fe/tests/cli_output.rs +expression: output +--- +=== STDOUT === +➖ basic v0.1.0 + +=== EXIT CODE: 0 === diff --git a/crates/fe/tests/fixtures/cli_output/ingots/basic/fe.toml b/crates/fe/tests/fixtures/cli_output/ingots/basic/fe.toml new file mode 100644 index 0000000000..38affdf33a --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/basic/fe.toml @@ -0,0 +1,3 @@ +[ingot] +name = "basic" +version = "0.1.0" \ No newline at end of file diff --git a/crates/fe/tests/fixtures/cli_output/ingots/basic/src/lib.fe b/crates/fe/tests/fixtures/cli_output/ingots/basic/src/lib.fe new file mode 100644 index 0000000000..f2bf7e1e7f --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/basic/src/lib.fe @@ -0,0 +1 @@ +pub fn main() {} \ No newline at end of file diff --git a/crates/fe/tests/fixtures/cli_output/ingots/basic_a/basic_a.snap b/crates/fe/tests/fixtures/cli_output/ingots/basic_a/basic_a.snap new file mode 100644 index 0000000000..9dd54baa06 --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/basic_a/basic_a.snap @@ -0,0 +1,5 @@ +--- +source: crates/fe/tests/cli_output.rs +expression: output +--- +=== EXIT CODE: 0 === diff --git a/crates/fe/tests/fixtures/cli_output/ingots/basic_a/basic_a_tree.snap b/crates/fe/tests/fixtures/cli_output/ingots/basic_a/basic_a_tree.snap new file mode 100644 index 0000000000..c65edbc75f --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/basic_a/basic_a_tree.snap @@ -0,0 +1,12 @@ +--- +source: crates/fe/tests/cli_output.rs +expression: output +--- +=== STDOUT === +➖ basic_a v0.1.0 +├── ➖ basic_b v0.1.0 +│ └── ➖ basic_d v0.1.0 +└── ➖ basic_c v0.1.0 + └── ➖ basic_d v0.1.0 + +=== EXIT CODE: 0 === diff --git a/crates/fe/tests/fixtures/cli_output/ingots/basic_a/fe.toml b/crates/fe/tests/fixtures/cli_output/ingots/basic_a/fe.toml new file mode 100644 index 0000000000..897bfcc4e2 --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/basic_a/fe.toml @@ -0,0 +1,7 @@ +[ingot] +name = "basic_a" +version = "0.1.0" + +[dependencies] +basic_b = "../basic_b" +basic_c = "../basic_c" \ No newline at end of file diff --git a/crates/fe/tests/fixtures/cli_output/ingots/basic_a/src/lib.fe b/crates/fe/tests/fixtures/cli_output/ingots/basic_a/src/lib.fe new file mode 100644 index 0000000000..f2bf7e1e7f --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/basic_a/src/lib.fe @@ -0,0 +1 @@ +pub fn main() {} \ No newline at end of file diff --git a/crates/fe/tests/fixtures/cli_output/ingots/basic_b/basic_b.snap b/crates/fe/tests/fixtures/cli_output/ingots/basic_b/basic_b.snap new file mode 100644 index 0000000000..9dd54baa06 --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/basic_b/basic_b.snap @@ -0,0 +1,5 @@ +--- +source: crates/fe/tests/cli_output.rs +expression: output +--- +=== EXIT CODE: 0 === diff --git a/crates/fe/tests/fixtures/cli_output/ingots/basic_b/basic_b_tree.snap b/crates/fe/tests/fixtures/cli_output/ingots/basic_b/basic_b_tree.snap new file mode 100644 index 0000000000..c67581d059 --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/basic_b/basic_b_tree.snap @@ -0,0 +1,9 @@ +--- +source: crates/fe/tests/cli_output.rs +expression: output +--- +=== STDOUT === +➖ basic_b v0.1.0 +└── ➖ basic_d v0.1.0 + +=== EXIT CODE: 0 === diff --git a/crates/fe/tests/fixtures/cli_output/ingots/basic_b/fe.toml b/crates/fe/tests/fixtures/cli_output/ingots/basic_b/fe.toml new file mode 100644 index 0000000000..0023494847 --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/basic_b/fe.toml @@ -0,0 +1,6 @@ +[ingot] +name = "basic_b" +version = "0.1.0" + +[dependencies] +basic_d = "../basic_d" \ No newline at end of file diff --git a/crates/fe/tests/fixtures/cli_output/ingots/basic_b/src/lib.fe b/crates/fe/tests/fixtures/cli_output/ingots/basic_b/src/lib.fe new file mode 100644 index 0000000000..f2bf7e1e7f --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/basic_b/src/lib.fe @@ -0,0 +1 @@ +pub fn main() {} \ No newline at end of file diff --git a/crates/fe/tests/fixtures/cli_output/ingots/basic_c/basic_c.snap b/crates/fe/tests/fixtures/cli_output/ingots/basic_c/basic_c.snap new file mode 100644 index 0000000000..9dd54baa06 --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/basic_c/basic_c.snap @@ -0,0 +1,5 @@ +--- +source: crates/fe/tests/cli_output.rs +expression: output +--- +=== EXIT CODE: 0 === diff --git a/crates/fe/tests/fixtures/cli_output/ingots/basic_c/basic_c_tree.snap b/crates/fe/tests/fixtures/cli_output/ingots/basic_c/basic_c_tree.snap new file mode 100644 index 0000000000..bf60093c97 --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/basic_c/basic_c_tree.snap @@ -0,0 +1,9 @@ +--- +source: crates/fe/tests/cli_output.rs +expression: output +--- +=== STDOUT === +➖ basic_c v0.1.0 +└── ➖ basic_d v0.1.0 + +=== EXIT CODE: 0 === diff --git a/crates/fe/tests/fixtures/cli_output/ingots/basic_c/fe.toml b/crates/fe/tests/fixtures/cli_output/ingots/basic_c/fe.toml new file mode 100644 index 0000000000..f5c8c5f1e0 --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/basic_c/fe.toml @@ -0,0 +1,6 @@ +[ingot] +name = "basic_c" +version = "0.1.0" + +[dependencies] +basic_d = "../basic_d" \ No newline at end of file diff --git a/crates/fe/tests/fixtures/cli_output/ingots/basic_c/src/lib.fe b/crates/fe/tests/fixtures/cli_output/ingots/basic_c/src/lib.fe new file mode 100644 index 0000000000..f2bf7e1e7f --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/basic_c/src/lib.fe @@ -0,0 +1 @@ +pub fn main() {} \ No newline at end of file diff --git a/crates/fe/tests/fixtures/cli_output/ingots/basic_d/basic_d.snap b/crates/fe/tests/fixtures/cli_output/ingots/basic_d/basic_d.snap new file mode 100644 index 0000000000..9dd54baa06 --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/basic_d/basic_d.snap @@ -0,0 +1,5 @@ +--- +source: crates/fe/tests/cli_output.rs +expression: output +--- +=== EXIT CODE: 0 === diff --git a/crates/fe/tests/fixtures/cli_output/ingots/basic_d/basic_d_tree.snap b/crates/fe/tests/fixtures/cli_output/ingots/basic_d/basic_d_tree.snap new file mode 100644 index 0000000000..de34cd4bdf --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/basic_d/basic_d_tree.snap @@ -0,0 +1,8 @@ +--- +source: crates/fe/tests/cli_output.rs +expression: output +--- +=== STDOUT === +➖ basic_d v0.1.0 + +=== EXIT CODE: 0 === diff --git a/crates/fe/tests/fixtures/cli_output/ingots/basic_d/fe.toml b/crates/fe/tests/fixtures/cli_output/ingots/basic_d/fe.toml new file mode 100644 index 0000000000..a0c86ec570 --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/basic_d/fe.toml @@ -0,0 +1,3 @@ +[ingot] +name = "basic_d" +version = "0.1.0" \ No newline at end of file diff --git a/crates/fe/tests/fixtures/cli_output/ingots/basic_d/src/lib.fe b/crates/fe/tests/fixtures/cli_output/ingots/basic_d/src/lib.fe new file mode 100644 index 0000000000..f2bf7e1e7f --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/basic_d/src/lib.fe @@ -0,0 +1 @@ +pub fn main() {} \ No newline at end of file diff --git a/crates/fe/tests/fixtures/cli_output/ingots/config_and_missing_dep/config_and_missing_dep.snap b/crates/fe/tests/fixtures/cli_output/ingots/config_and_missing_dep/config_and_missing_dep.snap new file mode 100644 index 0000000000..97eb8234c4 --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/config_and_missing_dep/config_and_missing_dep.snap @@ -0,0 +1,9 @@ +--- +source: crates/fe/tests/cli_output.rs +expression: output +--- +=== STDERR === +❌ Config validation error at file:///crates/fe/tests/fixtures/cli_output/ingots/config_and_missing_dep/: Invalid ingot name "invalid name!" +❌ Failed to resolve ingot dependency 'file:///crates/fe/tests/fixtures/cli_output/ingots/config_and_missing_dep/nonexistent/': Directory does not exist: file:///crates/fe/tests/fixtures/cli_output/ingots/config_and_missing_dep/nonexistent/ + +=== EXIT CODE: 1 === diff --git a/crates/fe/tests/fixtures/cli_output/ingots/config_and_missing_dep/config_and_missing_dep_tree.snap b/crates/fe/tests/fixtures/cli_output/ingots/config_and_missing_dep/config_and_missing_dep_tree.snap new file mode 100644 index 0000000000..9efd1dd842 --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/config_and_missing_dep/config_and_missing_dep_tree.snap @@ -0,0 +1,12 @@ +--- +source: crates/fe/tests/cli_output.rs +expression: output +--- +=== STDOUT === +➖ null v0.1.0 + +=== STDERR === +❌ Config validation error at file:///crates/fe/tests/fixtures/cli_output/ingots/config_and_missing_dep/: Invalid ingot name "invalid name!" +❌ Failed to resolve ingot dependency 'file:///crates/fe/tests/fixtures/cli_output/ingots/config_and_missing_dep/nonexistent/': Directory does not exist: file:///crates/fe/tests/fixtures/cli_output/ingots/config_and_missing_dep/nonexistent/ + +=== EXIT CODE: 0 === diff --git a/crates/fe/tests/fixtures/cli_output/ingots/config_and_missing_dep/fe.toml b/crates/fe/tests/fixtures/cli_output/ingots/config_and_missing_dep/fe.toml new file mode 100644 index 0000000000..5980e07f40 --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/config_and_missing_dep/fe.toml @@ -0,0 +1,6 @@ +[ingot] +name = "invalid name!" +version = "0.1.0" + +[dependencies] +nonexistent = "./nonexistent" \ No newline at end of file diff --git a/crates/fe/tests/fixtures/cli_output/ingots/config_and_missing_dep/src/lib.fe b/crates/fe/tests/fixtures/cli_output/ingots/config_and_missing_dep/src/lib.fe new file mode 100644 index 0000000000..f2bf7e1e7f --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/config_and_missing_dep/src/lib.fe @@ -0,0 +1 @@ +pub fn main() {} \ No newline at end of file diff --git a/crates/fe/tests/fixtures/cli_output/ingots/cycle_a/cycle_a.snap b/crates/fe/tests/fixtures/cli_output/ingots/cycle_a/cycle_a.snap new file mode 100644 index 0000000000..4960e647cf --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/cycle_a/cycle_a.snap @@ -0,0 +1,20 @@ +--- +source: crates/fe/tests/cli_output.rs +expression: output +--- +=== STDERR === +❌ Detected cycle(s) in ingot dependencies: + +➖ cycle_a v0.1.0 +├── ➖ cycle_b v0.1.0 +│ ├── ➖ cycle_c v0.1.0 +│ │ └── ➖ cycle_b v0.1.0 🔄 [cycle] +│ └── ➖ cycle_e v0.1.0 +│ └── ➖ cycle_d v0.1.0 +│ └── ➖ cycle_e v0.1.0 🔄 [cycle] +└── ➖ cycle_d v0.1.0 + └── ➖ cycle_e v0.1.0 + └── ➖ cycle_d v0.1.0 🔄 [cycle] + + +=== EXIT CODE: 1 === diff --git a/crates/fe/tests/fixtures/cli_output/ingots/cycle_a/cycle_a_tree.snap b/crates/fe/tests/fixtures/cli_output/ingots/cycle_a/cycle_a_tree.snap new file mode 100644 index 0000000000..12587fd33d --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/cycle_a/cycle_a_tree.snap @@ -0,0 +1,17 @@ +--- +source: crates/fe/tests/cli_output.rs +expression: output +--- +=== STDOUT === +➖ cycle_a v0.1.0 +├── ➖ cycle_b v0.1.0 +│ ├── ➖ cycle_c v0.1.0 +│ │ └── ➖ cycle_b v0.1.0 🔄 [cycle] +│ └── ➖ cycle_e v0.1.0 +│ └── ➖ cycle_d v0.1.0 +│ └── ➖ cycle_e v0.1.0 🔄 [cycle] +└── ➖ cycle_d v0.1.0 + └── ➖ cycle_e v0.1.0 + └── ➖ cycle_d v0.1.0 🔄 [cycle] + +=== EXIT CODE: 0 === diff --git a/crates/fe/tests/fixtures/cli_output/ingots/cycle_a/fe.toml b/crates/fe/tests/fixtures/cli_output/ingots/cycle_a/fe.toml new file mode 100644 index 0000000000..78234cfbf2 --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/cycle_a/fe.toml @@ -0,0 +1,7 @@ +[ingot] +name = "cycle_a" +version = "0.1.0" + +[dependencies] +cycle_b = "../cycle_b" +cycle_d = "../cycle_d" \ No newline at end of file diff --git a/crates/fe/tests/fixtures/cli_output/ingots/cycle_a/src/lib.fe b/crates/fe/tests/fixtures/cli_output/ingots/cycle_a/src/lib.fe new file mode 100644 index 0000000000..f2bf7e1e7f --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/cycle_a/src/lib.fe @@ -0,0 +1 @@ +pub fn main() {} \ No newline at end of file diff --git a/crates/fe/tests/fixtures/cli_output/ingots/cycle_b/cycle_b.snap b/crates/fe/tests/fixtures/cli_output/ingots/cycle_b/cycle_b.snap new file mode 100644 index 0000000000..e005bd431f --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/cycle_b/cycle_b.snap @@ -0,0 +1,16 @@ +--- +source: crates/fe/tests/cli_output.rs +expression: output +--- +=== STDERR === +❌ Detected cycle(s) in ingot dependencies: + +➖ cycle_b v0.1.0 +├── ➖ cycle_c v0.1.0 +│ └── ➖ cycle_b v0.1.0 🔄 [cycle] +└── ➖ cycle_e v0.1.0 + └── ➖ cycle_d v0.1.0 + └── ➖ cycle_e v0.1.0 🔄 [cycle] + + +=== EXIT CODE: 1 === diff --git a/crates/fe/tests/fixtures/cli_output/ingots/cycle_b/cycle_b_tree.snap b/crates/fe/tests/fixtures/cli_output/ingots/cycle_b/cycle_b_tree.snap new file mode 100644 index 0000000000..61fa13f313 --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/cycle_b/cycle_b_tree.snap @@ -0,0 +1,13 @@ +--- +source: crates/fe/tests/cli_output.rs +expression: output +--- +=== STDOUT === +➖ cycle_b v0.1.0 +├── ➖ cycle_c v0.1.0 +│ └── ➖ cycle_b v0.1.0 🔄 [cycle] +└── ➖ cycle_e v0.1.0 + └── ➖ cycle_d v0.1.0 + └── ➖ cycle_e v0.1.0 🔄 [cycle] + +=== EXIT CODE: 0 === diff --git a/crates/fe/tests/fixtures/cli_output/ingots/cycle_b/fe.toml b/crates/fe/tests/fixtures/cli_output/ingots/cycle_b/fe.toml new file mode 100644 index 0000000000..1631fb15c0 --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/cycle_b/fe.toml @@ -0,0 +1,7 @@ +[ingot] +name = "cycle_b" +version = "0.1.0" + +[dependencies] +cycle_c = "../cycle_c" +cycle_e = "../cycle_e" \ No newline at end of file diff --git a/crates/fe/tests/fixtures/cli_output/ingots/cycle_b/src/lib.fe b/crates/fe/tests/fixtures/cli_output/ingots/cycle_b/src/lib.fe new file mode 100644 index 0000000000..f2bf7e1e7f --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/cycle_b/src/lib.fe @@ -0,0 +1 @@ +pub fn main() {} \ No newline at end of file diff --git a/crates/fe/tests/fixtures/cli_output/ingots/cycle_c/cycle_c.snap b/crates/fe/tests/fixtures/cli_output/ingots/cycle_c/cycle_c.snap new file mode 100644 index 0000000000..09682d5cb8 --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/cycle_c/cycle_c.snap @@ -0,0 +1,16 @@ +--- +source: crates/fe/tests/cli_output.rs +expression: output +--- +=== STDERR === +❌ Detected cycle(s) in ingot dependencies: + +➖ cycle_c v0.1.0 +└── ➖ cycle_b v0.1.0 + ├── ➖ cycle_e v0.1.0 + │ └── ➖ cycle_d v0.1.0 + │ └── ➖ cycle_e v0.1.0 🔄 [cycle] + └── ➖ cycle_c v0.1.0 🔄 [cycle] + + +=== EXIT CODE: 1 === diff --git a/crates/fe/tests/fixtures/cli_output/ingots/cycle_c/cycle_c_tree.snap b/crates/fe/tests/fixtures/cli_output/ingots/cycle_c/cycle_c_tree.snap new file mode 100644 index 0000000000..2cb9d1c181 --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/cycle_c/cycle_c_tree.snap @@ -0,0 +1,13 @@ +--- +source: crates/fe/tests/cli_output.rs +expression: output +--- +=== STDOUT === +➖ cycle_c v0.1.0 +└── ➖ cycle_b v0.1.0 + ├── ➖ cycle_e v0.1.0 + │ └── ➖ cycle_d v0.1.0 + │ └── ➖ cycle_e v0.1.0 🔄 [cycle] + └── ➖ cycle_c v0.1.0 🔄 [cycle] + +=== EXIT CODE: 0 === diff --git a/crates/fe/tests/fixtures/cli_output/ingots/cycle_c/fe.toml b/crates/fe/tests/fixtures/cli_output/ingots/cycle_c/fe.toml new file mode 100644 index 0000000000..c2eb7a284a --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/cycle_c/fe.toml @@ -0,0 +1,6 @@ +[ingot] +name = "cycle_c" +version = "0.1.0" + +[dependencies] +cycle_b = "../cycle_b" \ No newline at end of file diff --git a/crates/fe/tests/fixtures/cli_output/ingots/cycle_c/src/lib.fe b/crates/fe/tests/fixtures/cli_output/ingots/cycle_c/src/lib.fe new file mode 100644 index 0000000000..f2bf7e1e7f --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/cycle_c/src/lib.fe @@ -0,0 +1 @@ +pub fn main() {} \ No newline at end of file diff --git a/crates/fe/tests/fixtures/cli_output/ingots/cycle_d/cycle_d.snap b/crates/fe/tests/fixtures/cli_output/ingots/cycle_d/cycle_d.snap new file mode 100644 index 0000000000..5bc5b07e4c --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/cycle_d/cycle_d.snap @@ -0,0 +1,13 @@ +--- +source: crates/fe/tests/cli_output.rs +expression: output +--- +=== STDERR === +❌ Detected cycle(s) in ingot dependencies: + +➖ cycle_d v0.1.0 +└── ➖ cycle_e v0.1.0 + └── ➖ cycle_d v0.1.0 🔄 [cycle] + + +=== EXIT CODE: 1 === diff --git a/crates/fe/tests/fixtures/cli_output/ingots/cycle_d/cycle_d_tree.snap b/crates/fe/tests/fixtures/cli_output/ingots/cycle_d/cycle_d_tree.snap new file mode 100644 index 0000000000..549cd5a5a2 --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/cycle_d/cycle_d_tree.snap @@ -0,0 +1,10 @@ +--- +source: crates/fe/tests/cli_output.rs +expression: output +--- +=== STDOUT === +➖ cycle_d v0.1.0 +└── ➖ cycle_e v0.1.0 + └── ➖ cycle_d v0.1.0 🔄 [cycle] + +=== EXIT CODE: 0 === diff --git a/crates/fe/tests/fixtures/cli_output/ingots/cycle_d/fe.toml b/crates/fe/tests/fixtures/cli_output/ingots/cycle_d/fe.toml new file mode 100644 index 0000000000..f36d577a29 --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/cycle_d/fe.toml @@ -0,0 +1,6 @@ +[ingot] +name = "cycle_d" +version = "0.1.0" + +[dependencies] +cycle_e = "../cycle_e" \ No newline at end of file diff --git a/crates/fe/tests/fixtures/cli_output/ingots/cycle_d/src/lib.fe b/crates/fe/tests/fixtures/cli_output/ingots/cycle_d/src/lib.fe new file mode 100644 index 0000000000..f2bf7e1e7f --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/cycle_d/src/lib.fe @@ -0,0 +1 @@ +pub fn main() {} \ No newline at end of file diff --git a/crates/fe/tests/fixtures/cli_output/ingots/cycle_e/cycle_e.snap b/crates/fe/tests/fixtures/cli_output/ingots/cycle_e/cycle_e.snap new file mode 100644 index 0000000000..6718141718 --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/cycle_e/cycle_e.snap @@ -0,0 +1,13 @@ +--- +source: crates/fe/tests/cli_output.rs +expression: output +--- +=== STDERR === +❌ Detected cycle(s) in ingot dependencies: + +➖ cycle_e v0.1.0 +└── ➖ cycle_d v0.1.0 + └── ➖ cycle_e v0.1.0 🔄 [cycle] + + +=== EXIT CODE: 1 === diff --git a/crates/fe/tests/fixtures/cli_output/ingots/cycle_e/cycle_e_tree.snap b/crates/fe/tests/fixtures/cli_output/ingots/cycle_e/cycle_e_tree.snap new file mode 100644 index 0000000000..187d45fb59 --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/cycle_e/cycle_e_tree.snap @@ -0,0 +1,10 @@ +--- +source: crates/fe/tests/cli_output.rs +expression: output +--- +=== STDOUT === +➖ cycle_e v0.1.0 +└── ➖ cycle_d v0.1.0 + └── ➖ cycle_e v0.1.0 🔄 [cycle] + +=== EXIT CODE: 0 === diff --git a/crates/fe/tests/fixtures/cli_output/ingots/cycle_e/fe.toml b/crates/fe/tests/fixtures/cli_output/ingots/cycle_e/fe.toml new file mode 100644 index 0000000000..68dd569017 --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/cycle_e/fe.toml @@ -0,0 +1,6 @@ +[ingot] +name = "cycle_e" +version = "0.1.0" + +[dependencies] +cycle_d = "../cycle_d" \ No newline at end of file diff --git a/crates/fe/tests/fixtures/cli_output/ingots/cycle_e/src/lib.fe b/crates/fe/tests/fixtures/cli_output/ingots/cycle_e/src/lib.fe new file mode 100644 index 0000000000..f2bf7e1e7f --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/cycle_e/src/lib.fe @@ -0,0 +1 @@ +pub fn main() {} \ No newline at end of file diff --git a/crates/fe/tests/fixtures/cli_output/ingots/duplicate_toml_keys/duplicate_toml_keys.snap b/crates/fe/tests/fixtures/cli_output/ingots/duplicate_toml_keys/duplicate_toml_keys.snap new file mode 100644 index 0000000000..384ff1a0bb --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/duplicate_toml_keys/duplicate_toml_keys.snap @@ -0,0 +1,13 @@ +--- +source: crates/fe/tests/cli_output.rs +expression: output +--- +=== STDERR === +❌ Invalid fe.toml file in ingot file:///crates/fe/tests/fixtures/cli_output/ingots/duplicate_toml_keys/: TOML parse error at line 3, column 1 + | +3 | name = "duplicate" + | ^ +duplicate key `name` in table `ingot` + + +=== EXIT CODE: 1 === diff --git a/crates/fe/tests/fixtures/cli_output/ingots/duplicate_toml_keys/duplicate_toml_keys_tree.snap b/crates/fe/tests/fixtures/cli_output/ingots/duplicate_toml_keys/duplicate_toml_keys_tree.snap new file mode 100644 index 0000000000..26eba538a2 --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/duplicate_toml_keys/duplicate_toml_keys_tree.snap @@ -0,0 +1,16 @@ +--- +source: crates/fe/tests/cli_output.rs +expression: output +--- +=== STDOUT === +➖ [invalid fe.toml] + +=== STDERR === +❌ Invalid fe.toml file at file:///crates/fe/tests/fixtures/cli_output/ingots/duplicate_toml_keys/: TOML parse error at line 3, column 1 + | +3 | name = "duplicate" + | ^ +duplicate key `name` in table `ingot` + + +=== EXIT CODE: 0 === diff --git a/crates/fe/tests/fixtures/cli_output/ingots/duplicate_toml_keys/fe.toml b/crates/fe/tests/fixtures/cli_output/ingots/duplicate_toml_keys/fe.toml new file mode 100644 index 0000000000..6802973ea4 --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/duplicate_toml_keys/fe.toml @@ -0,0 +1,4 @@ +[ingot] +name = "test" +name = "duplicate" +version = "0.1.0" \ No newline at end of file diff --git a/crates/fe/tests/fixtures/cli_output/ingots/duplicate_toml_keys/src/lib.fe b/crates/fe/tests/fixtures/cli_output/ingots/duplicate_toml_keys/src/lib.fe new file mode 100644 index 0000000000..f2bf7e1e7f --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/duplicate_toml_keys/src/lib.fe @@ -0,0 +1 @@ +pub fn main() {} \ No newline at end of file diff --git a/crates/fe/tests/fixtures/cli_output/ingots/everything_broken/everything_broken.snap b/crates/fe/tests/fixtures/cli_output/ingots/everything_broken/everything_broken.snap new file mode 100644 index 0000000000..931718fd3a --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/everything_broken/everything_broken.snap @@ -0,0 +1,14 @@ +--- +source: crates/fe/tests/cli_output.rs +expression: output +--- +=== STDERR === +❌ Invalid fe.toml file in ingot file:///crates/fe/tests/fixtures/cli_output/ingots/everything_broken/: TOML parse error at line 1, column 7 + | +1 | [ingot + | ^ +invalid table header +expected `.`, `]` + + +=== EXIT CODE: 1 === diff --git a/crates/fe/tests/fixtures/cli_output/ingots/everything_broken/everything_broken_tree.snap b/crates/fe/tests/fixtures/cli_output/ingots/everything_broken/everything_broken_tree.snap new file mode 100644 index 0000000000..930a70e26b --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/everything_broken/everything_broken_tree.snap @@ -0,0 +1,17 @@ +--- +source: crates/fe/tests/cli_output.rs +expression: output +--- +=== STDOUT === +➖ [invalid fe.toml] + +=== STDERR === +❌ Invalid fe.toml file at file:///crates/fe/tests/fixtures/cli_output/ingots/everything_broken/: TOML parse error at line 1, column 7 + | +1 | [ingot + | ^ +invalid table header +expected `.`, `]` + + +=== EXIT CODE: 0 === diff --git a/crates/fe/tests/fixtures/cli_output/ingots/everything_broken/fe.toml b/crates/fe/tests/fixtures/cli_output/ingots/everything_broken/fe.toml new file mode 100644 index 0000000000..5f98c9da0f --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/everything_broken/fe.toml @@ -0,0 +1,6 @@ +[ingot +# Missing closing bracket, missing name, bad version +version = "not.a.version" + +[dependencies] +"bad alias!" = "./nonexistent" \ No newline at end of file diff --git a/crates/fe/tests/fixtures/cli_output/ingots/everything_broken/src/lib.fe b/crates/fe/tests/fixtures/cli_output/ingots/everything_broken/src/lib.fe new file mode 100644 index 0000000000..f2bf7e1e7f --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/everything_broken/src/lib.fe @@ -0,0 +1 @@ +pub fn main() {} \ No newline at end of file diff --git a/crates/fe/tests/fixtures/cli_output/ingots/invalid_dep_alias/fe.toml b/crates/fe/tests/fixtures/cli_output/ingots/invalid_dep_alias/fe.toml new file mode 100644 index 0000000000..57c886b3da --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/invalid_dep_alias/fe.toml @@ -0,0 +1,6 @@ +[ingot] +name = "test" +version = "0.1.0" + +[dependencies] +"bad-alias!" = "./nonexistent" \ No newline at end of file diff --git a/crates/fe/tests/fixtures/cli_output/ingots/invalid_dep_alias/invalid_dep_alias.snap b/crates/fe/tests/fixtures/cli_output/ingots/invalid_dep_alias/invalid_dep_alias.snap new file mode 100644 index 0000000000..6bd7959a5d --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/invalid_dep_alias/invalid_dep_alias.snap @@ -0,0 +1,9 @@ +--- +source: crates/fe/tests/cli_output.rs +expression: output +--- +=== STDERR === +❌ Config validation error at file:///crates/fe/tests/fixtures/cli_output/ingots/invalid_dep_alias/: Invalid dependency alias "bad-alias!" +❌ Failed to resolve ingot dependency 'file:///crates/fe/tests/fixtures/cli_output/ingots/invalid_dep_alias/nonexistent/': Directory does not exist: file:///crates/fe/tests/fixtures/cli_output/ingots/invalid_dep_alias/nonexistent/ + +=== EXIT CODE: 1 === diff --git a/crates/fe/tests/fixtures/cli_output/ingots/invalid_dep_alias/invalid_dep_alias_tree.snap b/crates/fe/tests/fixtures/cli_output/ingots/invalid_dep_alias/invalid_dep_alias_tree.snap new file mode 100644 index 0000000000..f0d10aac41 --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/invalid_dep_alias/invalid_dep_alias_tree.snap @@ -0,0 +1,12 @@ +--- +source: crates/fe/tests/cli_output.rs +expression: output +--- +=== STDOUT === +➖ test v0.1.0 + +=== STDERR === +❌ Config validation error at file:///crates/fe/tests/fixtures/cli_output/ingots/invalid_dep_alias/: Invalid dependency alias "bad-alias!" +❌ Failed to resolve ingot dependency 'file:///crates/fe/tests/fixtures/cli_output/ingots/invalid_dep_alias/nonexistent/': Directory does not exist: file:///crates/fe/tests/fixtures/cli_output/ingots/invalid_dep_alias/nonexistent/ + +=== EXIT CODE: 0 === diff --git a/crates/fe/tests/fixtures/cli_output/ingots/invalid_dep_alias/src/lib.fe b/crates/fe/tests/fixtures/cli_output/ingots/invalid_dep_alias/src/lib.fe new file mode 100644 index 0000000000..f2bf7e1e7f --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/invalid_dep_alias/src/lib.fe @@ -0,0 +1 @@ +pub fn main() {} \ No newline at end of file diff --git a/crates/fe/tests/fixtures/cli_output/ingots/invalid_dep_name/fe.toml b/crates/fe/tests/fixtures/cli_output/ingots/invalid_dep_name/fe.toml new file mode 100644 index 0000000000..965364010b --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/invalid_dep_name/fe.toml @@ -0,0 +1,6 @@ +[ingot] +name = "test" +version = "0.1.0" + +[dependencies] +foo = { path = "./nonexistent", name = "bad name!" } \ No newline at end of file diff --git a/crates/fe/tests/fixtures/cli_output/ingots/invalid_dep_name/invalid_dep_name.snap b/crates/fe/tests/fixtures/cli_output/ingots/invalid_dep_name/invalid_dep_name.snap new file mode 100644 index 0000000000..2f6ee941b6 --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/invalid_dep_name/invalid_dep_name.snap @@ -0,0 +1,9 @@ +--- +source: crates/fe/tests/cli_output.rs +expression: output +--- +=== STDERR === +❌ Config validation error at file:///crates/fe/tests/fixtures/cli_output/ingots/invalid_dep_name/: Invalid dependency name "bad name!" +❌ Failed to resolve ingot dependency 'file:///crates/fe/tests/fixtures/cli_output/ingots/invalid_dep_name/nonexistent/': Directory does not exist: file:///crates/fe/tests/fixtures/cli_output/ingots/invalid_dep_name/nonexistent/ + +=== EXIT CODE: 1 === diff --git a/crates/fe/tests/fixtures/cli_output/ingots/invalid_dep_name/invalid_dep_name_tree.snap b/crates/fe/tests/fixtures/cli_output/ingots/invalid_dep_name/invalid_dep_name_tree.snap new file mode 100644 index 0000000000..bb23721f76 --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/invalid_dep_name/invalid_dep_name_tree.snap @@ -0,0 +1,12 @@ +--- +source: crates/fe/tests/cli_output.rs +expression: output +--- +=== STDOUT === +➖ test v0.1.0 + +=== STDERR === +❌ Config validation error at file:///crates/fe/tests/fixtures/cli_output/ingots/invalid_dep_name/: Invalid dependency name "bad name!" +❌ Failed to resolve ingot dependency 'file:///crates/fe/tests/fixtures/cli_output/ingots/invalid_dep_name/nonexistent/': Directory does not exist: file:///crates/fe/tests/fixtures/cli_output/ingots/invalid_dep_name/nonexistent/ + +=== EXIT CODE: 0 === diff --git a/crates/fe/tests/fixtures/cli_output/ingots/invalid_dep_name/src/lib.fe b/crates/fe/tests/fixtures/cli_output/ingots/invalid_dep_name/src/lib.fe new file mode 100644 index 0000000000..f2bf7e1e7f --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/invalid_dep_name/src/lib.fe @@ -0,0 +1 @@ +pub fn main() {} \ No newline at end of file diff --git a/crates/fe/tests/fixtures/cli_output/ingots/invalid_name/fe.toml b/crates/fe/tests/fixtures/cli_output/ingots/invalid_name/fe.toml index e214e84a7d..46c49eb4fe 100644 --- a/crates/fe/tests/fixtures/cli_output/ingots/invalid_name/fe.toml +++ b/crates/fe/tests/fixtures/cli_output/ingots/invalid_name/fe.toml @@ -1,3 +1,3 @@ [ingot] -name = "invalid_name_with_underscores" +name = "invalid-name-with-dashes" version = "0.0.1" \ No newline at end of file diff --git a/crates/fe/tests/fixtures/cli_output/ingots/invalid_name/invalid_name.snap b/crates/fe/tests/fixtures/cli_output/ingots/invalid_name/invalid_name.snap index 25d4ac610f..e5913fc939 100644 --- a/crates/fe/tests/fixtures/cli_output/ingots/invalid_name/invalid_name.snap +++ b/crates/fe/tests/fixtures/cli_output/ingots/invalid_name/invalid_name.snap @@ -1,10 +1,8 @@ --- source: crates/fe/tests/cli_output.rs -assertion_line: 94 expression: output --- === STDERR === -there are issues with the local fe.toml file file:///crates/fe/tests/fixtures/cli_output/ingots/invalid_name/fe.toml - invalid ingot name "invalid_name_with_underscores" +❌ Config validation error at file:///crates/fe/tests/fixtures/cli_output/ingots/invalid_name/: Invalid ingot name "invalid-name-with-dashes" === EXIT CODE: 1 === diff --git a/crates/fe/tests/fixtures/cli_output/ingots/invalid_name/invalid_name_tree.snap b/crates/fe/tests/fixtures/cli_output/ingots/invalid_name/invalid_name_tree.snap new file mode 100644 index 0000000000..d09beb31d3 --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/invalid_name/invalid_name_tree.snap @@ -0,0 +1,11 @@ +--- +source: crates/fe/tests/cli_output.rs +expression: output +--- +=== STDOUT === +➖ null v0.0.1 + +=== STDERR === +❌ Config validation error at file:///crates/fe/tests/fixtures/cli_output/ingots/invalid_name/: Invalid ingot name "invalid-name-with-dashes" + +=== EXIT CODE: 0 === diff --git a/crates/fe/tests/fixtures/cli_output/ingots/invalid_name_spaces/fe.toml b/crates/fe/tests/fixtures/cli_output/ingots/invalid_name_spaces/fe.toml new file mode 100644 index 0000000000..c5999d0276 --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/invalid_name_spaces/fe.toml @@ -0,0 +1,3 @@ +[ingot] +name = "invalid name with spaces" +version = "0.1.0" \ No newline at end of file diff --git a/crates/fe/tests/fixtures/cli_output/ingots/invalid_name_spaces/invalid_name_spaces.snap b/crates/fe/tests/fixtures/cli_output/ingots/invalid_name_spaces/invalid_name_spaces.snap new file mode 100644 index 0000000000..5b52e10291 --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/invalid_name_spaces/invalid_name_spaces.snap @@ -0,0 +1,8 @@ +--- +source: crates/fe/tests/cli_output.rs +expression: output +--- +=== STDERR === +❌ Config validation error at file:///crates/fe/tests/fixtures/cli_output/ingots/invalid_name_spaces/: Invalid ingot name "invalid name with spaces" + +=== EXIT CODE: 1 === diff --git a/crates/fe/tests/fixtures/cli_output/ingots/invalid_name_spaces/invalid_name_spaces_tree.snap b/crates/fe/tests/fixtures/cli_output/ingots/invalid_name_spaces/invalid_name_spaces_tree.snap new file mode 100644 index 0000000000..9b93a0931e --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/invalid_name_spaces/invalid_name_spaces_tree.snap @@ -0,0 +1,11 @@ +--- +source: crates/fe/tests/cli_output.rs +expression: output +--- +=== STDOUT === +➖ null v0.1.0 + +=== STDERR === +❌ Config validation error at file:///crates/fe/tests/fixtures/cli_output/ingots/invalid_name_spaces/: Invalid ingot name "invalid name with spaces" + +=== EXIT CODE: 0 === diff --git a/crates/fe/tests/fixtures/cli_output/ingots/invalid_name_spaces/src/lib.fe b/crates/fe/tests/fixtures/cli_output/ingots/invalid_name_spaces/src/lib.fe new file mode 100644 index 0000000000..f2bf7e1e7f --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/invalid_name_spaces/src/lib.fe @@ -0,0 +1 @@ +pub fn main() {} \ No newline at end of file diff --git a/crates/fe/tests/fixtures/cli_output/ingots/invalid_name_symbols/fe.toml b/crates/fe/tests/fixtures/cli_output/ingots/invalid_name_symbols/fe.toml new file mode 100644 index 0000000000..9e73c5e50c --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/invalid_name_symbols/fe.toml @@ -0,0 +1,3 @@ +[ingot] +name = "invalid@name!" +version = "0.1.0" \ No newline at end of file diff --git a/crates/fe/tests/fixtures/cli_output/ingots/invalid_name_symbols/invalid_name_symbols.snap b/crates/fe/tests/fixtures/cli_output/ingots/invalid_name_symbols/invalid_name_symbols.snap new file mode 100644 index 0000000000..7585e4b3e0 --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/invalid_name_symbols/invalid_name_symbols.snap @@ -0,0 +1,8 @@ +--- +source: crates/fe/tests/cli_output.rs +expression: output +--- +=== STDERR === +❌ Config validation error at file:///crates/fe/tests/fixtures/cli_output/ingots/invalid_name_symbols/: Invalid ingot name "invalid@name!" + +=== EXIT CODE: 1 === diff --git a/crates/fe/tests/fixtures/cli_output/ingots/invalid_name_symbols/invalid_name_symbols_tree.snap b/crates/fe/tests/fixtures/cli_output/ingots/invalid_name_symbols/invalid_name_symbols_tree.snap new file mode 100644 index 0000000000..8001d928db --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/invalid_name_symbols/invalid_name_symbols_tree.snap @@ -0,0 +1,11 @@ +--- +source: crates/fe/tests/cli_output.rs +expression: output +--- +=== STDOUT === +➖ null v0.1.0 + +=== STDERR === +❌ Config validation error at file:///crates/fe/tests/fixtures/cli_output/ingots/invalid_name_symbols/: Invalid ingot name "invalid@name!" + +=== EXIT CODE: 0 === diff --git a/crates/fe/tests/fixtures/cli_output/ingots/invalid_name_symbols/src/lib.fe b/crates/fe/tests/fixtures/cli_output/ingots/invalid_name_symbols/src/lib.fe new file mode 100644 index 0000000000..f2bf7e1e7f --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/invalid_name_symbols/src/lib.fe @@ -0,0 +1 @@ +pub fn main() {} \ No newline at end of file diff --git a/crates/fe/tests/fixtures/cli_output/ingots/invalid_toml/invalid_toml.snap b/crates/fe/tests/fixtures/cli_output/ingots/invalid_toml/invalid_toml.snap index 2460400481..b3708cab96 100644 --- a/crates/fe/tests/fixtures/cli_output/ingots/invalid_toml/invalid_toml.snap +++ b/crates/fe/tests/fixtures/cli_output/ingots/invalid_toml/invalid_toml.snap @@ -1,11 +1,9 @@ --- source: crates/fe/tests/cli_output.rs -assertion_line: 94 expression: output --- === STDERR === -there are issues with the local fe.toml file file:///crates/fe/tests/fixtures/cli_output/ingots/invalid_toml/fe.toml - invalid TOML syntax: TOML parse error at line 2, column 20 +❌ Invalid fe.toml file in ingot file:///crates/fe/tests/fixtures/cli_output/ingots/invalid_toml/: TOML parse error at line 2, column 20 | 2 | name = "invalidtoml | ^ diff --git a/crates/fe/tests/fixtures/cli_output/ingots/invalid_toml/invalid_toml_tree.snap b/crates/fe/tests/fixtures/cli_output/ingots/invalid_toml/invalid_toml_tree.snap new file mode 100644 index 0000000000..4677ded647 --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/invalid_toml/invalid_toml_tree.snap @@ -0,0 +1,16 @@ +--- +source: crates/fe/tests/cli_output.rs +expression: output +--- +=== STDOUT === +➖ [invalid fe.toml] + +=== STDERR === +❌ Invalid fe.toml file at file:///crates/fe/tests/fixtures/cli_output/ingots/invalid_toml/: TOML parse error at line 2, column 20 + | +2 | name = "invalidtoml + | ^ +invalid basic string + + +=== EXIT CODE: 0 === diff --git a/crates/fe/tests/fixtures/cli_output/ingots/invalid_toml_structure/fe.toml b/crates/fe/tests/fixtures/cli_output/ingots/invalid_toml_structure/fe.toml new file mode 100644 index 0000000000..14716f0dc2 --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/invalid_toml_structure/fe.toml @@ -0,0 +1,3 @@ +[ingot +name = "test" +version = "0.1.0" \ No newline at end of file diff --git a/crates/fe/tests/fixtures/cli_output/ingots/invalid_toml_structure/invalid_toml_structure.snap b/crates/fe/tests/fixtures/cli_output/ingots/invalid_toml_structure/invalid_toml_structure.snap new file mode 100644 index 0000000000..3062d2eb79 --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/invalid_toml_structure/invalid_toml_structure.snap @@ -0,0 +1,14 @@ +--- +source: crates/fe/tests/cli_output.rs +expression: output +--- +=== STDERR === +❌ Invalid fe.toml file in ingot file:///crates/fe/tests/fixtures/cli_output/ingots/invalid_toml_structure/: TOML parse error at line 1, column 7 + | +1 | [ingot + | ^ +invalid table header +expected `.`, `]` + + +=== EXIT CODE: 1 === diff --git a/crates/fe/tests/fixtures/cli_output/ingots/invalid_toml_structure/invalid_toml_structure_tree.snap b/crates/fe/tests/fixtures/cli_output/ingots/invalid_toml_structure/invalid_toml_structure_tree.snap new file mode 100644 index 0000000000..8da297c783 --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/invalid_toml_structure/invalid_toml_structure_tree.snap @@ -0,0 +1,17 @@ +--- +source: crates/fe/tests/cli_output.rs +expression: output +--- +=== STDOUT === +➖ [invalid fe.toml] + +=== STDERR === +❌ Invalid fe.toml file at file:///crates/fe/tests/fixtures/cli_output/ingots/invalid_toml_structure/: TOML parse error at line 1, column 7 + | +1 | [ingot + | ^ +invalid table header +expected `.`, `]` + + +=== EXIT CODE: 0 === diff --git a/crates/fe/tests/fixtures/cli_output/ingots/invalid_toml_structure/src/lib.fe b/crates/fe/tests/fixtures/cli_output/ingots/invalid_toml_structure/src/lib.fe new file mode 100644 index 0000000000..f2bf7e1e7f --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/invalid_toml_structure/src/lib.fe @@ -0,0 +1 @@ +pub fn main() {} \ No newline at end of file diff --git a/crates/fe/tests/fixtures/cli_output/ingots/invalid_version/fe.toml b/crates/fe/tests/fixtures/cli_output/ingots/invalid_version/fe.toml new file mode 100644 index 0000000000..65ba635e7e --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/invalid_version/fe.toml @@ -0,0 +1,3 @@ +[ingot] +name = "test" +version = "not.a.version" \ No newline at end of file diff --git a/crates/fe/tests/fixtures/cli_output/ingots/invalid_version/invalid_version.snap b/crates/fe/tests/fixtures/cli_output/ingots/invalid_version/invalid_version.snap new file mode 100644 index 0000000000..22fdc4d8ad --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/invalid_version/invalid_version.snap @@ -0,0 +1,8 @@ +--- +source: crates/fe/tests/cli_output.rs +expression: output +--- +=== STDERR === +❌ Config validation error at file:///crates/fe/tests/fixtures/cli_output/ingots/invalid_version/: Invalid ingot version ""not.a.version"" + +=== EXIT CODE: 1 === diff --git a/crates/fe/tests/fixtures/cli_output/ingots/invalid_version/invalid_version_tree.snap b/crates/fe/tests/fixtures/cli_output/ingots/invalid_version/invalid_version_tree.snap new file mode 100644 index 0000000000..8e65412ff8 --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/invalid_version/invalid_version_tree.snap @@ -0,0 +1,11 @@ +--- +source: crates/fe/tests/cli_output.rs +expression: output +--- +=== STDOUT === +➖ test vnull + +=== STDERR === +❌ Config validation error at file:///crates/fe/tests/fixtures/cli_output/ingots/invalid_version/: Invalid ingot version ""not.a.version"" + +=== EXIT CODE: 0 === diff --git a/crates/fe/tests/fixtures/cli_output/ingots/invalid_version/src/lib.fe b/crates/fe/tests/fixtures/cli_output/ingots/invalid_version/src/lib.fe new file mode 100644 index 0000000000..f2bf7e1e7f --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/invalid_version/src/lib.fe @@ -0,0 +1 @@ +pub fn main() {} \ No newline at end of file diff --git a/crates/fe/tests/fixtures/cli_output/ingots/missing_dep/missing_dep.snap b/crates/fe/tests/fixtures/cli_output/ingots/missing_dep/missing_dep.snap index 4236e91305..d51dd3dae0 100644 --- a/crates/fe/tests/fixtures/cli_output/ingots/missing_dep/missing_dep.snap +++ b/crates/fe/tests/fixtures/cli_output/ingots/missing_dep/missing_dep.snap @@ -1,10 +1,8 @@ --- source: crates/fe/tests/cli_output.rs -assertion_line: 94 expression: output --- === STDERR === -there are issues with the local fe.toml file file:///crates/fe/tests/fixtures/cli_output/ingots/missing_dep/fe.toml - dependencies are not yet supported +❌ Failed to resolve ingot dependency 'file:///crates/fe/tests/fixtures/cli_output/ingots/nonexistent/': Directory does not exist: file:///crates/fe/tests/fixtures/cli_output/ingots/nonexistent/ === EXIT CODE: 1 === diff --git a/crates/fe/tests/fixtures/cli_output/ingots/missing_dep/missing_dep_tree.snap b/crates/fe/tests/fixtures/cli_output/ingots/missing_dep/missing_dep_tree.snap new file mode 100644 index 0000000000..43a773e150 --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/missing_dep/missing_dep_tree.snap @@ -0,0 +1,11 @@ +--- +source: crates/fe/tests/cli_output.rs +expression: output +--- +=== STDOUT === +➖ missingdep v0.0.1 + +=== STDERR === +❌ Failed to resolve ingot dependency 'file:///crates/fe/tests/fixtures/cli_output/ingots/nonexistent/': Directory does not exist: file:///crates/fe/tests/fixtures/cli_output/ingots/nonexistent/ + +=== EXIT CODE: 0 === diff --git a/crates/fe/tests/fixtures/cli_output/ingots/missing_dep_path/fe.toml b/crates/fe/tests/fixtures/cli_output/ingots/missing_dep_path/fe.toml new file mode 100644 index 0000000000..d80272777b --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/missing_dep_path/fe.toml @@ -0,0 +1,6 @@ +[ingot] +name = "test" +version = "0.1.0" + +[dependencies] +foo = { version = "0.1.0" } \ No newline at end of file diff --git a/crates/fe/tests/fixtures/cli_output/ingots/missing_dep_path/missing_dep_path.snap b/crates/fe/tests/fixtures/cli_output/ingots/missing_dep_path/missing_dep_path.snap new file mode 100644 index 0000000000..16b18392ed --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/missing_dep_path/missing_dep_path.snap @@ -0,0 +1,8 @@ +--- +source: crates/fe/tests/cli_output.rs +expression: output +--- +=== STDERR === +❌ Config validation error at file:///crates/fe/tests/fixtures/cli_output/ingots/missing_dep_path/: The dependency "foo" is missing a path argument "{ version = "0.1.0" }" + +=== EXIT CODE: 1 === diff --git a/crates/fe/tests/fixtures/cli_output/ingots/missing_dep_path/missing_dep_path_tree.snap b/crates/fe/tests/fixtures/cli_output/ingots/missing_dep_path/missing_dep_path_tree.snap new file mode 100644 index 0000000000..aff43d55a4 --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/missing_dep_path/missing_dep_path_tree.snap @@ -0,0 +1,11 @@ +--- +source: crates/fe/tests/cli_output.rs +expression: output +--- +=== STDOUT === +➖ test v0.1.0 + +=== STDERR === +❌ Config validation error at file:///crates/fe/tests/fixtures/cli_output/ingots/missing_dep_path/: The dependency "foo" is missing a path argument "{ version = "0.1.0" }" + +=== EXIT CODE: 0 === diff --git a/crates/fe/tests/fixtures/cli_output/ingots/missing_dep_path/src/lib.fe b/crates/fe/tests/fixtures/cli_output/ingots/missing_dep_path/src/lib.fe new file mode 100644 index 0000000000..f2bf7e1e7f --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/missing_dep_path/src/lib.fe @@ -0,0 +1 @@ +pub fn main() {} \ No newline at end of file diff --git a/crates/fe/tests/fixtures/cli_output/ingots/missing_fe_toml/src/lib.fe b/crates/fe/tests/fixtures/cli_output/ingots/missing_fe_toml/src/lib.fe new file mode 100644 index 0000000000..f2bf7e1e7f --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/missing_fe_toml/src/lib.fe @@ -0,0 +1 @@ +pub fn main() {} \ No newline at end of file diff --git a/crates/fe/tests/fixtures/cli_output/ingots/missing_lib_fe/fe.toml b/crates/fe/tests/fixtures/cli_output/ingots/missing_lib_fe/fe.toml new file mode 100644 index 0000000000..70b45edfd9 --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/missing_lib_fe/fe.toml @@ -0,0 +1,3 @@ +[ingot] +name = "test" +version = "0.1.0" \ No newline at end of file diff --git a/crates/fe/tests/fixtures/cli_output/ingots/missing_lib_fe/missing_lib_fe.snap b/crates/fe/tests/fixtures/cli_output/ingots/missing_lib_fe/missing_lib_fe.snap new file mode 100644 index 0000000000..27555bda6b --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/missing_lib_fe/missing_lib_fe.snap @@ -0,0 +1,8 @@ +--- +source: crates/fe/tests/cli_output.rs +expression: output +--- +=== STDERR === +❌ File resolution error: Missing required file 'src/lib.fe' in ingot at file:///crates/fe/tests/fixtures/cli_output/ingots/missing_lib_fe/ + +=== EXIT CODE: 1 === diff --git a/crates/fe/tests/fixtures/cli_output/ingots/missing_lib_fe/missing_lib_fe_tree.snap b/crates/fe/tests/fixtures/cli_output/ingots/missing_lib_fe/missing_lib_fe_tree.snap new file mode 100644 index 0000000000..26748c4397 --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/missing_lib_fe/missing_lib_fe_tree.snap @@ -0,0 +1,8 @@ +--- +source: crates/fe/tests/cli_output.rs +expression: output +--- +=== STDOUT === +➖ test v0.1.0 + +=== EXIT CODE: 0 === diff --git a/crates/fe/tests/fixtures/cli_output/ingots/missing_lib_fe/src/.gitignore b/crates/fe/tests/fixtures/cli_output/ingots/missing_lib_fe/src/.gitignore new file mode 100644 index 0000000000..d6b7ef32c8 --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/missing_lib_fe/src/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/crates/fe/tests/fixtures/cli_output/ingots/missing_metadata/fe.toml b/crates/fe/tests/fixtures/cli_output/ingots/missing_metadata/fe.toml new file mode 100644 index 0000000000..412ceabe1b --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/missing_metadata/fe.toml @@ -0,0 +1,2 @@ +[dependencies] +nonexistent = "./nonexistent" \ No newline at end of file diff --git a/crates/fe/tests/fixtures/cli_output/ingots/missing_metadata/missing_metadata.snap b/crates/fe/tests/fixtures/cli_output/ingots/missing_metadata/missing_metadata.snap new file mode 100644 index 0000000000..5a437c20ca --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/missing_metadata/missing_metadata.snap @@ -0,0 +1,9 @@ +--- +source: crates/fe/tests/cli_output.rs +expression: output +--- +=== STDERR === +❌ Config validation error at file:///crates/fe/tests/fixtures/cli_output/ingots/missing_metadata/: Missing ingot metadata +❌ Failed to resolve ingot dependency 'file:///crates/fe/tests/fixtures/cli_output/ingots/missing_metadata/nonexistent/': Directory does not exist: file:///crates/fe/tests/fixtures/cli_output/ingots/missing_metadata/nonexistent/ + +=== EXIT CODE: 1 === diff --git a/crates/fe/tests/fixtures/cli_output/ingots/missing_metadata/missing_metadata_tree.snap b/crates/fe/tests/fixtures/cli_output/ingots/missing_metadata/missing_metadata_tree.snap new file mode 100644 index 0000000000..e89ebf68b9 --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/missing_metadata/missing_metadata_tree.snap @@ -0,0 +1,12 @@ +--- +source: crates/fe/tests/cli_output.rs +expression: output +--- +=== STDOUT === +➖ null vnull + +=== STDERR === +❌ Config validation error at file:///crates/fe/tests/fixtures/cli_output/ingots/missing_metadata/: Missing ingot metadata +❌ Failed to resolve ingot dependency 'file:///crates/fe/tests/fixtures/cli_output/ingots/missing_metadata/nonexistent/': Directory does not exist: file:///crates/fe/tests/fixtures/cli_output/ingots/missing_metadata/nonexistent/ + +=== EXIT CODE: 0 === diff --git a/crates/fe/tests/fixtures/cli_output/ingots/missing_metadata/src/lib.fe b/crates/fe/tests/fixtures/cli_output/ingots/missing_metadata/src/lib.fe new file mode 100644 index 0000000000..f2bf7e1e7f --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/missing_metadata/src/lib.fe @@ -0,0 +1 @@ +pub fn main() {} \ No newline at end of file diff --git a/crates/fe/tests/fixtures/cli_output/ingots/missing_name/fe.toml b/crates/fe/tests/fixtures/cli_output/ingots/missing_name/fe.toml new file mode 100644 index 0000000000..107dba6ecf --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/missing_name/fe.toml @@ -0,0 +1,5 @@ +[ingot] +version = "0.1.0" + +[dependencies] +nonexistent = "./nonexistent" \ No newline at end of file diff --git a/crates/fe/tests/fixtures/cli_output/ingots/missing_name/missing_name.snap b/crates/fe/tests/fixtures/cli_output/ingots/missing_name/missing_name.snap new file mode 100644 index 0000000000..919d720e7c --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/missing_name/missing_name.snap @@ -0,0 +1,9 @@ +--- +source: crates/fe/tests/cli_output.rs +expression: output +--- +=== STDERR === +❌ Config validation error at file:///crates/fe/tests/fixtures/cli_output/ingots/missing_name/: Missing ingot name +❌ Failed to resolve ingot dependency 'file:///crates/fe/tests/fixtures/cli_output/ingots/missing_name/nonexistent/': Directory does not exist: file:///crates/fe/tests/fixtures/cli_output/ingots/missing_name/nonexistent/ + +=== EXIT CODE: 1 === diff --git a/crates/fe/tests/fixtures/cli_output/ingots/missing_name/missing_name_tree.snap b/crates/fe/tests/fixtures/cli_output/ingots/missing_name/missing_name_tree.snap new file mode 100644 index 0000000000..24efa06407 --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/missing_name/missing_name_tree.snap @@ -0,0 +1,12 @@ +--- +source: crates/fe/tests/cli_output.rs +expression: output +--- +=== STDOUT === +➖ null v0.1.0 + +=== STDERR === +❌ Config validation error at file:///crates/fe/tests/fixtures/cli_output/ingots/missing_name/: Missing ingot name +❌ Failed to resolve ingot dependency 'file:///crates/fe/tests/fixtures/cli_output/ingots/missing_name/nonexistent/': Directory does not exist: file:///crates/fe/tests/fixtures/cli_output/ingots/missing_name/nonexistent/ + +=== EXIT CODE: 0 === diff --git a/crates/fe/tests/fixtures/cli_output/ingots/missing_name/src/lib.fe b/crates/fe/tests/fixtures/cli_output/ingots/missing_name/src/lib.fe new file mode 100644 index 0000000000..f2bf7e1e7f --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/missing_name/src/lib.fe @@ -0,0 +1 @@ +pub fn main() {} \ No newline at end of file diff --git a/crates/fe/tests/fixtures/cli_output/ingots/missing_src/missing_src.snap b/crates/fe/tests/fixtures/cli_output/ingots/missing_src/missing_src.snap index 13c7ee109b..75f1ee5ee8 100644 --- a/crates/fe/tests/fixtures/cli_output/ingots/missing_src/missing_src.snap +++ b/crates/fe/tests/fixtures/cli_output/ingots/missing_src/missing_src.snap @@ -1,9 +1,9 @@ --- source: crates/fe/tests/cli_output.rs -assertion_line: 78 expression: output --- === STDERR === -source files resolution error: `src` folder does not exist in the ingot directory +❌ File resolution error: Missing required directory 'src' in ingot at file:///crates/fe/tests/fixtures/cli_output/ingots/missing_src/ +❌ File resolution error: Missing required file 'src/lib.fe' in ingot at file:///crates/fe/tests/fixtures/cli_output/ingots/missing_src/ === EXIT CODE: 1 === diff --git a/crates/fe/tests/fixtures/cli_output/ingots/missing_src/missing_src_tree.snap b/crates/fe/tests/fixtures/cli_output/ingots/missing_src/missing_src_tree.snap new file mode 100644 index 0000000000..827d6871a0 --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/missing_src/missing_src_tree.snap @@ -0,0 +1,8 @@ +--- +source: crates/fe/tests/cli_output.rs +expression: output +--- +=== STDOUT === +➖ missingsrc v0.0.1 + +=== EXIT CODE: 0 === diff --git a/crates/fe/tests/fixtures/cli_output/ingots/missing_src_directory/fe.toml b/crates/fe/tests/fixtures/cli_output/ingots/missing_src_directory/fe.toml new file mode 100644 index 0000000000..70b45edfd9 --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/missing_src_directory/fe.toml @@ -0,0 +1,3 @@ +[ingot] +name = "test" +version = "0.1.0" \ No newline at end of file diff --git a/crates/fe/tests/fixtures/cli_output/ingots/missing_src_directory/missing_src_directory.snap b/crates/fe/tests/fixtures/cli_output/ingots/missing_src_directory/missing_src_directory.snap new file mode 100644 index 0000000000..baec18a5ad --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/missing_src_directory/missing_src_directory.snap @@ -0,0 +1,9 @@ +--- +source: crates/fe/tests/cli_output.rs +expression: output +--- +=== STDERR === +❌ File resolution error: Missing required directory 'src' in ingot at file:///crates/fe/tests/fixtures/cli_output/ingots/missing_src_directory/ +❌ File resolution error: Missing required file 'src/lib.fe' in ingot at file:///crates/fe/tests/fixtures/cli_output/ingots/missing_src_directory/ + +=== EXIT CODE: 1 === diff --git a/crates/fe/tests/fixtures/cli_output/ingots/missing_src_directory/missing_src_directory_tree.snap b/crates/fe/tests/fixtures/cli_output/ingots/missing_src_directory/missing_src_directory_tree.snap new file mode 100644 index 0000000000..26748c4397 --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/missing_src_directory/missing_src_directory_tree.snap @@ -0,0 +1,8 @@ +--- +source: crates/fe/tests/cli_output.rs +expression: output +--- +=== STDOUT === +➖ test v0.1.0 + +=== EXIT CODE: 0 === diff --git a/crates/fe/tests/fixtures/cli_output/ingots/missing_version/fe.toml b/crates/fe/tests/fixtures/cli_output/ingots/missing_version/fe.toml new file mode 100644 index 0000000000..ebadf6aed8 --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/missing_version/fe.toml @@ -0,0 +1,5 @@ +[ingot] +name = "test" + +[dependencies] +nonexistent = "./nonexistent" \ No newline at end of file diff --git a/crates/fe/tests/fixtures/cli_output/ingots/missing_version/missing_version.snap b/crates/fe/tests/fixtures/cli_output/ingots/missing_version/missing_version.snap new file mode 100644 index 0000000000..dbcdb265c7 --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/missing_version/missing_version.snap @@ -0,0 +1,9 @@ +--- +source: crates/fe/tests/cli_output.rs +expression: output +--- +=== STDERR === +❌ Config validation error at file:///crates/fe/tests/fixtures/cli_output/ingots/missing_version/: Missing ingot version +❌ Failed to resolve ingot dependency 'file:///crates/fe/tests/fixtures/cli_output/ingots/missing_version/nonexistent/': Directory does not exist: file:///crates/fe/tests/fixtures/cli_output/ingots/missing_version/nonexistent/ + +=== EXIT CODE: 1 === diff --git a/crates/fe/tests/fixtures/cli_output/ingots/missing_version/missing_version_tree.snap b/crates/fe/tests/fixtures/cli_output/ingots/missing_version/missing_version_tree.snap new file mode 100644 index 0000000000..7753e9d658 --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/missing_version/missing_version_tree.snap @@ -0,0 +1,12 @@ +--- +source: crates/fe/tests/cli_output.rs +expression: output +--- +=== STDOUT === +➖ test vnull + +=== STDERR === +❌ Config validation error at file:///crates/fe/tests/fixtures/cli_output/ingots/missing_version/: Missing ingot version +❌ Failed to resolve ingot dependency 'file:///crates/fe/tests/fixtures/cli_output/ingots/missing_version/nonexistent/': Directory does not exist: file:///crates/fe/tests/fixtures/cli_output/ingots/missing_version/nonexistent/ + +=== EXIT CODE: 0 === diff --git a/crates/fe/tests/fixtures/cli_output/ingots/missing_version/src/lib.fe b/crates/fe/tests/fixtures/cli_output/ingots/missing_version/src/lib.fe new file mode 100644 index 0000000000..f2bf7e1e7f --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/missing_version/src/lib.fe @@ -0,0 +1 @@ +pub fn main() {} \ No newline at end of file diff --git a/crates/fe/tests/fixtures/cli_output/ingots/multiple_config_errors/fe.toml b/crates/fe/tests/fixtures/cli_output/ingots/multiple_config_errors/fe.toml new file mode 100644 index 0000000000..4005073005 --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/multiple_config_errors/fe.toml @@ -0,0 +1,6 @@ +[ingot] +# Missing name +version = "not.a.version" + +[dependencies] +"bad alias!" = "./nonexistent" \ No newline at end of file diff --git a/crates/fe/tests/fixtures/cli_output/ingots/multiple_config_errors/multiple_config_errors.snap b/crates/fe/tests/fixtures/cli_output/ingots/multiple_config_errors/multiple_config_errors.snap new file mode 100644 index 0000000000..e0c897791c --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/multiple_config_errors/multiple_config_errors.snap @@ -0,0 +1,11 @@ +--- +source: crates/fe/tests/cli_output.rs +expression: output +--- +=== STDERR === +❌ Config validation error at file:///crates/fe/tests/fixtures/cli_output/ingots/multiple_config_errors/: Missing ingot name +❌ Config validation error at file:///crates/fe/tests/fixtures/cli_output/ingots/multiple_config_errors/: Invalid ingot version ""not.a.version"" +❌ Config validation error at file:///crates/fe/tests/fixtures/cli_output/ingots/multiple_config_errors/: Invalid dependency alias "bad alias!" +❌ Failed to resolve ingot dependency 'file:///crates/fe/tests/fixtures/cli_output/ingots/multiple_config_errors/nonexistent/': Directory does not exist: file:///crates/fe/tests/fixtures/cli_output/ingots/multiple_config_errors/nonexistent/ + +=== EXIT CODE: 1 === diff --git a/crates/fe/tests/fixtures/cli_output/ingots/multiple_config_errors/multiple_config_errors_tree.snap b/crates/fe/tests/fixtures/cli_output/ingots/multiple_config_errors/multiple_config_errors_tree.snap new file mode 100644 index 0000000000..cd68f220da --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/multiple_config_errors/multiple_config_errors_tree.snap @@ -0,0 +1,14 @@ +--- +source: crates/fe/tests/cli_output.rs +expression: output +--- +=== STDOUT === +➖ null vnull + +=== STDERR === +❌ Config validation error at file:///crates/fe/tests/fixtures/cli_output/ingots/multiple_config_errors/: Missing ingot name +❌ Config validation error at file:///crates/fe/tests/fixtures/cli_output/ingots/multiple_config_errors/: Invalid ingot version ""not.a.version"" +❌ Config validation error at file:///crates/fe/tests/fixtures/cli_output/ingots/multiple_config_errors/: Invalid dependency alias "bad alias!" +❌ Failed to resolve ingot dependency 'file:///crates/fe/tests/fixtures/cli_output/ingots/multiple_config_errors/nonexistent/': Directory does not exist: file:///crates/fe/tests/fixtures/cli_output/ingots/multiple_config_errors/nonexistent/ + +=== EXIT CODE: 0 === diff --git a/crates/fe/tests/fixtures/cli_output/ingots/multiple_config_errors/src/lib.fe b/crates/fe/tests/fixtures/cli_output/ingots/multiple_config_errors/src/lib.fe new file mode 100644 index 0000000000..f2bf7e1e7f --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/multiple_config_errors/src/lib.fe @@ -0,0 +1 @@ +pub fn main() {} \ No newline at end of file diff --git a/crates/fe/tests/fixtures/cli_output/ingots/nonexistent_dependency/fe.toml b/crates/fe/tests/fixtures/cli_output/ingots/nonexistent_dependency/fe.toml new file mode 100644 index 0000000000..e323f2e4fc --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/nonexistent_dependency/fe.toml @@ -0,0 +1,6 @@ +[ingot] +name = "test" +version = "0.1.0" + +[dependencies] +foo = "./nonexistent" \ No newline at end of file diff --git a/crates/fe/tests/fixtures/cli_output/ingots/nonexistent_dependency/nonexistent_dependency.snap b/crates/fe/tests/fixtures/cli_output/ingots/nonexistent_dependency/nonexistent_dependency.snap new file mode 100644 index 0000000000..2f9d77e3d0 --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/nonexistent_dependency/nonexistent_dependency.snap @@ -0,0 +1,8 @@ +--- +source: crates/fe/tests/cli_output.rs +expression: output +--- +=== STDERR === +❌ Failed to resolve ingot dependency 'file:///crates/fe/tests/fixtures/cli_output/ingots/nonexistent_dependency/nonexistent/': Directory does not exist: file:///crates/fe/tests/fixtures/cli_output/ingots/nonexistent_dependency/nonexistent/ + +=== EXIT CODE: 1 === diff --git a/crates/fe/tests/fixtures/cli_output/ingots/nonexistent_dependency/nonexistent_dependency_tree.snap b/crates/fe/tests/fixtures/cli_output/ingots/nonexistent_dependency/nonexistent_dependency_tree.snap new file mode 100644 index 0000000000..d181b66349 --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/nonexistent_dependency/nonexistent_dependency_tree.snap @@ -0,0 +1,11 @@ +--- +source: crates/fe/tests/cli_output.rs +expression: output +--- +=== STDOUT === +➖ test v0.1.0 + +=== STDERR === +❌ Failed to resolve ingot dependency 'file:///crates/fe/tests/fixtures/cli_output/ingots/nonexistent_dependency/nonexistent/': Directory does not exist: file:///crates/fe/tests/fixtures/cli_output/ingots/nonexistent_dependency/nonexistent/ + +=== EXIT CODE: 0 === diff --git a/crates/fe/tests/fixtures/cli_output/ingots/nonexistent_dependency/src/lib.fe b/crates/fe/tests/fixtures/cli_output/ingots/nonexistent_dependency/src/lib.fe new file mode 100644 index 0000000000..f2bf7e1e7f --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/nonexistent_dependency/src/lib.fe @@ -0,0 +1 @@ +pub fn main() {} \ No newline at end of file diff --git a/crates/fe/tests/fixtures/cli_output/ingots/unclosed_string/fe.toml b/crates/fe/tests/fixtures/cli_output/ingots/unclosed_string/fe.toml new file mode 100644 index 0000000000..34f73b3dee --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/unclosed_string/fe.toml @@ -0,0 +1,3 @@ +[ingot] +name = "unclosed +version = "0.1.0" \ No newline at end of file diff --git a/crates/fe/tests/fixtures/cli_output/ingots/unclosed_string/src/lib.fe b/crates/fe/tests/fixtures/cli_output/ingots/unclosed_string/src/lib.fe new file mode 100644 index 0000000000..f2bf7e1e7f --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/unclosed_string/src/lib.fe @@ -0,0 +1 @@ +pub fn main() {} \ No newline at end of file diff --git a/crates/fe/tests/fixtures/cli_output/ingots/unclosed_string/unclosed_string.snap b/crates/fe/tests/fixtures/cli_output/ingots/unclosed_string/unclosed_string.snap new file mode 100644 index 0000000000..6d5b883e0e --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/unclosed_string/unclosed_string.snap @@ -0,0 +1,13 @@ +--- +source: crates/fe/tests/cli_output.rs +expression: output +--- +=== STDERR === +❌ Invalid fe.toml file in ingot file:///crates/fe/tests/fixtures/cli_output/ingots/unclosed_string/: TOML parse error at line 2, column 17 + | +2 | name = "unclosed + | ^ +invalid basic string + + +=== EXIT CODE: 1 === diff --git a/crates/fe/tests/fixtures/cli_output/ingots/unclosed_string/unclosed_string_tree.snap b/crates/fe/tests/fixtures/cli_output/ingots/unclosed_string/unclosed_string_tree.snap new file mode 100644 index 0000000000..cd1ef9ae02 --- /dev/null +++ b/crates/fe/tests/fixtures/cli_output/ingots/unclosed_string/unclosed_string_tree.snap @@ -0,0 +1,16 @@ +--- +source: crates/fe/tests/cli_output.rs +expression: output +--- +=== STDOUT === +➖ [invalid fe.toml] + +=== STDERR === +❌ Invalid fe.toml file at file:///crates/fe/tests/fixtures/cli_output/ingots/unclosed_string/: TOML parse error at line 2, column 17 + | +2 | name = "unclosed + | ^ +invalid basic string + + +=== EXIT CODE: 0 === diff --git a/crates/hir-analysis/Cargo.toml b/crates/hir-analysis/Cargo.toml index 33c3693cc4..3710e8704f 100644 --- a/crates/hir-analysis/Cargo.toml +++ b/crates/hir-analysis/Cargo.toml @@ -23,7 +23,6 @@ rustc-hash.workspace = true salsa.workspace = true smallvec.workspace = true smallvec1.workspace = true -smol_str = "0.2" thin-vec.workspace = true tracing.workspace = true diff --git a/crates/hir/Cargo.toml b/crates/hir/Cargo.toml index 4ec8bf32ed..437e4836c9 100644 --- a/crates/hir/Cargo.toml +++ b/crates/hir/Cargo.toml @@ -19,10 +19,8 @@ num-traits = "0.2.19" paste.workspace = true rustc-hash.workspace = true salsa.workspace = true -smallvec.workspace = true thin-vec.workspace = true common.workspace = true parser.workspace = true url.workspace = true -tracing.workspace = true diff --git a/crates/language-server/Cargo.toml b/crates/language-server/Cargo.toml index b10c95e9da..2981ea0348 100644 --- a/crates/language-server/Cargo.toml +++ b/crates/language-server/Cargo.toml @@ -20,13 +20,10 @@ codespan-reporting = "0.11" futures = "0.3.31" futures-batch = "0.6.1" glob.workspace = true -patricia_tree = "0.9.0" rustc-hash.workspace = true salsa.workspace = true -serde = "1.0.217" serde_json = "1.0.135" tokio = { version = "1.43.0", features = ["full", "io-std", "tracing", "net"] } -tokio-macros = "2.5.0" tower = "0.5.2" tracing.workspace = true tracing-subscriber.workspace = true @@ -38,7 +35,6 @@ driver.workspace = true hir.workspace = true hir-analysis.workspace = true parser.workspace = true -resolver.workspace = true tempfile = "3.20.0" [dev-dependencies] diff --git a/crates/language-server/src/functionality/handlers.rs b/crates/language-server/src/functionality/handlers.rs index 5112279ea0..da3d6adec1 100644 --- a/crates/language-server/src/functionality/handlers.rs +++ b/crates/language-server/src/functionality/handlers.rs @@ -9,10 +9,7 @@ use async_lsp::{ }; use common::InputDb; -use resolver::{ - ingot::{source_files::SourceFiles, Ingot as ResolvedIngot, IngotResolver}, - Resolver, -}; +use driver::init_ingot; use rustc_hash::FxHashSet; use url::Url; @@ -65,9 +62,7 @@ async fn discover_and_load_ingots( .filter_map(Result::ok) .collect::>(); - let mut ingot_resolver = IngotResolver::default(); - - // Resolve each ingot + // Initialize each ingot using the driver's init_ingot function for config_path in &config_paths { let ingot_dir = config_path.parent().unwrap(); let ingot_url = Url::from_directory_path(ingot_dir).map_err(|_| { @@ -77,33 +72,14 @@ async fn discover_and_load_ingots( ) })?; - match ingot_resolver.resolve(&ingot_url) { - Ok(ResolvedIngot::Folder { - config, - source_files: Some(SourceFiles { files, .. }), - }) => { - // Touch the config file if it exists - if let Some(config) = config { - backend - .db - .workspace() - .touch(&mut backend.db, config.url, Some(config.content)); - } + let diagnostics = init_ingot(&mut backend.db, &ingot_url); - // Touch all source files - for (file_url, content) in files { - backend - .db - .workspace() - .touch(&mut backend.db, file_url, Some(content)); - } - } - Ok(_) => { - warn!("No source files found in ingot: {:?}", ingot_dir); - } - Err(e) => { - error!("Failed to resolve ingot at {:?}: {:?}", ingot_dir, e); - } + // Log any diagnostics + for diagnostic in diagnostics { + warn!( + "Ingot initialization diagnostic for {:?}: {}", + ingot_dir, diagnostic + ); } } @@ -116,36 +92,14 @@ async fn discover_and_load_ingots( ) })?; - match ingot_resolver.resolve(&root_url) { - Ok(ResolvedIngot::Folder { - config, - source_files: Some(SourceFiles { files, .. }), - }) => { - if let Some(config) = config { - backend - .db - .workspace() - .touch(&mut backend.db, config.url, Some(config.content)); - } - for (file_url, content) in files { - backend - .db - .workspace() - .touch(&mut backend.db, file_url, Some(content)); - } - } - Ok(ResolvedIngot::SingleFile { url, content }) => { - backend - .db - .workspace() - .touch(&mut backend.db, url, Some(content)); - } - Ok(_) => { - info!("No Fe source files found in workspace root"); - } - Err(e) => { - warn!("Workspace root is not a valid ingot: {:?}", e); - } + let diagnostics = init_ingot(&mut backend.db, &root_url); + + // Log any diagnostics + for diagnostic in diagnostics { + warn!( + "Ingot initialization diagnostic for workspace root: {}", + diagnostic + ); } } @@ -371,7 +325,6 @@ async fn load_ingot_files( ) -> Result<(), ResponseError> { info!("Loading ingot files from: {:?}", ingot_dir); - let mut ingot_resolver = IngotResolver::default(); let ingot_url = Url::from_directory_path(ingot_dir).map_err(|_| { ResponseError::new( ErrorCode::INTERNAL_ERROR, @@ -379,26 +332,27 @@ async fn load_ingot_files( ) })?; - match ingot_resolver.resolve(&ingot_url) { - Ok(ResolvedIngot::Folder { - config: _, // Already loaded by the file change handler - source_files: Some(SourceFiles { files, .. }), - }) => { - // Touch all source files - for (file_url, content) in files { - backend - .db - .workspace() - .touch(&mut backend.db, file_url.clone(), Some(content)); - let _ = backend.client.emit(NeedsDiagnostics(file_url)); - } - } - Ok(_) => { - warn!("No source files found in ingot: {:?}", ingot_dir); - } - Err(e) => { - error!("Failed to resolve ingot at {:?}: {:?}", ingot_dir, e); - } + let diagnostics = init_ingot(&mut backend.db, &ingot_url); + + // Log any diagnostics + for diagnostic in diagnostics { + warn!( + "Ingot initialization diagnostic for {:?}: {}", + ingot_dir, diagnostic + ); + } + + // Emit diagnostics for all files that were loaded + let all_files: Vec<_> = backend + .db + .workspace() + .all_files(&backend.db) + .iter() + .map(|(url, _file)| url) + .collect(); + + for url in all_files { + let _ = backend.client.emit(NeedsDiagnostics(url)); } Ok(()) diff --git a/crates/language-server/src/test_utils.rs b/crates/language-server/src/test_utils.rs index 545bc15511..1a82455013 100644 --- a/crates/language-server/src/test_utils.rs +++ b/crates/language-server/src/test_utils.rs @@ -1,10 +1,5 @@ -use common::InputDb; #[cfg(test)] -use driver::DriverDataBase; -use resolver::{ - ingot::{source_files::SourceFiles, Ingot as ResolvedIngot, IngotResolver}, - Resolver, -}; +use driver::{init_ingot, DriverDataBase}; use std::path::Path; use url::Url; @@ -12,25 +7,22 @@ use url::Url; /// This is similar to what happens during initialization or when a new fe.toml is created #[cfg(test)] pub fn load_ingot_from_directory(db: &mut DriverDataBase, ingot_dir: &Path) { - let mut ingot_resolver = IngotResolver::default(); let ingot_url = Url::from_directory_path(ingot_dir).expect("Failed to create URL from directory path"); - match ingot_resolver.resolve(&ingot_url) { - Ok(ResolvedIngot::Folder { - config, - source_files: Some(SourceFiles { files, .. }), - }) => { - // Touch the config file if it exists - if let Some(config) = config { - db.workspace().touch(db, config.url, Some(config.content)); - } + let diagnostics = init_ingot(db, &ingot_url); - // Touch all source files - for (file_url, content) in files { - db.workspace().touch(db, file_url, Some(content)); + // In tests, we might want to panic on serious errors + for diagnostic in &diagnostics { + match diagnostic { + driver::IngotInitDiagnostics::MissingFeToml { .. } + | driver::IngotInitDiagnostics::InvalidToml { .. } => { + panic!("Failed to resolve test ingot at {ingot_dir:?}: {diagnostic}"); + } + _ => { + // Log other diagnostics but don't panic + eprintln!("Test ingot diagnostic for {ingot_dir:?}: {diagnostic}"); } } - _ => panic!("Failed to resolve test ingot at {ingot_dir:?}"), } } diff --git a/crates/parser/Cargo.toml b/crates/parser/Cargo.toml index f872c73db9..e6a66f19d3 100644 --- a/crates/parser/Cargo.toml +++ b/crates/parser/Cargo.toml @@ -16,7 +16,6 @@ derive_more.workspace = true lazy_static = "1.5.0" logos = "0.15" rowan = "0.16.1" -rustc-hash.workspace = true smallvec.workspace = true unwrap-infallible = "0.1.5" tracing.workspace = true diff --git a/crates/resolver/Cargo.toml b/crates/resolver/Cargo.toml index b3ac2e9e7c..516527978d 100644 --- a/crates/resolver/Cargo.toml +++ b/crates/resolver/Cargo.toml @@ -11,13 +11,13 @@ description = "Resolver lib for Fe." doctest = false [dependencies] -common.workspace = true - camino.workspace = true +url.workspace = true glob.workspace = true -serde-semver.workspace = true -toml = "0.8" -serde = { version = "1", features = ["derive"] } -smol_str.workspace = true +petgraph.workspace = true tracing.workspace = true -url.workspace = true + +indexmap = "2.9" +dir-test.workspace = true +test-utils.workspace = true +toml = "0.8" diff --git a/crates/resolver/fixtures/graph/dag.snap b/crates/resolver/fixtures/graph/dag.snap new file mode 100644 index 0000000000..7a205d48b5 --- /dev/null +++ b/crates/resolver/fixtures/graph/dag.snap @@ -0,0 +1,20 @@ +--- +source: crates/resolver/tests/graph.rs +assertion_line: 94 +expression: "format!(\"{:#?}\\n{:#?}\", graph, resolver.take_diagnostics())" +input_file: fixtures/graph/dag.toml +--- +Graph { + Ty: "Directed", + node_count: 5, + edge_count: 7, + edges: (0, 1), (1, 2), (0, 3), (3, 1), (0, 4), (4, 3), (4, 1), + node weights: { + 0: "a", + 1: "d", + 2: "e", + 3: "c", + 4: "b", + }, +} +[] diff --git a/crates/resolver/fixtures/graph/dag.toml b/crates/resolver/fixtures/graph/dag.toml new file mode 100644 index 0000000000..66f8d8183e --- /dev/null +++ b/crates/resolver/fixtures/graph/dag.toml @@ -0,0 +1,5 @@ +"a" = ["b","c","d"] +"b" = ["c","d"] +"c" = ["d"] +"d" = ["e"] +"e" = [] diff --git a/crates/resolver/fixtures/graph/dcg.snap b/crates/resolver/fixtures/graph/dcg.snap new file mode 100644 index 0000000000..0ecfafdb11 --- /dev/null +++ b/crates/resolver/fixtures/graph/dcg.snap @@ -0,0 +1,20 @@ +--- +source: crates/resolver/tests/graph.rs +assertion_line: 94 +expression: "format!(\"{:#?}\\n{:#?}\", graph, resolver.take_diagnostics())" +input_file: fixtures/graph/dcg.toml +--- +Graph { + Ty: "Directed", + node_count: 5, + edge_count: 9, + edges: (0, 1), (1, 2), (0, 3), (3, 0), (3, 1), (0, 4), (2, 4), (4, 3), (4, 1), + node weights: { + 0: "a", + 1: "d", + 2: "e", + 3: "c", + 4: "b", + }, +} +[] diff --git a/crates/resolver/fixtures/graph/dcg.toml b/crates/resolver/fixtures/graph/dcg.toml new file mode 100644 index 0000000000..e9ce572ac9 --- /dev/null +++ b/crates/resolver/fixtures/graph/dcg.toml @@ -0,0 +1,5 @@ +"a" = ["b","c","d"] +"b" = ["c","d"] +"c" = ["a", "d"] +"d" = ["e"] +"e" = ["b"] diff --git a/crates/resolver/fixtures/graph/unresolvable_nodes.snap b/crates/resolver/fixtures/graph/unresolvable_nodes.snap new file mode 100644 index 0000000000..2b0f5b9665 --- /dev/null +++ b/crates/resolver/fixtures/graph/unresolvable_nodes.snap @@ -0,0 +1,26 @@ +--- +source: crates/resolver/tests/graph.rs +assertion_line: 94 +expression: "format!(\"{:#?}\\n{:#?}\", graph, resolver.take_diagnostics())" +input_file: fixtures/graph/unresolvable_nodes.toml +--- +Graph { + Ty: "Directed", + node_count: 2, + edge_count: 1, + edges: (0, 1), + node weights: { + 0: "a", + 1: "b", + }, +} +[ + UnresolvableNode( + "c", + EntryDoesNotExist, + ), + UnresolvableNode( + "d", + EntryDoesNotExist, + ), +] diff --git a/crates/resolver/fixtures/graph/unresolvable_nodes.toml b/crates/resolver/fixtures/graph/unresolvable_nodes.toml new file mode 100644 index 0000000000..55d83472c9 --- /dev/null +++ b/crates/resolver/fixtures/graph/unresolvable_nodes.toml @@ -0,0 +1,2 @@ +"a" = ["b", "c"] +"b" = ["d"] diff --git a/crates/resolver/src/files.rs b/crates/resolver/src/files.rs new file mode 100644 index 0000000000..89d30ced18 --- /dev/null +++ b/crates/resolver/src/files.rs @@ -0,0 +1,276 @@ +use camino::Utf8PathBuf; +use glob::glob; +use std::io; +use std::path::PathBuf; +use std::{fmt, fs}; +use url::Url; + +use crate::Resolver; + +pub struct FilesResolver { + pub file_patterns: Vec, + pub required_files: Vec, + pub required_directories: Vec, + diagnostics: Vec, +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct RequiredFile { + pub path: String, +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct RequiredDirectory { + pub path: String, +} + +#[derive(Debug)] +pub struct File { + pub path: Utf8PathBuf, + pub content: String, +} + +#[derive(Debug)] +pub struct FilesResource { + pub files: Vec, +} + +#[derive(Debug)] +pub enum FilesResolutionError { + DirectoryDoesNotExist(Url), + GlobError(glob::GlobError), + PatternError(glob::PatternError), + IoError(io::Error), +} + +#[derive(Debug)] +pub enum FilesResolutionDiagnostic { + SkippedNonUtf8(PathBuf), + FileIoError(Utf8PathBuf, io::Error), + RequiredFileMissing(Url, String), + RequiredDirectoryMissing(Url, String), +} + +impl fmt::Display for FilesResolutionError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + FilesResolutionError::DirectoryDoesNotExist(url) => { + write!(f, "Directory does not exist: {url}") + } + FilesResolutionError::GlobError(err) => { + write!(f, "Glob pattern error: {err}") + } + FilesResolutionError::PatternError(err) => { + write!(f, "Pattern error: {err}") + } + FilesResolutionError::IoError(err) => { + write!(f, "IO error: {err}") + } + } + } +} + +impl std::error::Error for FilesResolutionError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + FilesResolutionError::GlobError(err) => Some(err), + FilesResolutionError::PatternError(err) => Some(err), + FilesResolutionError::IoError(err) => Some(err), + _ => None, + } + } +} + +impl fmt::Display for FilesResolutionDiagnostic { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + FilesResolutionDiagnostic::SkippedNonUtf8(path) => { + write!(f, "Skipped non-UTF8 file: {}", path.display()) + } + FilesResolutionDiagnostic::FileIoError(path, err) => { + write!(f, "IO error reading file {path}: {err}") + } + FilesResolutionDiagnostic::RequiredFileMissing(url, path) => { + write!(f, "Missing required file '{}' in ingot at {}", path, url) + } + FilesResolutionDiagnostic::RequiredDirectoryMissing(url, path) => { + write!( + f, + "Missing required directory '{}' in ingot at {}", + path, url + ) + } + } + } +} + +impl FilesResolutionDiagnostic { + pub fn url(&self) -> Option<&Url> { + match self { + FilesResolutionDiagnostic::SkippedNonUtf8(_) => None, + FilesResolutionDiagnostic::FileIoError(_, _) => None, + FilesResolutionDiagnostic::RequiredFileMissing(url, _) => Some(url), + FilesResolutionDiagnostic::RequiredDirectoryMissing(url, _) => Some(url), + } + } +} + +impl Default for FilesResolver { + fn default() -> Self { + Self::new() + } +} + +impl FilesResolver { + pub fn new() -> Self { + Self { + file_patterns: vec![], + required_files: vec![], + required_directories: vec![], + diagnostics: vec![], + } + } + + pub fn with_patterns(patterns: &[&str]) -> Self { + Self { + file_patterns: patterns.iter().map(|p| p.to_string()).collect(), + required_files: vec![], + required_directories: vec![], + diagnostics: vec![], + } + } + + pub fn with_required_file(mut self, path: &str) -> Self { + self.required_files.push(RequiredFile { + path: path.to_string(), + }); + self + } + + pub fn with_required_directory(mut self, path: &str) -> Self { + self.required_directories.push(RequiredDirectory { + path: path.to_string(), + }); + self + } + + pub fn with_pattern(mut self, pattern: &str) -> Self { + self.file_patterns.push(pattern.to_string()); + self + } +} + +impl Resolver for FilesResolver { + type Description = Url; + type Resource = FilesResource; + type Error = FilesResolutionError; + type Diagnostic = FilesResolutionDiagnostic; + + fn resolve(&mut self, handler: &mut H, ingot_url: &Url) -> Result + where + H: crate::ResolutionHandler, + { + tracing::trace!(target: "resolver", "Starting file resolution for URL: {}", ingot_url); + let mut files = vec![]; + + let ingot_path = Utf8PathBuf::from(ingot_url.path()); + tracing::trace!(target: "resolver", "Resolving files in path: {}", ingot_path); + + // Check if the directory exists + if !ingot_path.exists() || !ingot_path.is_dir() { + return Err(FilesResolutionError::DirectoryDoesNotExist( + ingot_url.clone(), + )); + } + + // Check for required directories first + for required_dir in &self.required_directories { + let required_dir_path = ingot_path.join(&required_dir.path); + if !required_dir_path.exists() || !required_dir_path.is_dir() { + self.diagnostics + .push(FilesResolutionDiagnostic::RequiredDirectoryMissing( + ingot_url.clone(), + required_dir.path.clone(), + )); + } + } + + // Check for required files + for required_file in &self.required_files { + let required_path = ingot_path.join(&required_file.path); + if !required_path.exists() { + self.diagnostics + .push(FilesResolutionDiagnostic::RequiredFileMissing( + ingot_url.clone(), + required_file.path.clone(), + )); + } else { + // If required file exists, load it + match fs::read_to_string(&required_path) { + Ok(content) => { + tracing::trace!(target: "resolver", "Successfully read required file: {}", required_path); + files.push(File { + path: required_path, + content, + }); + } + Err(error) => { + tracing::warn!(target: "resolver", "Failed to read required file {}: {}", required_path, error); + self.diagnostics + .push(FilesResolutionDiagnostic::FileIoError(required_path, error)); + } + } + } + } + + // Process file patterns + for pattern in &self.file_patterns { + let pattern_path = ingot_path.join(pattern); + let entries = + glob(pattern_path.as_str()).map_err(FilesResolutionError::PatternError)?; + + for entry in entries { + match entry { + Ok(path) => { + if path.is_file() { + match Utf8PathBuf::from_path_buf(path) { + Ok(path) => { + // Skip if this file was already loaded as a required file + if files.iter().any(|f| f.path == path) { + continue; + } + + match fs::read_to_string(&path) { + Ok(content) => { + tracing::trace!(target: "resolver", "Successfully read file: {}", path); + files.push(File { path, content }); + } + Err(error) => { + tracing::warn!(target: "resolver", "Failed to read file {}: {}", path, error); + self.diagnostics.push( + FilesResolutionDiagnostic::FileIoError(path, error), + ); + } + } + } + Err(error) => { + self.diagnostics + .push(FilesResolutionDiagnostic::SkippedNonUtf8(error)); + } + } + } + } + Err(e) => return Err(FilesResolutionError::GlobError(e)), + } + } + } + + tracing::trace!(target: "resolver", "File resolution completed successfully, found {} files", files.len()); + let resource = FilesResource { files }; + Ok(handler.handle_resolution(ingot_url, resource)) + } + + fn take_diagnostics(&mut self) -> Vec { + std::mem::take(&mut self.diagnostics) + } +} diff --git a/crates/resolver/src/graph.rs b/crates/resolver/src/graph.rs new file mode 100644 index 0000000000..dbcf4fdcfc --- /dev/null +++ b/crates/resolver/src/graph.rs @@ -0,0 +1,194 @@ +use std::{fmt, marker::PhantomData, mem::take}; + +use indexmap::IndexMap; +pub use petgraph::graph::{DiGraph, NodeIndex}; + +pub use petgraph; + +use crate::Resolver; + +pub trait GraphResolutionHandler { + type Item; + + fn handle_graph_resolution(&mut self, description: &D, resource: R) -> Self::Item; +} + +pub trait GraphResolver: Sized +where + NR: Resolver, + H: GraphResolutionHandler> + + crate::ResolutionHandler, + >::Item: IntoIterator, + NR::Description: Eq + std::hash::Hash + Clone, + E: Clone, +{ + #[allow(clippy::type_complexity)] + fn graph_resolve( + &mut self, + handler: &mut H, + root_node: &NR::Description, + ) -> Result< + >>::Item, + UnresolvableRootNode, + >; + + fn take_diagnostics(&mut self) -> Vec>; +} + +impl GraphResolver for GraphResolverImpl +where + NR: Resolver, + H: GraphResolutionHandler> + + crate::ResolutionHandler, + >::Item: IntoIterator, + NR::Description: Eq + std::hash::Hash + Clone, + E: Clone, +{ + fn graph_resolve( + &mut self, + handler: &mut H, + root_node: &NR::Description, + ) -> Result< + >>::Item, + UnresolvableRootNode, + > { + tracing::trace!(target: "resolver", "Starting graph resolution"); + + let mut graph = DiGraph::default(); + let mut nodes: IndexMap = IndexMap::new(); + let mut unresolved_nodes: IndexMap> = IndexMap::new(); + let mut unresolvable_nodes: IndexMap> = + IndexMap::new(); + + unresolved_nodes.entry(root_node.clone()).or_default(); + + while let Some((unresolved_node_description, back_nodes)) = unresolved_nodes.pop() { + tracing::trace!(target: "resolver", "Resolving node"); + match self + .node_resolver + .resolve(handler, &unresolved_node_description) + { + Ok(forward_nodes) => { + tracing::trace!(target: "resolver", "Successfully resolved node"); + let resolved_node_description = unresolved_node_description; + + let resolved_node_index = graph.add_node(resolved_node_description.clone()); + nodes.insert(resolved_node_description.clone(), resolved_node_index); + + for (back_node_index, back_edge) in &back_nodes { + graph.add_edge(*back_node_index, resolved_node_index, back_edge.clone()); + } + + for (forward_node_description, forward_edge) in forward_nodes { + if unresolvable_nodes.contains_key(&forward_node_description) { + unresolvable_nodes + .entry(forward_node_description) + .or_default() + .push((resolved_node_index, forward_edge)); + } else if !nodes.contains_key(&forward_node_description) { + unresolved_nodes + .entry(forward_node_description) + .or_default() + .push((resolved_node_index, forward_edge)); + } else if let Some(&existing_index) = nodes.get(&forward_node_description) { + graph.add_edge(resolved_node_index, existing_index, forward_edge); + } + } + } + Err(error) => { + tracing::warn!(target: "resolver", "Failed to resolve node"); + self.diagnostics + .push(UnresolvableNode(unresolved_node_description.clone(), error)); + unresolvable_nodes + .entry(unresolved_node_description) + .or_default() + .extend(back_nodes); + } + } + } + + if graph.node_count() == 0 { + tracing::warn!(target: "resolver", "Graph resolution failed: root node is unresolvable"); + Err(UnresolvableRootNode) + } else { + tracing::trace!(target: "resolver", "Graph resolution completed successfully with {} nodes", graph.node_count()); + let result = handler.handle_graph_resolution(root_node, graph); + Ok(result) + } + } + + fn take_diagnostics(&mut self) -> Vec> { + take(&mut self.diagnostics) + } +} + +impl Default for GraphResolverImpl +where + NR: Resolver + Default, +{ + fn default() -> Self { + Self { + node_resolver: NR::default(), + diagnostics: vec![], + _handler: PhantomData, + _edge: PhantomData, + } + } +} + +pub struct GraphResolverImpl { + pub node_resolver: NR, + pub diagnostics: Vec>, + // These phantom data fields are necessary because H and E are used in the trait implementation + // but are not stored as fields. They ensure correct type inference for the GraphResolver trait. + pub _handler: PhantomData, + pub _edge: PhantomData, +} + +impl GraphResolverImpl +where + NR: Resolver, +{ + pub fn new(node_resolver: NR) -> Self { + Self { + node_resolver, + diagnostics: vec![], + _handler: PhantomData, + _edge: PhantomData, + } + } +} + +#[derive(Debug)] +pub struct UnresolvableNode(pub N, pub E); + +#[derive(Debug)] +pub struct UnresolvableRootNode; + +impl fmt::Display for UnresolvableNode +where + N: fmt::Display, + E: fmt::Display, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Unresolvable node '{}': {}", self.0, self.1) + } +} + +impl std::error::Error for UnresolvableNode +where + N: fmt::Debug + fmt::Display, + E: fmt::Debug + fmt::Display + std::error::Error + 'static, +{ + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + Some(&self.1) + } +} + +impl fmt::Display for UnresolvableRootNode { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Root node is unresolvable") + } +} + +impl std::error::Error for UnresolvableRootNode {} diff --git a/crates/resolver/src/ingot.rs b/crates/resolver/src/ingot.rs deleted file mode 100644 index ee929a2081..0000000000 --- a/crates/resolver/src/ingot.rs +++ /dev/null @@ -1,139 +0,0 @@ -use std::{fmt, fs}; - -use camino::Utf8PathBuf; -use config::{Config, ConfigResolver}; -use source_files::{SourceFiles, SourceFilesResolver}; -use url::Url; - -use crate::Resolver; - -pub mod config; -pub mod source_files; - -#[derive(Debug)] -pub enum Ingot { - SingleFile { - url: Url, - content: String, - }, - Folder { - config: Option, - source_files: Option, - }, -} - -#[derive(Debug)] -pub enum Diagnostic { - ConfigError(config::Error), - SourceFilesError(source_files::Error), - SourceFilesDiagnostics(Vec), -} - -#[derive(Debug)] -pub enum Error { - IngotUrlDoesNotExist(Url), - IngotIsEmpty(Url), - StandaloneFileReadError { url: Url, error: std::io::Error }, -} - -#[derive(Default)] -pub struct IngotResolver { - diagnostics: Vec, -} - -impl Resolver for IngotResolver { - type Description = Url; - type Resource = Ingot; - type Error = Error; - type Diagnostic = Diagnostic; - - fn resolve(&mut self, ingot_url: &Url) -> Result { - let ingot_path = Utf8PathBuf::from(ingot_url.path()); - - if ingot_path.exists() { - if ingot_path.is_dir() { - let mut config_resolver = ConfigResolver; - let mut source_files_resolver = SourceFilesResolver::default(); - - let config = match config_resolver.resolve(ingot_url) { - Ok(config) => Some(config), - Err(error) => { - self.diagnostics.push(Diagnostic::ConfigError(error)); - None - } - }; - - let source_files = match source_files_resolver.resolve(ingot_url) { - Ok(source_files) => Some(source_files), - Err(error) => { - self.diagnostics.push(Diagnostic::SourceFilesError(error)); - None - } - }; - - let source_files_diags = source_files_resolver.take_diagnostics(); - - if !source_files_diags.is_empty() { - self.diagnostics - .push(Diagnostic::SourceFilesDiagnostics(source_files_diags)); - } - - Ok(Ingot::Folder { - config, - source_files, - }) - } else { - match fs::read_to_string(ingot_path) { - Ok(content) => Ok(Ingot::SingleFile { - url: ingot_url.clone(), - content, - }), - Err(error) => Err(Error::StandaloneFileReadError { - url: ingot_url.clone(), - error, - }), - } - } - } else { - Err(Error::IngotUrlDoesNotExist(ingot_url.clone())) - } - } - - fn take_diagnostics(&mut self) -> Vec { - std::mem::take(&mut self.diagnostics) - } -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::IngotUrlDoesNotExist(url) => write!(f, "the ingot url {url} does not exist"), - Self::IngotIsEmpty(url) => write!( - f, - "the ingot url {url} exists, but does not contain configuration or source files" - ), - Self::StandaloneFileReadError { url, error } => { - write!(f, "unable to read standalone ingot file {url}: {error}") - } - } - } -} - -impl fmt::Display for Diagnostic { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::ConfigError(error) => write!(f, "config resolution error: {error}"), - Self::SourceFilesError(error) => write!(f, "source files resolution error: {error}"), - Self::SourceFilesDiagnostics(diagnostics) => { - writeln!( - f, - "the following errors were encountered during source file resolution:" - )?; - for diagnostic in diagnostics { - writeln!(f, " {diagnostic}")? - } - Ok(()) - } - } - } -} diff --git a/crates/resolver/src/ingot/config.rs b/crates/resolver/src/ingot/config.rs deleted file mode 100644 index 78b8b07f06..0000000000 --- a/crates/resolver/src/ingot/config.rs +++ /dev/null @@ -1,57 +0,0 @@ -use camino::Utf8PathBuf; -use std::{fmt, fs}; -use url::Url; - -use crate::Resolver; - -const FE_CONFIG_SUFFIX: &str = "fe.toml"; - -#[derive(Debug, Clone)] -pub struct Config { - pub url: Url, - pub content: String, -} - -#[derive(Debug)] -pub enum Error { - ConfigFileDoesNotExist(Utf8PathBuf), - FileReadError(std::io::Error), -} - -#[derive(Default)] -pub struct ConfigResolver; - -impl Resolver for ConfigResolver { - type Description = Url; - type Resource = Config; - type Error = Error; - type Diagnostic = (); - - fn resolve(&mut self, ingot_url: &Url) -> Result { - let config_path = Utf8PathBuf::from(ingot_url.path()).join(FE_CONFIG_SUFFIX); - - if config_path.exists() { - Ok(Config { - url: Url::from_file_path(&config_path).unwrap(), - content: fs::read_to_string(&config_path).map_err(Error::FileReadError)?, - }) - } else { - Err(Error::ConfigFileDoesNotExist(config_path)) - } - } - - fn take_diagnostics(&mut self) -> Vec<()> { - vec![] - } -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::ConfigFileDoesNotExist(path) => { - write!(f, "`{path}` does not exist in the ingot directory") - } - Self::FileReadError(error) => write!(f, "file read error: {error}"), - } - } -} diff --git a/crates/resolver/src/ingot/source_files.rs b/crates/resolver/src/ingot/source_files.rs deleted file mode 100644 index 95e662a9d8..0000000000 --- a/crates/resolver/src/ingot/source_files.rs +++ /dev/null @@ -1,121 +0,0 @@ -use std::{fmt, fs, mem, path::PathBuf}; - -use crate::Resolver; -use camino::Utf8PathBuf; -use glob::glob; -use url::Url; - -#[derive(Debug)] -pub struct SourceFiles { - pub root: Option, - pub files: Vec<(Url, String)>, -} - -#[derive(Debug)] -pub enum Error { - SourceFolderDoesNotExist, - SourcePathIsFile, -} - -#[derive(Debug)] -pub enum Diagnostic { - RootFileDoesNotExist, - NonUtf8Path(PathBuf), - GlobError(glob::GlobError), - FileReadError(Utf8PathBuf, std::io::Error), -} - -#[derive(Default)] -pub struct SourceFilesResolver { - diagnostics: Vec, -} - -impl Resolver for SourceFilesResolver { - type Description = Url; - type Resource = SourceFiles; - type Error = Error; - type Diagnostic = Diagnostic; - - fn resolve(&mut self, ingot_url: &Url) -> Result { - let ingot_path = Utf8PathBuf::from(ingot_url.path()); - let source_path = ingot_path.join("src"); - - if !source_path.exists() { - return Err(Error::SourceFolderDoesNotExist); - } - if !source_path.is_dir() { - return Err(Error::SourcePathIsFile); - } - - let root = source_path.join("lib.fe"); - let files = source_path.join("**/*.fe"); - - let files = glob(files.as_str()) - .expect("failed to read glob pattern") - .filter_map(|entry| match entry { - Ok(path) => match Utf8PathBuf::from_path_buf(path.to_path_buf()) { - Ok(path) => match fs::read_to_string(&path) { - Ok(content) => Some((Url::from_file_path(path).unwrap(), content)), - Err(error) => { - self.diagnostics - .push(Diagnostic::FileReadError(path, error)); - None - } - }, - Err(path) => { - self.diagnostics.push(Diagnostic::NonUtf8Path(path)); - None - } - }, - Err(error) => { - self.diagnostics.push(Diagnostic::GlobError(error)); - None - } - }) - .collect(); - - let root = if root.exists() { - Some(Url::from_file_path(root).unwrap()) - } else { - self.diagnostics.push(Diagnostic::RootFileDoesNotExist); - None - }; - - Ok(SourceFiles { root, files }) - } - - fn take_diagnostics(&mut self) -> Vec { - mem::take(&mut self.diagnostics) - } -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::SourceFolderDoesNotExist => { - write!(f, "`src` folder does not exist in the ingot directory") - } - Self::SourcePathIsFile => { - write!(f, "`src` path is a file") - } - } - } -} - -impl fmt::Display for Diagnostic { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::RootFileDoesNotExist => { - write!( - f, - "a `lib.fe` file does not exist in the ingot `src` directory" - ) - } - Self::NonUtf8Path(path) => { - write!(f, "the path `{}` is not utf8", path.to_string_lossy()) - } - Self::GlobError(error) => write!(f, "glob error: {error}"), - Self::FileReadError(path, error) => write!(f, "unable to read `{path}`: {error}"), - } - } -} diff --git a/crates/resolver/src/lib.rs b/crates/resolver/src/lib.rs index 4a1d3962d8..833d7ae569 100644 --- a/crates/resolver/src/lib.rs +++ b/crates/resolver/src/lib.rs @@ -1,11 +1,32 @@ -pub mod ingot; +pub mod files; +pub mod graph; -pub trait Resolver { +pub trait Resolver: Sized { type Description; type Resource; type Error; type Diagnostic; - fn resolve(&mut self, description: &Self::Description) -> Result; + fn resolve( + &mut self, + handler: &mut H, + description: &Self::Description, + ) -> Result + where + H: ResolutionHandler; + fn take_diagnostics(&mut self) -> Vec; } + +pub trait ResolutionHandler +where + R: Resolver, +{ + type Item; + + fn handle_resolution( + &mut self, + description: &R::Description, + resource: R::Resource, + ) -> Self::Item; +} diff --git a/crates/resolver/tests/graph.rs b/crates/resolver/tests/graph.rs new file mode 100644 index 0000000000..82ced50439 --- /dev/null +++ b/crates/resolver/tests/graph.rs @@ -0,0 +1,104 @@ +use core::panic; +use std::{collections::HashMap, hash::Hash, marker::PhantomData}; + +use dir_test::{dir_test, Fixture}; +use fe_resolver::{ + graph::{GraphResolutionHandler, GraphResolver, GraphResolverImpl}, + ResolutionHandler, Resolver, +}; +use test_utils::snap_test; + +struct FixtureEntryResolver(pub Fixture>); + +impl FixtureEntryResolver {} + +#[derive(Debug)] +struct EntryDoesNotExist; + +impl Resolver for FixtureEntryResolver { + type Description = K; + type Resource = V; + type Error = EntryDoesNotExist; + type Diagnostic = (); + + fn resolve( + &mut self, + handler: &mut H, + description: &Self::Description, + ) -> Result + where + H: ResolutionHandler, + { + if let Some(value) = self.0.content().get(description) { + Ok(handler.handle_resolution(description, value.clone())) + } else { + Err(EntryDoesNotExist) + } + } + + fn take_diagnostics(&mut self) -> Vec { + panic!() + } +} + +#[derive(Default)] +pub struct MockNodeHandler; + +impl ResolutionHandler>> for MockNodeHandler { + type Item = Vec<(String, ())>; + + fn handle_resolution(&mut self, _source: &String, targets: Vec) -> Self::Item { + targets.into_iter().map(|target| (target, ())).collect() + } +} + +impl GraphResolutionHandler> for MockNodeHandler { + type Item = petgraph::Graph; + + fn handle_graph_resolution( + &mut self, + _source: &String, + graph: petgraph::Graph, + ) -> Self::Item { + graph + } +} + +fn load_toml(path: &str) -> HashMap> { + let text = std::fs::read_to_string(path).expect("Failed to read TOML file"); + toml::from_str(&text).expect("Invalid TOML") +} + +type FixtureEntryGraphResolver = + GraphResolverImpl, MockNodeHandler, ()>; + +fn fixture_resolver(fixture: Fixture>) -> FixtureEntryGraphResolver +where + K: Eq + Hash + Clone, + V: Eq + Hash + Clone, +{ + GraphResolverImpl { + node_resolver: FixtureEntryResolver(fixture), + _handler: PhantomData, + diagnostics: vec![], + _edge: PhantomData, + } +} + +#[dir_test( + dir: "$CARGO_MANIFEST_DIR/fixtures/graph", + glob: "*.toml", + loader: load_toml, +)] +fn graph_resolution(fixture: Fixture>>) { + let fixture_path = fixture.path(); + let mut resolver = fixture_resolver(fixture); + let mut handler = MockNodeHandler; + let graph = resolver + .graph_resolve(&mut handler, &"a".to_string()) + .unwrap(); + snap_test!( + format!("{:#?}\n{:#?}", graph, resolver.take_diagnostics()), + fixture_path + ); +} diff --git a/crates/test-utils/Cargo.toml b/crates/test-utils/Cargo.toml index f20f27307a..b63c99ad39 100644 --- a/crates/test-utils/Cargo.toml +++ b/crates/test-utils/Cargo.toml @@ -12,4 +12,3 @@ tracing.workspace = true tracing-subscriber.workspace = true tracing-tree.workspace = true url.workspace = true -camino.workspace = true diff --git a/crates/uitest/Cargo.toml b/crates/uitest/Cargo.toml index c6f85f57fc..40ad2cd867 100644 --- a/crates/uitest/Cargo.toml +++ b/crates/uitest/Cargo.toml @@ -7,14 +7,11 @@ repository = "https://github.com/ethereum/fe" publish = false [dependencies] -camino.workspace = true dir-test.workspace = true wasm-bindgen-test.workspace = true url.workspace = true driver.workspace = true test-utils.workspace = true -hir.workspace = true hir-analysis.workspace = true common.workspace = true -tracing.workspace = true diff --git a/crates/uitest/fixtures/name_resolution/import_ambiguous.snap b/crates/uitest/fixtures/name_resolution/import_ambiguous.snap index 41f7556dcb..6548a417b9 100644 --- a/crates/uitest/fixtures/name_resolution/import_ambiguous.snap +++ b/crates/uitest/fixtures/name_resolution/import_ambiguous.snap @@ -1,28 +1,7 @@ --- source: crates/uitest/tests/name_resolution.rs +assertion_line: 26 expression: diags input_file: fixtures/name_resolution/import_ambiguous.fe --- -error[2-0004]: `S` is ambiguous - ┌─ import_ambiguous.fe:2:9 - │ - 2 │ pub use S - │ ^ `S` is ambiguous - · -11 │ pub struct S {} - │ - candidate 1 - · -14 │ pub struct S {} - │ - candidate 2 -error[2-0004]: `S` is ambiguous - ┌─ import_ambiguous.fe:7:13 - │ - 7 │ pub use S - │ ^ `S` is ambiguous - · -11 │ pub struct S {} - │ - candidate 1 - · -14 │ pub struct S {} - │ - candidate 2 diff --git a/crates/uitest/fixtures/name_resolution/import_ambiguous_builtin.snap b/crates/uitest/fixtures/name_resolution/import_ambiguous_builtin.snap index d53f66d5b8..d6af4135b2 100644 --- a/crates/uitest/fixtures/name_resolution/import_ambiguous_builtin.snap +++ b/crates/uitest/fixtures/name_resolution/import_ambiguous_builtin.snap @@ -1,12 +1,11 @@ --- source: crates/uitest/tests/name_resolution.rs +assertion_line: 26 expression: diags -input_file: crates/uitest/fixtures/name_resolution/import_ambiguous_builtin.fe +input_file: fixtures/name_resolution/import_ambiguous_builtin.fe --- -error[2-0004]: `i32` is ambiguous +error[2-0005]: `i32` can't be used as a middle segment of a path ┌─ import_ambiguous_builtin.fe:3:5 │ 3 │ use i32::* - │ ^^^ `i32` is ambiguous - - + │ ^^^ `i32` can't be used as a middle segment of a path diff --git a/crates/uitest/fixtures/ty_check/method_selection/ambiguous_trait_def.snap b/crates/uitest/fixtures/ty_check/method_selection/ambiguous_trait_def.snap index 8dad28b1df..bd6333f8e4 100644 --- a/crates/uitest/fixtures/ty_check/method_selection/ambiguous_trait_def.snap +++ b/crates/uitest/fixtures/ty_check/method_selection/ambiguous_trait_def.snap @@ -10,8 +10,8 @@ error[8-0026]: multiple trait candidates found │ ^^^ │ │ │ `foo` is ambiguous - │ candidate: `Trait2::foo` │ candidate: `Trait1::foo` + │ candidate: `Trait2::foo` error[8-0031]: type annotation is needed ┌─ ambiguous_trait_def.fe:37:9 diff --git a/crates/uitest/fixtures/ty_check/method_selection/ambiguous_trait_method.snap b/crates/uitest/fixtures/ty_check/method_selection/ambiguous_trait_method.snap index 4c13f4a531..88db1db7af 100644 --- a/crates/uitest/fixtures/ty_check/method_selection/ambiguous_trait_method.snap +++ b/crates/uitest/fixtures/ty_check/method_selection/ambiguous_trait_method.snap @@ -1,7 +1,7 @@ --- -source: crates/uitest/tests/name_resolution.rs +source: crates/uitest/tests/ty_check.rs expression: diags -input_file: fixtures/name_resolution/ambiguous_trait_method.fe +input_file: fixtures/ty_check/method_selection/ambiguous_trait_method.fe --- error[2-0016]: multiple trait candidates found ┌─ ambiguous_trait_method.fe:15:10 @@ -10,5 +10,5 @@ error[2-0016]: multiple trait candidates found │ ^^^ │ │ │ `foo` is ambiguous - │ candidate: `Trait2::foo` │ candidate: `Trait1::foo` + │ candidate: `Trait2::foo`