diff --git a/.config/hakari.toml b/.config/hakari.toml index b15be035e..6541383eb 100644 --- a/.config/hakari.toml +++ b/.config/hakari.toml @@ -31,6 +31,3 @@ third-party = [ # `rustls` entirely. { name = "rustls" } ] - -[final-excludes] -workspace-members = ["rustdoc_types", "rustdoc_ir", "rustdoc_ext", "rustdoc_processor", "rustdoc_resolver"] diff --git a/Cargo.lock b/Cargo.lock index 554cf9c84..3578016ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -35,7 +35,7 @@ checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ "cfg-if", "cipher", - "cpufeatures", + "cpufeatures 0.2.17", ] [[package]] @@ -97,7 +97,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" dependencies = [ "anstyle", - "anstyle-parse", + "anstyle-parse 0.2.7", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstream" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" +dependencies = [ + "anstyle", + "anstyle-parse 1.0.0", "anstyle-query", "anstyle-wincon", "colorchoice", @@ -107,9 +122,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" [[package]] name = "anstyle-parse" @@ -120,6 +135,15 @@ dependencies = [ "utf8parse", ] +[[package]] +name = "anstyle-parse" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" +dependencies = [ + "utf8parse", +] + [[package]] name = "anstyle-query" version = "1.1.5" @@ -142,9 +166,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.100" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" [[package]] name = "anymap2" @@ -154,11 +178,11 @@ checksum = "d301b3b94cb4b2f23d7917810addbbaff90738e0ca2be692bd027e70d7e0330c" [[package]] name = "ar_archive_writer" -version = "0.2.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0c269894b6fe5e9d7ada0cf69b5bf847ff35bc25fc271f08e1d080fce80339a" +checksum = "7eb93bbb63b9c227414f6eb3a0adfddca591a8ce1e9b60661bb08969b87e340b" dependencies = [ - "object 0.32.2", + "object", ] [[package]] @@ -172,9 +196,12 @@ dependencies = [ [[package]] name = "arc-swap" -version = "1.7.1" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" +checksum = "6a3a1fd6f75306b68087b831f025c712524bcb19aad54e557b1129cfa0a2b207" +dependencies = [ + "rustversion", +] [[package]] name = "arraydeque" @@ -240,9 +267,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "aws-lc-fips-sys" -version = "0.13.10" +version = "0.13.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57900537c00a0565a35b63c4c281b372edfc9744b072fd4a3b414350a8f5ed48" +checksum = "d3d619165468401dec3caa3366ebffbcb83f2f31883e5b3932f8e2dec2ddc568" dependencies = [ "bindgen", "cc", @@ -254,9 +281,9 @@ dependencies = [ [[package]] name = "aws-lc-rs" -version = "1.15.2" +version = "1.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a88aab2464f1f25453baa7a07c84c5b7684e274054ba06817f382357f77a288" +checksum = "0ec6fb3fe69024a75fa7e1bfb48aa6cf59706a101658ea01bfd33b2b248a038f" dependencies = [ "aws-lc-fips-sys", "aws-lc-sys", @@ -265,9 +292,9 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.35.0" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b45afffdee1e7c9126814751f88dddc747f41d91da16c9551a0f1e8a11e788a1" +checksum = "f50037ee5e1e41e7b8f9d161680a725bd1626cb6f8c7e901f91f942850852fe7" dependencies = [ "cc", "cmake", @@ -294,7 +321,7 @@ dependencies = [ "cfg-if", "libc", "miniz_oxide", - "object 0.37.3", + "object", "rustc-demangle", "windows-link", ] @@ -322,9 +349,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" -version = "1.8.1" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e050f626429857a27ddccb31e0aca21356bfa709c04041aefddac081a8f068a" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "better-panic" @@ -395,7 +422,7 @@ dependencies = [ "hmac", "jiff", "percent-encoding", - "rand 0.9.2", + "rand 0.9.4", "serde", "sha2", "subtle", @@ -403,9 +430,9 @@ dependencies = [ [[package]] name = "bitflags" -version = "2.10.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" +checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" dependencies = [ "serde_core", ] @@ -431,9 +458,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.19.1" +version = "3.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" [[package]] name = "bytecheck" @@ -460,9 +487,9 @@ dependencies = [ [[package]] name = "bytemuck" -version = "1.24.0" +version = "1.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4" +checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" [[package]] name = "byteorder" @@ -491,7 +518,7 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c50c664f439395debff6e1ceb05dd6abd57edfdf7d7eda7fce872f95481015a" dependencies = [ - "anstream", + "anstream 0.6.21", "anstyle", "anyhow", "fs-err", @@ -512,15 +539,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d8af896b707212cd0e99c112a78c9497dd32994192a463ed2f7419d29bd8c6" dependencies = [ "serde", - "thiserror 2.0.17", + "thiserror 2.0.18", "toml 0.8.23", ] [[package]] name = "cargo-platform" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87a0c0e6148f11f01f32650a2ea02d532b2ad4e81d8bd41e6e565b5adc5e6082" +checksum = "dd0061da739915fae12ea00e16397555ed4371a6bb285431aab930f61b0aa4ba" dependencies = [ "serde", "serde_core", @@ -537,14 +564,14 @@ dependencies = [ "semver", "serde", "serde_json", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] name = "cc" -version = "1.2.50" +version = "1.2.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f50d563227a1c37cc0a263f64eca3334388c01c5e4c4861a9def205c614383c" +checksum = "d16d90359e986641506914ba71350897565610e87ce0ad9e6f28569db3dd5c6d" dependencies = [ "find-msvc-tools", "jobserver", @@ -569,9 +596,9 @@ dependencies = [ [[package]] name = "cfg-expr" -version = "0.20.5" +version = "0.20.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21be0e1ce6cdb2ee7fff840f922fb04ead349e5cfb1e750b769132d44ce04720" +checksum = "3c6b04e07d8080154ed4ac03546d9a2b303cc2fe1901ba0b35b301516e289368" dependencies = [ "smallvec", "target-lexicon", @@ -589,6 +616,17 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +[[package]] +name = "chacha20" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "rand_core 0.10.1", +] + [[package]] name = "cipher" version = "0.4.4" @@ -612,9 +650,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.53" +version = "4.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8" +checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" dependencies = [ "clap_builder", "clap_derive", @@ -622,20 +660,20 @@ dependencies = [ [[package]] name = "clap-stdin" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d80e97074da664d8e2c49e82cba54c40b9cec15c73810d90bf6a78261ab3ccaa" +checksum = "3c597a504c7d12819c50d2e1f78719afb6e01d4e14cbc94eb4c5f6a850494d1c" dependencies = [ - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] name = "clap_builder" -version = "4.5.53" +version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" dependencies = [ - "anstream", + "anstream 1.0.0", "anstyle", "clap_lex", "strsim", @@ -643,9 +681,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.49" +version = "4.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" +checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9" dependencies = [ "heck", "proc-macro2", @@ -655,24 +693,24 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.6" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" [[package]] name = "cmake" -version = "0.1.57" +version = "0.1.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75443c44cd6b379beb8c5b45d85d0773baf31cce901fe7bb252f4eff3008ef7d" +checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678" dependencies = [ "cc", ] [[package]] name = "colorchoice" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" +checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" [[package]] name = "combine" @@ -699,9 +737,9 @@ dependencies = [ [[package]] name = "config" -version = "0.15.19" +version = "0.15.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b30fa8254caad766fc03cb0ccae691e14bf3bd72bfff27f72802ce729551b3d6" +checksum = "8e68cfe19cd7d23ffde002c24ffa5cda73931913ef394d5eaaa32037dc940c0c" dependencies = [ "async-trait", "convert_case 0.6.0", @@ -712,8 +750,8 @@ dependencies = [ "serde-untagged", "serde_core", "serde_json", - "toml 0.9.10+spec-1.1.0", - "winnow", + "toml 1.1.2+spec-1.1.0", + "winnow 1.0.2", "yaml-rust2", ] @@ -731,13 +769,12 @@ dependencies = [ [[package]] name = "console" -version = "0.16.2" +version = "0.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03e45a4a8926227e4197636ba97a9fc9b00477e9f4bd711395687c5f0734bec4" +checksum = "d64e8af5551369d19cf50138de61f1c42074ab970f74e99be916646777f8fc87" dependencies = [ "encode_unicode", "libc", - "once_cell", "unicode-width 0.2.2", "windows-sys 0.61.2", ] @@ -763,7 +800,7 @@ version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" dependencies = [ - "getrandom 0.2.16", + "getrandom 0.2.17", "once_cell", "tiny-keccak", ] @@ -799,9 +836,9 @@ dependencies = [ [[package]] name = "cookie_store" -version = "0.22.0" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fc4bff745c9b4c7fb1e97b25d13153da2bc7796260141df62378998d070207f" +checksum = "15b2c103cf610ec6cae3da84a766285b42fd16aad564758459e6ecf128c75206" dependencies = [ "cookie", "document-features", @@ -840,6 +877,15 @@ dependencies = [ "libc", ] +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + [[package]] name = "crc" version = "3.4.0" @@ -851,9 +897,9 @@ dependencies = [ [[package]] name = "crc-catalog" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" +checksum = "217698eaf96b4a3f0bc4f3662aaa55bdf913cd54d7204591faa790070c6d0853" [[package]] name = "crc32fast" @@ -943,7 +989,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" dependencies = [ "cfg-if", - "cpufeatures", + "cpufeatures 0.2.17", "curve25519-dalek-derive", "digest", "fiat-crypto", @@ -1049,9 +1095,9 @@ checksum = "ffe7ed1d93f4553003e20b629abe9085e1e81b1429520f897f8f8860bc6dfc21" [[package]] name = "deflate64" -version = "0.1.10" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26bf8fc351c5ed29b5c2f0cbbac1b209b74f60ecd62e675a998df72c49af5204" +checksum = "ac6b926516df9c60bfa16e107b21086399f8285a44ca9711344b9e553c5146e2" [[package]] name = "der" @@ -1066,9 +1112,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.5.5" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" dependencies = [ "powerfmt", ] @@ -1268,9 +1314,9 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "erased-serde" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e8918065695684b2b0702da20382d5ae6065cf3327bc2d6436bd49a71ce9f3" +checksum = "d2add8a07dd6a8d93ff627029c51de145e12686fbc36ecb298ac22e74cf02dec" dependencies = [ "serde", "serde_core", @@ -1329,9 +1375,9 @@ checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" [[package]] name = "fastrand" -version = "2.3.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" [[package]] name = "ff" @@ -1365,21 +1411,20 @@ dependencies = [ [[package]] name = "filetime" -version = "0.2.26" +version = "0.2.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc0505cd1b6fa6580283f6bdf70a73fcf4aba1184038c90902b92b3dd0df63ed" +checksum = "f98844151eee8917efc50bd9e8318cb963ae8b297431495d3f758616ea5c57db" dependencies = [ "cfg-if", "libc", "libredox", - "windows-sys 0.60.2", ] [[package]] name = "find-msvc-tools" -version = "0.1.5" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" [[package]] name = "fixedbitset" @@ -1389,9 +1434,9 @@ checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" [[package]] name = "flate2" -version = "1.1.5" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" dependencies = [ "crc32fast", "miniz_oxide", @@ -1431,9 +1476,9 @@ dependencies = [ [[package]] name = "fs-err" -version = "3.2.1" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "824f08d01d0f496b3eca4f001a13cf17690a6ee930043d20817f547455fd98f8" +checksum = "73fde052dbfc920003cfd2c8e2c6e6d4cc7c1091538c3a24226cec0665ab08c0" dependencies = [ "autocfg", ] @@ -1460,9 +1505,9 @@ checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" [[package]] name = "futures" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" dependencies = [ "futures-channel", "futures-core", @@ -1475,9 +1520,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" dependencies = [ "futures-core", "futures-sink", @@ -1485,15 +1530,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" [[package]] name = "futures-executor" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" dependencies = [ "futures-core", "futures-task", @@ -1513,15 +1558,15 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" [[package]] name = "futures-macro" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", @@ -1530,21 +1575,21 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" [[package]] name = "futures-task" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" [[package]] name = "futures-util" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" dependencies = [ "futures-channel", "futures-core", @@ -1554,7 +1599,6 @@ dependencies = [ "futures-task", "memchr", "pin-project-lite", - "pin-utils", "slab", ] @@ -1573,7 +1617,7 @@ dependencies = [ "regex", "sanitize-filename", "tempfile", - "toml 0.9.10+spec-1.1.0", + "toml 0.9.12+spec-1.1.0", "tracing", "walkdir", ] @@ -1591,9 +1635,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" dependencies = [ "cfg-if", "js-sys", @@ -1611,11 +1655,25 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "r-efi", + "r-efi 5.3.0", "wasip2", "wasm-bindgen", ] +[[package]] +name = "getrandom" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +dependencies = [ + "cfg-if", + "libc", + "r-efi 6.0.0", + "rand_core 0.10.1", + "wasip2", + "wasip3", +] + [[package]] name = "gimli" version = "0.32.3" @@ -1688,9 +1746,9 @@ dependencies = [ [[package]] name = "guppy" -version = "0.17.23" +version = "0.17.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93383817a8d78167955d55ea78081f940df5469a02cf40eb4e26e56194fe11cb" +checksum = "a502977572076a42d585dd12f04bccf9c9cafdd4546e99878c072460b904e115" dependencies = [ "ahash", "camino", @@ -1721,9 +1779,9 @@ checksum = "92620684d99f750bae383ecb3be3748142d6095760afd5cbcf2261e9a279d780" [[package]] name = "h2" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" +checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" dependencies = [ "atomic-waker", "bytes", @@ -1760,9 +1818,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.16.1" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" [[package]] name = "hashlink" @@ -1874,9 +1932,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" +checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca" dependencies = [ "atomic-waker", "bytes", @@ -1889,7 +1947,6 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "pin-utils", "smallvec", "tokio", "want", @@ -1897,31 +1954,29 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.7" +version = "0.27.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" +checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" dependencies = [ "http", "hyper", "hyper-util", "rustls", - "rustls-pki-types", "tokio", "tokio-rustls", "tower-service", - "webpki-roots 1.0.4", + "webpki-roots 1.0.7", ] [[package]] name = "hyper-util" -version = "0.1.19" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" dependencies = [ "base64", "bytes", "futures-channel", - "futures-core", "futures-util", "http", "http-body", @@ -1930,7 +1985,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.1", + "socket2 0.6.3", "tokio", "tower-service", "tracing", @@ -1938,12 +1993,13 @@ dependencies = [ [[package]] name = "icu_collections" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" dependencies = [ "displaydoc", "potential_utf", + "utf8_iter", "yoke", "zerofrom", "zerovec", @@ -1951,9 +2007,9 @@ dependencies = [ [[package]] name = "icu_locale_core" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" dependencies = [ "displaydoc", "litemap", @@ -1964,9 +2020,9 @@ dependencies = [ [[package]] name = "icu_normalizer" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" dependencies = [ "icu_collections", "icu_normalizer_data", @@ -1978,15 +2034,15 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" +checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" [[package]] name = "icu_properties" -version = "2.1.2" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" +checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" dependencies = [ "icu_collections", "icu_locale_core", @@ -1998,15 +2054,15 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "2.1.2" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" +checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" [[package]] name = "icu_provider" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" dependencies = [ "displaydoc", "icu_locale_core", @@ -2017,6 +2073,12 @@ dependencies = [ "zerovec", ] +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + [[package]] name = "ident_case" version = "1.0.1" @@ -2036,9 +2098,9 @@ dependencies = [ [[package]] name = "idna_adapter" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" dependencies = [ "icu_normalizer", "icu_properties", @@ -2081,23 +2143,23 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.12.1" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", - "hashbrown 0.16.1", + "hashbrown 0.17.0", "serde", "serde_core", ] [[package]] name = "indicatif" -version = "0.18.3" +version = "0.18.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9375e112e4b463ec1b1c6c011953545c65a30164fbab5b581df32b3abf0dcb88" +checksum = "25470f23803092da7d239834776d653104d551bc4d7eacaf31e6837854b8e9eb" dependencies = [ - "console 0.16.2", + "console 0.16.3", "portable-atomic", "unicode-width 0.2.2", "unit-prefix", @@ -2121,11 +2183,11 @@ dependencies = [ [[package]] name = "insta" -version = "1.45.0" +version = "1.47.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b76866be74d68b1595eb8060cb9191dca9c021db2316558e52ddc5d55d41b66c" +checksum = "7b4a6248eb93a4401ed2f37dfe8ea592d3cf05b7cf4f8efa867b6895af7e094e" dependencies = [ - "console 0.15.11", + "console 0.16.3", "once_cell", "similar", "tempfile", @@ -2133,15 +2195,15 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.11.0" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" +checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" [[package]] name = "iri-string" -version = "0.7.9" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f867b9d1d896b67beb18518eda36fdb77a32ea590de864f1325b294a6d14397" +checksum = "25e659a4bb38e810ebc252e53b5814ff908a8c58c2a9ce2fae1bbec24cbf4e20" dependencies = [ "memchr", "serde", @@ -2179,15 +2241,15 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.15" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "jiff" -version = "0.2.16" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49cce2b81f2098e7e3efc35bc2e0a6b7abec9d34128283d7a26fa8f32a6dbb35" +checksum = "f00b5dbd620d61dfdcb6007c9c1f6054ebd75319f163d886a9055cec1155073d" dependencies = [ "jiff-static", "jiff-tzdb-platform", @@ -2211,9 +2273,9 @@ dependencies = [ [[package]] name = "jiff-static" -version = "0.2.16" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "980af8b43c3ad5d8d349ace167ec8170839f753a42d233ba19e08afe1850fa69" +checksum = "e000de030ff8022ea1da3f466fbb0f3a809f5e51ed31f6dd931c35181ad8e6d7" dependencies = [ "proc-macro2", "quote", @@ -2222,9 +2284,9 @@ dependencies = [ [[package]] name = "jiff-tzdb" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68971ebff725b9e2ca27a601c5eb38a4c5d64422c4cbab0c535f248087eda5c2" +checksum = "c900ef84826f1338a557697dc8fc601df9ca9af4ac137c7fb61d4c6f2dfd3076" [[package]] name = "jiff-tzdb-platform" @@ -2244,7 +2306,7 @@ dependencies = [ "cesu8", "cfg-if", "combine", - "jni-sys", + "jni-sys 0.3.1", "log", "thiserror 1.0.69", "walkdir", @@ -2253,9 +2315,31 @@ dependencies = [ [[package]] name = "jni-sys" -version = "0.3.0" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41a652e1f9b6e0275df1f15b32661cf0d4b78d4d87ddec5e0c3c20f097433258" +dependencies = [ + "jni-sys 0.4.1", +] + +[[package]] +name = "jni-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" +dependencies = [ + "jni-sys-macros", +] + +[[package]] +name = "jni-sys-macros" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" +checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" +dependencies = [ + "quote", + "syn", +] [[package]] name = "jobserver" @@ -2269,10 +2353,12 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.83" +version = "0.3.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" +checksum = "a1840c94c045fbcf8ba2812c95db44499f7c64910a912551aaaa541decebcacf" dependencies = [ + "cfg-if", + "futures-util", "once_cell", "wasm-bindgen", ] @@ -2290,19 +2376,19 @@ dependencies = [ [[package]] name = "jsonwebtoken" -version = "10.2.0" +version = "10.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c76e1c7d7df3e34443b3621b459b066a7b79644f059fc8b2db7070c825fd417e" +checksum = "0529410abe238729a60b108898784df8984c87f6054c9c4fcacc47e4803c1ce1" dependencies = [ "base64", "ed25519-dalek", - "getrandom 0.2.16", + "getrandom 0.2.17", "hmac", "js-sys", "p256", "p384", "pem", - "rand 0.8.5", + "rand 0.8.6", "rsa", "serde", "serde_json", @@ -2336,11 +2422,17 @@ dependencies = [ "spin", ] +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + [[package]] name = "libc" -version = "0.2.178" +version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] name = "libloading" @@ -2354,19 +2446,20 @@ dependencies = [ [[package]] name = "libm" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libredox" -version = "0.1.11" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df15f6eac291ed1cf25865b1ee60399f57e7c227e7f51bdbd4c5270396a9ed50" +checksum = "e02f3bb43d335493c96bf3fd3a321600bf6bd07ed34bc64118e9293bdffea46c" dependencies = [ "bitflags", "libc", - "redox_syscall 0.6.0", + "plain", + "redox_syscall 0.7.4", ] [[package]] @@ -2382,11 +2475,11 @@ dependencies = [ [[package]] name = "libtest-mimic" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5297962ef19edda4ce33aaa484386e0a5b3d7f2f4e037cbeee00503ef6b29d33" +checksum = "14e6ba06f0ade6e504aff834d7c34298e5155c6baca353cc6a4aaff2f9fd7f33" dependencies = [ - "anstream", + "anstream 1.0.0", "anstyle", "clap", "escape8259", @@ -2394,9 +2487,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.11.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "liquid" @@ -2454,9 +2547,9 @@ dependencies = [ [[package]] name = "litemap" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" +checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" [[package]] name = "litrs" @@ -2523,9 +2616,9 @@ checksum = "2f926ade0c4e170215ae43342bf13b9310a437609c81f29f86c5df6657582ef9" [[package]] name = "matchit" -version = "0.9.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ea5f97102eb9e54ab99fb70bb175589073f554bdadfb74d9bd656482ea73e2a" +checksum = "8863b587001c1b9a8a4e36008cebc6b3612cb1226fe2de94858e06092687b608" [[package]] name = "md-5" @@ -2539,9 +2632,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.6" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] name = "miette" @@ -2597,9 +2690,9 @@ dependencies = [ [[package]] name = "mio" -version = "1.1.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" dependencies = [ "libc", "wasi", @@ -2693,16 +2786,16 @@ dependencies = [ "num-integer", "num-iter", "num-traits", - "rand 0.8.5", + "rand 0.8.6", "smallvec", "zeroize", ] [[package]] name = "num-conv" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" +checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967" [[package]] name = "num-integer" @@ -2753,15 +2846,6 @@ dependencies = [ "libc", ] -[[package]] -name = "object" -version = "0.32.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" -dependencies = [ - "memchr", -] - [[package]] name = "object" version = "0.37.3" @@ -2782,9 +2866,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.21.3" +version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" [[package]] name = "once_cell_polyfill" @@ -2800,9 +2884,9 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "openssl-probe" -version = "0.1.6" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" [[package]] name = "ordered-multimap" @@ -2816,9 +2900,9 @@ dependencies = [ [[package]] name = "owo-colors" -version = "4.2.3" +version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c6901729fa79e91a0913333229e9ca5dc725089d1c363b2f4b4760709dc4a52" +checksum = "d211803b9b6b570f68772237e415a029d5a50c65d382910b879fb19d3271f94d" [[package]] name = "p256" @@ -2926,7 +3010,7 @@ dependencies = [ "insta", "itertools 0.14.0", "jiff", - "matchit 0.9.0", + "matchit 0.9.2", "mime", "paste", "pavex_bp_schema", @@ -2947,8 +3031,8 @@ dependencies = [ "serde_path_to_error", "serde_yaml", "smallvec", - "socket2 0.6.1", - "thiserror 2.0.17", + "socket2 0.6.3", + "thiserror 2.0.18", "tokio", "tracing", "tracing_log_error", @@ -3013,9 +3097,9 @@ dependencies = [ "supports-color", "tar", "tempfile", - "thiserror 2.0.17", + "thiserror 2.0.18", "tokio", - "toml 0.9.10+spec-1.1.0", + "toml 0.9.12+spec-1.1.0", "tracing", "tracing-chrome", "tracing-subscriber", @@ -3035,7 +3119,7 @@ dependencies = [ "anyhow", "pavex", "px_workspace_hack", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] @@ -3047,7 +3131,7 @@ dependencies = [ "miette", "pavex_cli_shell", "px_workspace_hack", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] @@ -3057,7 +3141,7 @@ dependencies = [ "anyhow", "miette", "px_workspace_hack", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] @@ -3130,7 +3214,7 @@ dependencies = [ "serde", "serde_json", "static_assertions", - "thiserror 2.0.17", + "thiserror 2.0.18", "tokio", "tracing", "uuid", @@ -3194,7 +3278,7 @@ dependencies = [ "anyhow", "cargo-like-utils", "cargo_metadata", - "console 0.16.2", + "console 0.16.3", "fs-err", "globwalk", "guppy", @@ -3216,7 +3300,7 @@ dependencies = [ "sha2", "similar", "textwrap", - "toml 0.9.10+spec-1.1.0", + "toml 0.9.12+spec-1.1.0", "tracing-subscriber", "walkdir", ] @@ -3247,13 +3331,12 @@ dependencies = [ "elsa", "fixedbitset", "fs-err", - "globwalk", "guppy", "indexmap", "insta", "itertools 0.14.0", "la-arena", - "matchit 0.9.0", + "matchit 0.9.2", "miette", "once_cell", "pavex", @@ -3283,13 +3366,12 @@ dependencies = [ "serde_stacker", "syn", "textwrap", - "thiserror 2.0.17", - "toml 0.9.10+spec-1.1.0", - "toml_edit 0.24.0+spec-1.1.0", + "thiserror 2.0.18", + "toml 0.9.12+spec-1.1.0", + "toml_edit 0.24.1+spec-1.1.0", "tracing", "tracing_log_error", "xdg-home", - "xxhash-rust", ] [[package]] @@ -3319,7 +3401,7 @@ dependencies = [ "quote", "serde", "syn", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] @@ -3367,7 +3449,7 @@ dependencies = [ "anyhow", "pavex", "px_workspace_hack", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] @@ -3431,9 +3513,9 @@ dependencies = [ [[package]] name = "pest" -version = "2.8.4" +version = "2.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbcfd20a6d4eeba40179f05735784ad32bdaef05ce8e8af05f180d45bb3e7e22" +checksum = "e0848c601009d37dfa3430c4666e147e49cdcf1b92ecd3e63657d8a5f19da662" dependencies = [ "memchr", "ucd-trie", @@ -3441,9 +3523,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.8.4" +version = "2.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51f72981ade67b1ca6adc26ec221be9f463f2b5839c7508998daa17c23d94d7f" +checksum = "11f486f1ea21e6c10ed15d5a7c77165d0ee443402f0780849d1768e7d9d6fe77" dependencies = [ "pest", "pest_generator", @@ -3451,9 +3533,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.8.4" +version = "2.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee9efd8cdb50d719a80088b76f81aec7c41ed6d522ee750178f83883d271625" +checksum = "8040c4647b13b210a963c1ed407c1ff4fdfa01c31d6d2a098218702e6664f94f" dependencies = [ "pest", "pest_meta", @@ -3464,9 +3546,9 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.8.4" +version = "2.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf1d70880e76bdc13ba52eafa6239ce793d85c8e43896507e43dd8984ff05b82" +checksum = "89815c69d36021a140146f26659a81d6c2afa33d216d736dd4be5381a7362220" dependencies = [ "pest", "sha2", @@ -3486,9 +3568,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" [[package]] name = "pin-utils" @@ -3519,9 +3601,15 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.32" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" + +[[package]] +name = "plain" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" +checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" [[package]] name = "polyval" @@ -3530,31 +3618,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" dependencies = [ "cfg-if", - "cpufeatures", + "cpufeatures 0.2.17", "opaque-debug", "universal-hash", ] [[package]] name = "portable-atomic" -version = "1.12.0" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f59e70c4aef1e55797c2e8fd94a4f2a973fc972cfde0e0b05f683667b0cd39dd" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" [[package]] name = "portable-atomic-util" -version = "0.2.4" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" +checksum = "c2a106d1259c23fac8e543272398ae0e3c0b8d33c88ed73d0cc71b0f1d902618" dependencies = [ "portable-atomic", ] [[package]] name = "potential_utf" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" dependencies = [ "zerovec", ] @@ -3595,9 +3683,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.103" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ "unicode-ident", ] @@ -3617,9 +3705,9 @@ dependencies = [ [[package]] name = "psm" -version = "0.1.28" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d11f2fedc3b7dafdc2851bc52f277377c5473d378859be234bc7ebb593144d01" +checksum = "645dbe486e346d9b5de3ef16ede18c26e6c70ad97418f4874b8b1889d6e761ea" dependencies = [ "ar_archive_writer", "cc", @@ -3653,11 +3741,10 @@ dependencies = [ "aho-corasick", "base64", "bincode", - "bitflags", "byteorder", "clap", "clap_builder", - "console 0.16.2", + "console 0.16.3", "crossbeam-utils", "crypto-common", "darling_core 0.23.0", @@ -3673,7 +3760,7 @@ dependencies = [ "futures-sink", "futures-util", "generic-array", - "getrandom 0.2.16", + "getrandom 0.2.17", "getrandom 0.3.4", "hashbrown 0.14.5", "hashbrown 0.15.5", @@ -3703,8 +3790,9 @@ dependencies = [ "serde", "serde_core", "serde_json", - "serde_spanned 1.0.4", + "serde_spanned 1.1.1", "sha2", + "slab", "smallvec", "spin", "sqlx", @@ -3720,15 +3808,18 @@ dependencies = [ "textwrap", "time", "tokio", - "toml 0.9.10+spec-1.1.0", + "toml 0.9.12+spec-1.1.0", + "toml 1.1.2+spec-1.1.0", "toml_datetime 0.7.5+spec-1.1.0", + "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", "toml_writer", "tracing", "tracing-core", "tracing-log", "uuid", - "winnow", + "winnow 0.7.15", + "winnow 1.0.2", "zeroize", ] @@ -3745,8 +3836,8 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls", - "socket2 0.6.1", - "thiserror 2.0.17", + "socket2 0.6.3", + "thiserror 2.0.18", "tokio", "tracing", "web-time", @@ -3754,20 +3845,20 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.11.13" +version = "0.11.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" +checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" dependencies = [ "bytes", "getrandom 0.3.4", "lru-slab", - "rand 0.9.2", + "rand 0.9.4", "ring", "rustc-hash", "rustls", "rustls-pki-types", "slab", - "thiserror 2.0.17", + "thiserror 2.0.18", "tinyvec", "tracing", "web-time", @@ -3782,16 +3873,16 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.6.1", + "socket2 0.6.3", "tracing", "windows-sys 0.60.2", ] [[package]] name = "quote" -version = "1.0.42" +version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" dependencies = [ "proc-macro2", ] @@ -3802,6 +3893,12 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + [[package]] name = "r2d2" version = "0.8.10" @@ -3835,9 +3932,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" dependencies = [ "libc", "rand_chacha 0.3.1", @@ -3846,12 +3943,23 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.2" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" dependencies = [ "rand_chacha 0.9.0", - "rand_core 0.9.3", + "rand_core 0.9.5", +] + +[[package]] +name = "rand" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207" +dependencies = [ + "chacha20", + "getrandom 0.4.2", + "rand_core 0.10.1", ] [[package]] @@ -3871,7 +3979,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core 0.9.3", + "rand_core 0.9.5", ] [[package]] @@ -3880,23 +3988,29 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.16", + "getrandom 0.2.17", ] [[package]] name = "rand_core" -version = "0.9.3" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" dependencies = [ "getrandom 0.3.4", ] +[[package]] +name = "rand_core" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" + [[package]] name = "rayon" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" +checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d" dependencies = [ "either", "rayon-core", @@ -3957,18 +4071,18 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.6.0" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec96166dafa0886eb81fe1c0a388bece180fbef2135f97c1e2cf8302e74b43b5" +checksum = "f450ad9c3b1da563fb6948a8e0fb0fb9269711c9c73d9ea1de5058c79c8d643a" dependencies = [ "bitflags", ] [[package]] name = "regex" -version = "1.12.2" +version = "1.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" dependencies = [ "aho-corasick", "memchr", @@ -3978,9 +4092,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" dependencies = [ "aho-corasick", "memchr", @@ -3989,9 +4103,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.8" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" [[package]] name = "relative-path" @@ -4027,9 +4141,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.12.26" +version = "0.12.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b4c14b2d9afca6a60277086b0cc6a6ae0b568f6f7916c943a8cdc79f8be240f" +checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" dependencies = [ "base64", "bytes", @@ -4060,7 +4174,7 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "webpki-roots 1.0.4", + "webpki-roots 1.0.7", ] [[package]] @@ -4087,13 +4201,13 @@ dependencies = [ "anyhow", "async-trait", "futures", - "getrandom 0.2.16", + "getrandom 0.2.17", "http", "hyper", "reqwest", "reqwest-middleware", "retry-policies", - "thiserror 2.0.17", + "thiserror 2.0.18", "tokio", "tracing", "wasmtimer", @@ -4107,7 +4221,7 @@ checksum = "d70ea85f131b2ee9874f0b160ac5976f8af75f3c9badfe0d955880257d10bd83" dependencies = [ "anyhow", "async-trait", - "getrandom 0.2.16", + "getrandom 0.2.17", "http", "matchit 0.8.6", "reqwest", @@ -4121,7 +4235,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46a4bd6027df676bcb752d3724db0ea3c0c5fc1dd0376fec51ac7dcaf9cc69be" dependencies = [ - "rand 0.9.2", + "rand 0.9.4", ] [[package]] @@ -4142,7 +4256,7 @@ checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", - "getrandom 0.2.16", + "getrandom 0.2.17", "libc", "untrusted", "windows-sys 0.52.0", @@ -4150,13 +4264,13 @@ dependencies = [ [[package]] name = "rkyv" -version = "0.8.14" +version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "360b333c61ae24e5af3ae7c8660bd6b21ccd8200dbbc5d33c2454421e85b9c69" +checksum = "73389e0c99e664f919275ab5b5b0471391fe9a8de61e1dff9b1eaf56a90f16e3" dependencies = [ "bytecheck", "bytes", - "hashbrown 0.16.1", + "hashbrown 0.17.0", "indexmap", "munge", "ptr_meta", @@ -4169,9 +4283,9 @@ dependencies = [ [[package]] name = "rkyv_derive" -version = "0.8.14" +version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c02f8cdd12b307ab69fe0acf4cd2249c7460eb89dce64a0febadf934ebb6a9e" +checksum = "5d2ed0b54125315fb36bd021e82d314d1c126548f871634b483f46b31d13cac6" dependencies = [ "proc-macro2", "quote", @@ -4189,9 +4303,9 @@ dependencies = [ [[package]] name = "ron" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd490c5b18261893f14449cbd28cb9c0b637aebf161cd77900bfdedaff21ec32" +checksum = "4147b952f3f819eca0e99527022f7d6a8d05f111aeb0a62960c74eb283bec8fc" dependencies = [ "bitflags", "once_cell", @@ -4203,9 +4317,9 @@ dependencies = [ [[package]] name = "rsa" -version = "0.9.9" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40a0376c50d0358279d9d643e4bf7b7be212f1f4ff1da9070a7b54d22ef75c88" +checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d" dependencies = [ "const-oid", "digest", @@ -4247,15 +4361,15 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" +checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" [[package]] name = "rustc-hash" -version = "2.1.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" [[package]] name = "rustc_version" @@ -4280,7 +4394,9 @@ dependencies = [ [[package]] name = "rustdoc_ext" -version = "0.2.10" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6319c29b9fb4ad97bcf3f7099171b7b52d4a11c5d8e492c4213d8521ed8f8b3c" dependencies = [ "guppy", "rustdoc-types", @@ -4288,7 +4404,9 @@ dependencies = [ [[package]] name = "rustdoc_ir" -version = "0.2.10" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "346bd2e64092544e28f3d98a4bbe35944330aa268861ed4808ad26126def70b0" dependencies = [ "ahash", "anyhow", @@ -4299,13 +4417,15 @@ dependencies = [ "rustdoc_ext", "serde", "syn", - "thiserror 2.0.17", + "thiserror 2.0.18", "tracing", ] [[package]] name = "rustdoc_processor" -version = "0.2.10" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "078808d7a6fbfb7baa0d191ff6d0a624158ef106e8e5ddb3cd8c9074ef90703b" dependencies = [ "ahash", "anyhow", @@ -4332,8 +4452,8 @@ dependencies = [ "serde", "serde_json", "serde_stacker", - "thiserror 2.0.17", - "toml 0.9.10+spec-1.1.0", + "thiserror 2.0.18", + "toml 0.9.12+spec-1.1.0", "tracing", "tracing_log_error", "xxhash-rust", @@ -4341,7 +4461,9 @@ dependencies = [ [[package]] name = "rustdoc_resolver" -version = "0.2.10" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf839fadca0da000baab1df1d0f99d7aaf87a58725a0c75814d07072208393a" dependencies = [ "ahash", "anyhow", @@ -4351,15 +4473,15 @@ dependencies = [ "rustdoc_ext", "rustdoc_ir", "rustdoc_processor", - "thiserror 2.0.17", + "thiserror 2.0.18", "tracing", ] [[package]] name = "rustix" -version = "1.1.2" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ "bitflags", "errno", @@ -4370,9 +4492,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.35" +version = "0.23.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f" +checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" dependencies = [ "aws-lc-rs", "log", @@ -4386,9 +4508,9 @@ dependencies = [ [[package]] name = "rustls-native-certs" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9980d917ebb0c0536119ba501e90834767bffc3d60641457fd84a1f3fd337923" +checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" dependencies = [ "openssl-probe", "rustls-pki-types", @@ -4398,9 +4520,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.13.2" +version = "1.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21e6f2ab2928ca4291b86736a8bd920a277a399bba1589409d72154ff87c1282" +checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" dependencies = [ "web-time", "zeroize", @@ -4435,9 +4557,9 @@ checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" [[package]] name = "rustls-webpki" -version = "0.103.8" +version = "0.103.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" +checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" dependencies = [ "aws-lc-rs", "ring", @@ -4453,9 +4575,9 @@ checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "ryu" -version = "1.0.20" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" [[package]] name = "same-file" @@ -4477,9 +4599,9 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.28" +version = "0.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" +checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" dependencies = [ "windows-sys 0.61.2", ] @@ -4525,9 +4647,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "3.5.1" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" dependencies = [ "bitflags", "core-foundation", @@ -4538,9 +4660,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.15.0" +version = "2.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" dependencies = [ "core-foundation-sys", "libc", @@ -4559,9 +4681,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.27" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" dependencies = [ "serde", "serde_core", @@ -4624,15 +4746,15 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.145" +version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ "itoa", "memchr", - "ryu", "serde", "serde_core", + "zmij", ] [[package]] @@ -4657,9 +4779,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "1.0.4" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" dependencies = [ "serde_core", ] @@ -4707,7 +4829,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", - "cpufeatures", + "cpufeatures 0.2.17", "digest", ] @@ -4724,7 +4846,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", - "cpufeatures", + "cpufeatures 0.2.17", "digest", ] @@ -4755,9 +4877,9 @@ dependencies = [ [[package]] name = "simd-adler32" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" +checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" [[package]] name = "simdutf8" @@ -4773,21 +4895,21 @@ checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" [[package]] name = "simple_asn1" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "297f631f50729c8c99b84667867963997ec0b50f32b2a7dbcab828ef0541e8bb" +checksum = "0d585997b0ac10be3c5ee635f1bab02d512760d14b7c468801ac8a01d9ae5f1d" dependencies = [ "num-bigint", "num-traits", - "thiserror 2.0.17", + "thiserror 2.0.18", "time", ] [[package]] name = "slab" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] name = "smallvec" @@ -4816,12 +4938,12 @@ dependencies = [ [[package]] name = "socket2" -version = "0.6.1" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" +checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -4884,7 +5006,7 @@ dependencies = [ "serde_json", "sha2", "smallvec", - "thiserror 2.0.17", + "thiserror 2.0.18", "tokio", "tokio-stream", "tracing", @@ -4960,7 +5082,7 @@ dependencies = [ "memchr", "once_cell", "percent-encoding", - "rand 0.8.5", + "rand 0.8.6", "rsa", "serde", "sha1", @@ -4968,7 +5090,7 @@ dependencies = [ "smallvec", "sqlx-core", "stringprep", - "thiserror 2.0.17", + "thiserror 2.0.18", "tracing", "uuid", "whoami", @@ -4999,14 +5121,14 @@ dependencies = [ "md-5", "memchr", "once_cell", - "rand 0.8.5", + "rand 0.8.6", "serde", "serde_json", "sha2", "smallvec", "sqlx-core", "stringprep", - "thiserror 2.0.17", + "thiserror 2.0.18", "tracing", "uuid", "whoami", @@ -5031,7 +5153,7 @@ dependencies = [ "serde", "serde_urlencoded", "sqlx-core", - "thiserror 2.0.17", + "thiserror 2.0.18", "tracing", "url", "uuid", @@ -5045,15 +5167,15 @@ checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] name = "stacker" -version = "0.1.22" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1f8b29fb42aafcea4edeeb6b2f2d7ecd0d969c48b4cf0d2e64aafc471dd6e59" +checksum = "640c8cdd92b6b12f5bcb1803ca3bbf5ab96e5e6b6b96b9ab77dabe9e880b3190" dependencies = [ "cc", "cfg-if", "libc", "psm", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -5108,9 +5230,9 @@ checksum = "b7401a30af6cb5818bb64852270bb722533397edcfc7344954a38f420819ece2" [[package]] name = "syn" -version = "2.0.111" +version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" dependencies = [ "proc-macro2", "quote", @@ -5139,9 +5261,9 @@ dependencies = [ [[package]] name = "tar" -version = "0.4.44" +version = "0.4.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d863878d212c87a19c1a610eb53bb01fe12951c0501cf5a0d65f724914a667a" +checksum = "22692a6476a21fa75fdfc11d452fda482af402c008cdbaf3476414e122040973" dependencies = [ "filetime", "libc", @@ -5156,9 +5278,9 @@ checksum = "df7f62577c25e07834649fc3b39fafdc597c0a3527dc1c60129201ccfcbaa50c" [[package]] name = "target-spec" -version = "3.5.4" +version = "3.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3da9f675d5be234979ba2352a72510ac5fcf4a99cc48b402cd7bba300ec764" +checksum = "b00e973676af5497c2a69cc9787e2205c00f3b6f4f70e7d7b0112e28aa84b501" dependencies = [ "cfg-expr", "guppy-workspace-hack", @@ -5173,12 +5295,12 @@ checksum = "591ef38edfb78ca4771ee32cf494cb8771944bee237a9b91fc9c1424ac4b777b" [[package]] name = "tempfile" -version = "3.23.0" +version = "3.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.3.4", + "getrandom 0.4.2", "once_cell", "rustix", "windows-sys 0.61.2", @@ -5195,12 +5317,12 @@ dependencies = [ [[package]] name = "terminal_size" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b8cb979cb11c32ce1603f8137b22262a9d131aaa5c37b5678025f22b8becd0" +checksum = "230a1b821ccbd75b185820a1f1ff7b14d21da1e442e22c0863ea5f08771a8874" dependencies = [ "rustix", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -5225,11 +5347,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.17" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" dependencies = [ - "thiserror-impl 2.0.17", + "thiserror-impl 2.0.18", ] [[package]] @@ -5245,9 +5367,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.17" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", @@ -5307,9 +5429,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" dependencies = [ "displaydoc", "zerovec", @@ -5317,9 +5439,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" +checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" dependencies = [ "tinyvec_macros", ] @@ -5332,24 +5454,24 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.48.0" +version = "1.52.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" +checksum = "b67dee974fe86fd92cc45b7a95fdd2f99a36a6d7b0d431a231178d3d670bbcc6" dependencies = [ "bytes", "libc", "mio", "pin-project-lite", - "socket2 0.6.1", + "socket2 0.6.3", "tokio-macros", "windows-sys 0.61.2", ] [[package]] name = "tokio-macros" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" dependencies = [ "proc-macro2", "quote", @@ -5368,9 +5490,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.17" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" +checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" dependencies = [ "futures-core", "pin-project-lite", @@ -5379,9 +5501,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.17" +version = "0.7.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" dependencies = [ "bytes", "futures-core", @@ -5405,17 +5527,32 @@ dependencies = [ [[package]] name = "toml" -version = "0.9.10+spec-1.1.0" +version = "0.9.12+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0825052159284a1a8b4d6c0c86cbc801f2da5afd2b225fa548c72f2e74002f48" +checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" dependencies = [ "indexmap", "serde_core", - "serde_spanned 1.0.4", + "serde_spanned 1.1.1", "toml_datetime 0.7.5+spec-1.1.0", "toml_parser", "toml_writer", - "winnow", + "winnow 0.7.15", +] + +[[package]] +name = "toml" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" +dependencies = [ + "indexmap", + "serde_core", + "serde_spanned 1.1.1", + "toml_datetime 1.1.1+spec-1.1.0", + "toml_parser", + "toml_writer", + "winnow 1.0.2", ] [[package]] @@ -5436,6 +5573,15 @@ dependencies = [ "serde_core", ] +[[package]] +name = "toml_datetime" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +dependencies = [ + "serde_core", +] + [[package]] name = "toml_edit" version = "0.22.27" @@ -5446,44 +5592,44 @@ dependencies = [ "serde", "serde_spanned 0.6.9", "toml_datetime 0.6.11", - "winnow", + "winnow 0.7.15", ] [[package]] name = "toml_edit" -version = "0.24.0+spec-1.1.0" +version = "0.24.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c740b185920170a6d9191122cafef7010bd6270a3824594bff6784c04d7f09e" +checksum = "01f2eadbbc6b377a847be05f60791ef1058d9f696ecb51d2c07fe911d8569d8e" dependencies = [ "indexmap", "serde_core", - "serde_spanned 1.0.4", + "serde_spanned 1.1.1", "toml_datetime 0.7.5+spec-1.1.0", "toml_parser", "toml_writer", - "winnow", + "winnow 0.7.15", ] [[package]] name = "toml_parser" -version = "1.0.6+spec-1.1.0" +version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" dependencies = [ - "winnow", + "winnow 1.0.2", ] [[package]] name = "toml_writer" -version = "1.0.6+spec-1.1.0" +version = "1.1.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607" +checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" [[package]] name = "tower" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" dependencies = [ "futures-core", "futures-util", @@ -5581,9 +5727,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.22" +version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" +checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" dependencies = [ "matchers", "nu-ansi-term", @@ -5614,9 +5760,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "trybuild" -version = "1.0.114" +version = "1.0.116" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e17e807bff86d2a06b52bca4276746584a78375055b6e45843925ce2802b335" +checksum = "47c635f0191bd3a2941013e5062667100969f8c4e9cd787c14f977265d73616e" dependencies = [ "glob", "serde", @@ -5624,7 +5770,7 @@ dependencies = [ "serde_json", "target-triple", "termcolor", - "toml 0.9.10+spec-1.1.0", + "toml 1.1.2+spec-1.1.0", ] [[package]] @@ -5634,8 +5780,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd9267f90719e0433aae095640b294ff36ccbf89649ecb9ee34464ec504be157" dependencies = [ "arrayvec", - "rand 0.9.2", - "thiserror 2.0.17", + "rand 0.9.4", + "thiserror 2.0.18", "uuid", ] @@ -5647,9 +5793,9 @@ checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" [[package]] name = "typenum" -version = "1.19.0" +version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" [[package]] name = "ubyte" @@ -5683,9 +5829,9 @@ checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" [[package]] name = "unicode-ident" -version = "1.0.22" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] name = "unicode-linebreak" @@ -5710,9 +5856,9 @@ checksum = "7df058c713841ad818f1dc5d3fd88063241cc61f49f5fbea4b951e8cf5a8d71d" [[package]] name = "unicode-segmentation" -version = "1.12.0" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" +checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" [[package]] name = "unicode-width" @@ -5726,6 +5872,12 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + [[package]] name = "unit-prefix" version = "0.5.2" @@ -5762,9 +5914,9 @@ checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae" [[package]] name = "ureq" -version = "3.1.4" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d39cb1dbab692d82a977c0392ffac19e188bd9186a9f32806f0aaa859d75585a" +checksum = "dea7109cdcd5864d4eeb1b58a1648dc9bf520360d7af16ec26d0a9354bafcfc0" dependencies = [ "base64", "cookie_store", @@ -5776,15 +5928,15 @@ dependencies = [ "serde", "serde_json", "ureq-proto", - "utf-8", - "webpki-roots 1.0.4", + "utf8-zero", + "webpki-roots 1.0.7", ] [[package]] name = "ureq-proto" -version = "0.5.3" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d81f9efa9df032be5934a46a068815a10a042b494b6a58cb0a1a97bb5467ed6f" +checksum = "e994ba84b0bd1b1b0cf92878b7ef898a5c1760108fe7b6010327e274917a808c" dependencies = [ "base64", "http", @@ -5794,9 +5946,9 @@ dependencies = [ [[package]] name = "url" -version = "2.5.7" +version = "2.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" dependencies = [ "form_urlencoded", "idna", @@ -5805,10 +5957,10 @@ dependencies = [ ] [[package]] -name = "utf-8" -version = "0.7.6" +name = "utf8-zero" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +checksum = "b8c0a043c9540bae7c578c88f91dda8bd82e59ae27c21baca69c8b191aaf5a6e" [[package]] name = "utf8_iter" @@ -5824,13 +5976,13 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.19.0" +version = "1.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a" +checksum = "ddd74a9687298c6858e9b88ec8935ec45d22e8fd5e6394fa1bd4e99a87789c76" dependencies = [ - "getrandom 0.3.4", + "getrandom 0.4.2", "js-sys", - "rand 0.9.2", + "rand 0.10.1", "serde_core", "wasm-bindgen", ] @@ -5849,15 +6001,15 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "vergen" -version = "9.0.6" +version = "9.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b2bf58be11fc9414104c6d3a2e464163db5ef74b12296bda593cac37b6e4777" +checksum = "b849a1f6d8639e8de261e81ee0fc881e3e3620db1af9f2e0da015d4382ceaf75" dependencies = [ "anyhow", "derive_builder", "rustversion", "time", - "vergen-lib", + "vergen-lib 9.1.0", ] [[package]] @@ -5871,7 +6023,7 @@ dependencies = [ "rustversion", "time", "vergen", - "vergen-lib", + "vergen-lib 0.1.6", ] [[package]] @@ -5885,6 +6037,17 @@ dependencies = [ "rustversion", ] +[[package]] +name = "vergen-lib" +version = "9.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b34a29ba7e9c59e62f229ae1932fb1b8fb8a6fdcc99215a641913f5f5a59a569" +dependencies = [ + "anyhow", + "derive_builder", + "rustversion", +] + [[package]] name = "version_check" version = "0.9.5" @@ -5924,11 +6087,20 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" -version = "1.0.1+wasi-0.2.4" +version = "1.0.3+wasi-0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" dependencies = [ - "wit-bindgen", + "wit-bindgen 0.57.1", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen 0.51.0", ] [[package]] @@ -5939,9 +6111,9 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" [[package]] name = "wasm-bindgen" -version = "0.2.106" +version = "0.2.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" +checksum = "df52b6d9b87e0c74c9edfa1eb2d9bf85e5d63515474513aa50fa181b3c4f5db1" dependencies = [ "cfg-if", "once_cell", @@ -5952,22 +6124,19 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.56" +version = "0.4.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836d9622d604feee9e5de25ac10e3ea5f2d65b41eac0d9ce72eb5deae707ce7c" +checksum = "af934872acec734c2d80e6617bbb5ff4f12b052dd8e6332b0817bce889516084" dependencies = [ - "cfg-if", "js-sys", - "once_cell", "wasm-bindgen", - "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.106" +version = "0.2.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" +checksum = "78b1041f495fb322e64aca85f5756b2172e35cd459376e67f2a6c9dffcedb103" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -5975,9 +6144,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.106" +version = "0.2.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" +checksum = "9dcd0ff20416988a18ac686d4d4d0f6aae9ebf08a389ff5d29012b05af2a1b41" dependencies = [ "bumpalo", "proc-macro2", @@ -5988,13 +6157,47 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.106" +version = "0.2.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" +checksum = "49757b3c82ebf16c57d69365a142940b384176c24df52a087fb748e2085359ea" dependencies = [ "unicode-ident", ] +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + [[package]] name = "wasmtimer" version = "0.4.3" @@ -6011,9 +6214,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.83" +version = "0.3.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac" +checksum = "2eadbac71025cd7b0834f20d1fe8472e8495821b4e9801eb0a60bd1f19827602" dependencies = [ "js-sys", "wasm-bindgen", @@ -6031,9 +6234,9 @@ dependencies = [ [[package]] name = "webpki-root-certs" -version = "1.0.4" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee3e3b5f5e80bc89f30ce8d0343bf4e5f12341c51f3e26cbeecbc7c85443e85b" +checksum = "f31141ce3fc3e300ae89b78c0dd67f9708061d1d2eda54b8209346fd6be9a92c" dependencies = [ "rustls-pki-types", ] @@ -6044,14 +6247,14 @@ version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" dependencies = [ - "webpki-roots 1.0.4", + "webpki-roots 1.0.7", ] [[package]] name = "webpki-roots" -version = "1.0.4" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2878ef029c47c6e8cf779119f20fcf52bde7ad42a731b2a304bc221df17571e" +checksum = "52f5ee44c96cf55f1b349600768e3ece3a8f26010c05265ab73f945bb1a2eb9d" dependencies = [ "rustls-pki-types", ] @@ -6380,24 +6583,121 @@ checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" [[package]] name = "winnow" -version = "0.7.14" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" +dependencies = [ + "memchr", +] + +[[package]] +name = "winnow" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" +checksum = "2ee1708bef14716a11bae175f579062d4554d95be2c6829f518df847b7b3fdd0" dependencies = [ "memchr", ] [[package]] name = "wit-bindgen" -version = "0.46.0" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] [[package]] name = "writeable" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" +checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" [[package]] name = "xattr" @@ -6453,9 +6753,9 @@ checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" [[package]] name = "yoke" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" dependencies = [ "stable_deref_trait", "yoke-derive", @@ -6464,9 +6764,9 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" dependencies = [ "proc-macro2", "quote", @@ -6476,18 +6776,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.31" +version = "0.8.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3" +checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.31" +version = "0.8.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" +checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" dependencies = [ "proc-macro2", "quote", @@ -6496,18 +6796,18 @@ dependencies = [ [[package]] name = "zerofrom" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" dependencies = [ "proc-macro2", "quote", @@ -6523,9 +6823,9 @@ checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" [[package]] name = "zerotrie" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" dependencies = [ "displaydoc", "yoke", @@ -6534,9 +6834,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.5" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" dependencies = [ "yoke", "zerofrom", @@ -6545,9 +6845,9 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" dependencies = [ "proc-macro2", "quote", @@ -6569,10 +6869,16 @@ dependencies = [ "indexmap", "lzma-rs", "memchr", - "thiserror 2.0.17", + "thiserror 2.0.18", "zopfli", ] +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" + [[package]] name = "zopfli" version = "0.8.3" diff --git a/Cargo.toml b/Cargo.toml index 8752cebca..9d6899f41 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,6 @@ members = [ "compiler/pavex*", "compiler/generate_from_path", "compiler/persist_if_changed", - "rustdoc/*", "px_workspace_hack", ] resolver = "3" @@ -36,9 +35,6 @@ pavex_test_runner = { path = "compiler/pavex_test_runner", version = "0.2.7" } pavexc = { path = "compiler/pavexc", version = "0.2.10" } pavexc_attr_parser = { path = "compiler/pavexc_attr_parser", version = "0.2.10" } pavexc_cli_client = { path = "compiler/pavexc_cli_client", version = "0.2.10" } -rustdoc_ir = { path = "rustdoc/rustdoc_ir", version = "0.2.10" } -rustdoc_processor = { path = "rustdoc/rustdoc_processor", version = "0.2.10" } -rustdoc_resolver = { path = "rustdoc/rustdoc_resolver", version = "0.2.10" } pavexc_annotations = { path = "compiler/pavexc_annotations", version = "0.2.10" } persist_if_changed = { path = "compiler/persist_if_changed", version = "0.2.10" } @@ -125,6 +121,10 @@ reqwest = { version = "0.12", default-features = false, features = [ reqwest-middleware = "0.4" reqwest-retry = "0.8.0" reqwest-tracing = "0.5.8" +rustdoc_ext = "0.1.0" +rustdoc_ir = "0.1.0" +rustdoc_processor = "0.1.0" +rustdoc_resolver = "0.1.0" rustls = { version = "0.23", default-features = false } rustls-platform-verifier = "0.6.2" ring = "0.17.14" diff --git a/compiler/pavexc/Cargo.toml b/compiler/pavexc/Cargo.toml index cee3aa619..79e3ebf86 100644 --- a/compiler/pavexc/Cargo.toml +++ b/compiler/pavexc/Cargo.toml @@ -10,12 +10,6 @@ license.workspace = true [lints] clippy = { large_enum_variant = "allow", result_large_err = "allow" } -[build-dependencies] -xxhash-rust = { workspace = true, features = ["xxh64"] } -globwalk = { workspace = true } -anyhow = { workspace = true } -toml = { workspace = true } - [features] # Enable additional debug assertions to ensure correctness # Some of these assertions can be computationally expensive, @@ -26,11 +20,11 @@ debug_assertions = [] [dependencies] pavex = { workspace = true } rustdoc_ir = { workspace = true } -rustdoc_ext = { path = "../../rustdoc/rustdoc_ext", version = "=0.2.10" } +rustdoc_ext = { workspace = true } pavexc_annotations = { path = "../pavexc_annotations", version = "=0.2.10" } pavexc_attr_parser = { path = "../pavexc_attr_parser", version = "=0.2.10" } -rustdoc_processor = { path = "../../rustdoc/rustdoc_processor", version = "=0.2.10" } -rustdoc_resolver = { path = "../../rustdoc/rustdoc_resolver", version = "=0.2.10" } +rustdoc_processor = { workspace = true } +rustdoc_resolver = { workspace = true } pavex_bp_schema = { path = "../pavex_bp_schema", version = "=0.2.10" } pavex_cli_shell = { path = "../pavex_cli_shell", version = "=0.2.10" } pavex_cli_diagnostic = { path = "../pavex_cli_diagnostic", version = "=0.2.10" } diff --git a/compiler/pavexc/build.rs b/compiler/pavexc/build.rs deleted file mode 100644 index 75f291afc..000000000 --- a/compiler/pavexc/build.rs +++ /dev/null @@ -1,114 +0,0 @@ -use std::collections::BTreeSet; -use std::path::{Path, PathBuf}; - -use anyhow::{Context, Result}; - -pub fn main() -> Result<()> { - // Compute checksum of rustdoc_processor and its local dependencies. - // This checksum is used as part of the cache fingerprint to ensure - // the cache invalidates when the caching logic or serialized types change. - let base_path = Path::new(env!("CARGO_MANIFEST_DIR")).join(".."); - let cache_crate_path = base_path.join("../rustdoc/rustdoc_processor"); - - // Find all local crates that rustdoc_processor depends on (transitively) - let crates_to_checksum = collect_local_dependencies(&cache_crate_path)?; - - let mut combined_hasher = xxhash_rust::xxh64::Xxh64::new(24); - for crate_path in &crates_to_checksum { - let checksum = checksum_directory(crate_path)?; - combined_hasher.update(&checksum.to_le_bytes()); - - // Rerun if any of these crates change - println!("cargo::rerun-if-changed={}/src", crate_path.display()); - println!( - "cargo::rerun-if-changed={}/Cargo.toml", - crate_path.display() - ); - } - - let checksum = combined_hasher.digest(); - println!("cargo::rustc-env=RUSTDOC_CACHE_SOURCE_HASH={checksum:x}"); - - Ok(()) -} - -/// Collect all local path dependencies of a crate, including the crate itself. -/// This is done recursively to capture transitive local dependencies. -fn collect_local_dependencies(crate_path: &Path) -> Result> { - let mut visited = BTreeSet::new(); - let mut to_visit = vec![crate_path.to_path_buf()]; - - while let Some(current) = to_visit.pop() { - let canonical = current - .canonicalize() - .with_context(|| format!("Failed to canonicalize path: {}", current.display()))?; - - if !visited.insert(canonical.clone()) { - continue; - } - - // Parse Cargo.toml to find path dependencies - let cargo_toml_path = canonical.join("Cargo.toml"); - let cargo_toml_content = std::fs::read_to_string(&cargo_toml_path) - .with_context(|| format!("Failed to read {}", cargo_toml_path.display()))?; - - let cargo_toml: toml::Table = toml::from_str(&cargo_toml_content) - .with_context(|| format!("Failed to parse {}", cargo_toml_path.display()))?; - - // Check [dependencies] section for path dependencies - if let Some(toml::Value::Table(deps)) = cargo_toml.get("dependencies") { - for (_name, value) in deps { - if let Some(path) = value.get("path").and_then(|p| p.as_str()) { - let dep_path = canonical.join(path); - if dep_path.exists() { - to_visit.push(dep_path); - } - } - } - } - } - - Ok(visited) -} - -/// Checksum the contents of a crate directory. -fn checksum_directory(root_path: &Path) -> Result { - let paths = get_file_paths(root_path)?; - - let mut hasher = xxhash_rust::xxh64::Xxh64::new(24); - for path in paths { - let contents = std::fs::read(&path) - .with_context(|| format!("Failed to read file at `{}`", path.display()))?; - hasher.update(&contents); - // Include the file path in the hash to detect renames - if let Ok(relative) = path.strip_prefix(root_path) { - hasher.update(relative.to_string_lossy().as_bytes()); - } - } - Ok(hasher.digest()) -} - -/// Get all source files in a crate directory. -fn get_file_paths(root_dir: &Path) -> Result> { - let root_dir = root_dir - .canonicalize() - .context("Failed to canonicalize the path to the root directory")?; - - let patterns = vec!["src/**/*.rs", "Cargo.toml"]; - - let glob_walker = globwalk::GlobWalkerBuilder::from_patterns(&root_dir, &patterns).build()?; - - let included_files: BTreeSet = glob_walker - .into_iter() - .filter_map(|entry| { - let Ok(entry) = entry else { - return None; - }; - if !entry.file_type().is_file() { - return None; - } - Some(entry.into_path()) - }) - .collect(); - Ok(included_files) -} diff --git a/compiler/pavexc/src/rustdoc/cache.rs b/compiler/pavexc/src/rustdoc/cache.rs index 2166ae990..85f22cdac 100644 --- a/compiler/pavexc/src/rustdoc/cache.rs +++ b/compiler/pavexc/src/rustdoc/cache.rs @@ -12,16 +12,11 @@ pub(crate) fn pavex_rustdoc_cache( cache_workspace_package_docs: bool, package_graph: &PackageGraph, ) -> Result, anyhow::Error> { - let fingerprint = format!( - "{}-{}", - rustdoc_processor::CRATE_VERSION, - env!("RUSTDOC_CACHE_SOURCE_HASH") - ); let cache_dir = xdg_home::home_dir() .ok_or_else(|| anyhow::anyhow!("Failed to get the user's home directory"))? .join(".pavex/rustdoc/cache"); RustdocGlobalFsCache::new( - &fingerprint, + rustdoc_processor::CRATE_VERSION, toolchain_name, cache_workspace_package_docs, package_graph, diff --git a/compiler/pavexc_annotations/Cargo.toml b/compiler/pavexc_annotations/Cargo.toml index e85e47926..9c962d8c7 100644 --- a/compiler/pavexc_annotations/Cargo.toml +++ b/compiler/pavexc_annotations/Cargo.toml @@ -14,7 +14,7 @@ clippy = { large_enum_variant = "allow", result_large_err = "allow" } [dependencies] # Core dependencies rustdoc-types = { workspace = true } -rustdoc_ext = { path = "../../rustdoc/rustdoc_ext", version = "=0.2.10" } +rustdoc_ext = { workspace = true } pavexc_attr_parser = { path = "../pavexc_attr_parser", version = "=0.2.10" } pavex_bp_schema = { path = "../pavex_bp_schema", version = "=0.2.10" } diff --git a/docs/examples/workspace_hack/Cargo.toml b/docs/examples/workspace_hack/Cargo.toml index de8688009..03cbc9fe3 100644 --- a/docs/examples/workspace_hack/Cargo.toml +++ b/docs/examples/workspace_hack/Cargo.toml @@ -28,7 +28,6 @@ rustls = { version = "0.23", default-features = false, features = ["logging", "r serde = { version = "1", features = ["alloc", "derive", "rc"] } serde_core = { version = "1", features = ["alloc", "rc"] } serde_json = { version = "1", features = ["raw_value", "unbounded_depth"] } -slab = { version = "0.4" } smallvec = { version = "1", default-features = false, features = ["const_new", "serde"] } stable_deref_trait = { version = "1" } subtle = { version = "2" } @@ -48,7 +47,6 @@ rustls = { version = "0.23", default-features = false, features = ["logging", "r serde = { version = "1", features = ["alloc", "derive", "rc"] } serde_core = { version = "1", features = ["alloc", "rc"] } serde_json = { version = "1", features = ["raw_value", "unbounded_depth"] } -slab = { version = "0.4" } smallvec = { version = "1", default-features = false, features = ["const_new", "serde"] } stable_deref_trait = { version = "1" } subtle = { version = "2" } diff --git a/px_workspace_hack/Cargo.toml b/px_workspace_hack/Cargo.toml index 963f6bb8f..2a4926ad3 100644 --- a/px_workspace_hack/Cargo.toml +++ b/px_workspace_hack/Cargo.toml @@ -21,7 +21,6 @@ ahash = { version = "0.8" } aho-corasick = { version = "1" } base64 = { version = "0.22" } bincode = { version = "2", features = ["serde"] } -bitflags = { version = "2", default-features = false, features = ["serde"] } byteorder = { version = "1" } clap = { version = "4", features = ["derive", "env"] } clap_builder = { version = "4", default-features = false, features = ["color", "env", "help", "std", "suggestions", "usage"] } @@ -73,6 +72,7 @@ serde_core = { version = "1", features = ["alloc", "rc"] } serde_json = { version = "1", features = ["raw_value", "unbounded_depth"] } serde_spanned = { version = "1" } sha2 = { version = "0.10", features = ["oid"] } +slab = { version = "0.4" } smallvec = { version = "1", default-features = false, features = ["const_new", "serde"] } spin = { version = "0.9" } sqlx = { version = "0.8", features = ["mysql", "postgres", "runtime-tokio-rustls", "sqlite", "uuid"] } @@ -86,15 +86,18 @@ syn = { version = "2", features = ["extra-traits", "fold", "full", "visit", "vis textwrap = { version = "0.16" } time = { version = "0.3", features = ["formatting", "local-offset", "macros", "parsing"] } tokio = { version = "1", features = ["fs", "io-util", "macros", "net", "rt-multi-thread", "sync", "time"] } -toml = { version = "0.9", features = ["preserve_order"] } -toml_datetime = { version = "0.7", features = ["serde"] } +toml-274715c4dabd11b0 = { package = "toml", version = "0.9", features = ["preserve_order"] } +toml-dff4ba8e3ae991db = { package = "toml", version = "1" } +toml_datetime-ca01ad9e24f5d932 = { package = "toml_datetime", version = "0.7", features = ["serde"] } +toml_datetime-dff4ba8e3ae991db = { package = "toml_datetime", version = "1", default-features = false, features = ["serde", "std"] } toml_parser = { version = "1" } toml_writer = { version = "1" } tracing = { version = "0.1", features = ["log"] } tracing-core = { version = "0.1" } tracing-log = { version = "0.2" } uuid = { version = "1", features = ["fast-rng", "serde", "v4", "v7"] } -winnow = { version = "0.7" } +winnow-ca01ad9e24f5d932 = { package = "winnow", version = "0.7" } +winnow-dff4ba8e3ae991db = { package = "winnow", version = "1" } zeroize = { version = "1" } [build-dependencies] @@ -102,7 +105,6 @@ ahash = { version = "0.8" } aho-corasick = { version = "1" } base64 = { version = "0.22" } bincode = { version = "2", features = ["serde"] } -bitflags = { version = "2", default-features = false, features = ["serde"] } byteorder = { version = "1" } clap = { version = "4", features = ["derive", "env"] } clap_builder = { version = "4", default-features = false, features = ["color", "env", "help", "std", "suggestions", "usage"] } @@ -154,6 +156,7 @@ serde_core = { version = "1", features = ["alloc", "rc"] } serde_json = { version = "1", features = ["raw_value", "unbounded_depth"] } serde_spanned = { version = "1" } sha2 = { version = "0.10", features = ["oid"] } +slab = { version = "0.4" } smallvec = { version = "1", default-features = false, features = ["const_new", "serde"] } spin = { version = "0.9" } sqlx = { version = "0.8", features = ["mysql", "postgres", "runtime-tokio-rustls", "sqlite", "uuid"] } @@ -169,15 +172,18 @@ syn = { version = "2", features = ["extra-traits", "fold", "full", "visit", "vis textwrap = { version = "0.16" } time = { version = "0.3", features = ["formatting", "local-offset", "macros", "parsing"] } tokio = { version = "1", features = ["fs", "io-util", "macros", "net", "rt-multi-thread", "sync", "time"] } -toml = { version = "0.9", features = ["preserve_order"] } -toml_datetime = { version = "0.7", features = ["serde"] } +toml-274715c4dabd11b0 = { package = "toml", version = "0.9", features = ["preserve_order"] } +toml-dff4ba8e3ae991db = { package = "toml", version = "1" } +toml_datetime-ca01ad9e24f5d932 = { package = "toml_datetime", version = "0.7", features = ["serde"] } +toml_datetime-dff4ba8e3ae991db = { package = "toml_datetime", version = "1", default-features = false, features = ["serde", "std"] } toml_parser = { version = "1" } toml_writer = { version = "1" } tracing = { version = "0.1", features = ["log"] } tracing-core = { version = "0.1" } tracing-log = { version = "0.2" } uuid = { version = "1", features = ["fast-rng", "serde", "v4", "v7"] } -winnow = { version = "0.7" } +winnow-ca01ad9e24f5d932 = { package = "winnow", version = "0.7" } +winnow-dff4ba8e3ae991db = { package = "winnow", version = "1" } zeroize = { version = "1" } ### END HAKARI SECTION diff --git a/rustdoc/rustdoc_ext/Cargo.toml b/rustdoc/rustdoc_ext/Cargo.toml deleted file mode 100644 index 097764882..000000000 --- a/rustdoc/rustdoc_ext/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "rustdoc_ext" -description = "Extension traits for rustdoc_types" -edition.workspace = true -repository.workspace = true -homepage.workspace = true -license.workspace = true -version.workspace = true - -[dependencies] -guppy = { workspace = true } -rustdoc-types = { workspace = true } diff --git a/rustdoc/rustdoc_ext/src/global_item_id.rs b/rustdoc/rustdoc_ext/src/global_item_id.rs deleted file mode 100644 index ec953a76b..000000000 --- a/rustdoc/rustdoc_ext/src/global_item_id.rs +++ /dev/null @@ -1,25 +0,0 @@ -use guppy::PackageId; - -/// An identifier that unequivocally points to a type within a crate collection. -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct GlobalItemId { - pub rustdoc_item_id: rustdoc_types::Id, - pub package_id: PackageId, -} - -impl GlobalItemId { - pub fn new(rustdoc_item_id: rustdoc_types::Id, package_id: PackageId) -> Self { - Self { - rustdoc_item_id, - package_id, - } - } - - pub fn package_id(&self) -> &PackageId { - &self.package_id - } - - pub fn rustdoc_item_id(&self) -> &rustdoc_types::Id { - &self.rustdoc_item_id - } -} diff --git a/rustdoc/rustdoc_ext/src/lib.rs b/rustdoc/rustdoc_ext/src/lib.rs deleted file mode 100644 index fe24db01c..000000000 --- a/rustdoc/rustdoc_ext/src/lib.rs +++ /dev/null @@ -1,127 +0,0 @@ -//! Extension traits for types in `rustdoc_types`. -//! -//! This crate provides utility traits that augment the types from our fork of `rustdoc-types`. -//! We keep these extensions separate to maintain `rustdoc_types` as close to upstream -//! as possible. - -mod global_item_id; -pub use global_item_id::GlobalItemId; - -use rustdoc_types::{ItemEnum, ItemKind, MacroKind}; - -/// Extension trait for `ItemEnum` to get the corresponding `ItemKind`. -pub trait ItemEnumExt { - /// Returns the `ItemKind` corresponding to this `ItemEnum` variant. - fn item_kind(&self) -> ItemKind; -} - -impl ItemEnumExt for ItemEnum { - fn item_kind(&self) -> ItemKind { - match self { - ItemEnum::Module(_) => ItemKind::Module, - ItemEnum::ExternCrate { .. } => ItemKind::ExternCrate, - ItemEnum::Use(_) => ItemKind::Use, - ItemEnum::Union(_) => ItemKind::Union, - ItemEnum::Struct(_) => ItemKind::Struct, - ItemEnum::StructField(_) => ItemKind::StructField, - ItemEnum::Enum(_) => ItemKind::Enum, - ItemEnum::Variant(_) => ItemKind::Variant, - ItemEnum::Function(_) => ItemKind::Function, - ItemEnum::Trait(_) => ItemKind::Trait, - ItemEnum::TraitAlias(_) => ItemKind::TraitAlias, - ItemEnum::Impl(_) => ItemKind::Impl, - ItemEnum::TypeAlias(_) => ItemKind::TypeAlias, - ItemEnum::Constant { .. } => ItemKind::Constant, - ItemEnum::Static(_) => ItemKind::Static, - ItemEnum::ExternType => ItemKind::ExternType, - ItemEnum::Macro(_) => ItemKind::Macro, - ItemEnum::ProcMacro(pm) => match pm.kind { - MacroKind::Bang => ItemKind::Macro, - MacroKind::Attr => ItemKind::ProcAttribute, - MacroKind::Derive => ItemKind::ProcDerive, - }, - ItemEnum::Primitive(_) => ItemKind::Primitive, - ItemEnum::AssocConst { .. } => ItemKind::AssocConst, - ItemEnum::AssocType { .. } => ItemKind::AssocType, - } - } -} - -/// Extension trait for `ItemEnum` to get a human-readable description of the item kind. -pub trait RustdocKindExt { - /// Return a human-readable string description of this item's kind (e.g. "a function"). - fn kind(&self) -> &'static str; -} - -impl RustdocKindExt for ItemEnum { - fn kind(&self) -> &'static str { - match self { - ItemEnum::Module(_) => "a module", - ItemEnum::ExternCrate { .. } => "an external crate", - ItemEnum::Use(_) => "an import", - ItemEnum::Union(_) => "a union", - ItemEnum::Struct(_) => "a struct", - ItemEnum::StructField(_) => "a struct field", - ItemEnum::Enum(_) => "an enum", - ItemEnum::Variant(_) => "an enum variant", - ItemEnum::Function(func) => { - if let Some((param, _)) = func.sig.inputs.first() - && param == "self" - { - "a method" - } else { - "a function" - } - } - ItemEnum::Trait(_) => "a trait", - ItemEnum::TraitAlias(_) => "a trait alias", - ItemEnum::Impl(_) => "an impl block", - ItemEnum::TypeAlias(_) => "a type alias", - ItemEnum::Constant { .. } => "a constant", - ItemEnum::Static(_) => "a static", - ItemEnum::ExternType => "a foreign type", - ItemEnum::Macro(_) => "a macro", - ItemEnum::ProcMacro(_) => "a procedural macro", - ItemEnum::Primitive(_) => "a primitive type", - ItemEnum::AssocConst { .. } => "an associated constant", - ItemEnum::AssocType { .. } => "an associated type", - } - } -} - -/// Extension trait for `ItemKind` to get human-readable descriptions. -pub trait ItemKindExt { - /// Return the plural form of this item kind (e.g. "functions", "structs"). - fn plural(&self) -> &'static str; -} - -impl ItemKindExt for ItemKind { - fn plural(&self) -> &'static str { - match self { - ItemKind::Module => "modules", - ItemKind::ExternCrate => "extern crate declarations", - ItemKind::Use => "use declarations", - ItemKind::Struct => "structs", - ItemKind::StructField => "struct fields", - ItemKind::Union => "unions", - ItemKind::Enum => "enums", - ItemKind::Variant => "enum variants", - ItemKind::Function => "functions", - ItemKind::TypeAlias => "type aliases", - ItemKind::Constant => "constants", - ItemKind::Trait => "traits", - ItemKind::TraitAlias => "trait aliases", - ItemKind::Impl => "impl blocks", - ItemKind::Static => "statics", - ItemKind::ExternType => "extern types", - ItemKind::Macro => "macros", - ItemKind::ProcAttribute => "proc macro attributes", - ItemKind::ProcDerive => "derive macros", - ItemKind::AssocConst => "associated constants", - ItemKind::AssocType => "associated types", - ItemKind::Primitive => "primitive types", - ItemKind::Keyword => "keywords", - ItemKind::Attribute => "attributes", - } - } -} diff --git a/rustdoc/rustdoc_ir/Cargo.toml b/rustdoc/rustdoc_ir/Cargo.toml deleted file mode 100644 index 95d5b2ca1..000000000 --- a/rustdoc/rustdoc_ir/Cargo.toml +++ /dev/null @@ -1,21 +0,0 @@ -[package] -name = "rustdoc_ir" -description = "An IR for Rust types and functions" -edition.workspace = true -repository.workspace = true -homepage.workspace = true -license.workspace = true -version.workspace = true - -[dependencies] -ahash = { workspace = true } -anyhow = { workspace = true } -bimap = { workspace = true } -guppy = { workspace = true } -indexmap = { workspace = true, features = ["serde"] } -serde = { workspace = true, features = ["derive"] } -syn = { workspace = true } -rustdoc-types = { workspace = true } -rustdoc_ext = { path = "../rustdoc_ext" } -thiserror = { workspace = true } -tracing = { workspace = true } diff --git a/rustdoc/rustdoc_ir/src/array.rs b/rustdoc/rustdoc_ir/src/array.rs deleted file mode 100644 index 04aa0ae35..000000000 --- a/rustdoc/rustdoc_ir/src/array.rs +++ /dev/null @@ -1,18 +0,0 @@ -use std::fmt::{Debug, Formatter}; - -use crate::Type; - -#[derive(serde::Serialize, serde::Deserialize, Eq, PartialEq, Hash, Clone)] -/// A Rust fixed-size array—e.g. `[u8; 4]`. -pub struct Array { - /// The type of each element in the array. - pub element_type: Box, - /// The number of elements in the array. - pub len: usize, -} - -impl Debug for Array { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "[{:?}; {}]", self.element_type, self.len) - } -} diff --git a/rustdoc/rustdoc_ir/src/callable.rs b/rustdoc/rustdoc_ir/src/callable.rs deleted file mode 100644 index 8d870e3dc..000000000 --- a/rustdoc/rustdoc_ir/src/callable.rs +++ /dev/null @@ -1,460 +0,0 @@ -use std::collections::BTreeMap; -use std::collections::BTreeSet; -use std::fmt::Formatter; -use std::fmt::Write; - -use ahash::HashMap; -use bimap::BiHashMap; -use guppy::PackageId; -use indexmap::IndexSet; - -use crate::{ - EnumVariantConstructorPath, FreeFunctionPath, InherentMethodPath, Lifetime, StructLiteralPath, - TraitMethodPath, Type, -}; -use rustdoc_ext::GlobalItemId; - -/// A valid Rust identifier. -#[derive(Clone, Hash, Eq, PartialEq, Debug)] -pub struct RustIdentifier(String); - -impl RustIdentifier { - /// Create a new [`RustIdentifier`]. - /// - /// # Panics - /// - /// Panics if `name` is not a valid Rust identifier (`[a-zA-Z_][a-zA-Z0-9_]*`). - pub fn new(name: String) -> Self { - assert!( - Self::is_valid_identifier(&name), - "Invalid identifier: `{name}`" - ); - Self(name) - } - - /// Returns `true` if `name` matches `[a-zA-Z_][a-zA-Z0-9_]*`. - fn is_valid_identifier(name: &str) -> bool { - let mut chars = name.chars(); - match chars.next() { - Some(c) if c.is_ascii_alphabetic() || c == '_' => {} - _ => return false, - } - chars.all(|c| c.is_ascii_alphanumeric() || c == '_') - } - - pub fn as_str(&self) -> &str { - &self.0 - } -} - -impl std::fmt::Display for RustIdentifier { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str(&self.0) - } -} - -/// A named input parameter of a [`Callable`]. -#[derive(Clone, Hash, Eq, PartialEq, Debug)] -pub struct CallableInput { - pub name: RustIdentifier, - pub type_: Type, -} - -// ── Shared pieces ────────────────────────────────────────── - -/// Fields specific to callables that use function-call syntax. -#[derive(Clone, Hash, Eq, PartialEq, Debug)] -pub struct FnHeader { - pub output: Option, - pub inputs: Vec, - pub is_async: bool, - pub abi: rustdoc_types::Abi, - pub is_unsafe: bool, - pub is_c_variadic: bool, - pub symbol_name: Option, -} - -// ── Per-variant structs ──────────────────────────────────── - -#[derive(Clone, Hash, Eq, PartialEq, Debug)] -pub struct FreeFunction { - pub path: FreeFunctionPath, - pub header: FnHeader, - pub source_coordinates: Option, -} - -#[derive(Clone, Hash, Eq, PartialEq, Debug)] -pub struct InherentMethod { - pub path: InherentMethodPath, - pub header: FnHeader, - pub source_coordinates: Option, - pub takes_self_as_ref: bool, -} - -#[derive(Clone, Hash, Eq, PartialEq, Debug)] -pub struct TraitMethod { - pub path: TraitMethodPath, - pub header: FnHeader, - pub source_coordinates: Option, - pub takes_self_as_ref: bool, -} - -#[derive(Clone, Hash, Eq, PartialEq, Debug)] -pub struct StructLiteralInit { - pub path: StructLiteralPath, - pub self_: Option, - pub fields: Vec, - pub source_coordinates: Option, - /// Extra fields injected during codegen (e.g. `next` for middleware state). - pub extra_field2default_value: BTreeMap, -} - -#[derive(Clone, Hash, Eq, PartialEq, Debug)] -pub struct EnumVariantInit { - pub path: EnumVariantConstructorPath, - pub self_: Option, - pub fields: Vec, - pub source_coordinates: Option, -} - -// ── The enum ─────────────────────────────────────────────── - -/// A Rust type that can be invoked—e.g. a function, a method, a struct literal constructor. -#[derive(Clone, Hash, Eq, PartialEq)] -pub enum Callable { - FreeFunction(FreeFunction), - InherentMethod(InherentMethod), - TraitMethod(TraitMethod), - StructLiteralInit(StructLiteralInit), - EnumVariantInit(EnumVariantInit), -} - -impl Callable { - // ── Accessors ────────────────────────────────────────── - - pub fn output(&self) -> Option<&Type> { - match self { - Callable::FreeFunction(f) => f.header.output.as_ref(), - Callable::InherentMethod(m) => m.header.output.as_ref(), - Callable::TraitMethod(m) => m.header.output.as_ref(), - Callable::StructLiteralInit(s) => s.self_.as_ref(), - Callable::EnumVariantInit(e) => e.self_.as_ref(), - } - } - - pub fn inputs(&self) -> &[CallableInput] { - match self { - Callable::FreeFunction(f) => &f.header.inputs, - Callable::InherentMethod(m) => &m.header.inputs, - Callable::TraitMethod(m) => &m.header.inputs, - Callable::StructLiteralInit(s) => &s.fields, - Callable::EnumVariantInit(e) => &e.fields, - } - } - - pub fn source_coordinates(&self) -> Option<&GlobalItemId> { - match self { - Callable::FreeFunction(f) => f.source_coordinates.as_ref(), - Callable::InherentMethod(m) => m.source_coordinates.as_ref(), - Callable::TraitMethod(m) => m.source_coordinates.as_ref(), - Callable::StructLiteralInit(s) => s.source_coordinates.as_ref(), - Callable::EnumVariantInit(e) => e.source_coordinates.as_ref(), - } - } - - /// Returns an iterator over the types of the input parameters. - pub fn input_types(&self) -> impl Iterator + '_ { - self.inputs().iter().map(|i| &i.type_) - } - - /// Returns `true` if this callable is fallible—i.e. if it returns a `Result` type. - pub fn is_fallible(&self) -> bool { - if let Some(output) = self.output() { - output.is_result() - } else { - false - } - } - - /// Returns `true` if the callable declaration uses the `async` keyword. - pub fn is_async(&self) -> bool { - match self { - Callable::FreeFunction(f) => f.header.is_async, - Callable::InherentMethod(m) => m.header.is_async, - Callable::TraitMethod(m) => m.header.is_async, - Callable::StructLiteralInit(_) | Callable::EnumVariantInit(_) => false, - } - } - - // ── Path accessors ───────────────────────────────────── - - pub fn package_id(&self) -> &PackageId { - match self { - Callable::FreeFunction(f) => &f.path.package_id, - Callable::InherentMethod(m) => &m.path.package_id, - Callable::TraitMethod(m) => &m.path.package_id, - Callable::StructLiteralInit(s) => &s.path.package_id, - Callable::EnumVariantInit(e) => &e.path.package_id, - } - } - - pub fn render_as_expression_path( - &self, - id2name: &BiHashMap, - buffer: &mut String, - ) { - match self { - Callable::FreeFunction(f) => f.path.render_path(id2name, buffer), - Callable::InherentMethod(m) => m.path.render_path(id2name, buffer), - Callable::TraitMethod(m) => m.path.render_path(id2name, buffer), - Callable::StructLiteralInit(s) => s.path.render_path(id2name, buffer), - Callable::EnumVariantInit(e) => e.path.render_path(id2name, buffer), - } - } - - pub fn render_for_error(&self, buffer: &mut String) { - match self { - Callable::FreeFunction(f) => f.path.render_for_error(buffer), - Callable::InherentMethod(m) => m.path.render_for_error(buffer), - Callable::TraitMethod(m) => m.path.render_for_error(buffer), - Callable::StructLiteralInit(s) => s.path.render_for_error(buffer), - Callable::EnumVariantInit(e) => e.path.render_for_error(buffer), - } - } - - // ── Existing methods ─────────────────────────────────── - - /// Returns the set of all unassigned generic type parameters in this callable. - #[allow(unused)] - pub fn unassigned_generic_type_parameters(&self) -> IndexSet { - let mut result = IndexSet::new(); - for input in self.input_types() { - result.extend(input.unassigned_generic_type_parameters()); - } - if let Some(output) = self.output() { - result.extend(output.unassigned_generic_type_parameters()); - } - result - } - - /// Replace all unassigned generic type parameters in this callable with the - /// concrete types specified in `bindings`. - pub fn bind_generic_type_parameters(&self, bindings: &HashMap) -> Callable { - let inputs: Vec = self - .inputs() - .iter() - .map(|i| CallableInput { - name: i.name.clone(), - type_: i.type_.bind_generic_type_parameters(bindings), - }) - .collect(); - let output = self - .output() - .map(|t| t.bind_generic_type_parameters(bindings)); - let mut result = self.clone(); - match &mut result { - Callable::FreeFunction(f) => { - f.header.inputs = inputs; - f.header.output = output; - } - Callable::InherentMethod(m) => { - m.header.inputs = inputs; - m.header.output = output; - } - Callable::TraitMethod(m) => { - m.header.inputs = inputs; - m.header.output = output; - } - Callable::StructLiteralInit(s) => { - s.fields = inputs; - s.self_ = output; - } - Callable::EnumVariantInit(e) => { - e.fields = inputs; - e.self_ = output; - } - } - result - } - - /// Returns a new [`Callable`] where all lifetime parameters in the - /// output type (if present) are explicitly named. - pub fn unelide_output_lifetimes(&self) -> Self { - // Struct literals and enum variant constructors don't have lifetime elision. - if matches!( - self, - Callable::StructLiteralInit(_) | Callable::EnumVariantInit(_) - ) { - return self.clone(); - } - - let Some(output) = self.output() else { - return self.clone(); - }; - if !output.has_implicit_lifetime_parameters() { - return self.clone(); - } - - let mut elided_output_lifetime: String = "elided".to_string(); - let mut inputs = self.inputs().to_vec(); - for input in inputs.iter_mut() { - if input.type_.has_implicit_lifetime_parameters() { - elided_output_lifetime = { - let mut named_lifetime_parameters = BTreeSet::new(); - for input in self.input_types() { - named_lifetime_parameters.extend(input.named_lifetime_parameters()); - } - named_lifetime_parameters.extend(output.named_lifetime_parameters()); - - if named_lifetime_parameters.contains("elided") { - let last = named_lifetime_parameters.last().unwrap(); - format!("{last}_") - } else { - "elided".to_string() - } - }; - - input - .type_ - .set_implicit_lifetimes(elided_output_lifetime.clone()); - break; - } - - let named_params = input.type_.named_lifetime_parameters(); - if !named_params.is_empty() { - elided_output_lifetime = named_params.first().unwrap().to_string(); - break; - } - } - - let mut output = output.clone(); - output.set_implicit_lifetimes(elided_output_lifetime); - - let mut result = self.clone(); - match &mut result { - Callable::FreeFunction(f) => { - f.header.output = Some(output); - f.header.inputs = inputs; - } - Callable::InherentMethod(m) => { - m.header.output = Some(output); - m.header.inputs = inputs; - } - Callable::TraitMethod(m) => { - m.header.output = Some(output); - m.header.inputs = inputs; - } - // Early return above ensures we never reach here. - Callable::StructLiteralInit(_) | Callable::EnumVariantInit(_) => unreachable!(), - } - result - } - - /// Returns the indices of all input parameters that the output type - /// borrows immutably from (i.e. not `&mut`). - pub fn inputs_that_output_borrows_immutably_from(&self) -> Vec { - let c = self.unelide_output_lifetimes(); - let Some(output) = c.output() else { - return vec![]; - }; - - let output_lifetime_parameters = output.named_lifetime_parameters(); - - let mut borrowed_indexes = vec![]; - for (i, input) in c.input_types().enumerate() { - let Type::Reference(ref_ty) = input else { - continue; - }; - if ref_ty.is_mutable { - continue; - } - let Lifetime::Named(lifetime) = &ref_ty.lifetime else { - continue; - }; - if output_lifetime_parameters.contains(lifetime.as_str()) { - borrowed_indexes.push(i) - } - } - borrowed_indexes - } - - /// Returns the indices of all input parameters share a lifetime parameter with the output - pub fn inputs_with_lifetime_tied_with_output(&self) -> Vec { - let c = self.unelide_output_lifetimes(); - let Some(output) = c.output() else { - return vec![]; - }; - - let output_lifetime_parameters = output.named_lifetime_parameters(); - - let mut borrowed_indexes = vec![]; - for (i, input) in c.input_types().enumerate() { - if input - .named_lifetime_parameters() - .intersection(&output_lifetime_parameters) - .next() - .is_some() - { - borrowed_indexes.push(i) - } - } - borrowed_indexes - } - - pub fn render_signature(&self, package_ids2names: &BiHashMap) -> String { - let mut buffer = String::new(); - write!(&mut buffer, "{}", self).unwrap(); - write!(&mut buffer, "(").unwrap(); - let mut inputs = self.input_types().peekable(); - while let Some(input) = inputs.next() { - write!(&mut buffer, "{}", input.render_type(package_ids2names)).unwrap(); - if inputs.peek().is_some() { - write!(&mut buffer, ", ").unwrap(); - } - } - write!(&mut buffer, ")",).unwrap(); - if let Some(output) = self.output() { - write!(&mut buffer, " -> {}", output.render_type(package_ids2names)).unwrap(); - } - buffer - } - - /// Returns the `extra_field2default_value` map if this is a `StructLiteralInit`, otherwise `None`. - pub fn extra_field2default_value(&self) -> Option<&BTreeMap> { - match self { - Callable::StructLiteralInit(s) => Some(&s.extra_field2default_value), - _ => None, - } - } -} - -impl std::fmt::Display for Callable { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - match self { - Callable::FreeFunction(inner) => write!(f, "{}", inner.path), - Callable::InherentMethod(inner) => write!(f, "{}", inner.path), - Callable::TraitMethod(inner) => write!(f, "{}", inner.path), - Callable::StructLiteralInit(inner) => write!(f, "{}", inner.path), - Callable::EnumVariantInit(inner) => write!(f, "{}", inner.path), - } - } -} - -impl std::fmt::Debug for Callable { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self)?; - write!(f, "(")?; - let mut inputs = self.input_types().peekable(); - while let Some(input) = inputs.next() { - write!(f, "{input:?}")?; - if inputs.peek().is_some() { - write!(f, ", ")?; - } - } - write!(f, ")")?; - if let Some(output) = self.output() { - write!(f, " -> {output:?}")?; - } - Ok(()) - } -} diff --git a/rustdoc/rustdoc_ir/src/callable_path.rs b/rustdoc/rustdoc_ir/src/callable_path.rs deleted file mode 100644 index ca581d2e4..000000000 --- a/rustdoc/rustdoc_ir/src/callable_path.rs +++ /dev/null @@ -1,387 +0,0 @@ -use std::fmt::{Display, Formatter, Write}; - -use anyhow::Context; -use bimap::BiHashMap; -use guppy::PackageId; - -use crate::render::{LifetimeStyle, PathStyle, RenderConfig}; -use crate::{GenericArgument, Type}; - -#[derive(Clone, Debug, Hash, Eq, PartialEq)] -pub struct FreeFunctionPath { - pub package_id: PackageId, - /// Crate name as registered in the dependency graph. - pub crate_name: String, - /// Module path from crate root (excludes crate name and function name). - pub module_path: Vec, - pub function_name: String, - pub function_generics: Vec, -} - -#[derive(Clone, Debug, Hash, Eq, PartialEq)] -pub struct InherentMethodPath { - pub package_id: PackageId, - pub crate_name: String, - /// Module path from crate root (excludes crate name and type name). - pub module_path: Vec, - pub type_name: String, - pub type_generics: Vec, - pub method_name: String, - pub method_generics: Vec, -} - -#[derive(Clone, Debug, Hash, Eq, PartialEq)] -pub struct TraitMethodPath { - /// Package of the trait (not the Self type). - pub package_id: PackageId, - pub crate_name: String, - /// Module path from crate root (excludes crate name and trait name). - pub module_path: Vec, - pub trait_name: String, - pub trait_generics: Vec, - pub self_type: Type, - pub method_name: String, - pub method_generics: Vec, -} - -#[derive(Clone, Debug, Hash, Eq, PartialEq)] -pub struct StructLiteralPath { - pub package_id: PackageId, - pub crate_name: String, - /// Module path from crate root (excludes crate name and type name). - pub module_path: Vec, - pub type_name: String, - pub type_generics: Vec, -} - -#[derive(Clone, Debug, Hash, Eq, PartialEq)] -pub struct EnumVariantConstructorPath { - pub package_id: PackageId, - pub crate_name: String, - /// Module path from crate root (excludes crate name and enum name). - pub module_path: Vec, - pub enum_name: String, - pub enum_generics: Vec, - pub variant_name: String, -} - -// --- Helper: render generics --- - -/// Render a generic argument list (e.g. `::`) into `buffer`. -/// -/// Writes nothing if `generics` is empty. -fn render_generics( - generics: &[GenericArgument], - config: &RenderConfig<'_>, - buffer: &mut W, -) { - if !generics.is_empty() { - write!(buffer, "::<").unwrap(); - let mut args = generics.iter().peekable(); - while let Some(arg) = args.next() { - arg.render_into(config, buffer); - if args.peek().is_some() { - write!(buffer, ", ").unwrap(); - } - } - write!(buffer, ">").unwrap(); - } -} - -// --- FreeFunctionPath --- - -impl FreeFunctionPath { - pub fn render_path(&self, id2name: &BiHashMap, buffer: &mut String) { - let crate_name = id2name - .get_by_left(&self.package_id) - .with_context(|| { - format!( - "The package id '{}' is missing from the id<>name mapping for crates.", - self.package_id - ) - }) - .unwrap(); - let config = RenderConfig { - path: PathStyle::CrateLookup(id2name), - lifetime: LifetimeStyle::Erase, - }; - write!(buffer, "{crate_name}").unwrap(); - for module in &self.module_path { - write!(buffer, "::{module}").unwrap(); - } - write!(buffer, "::{}", self.function_name).unwrap(); - render_generics(&self.function_generics, &config, buffer); - } - - pub fn render_for_error(&self, buffer: &mut String) { - let config = RenderConfig { - path: PathStyle::Direct, - lifetime: LifetimeStyle::Preserve, - }; - buffer.push_str(&self.crate_name); - for module in &self.module_path { - write!(buffer, "::{module}").unwrap(); - } - write!(buffer, "::{}", self.function_name).unwrap(); - render_generics(&self.function_generics, &config, buffer); - } -} - -impl Display for FreeFunctionPath { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let config = RenderConfig { - path: PathStyle::Direct, - lifetime: LifetimeStyle::Preserve, - }; - write!(f, "{}", self.crate_name)?; - for module in &self.module_path { - write!(f, "::{module}")?; - } - write!(f, "::{}", self.function_name)?; - render_generics(&self.function_generics, &config, f); - Ok(()) - } -} - -// --- InherentMethodPath --- - -impl InherentMethodPath { - pub fn render_path(&self, id2name: &BiHashMap, buffer: &mut String) { - let crate_name = id2name - .get_by_left(&self.package_id) - .with_context(|| { - format!( - "The package id '{}' is missing from the id<>name mapping for crates.", - self.package_id - ) - }) - .unwrap(); - let config = RenderConfig { - path: PathStyle::CrateLookup(id2name), - lifetime: LifetimeStyle::Erase, - }; - write!(buffer, "{crate_name}").unwrap(); - for module in &self.module_path { - write!(buffer, "::{module}").unwrap(); - } - write!(buffer, "::{}", self.type_name).unwrap(); - render_generics(&self.type_generics, &config, buffer); - write!(buffer, "::{}", self.method_name).unwrap(); - render_generics(&self.method_generics, &config, buffer); - } - - pub fn render_for_error(&self, buffer: &mut String) { - let config = RenderConfig { - path: PathStyle::Direct, - lifetime: LifetimeStyle::Preserve, - }; - buffer.push_str(&self.crate_name); - for module in &self.module_path { - write!(buffer, "::{module}").unwrap(); - } - write!(buffer, "::{}", self.type_name).unwrap(); - render_generics(&self.type_generics, &config, buffer); - write!(buffer, "::{}", self.method_name).unwrap(); - render_generics(&self.method_generics, &config, buffer); - } -} - -impl Display for InherentMethodPath { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let config = RenderConfig { - path: PathStyle::Direct, - lifetime: LifetimeStyle::Preserve, - }; - write!(f, "{}", self.crate_name)?; - for module in &self.module_path { - write!(f, "::{module}")?; - } - write!(f, "::{}", self.type_name)?; - render_generics(&self.type_generics, &config, f); - write!(f, "::{}", self.method_name)?; - render_generics(&self.method_generics, &config, f); - Ok(()) - } -} - -// --- TraitMethodPath --- - -impl TraitMethodPath { - pub fn render_path(&self, id2name: &BiHashMap, buffer: &mut String) { - let crate_name = id2name - .get_by_left(&self.package_id) - .with_context(|| { - format!( - "The package id '{}' is missing from the id<>name mapping for crates.", - self.package_id - ) - }) - .unwrap(); - let config = RenderConfig { - path: PathStyle::CrateLookup(id2name), - lifetime: LifetimeStyle::Erase, - }; - write!(buffer, "<").unwrap(); - self.self_type.render_into(&config, buffer); - write!(buffer, " as {crate_name}").unwrap(); - for module in &self.module_path { - write!(buffer, "::{module}").unwrap(); - } - write!(buffer, "::{}", self.trait_name).unwrap(); - render_generics(&self.trait_generics, &config, buffer); - write!(buffer, ">").unwrap(); - write!(buffer, "::{}", self.method_name).unwrap(); - render_generics(&self.method_generics, &config, buffer); - } - - pub fn render_for_error(&self, buffer: &mut String) { - let config = RenderConfig { - path: PathStyle::Direct, - lifetime: LifetimeStyle::Preserve, - }; - write!(buffer, "<").unwrap(); - self.self_type.render_into(&config, buffer); - write!(buffer, " as {}", self.crate_name).unwrap(); - for module in &self.module_path { - write!(buffer, "::{module}").unwrap(); - } - write!(buffer, "::{}", self.trait_name).unwrap(); - render_generics(&self.trait_generics, &config, buffer); - write!(buffer, ">").unwrap(); - write!(buffer, "::{}", self.method_name).unwrap(); - render_generics(&self.method_generics, &config, buffer); - } -} - -impl Display for TraitMethodPath { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let config = RenderConfig { - path: PathStyle::Direct, - lifetime: LifetimeStyle::Preserve, - }; - write!(f, "<")?; - self.self_type.render_into(&config, f); - write!(f, " as {}", self.crate_name)?; - for module in &self.module_path { - write!(f, "::{module}")?; - } - write!(f, "::{}", self.trait_name)?; - render_generics(&self.trait_generics, &config, f); - write!(f, ">")?; - write!(f, "::{}", self.method_name)?; - render_generics(&self.method_generics, &config, f); - Ok(()) - } -} - -// --- StructLiteralPath --- - -impl StructLiteralPath { - pub fn render_path(&self, id2name: &BiHashMap, buffer: &mut String) { - let crate_name = id2name - .get_by_left(&self.package_id) - .with_context(|| { - format!( - "The package id '{}' is missing from the id<>name mapping for crates.", - self.package_id - ) - }) - .unwrap(); - let config = RenderConfig { - path: PathStyle::CrateLookup(id2name), - lifetime: LifetimeStyle::Erase, - }; - write!(buffer, "{crate_name}").unwrap(); - for module in &self.module_path { - write!(buffer, "::{module}").unwrap(); - } - write!(buffer, "::{}", self.type_name).unwrap(); - render_generics(&self.type_generics, &config, buffer); - } - - pub fn render_for_error(&self, buffer: &mut String) { - let config = RenderConfig { - path: PathStyle::Direct, - lifetime: LifetimeStyle::Preserve, - }; - buffer.push_str(&self.crate_name); - for module in &self.module_path { - write!(buffer, "::{module}").unwrap(); - } - write!(buffer, "::{}", self.type_name).unwrap(); - render_generics(&self.type_generics, &config, buffer); - } -} - -impl Display for StructLiteralPath { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let config = RenderConfig { - path: PathStyle::Direct, - lifetime: LifetimeStyle::Preserve, - }; - write!(f, "{}", self.crate_name)?; - for module in &self.module_path { - write!(f, "::{module}")?; - } - write!(f, "::{}", self.type_name)?; - render_generics(&self.type_generics, &config, f); - Ok(()) - } -} - -// --- EnumVariantConstructorPath --- - -impl EnumVariantConstructorPath { - pub fn render_path(&self, id2name: &BiHashMap, buffer: &mut String) { - let crate_name = id2name - .get_by_left(&self.package_id) - .with_context(|| { - format!( - "The package id '{}' is missing from the id<>name mapping for crates.", - self.package_id - ) - }) - .unwrap(); - let config = RenderConfig { - path: PathStyle::CrateLookup(id2name), - lifetime: LifetimeStyle::Erase, - }; - write!(buffer, "{crate_name}").unwrap(); - for module in &self.module_path { - write!(buffer, "::{module}").unwrap(); - } - write!(buffer, "::{}", self.enum_name).unwrap(); - render_generics(&self.enum_generics, &config, buffer); - write!(buffer, "::{}", self.variant_name).unwrap(); - } - - pub fn render_for_error(&self, buffer: &mut String) { - let config = RenderConfig { - path: PathStyle::Direct, - lifetime: LifetimeStyle::Preserve, - }; - buffer.push_str(&self.crate_name); - for module in &self.module_path { - write!(buffer, "::{module}").unwrap(); - } - write!(buffer, "::{}", self.enum_name).unwrap(); - render_generics(&self.enum_generics, &config, buffer); - write!(buffer, "::{}", self.variant_name).unwrap(); - } -} - -impl Display for EnumVariantConstructorPath { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let config = RenderConfig { - path: PathStyle::Direct, - lifetime: LifetimeStyle::Preserve, - }; - write!(f, "{}", self.crate_name)?; - for module in &self.module_path { - write!(f, "::{module}")?; - } - write!(f, "::{}", self.enum_name)?; - render_generics(&self.enum_generics, &config, f); - write!(f, "::{}", self.variant_name) - } -} diff --git a/rustdoc/rustdoc_ir/src/canonical_path_resolver.rs b/rustdoc/rustdoc_ir/src/canonical_path_resolver.rs deleted file mode 100644 index 2797d4668..000000000 --- a/rustdoc/rustdoc_ir/src/canonical_path_resolver.rs +++ /dev/null @@ -1,25 +0,0 @@ -use rustdoc_ext::GlobalItemId; - -/// Resolves a [`GlobalItemId`] to the canonical shortest importable path for that item. -/// -/// Used by [`Type::canonicalize`](crate::Type::canonicalize) to rewrite -/// [`PathType::base_type`](crate::PathType::base_type) so that the same underlying type -/// reached via different exports canonicalizes to the same value. -/// -/// The trait lives in `rustdoc_ir` to avoid a dependency on `rustdoc_processor`; -/// `rustdoc_processor` provides the concrete implementation on `CrateCollection`. -pub trait CanonicalPathResolver { - /// Return the canonical shortest importable path for the given item, or `None` if - /// no canonical path is available. - fn canonical_path(&self, id: &GlobalItemId) -> Option>; -} - -/// A resolver that never rewrites paths. Intended for tests and for the rare call sites -/// that genuinely have no [`CrateCollection`](https://docs.rs/rustdoc_processor) available. -pub struct NoOpResolver; - -impl CanonicalPathResolver for NoOpResolver { - fn canonical_path(&self, _: &GlobalItemId) -> Option> { - None - } -} diff --git a/rustdoc/rustdoc_ir/src/function_pointer.rs b/rustdoc/rustdoc_ir/src/function_pointer.rs deleted file mode 100644 index 87654e2e1..000000000 --- a/rustdoc/rustdoc_ir/src/function_pointer.rs +++ /dev/null @@ -1,91 +0,0 @@ -use std::fmt::{self, Debug, Formatter}; - -use rustdoc_types::Abi; - -use crate::Type; - -/// A single input parameter for a function pointer type. -#[derive(serde::Serialize, serde::Deserialize, Eq, PartialEq, Hash, Clone)] -pub struct FunctionPointerInput { - /// The name of the parameter, if any. - /// - /// `None` for unnamed parameters (e.g. `fn(usize)`), - /// `Some` for named parameters (e.g. `fn(a: usize)`). - pub name: Option, - /// The type of the parameter. - pub type_: Type, -} - -/// A Rust function pointer type—e.g. `fn(u32) -> u8` or `unsafe extern "C" fn()`. -#[derive(serde::Serialize, serde::Deserialize, Eq, PartialEq, Hash, Clone)] -pub struct FunctionPointer { - /// The input parameters, including optional names and types. - pub inputs: Vec, - /// The return type. `None` means the unit type `()`. - pub output: Option>, - /// The ABI of the function pointer (e.g. `Abi::Rust`, `Abi::C { unwind: false }`). - pub abi: Abi, - /// Whether this is an `unsafe` function pointer. - pub is_unsafe: bool, -} - -/// Write the `unsafe` and/or `extern "..."` prefix for a function pointer into `f`. -/// -/// - If `is_unsafe`, writes `unsafe `. -/// - If `abi` is not `Abi::Rust`, writes `extern "..." `. -pub fn write_fn_pointer_prefix(f: &mut impl fmt::Write, abi: &Abi, is_unsafe: bool) -> fmt::Result { - if is_unsafe { - write!(f, "unsafe ")?; - } - if let Some(abi_str) = abi_to_str(abi) { - write!(f, "extern \"{abi_str}\" ")?; - } - Ok(()) -} - -/// Convert an [`Abi`] to the string that appears inside `extern "..."`. -/// -/// Returns `None` for `Abi::Rust` (the default, which omits the `extern` keyword). -fn abi_to_str(abi: &Abi) -> Option<&str> { - match abi { - Abi::Rust => None, - Abi::C { unwind: false } => Some("C"), - Abi::C { unwind: true } => Some("C-unwind"), - Abi::Cdecl { unwind: false } => Some("cdecl"), - Abi::Cdecl { unwind: true } => Some("cdecl-unwind"), - Abi::Stdcall { unwind: false } => Some("stdcall"), - Abi::Stdcall { unwind: true } => Some("stdcall-unwind"), - Abi::Fastcall { unwind: false } => Some("fastcall"), - Abi::Fastcall { unwind: true } => Some("fastcall-unwind"), - Abi::Aapcs { unwind: false } => Some("aapcs"), - Abi::Aapcs { unwind: true } => Some("aapcs-unwind"), - Abi::Win64 { unwind: false } => Some("win64"), - Abi::Win64 { unwind: true } => Some("win64-unwind"), - Abi::SysV64 { unwind: false } => Some("sysv64"), - Abi::SysV64 { unwind: true } => Some("sysv64-unwind"), - Abi::System { unwind: false } => Some("system"), - Abi::System { unwind: true } => Some("system-unwind"), - Abi::Other(s) => Some(s), - } -} - -impl Debug for FunctionPointer { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write_fn_pointer_prefix(f, &self.abi, self.is_unsafe)?; - write!(f, "fn(")?; - for (i, input) in self.inputs.iter().enumerate() { - if i > 0 { - write!(f, ", ")?; - } - if let Some(name) = &input.name { - write!(f, "{name}: ")?; - } - write!(f, "{:?}", input.type_)?; - } - write!(f, ")")?; - if let Some(output) = &self.output { - write!(f, " -> {:?}", output)?; - } - Ok(()) - } -} diff --git a/rustdoc/rustdoc_ir/src/generic.rs b/rustdoc/rustdoc_ir/src/generic.rs deleted file mode 100644 index 34b9a7d0b..000000000 --- a/rustdoc/rustdoc_ir/src/generic.rs +++ /dev/null @@ -1,14 +0,0 @@ -use std::fmt::{Debug, Formatter}; - -#[derive(serde::Serialize, serde::Deserialize, Eq, PartialEq, Hash, Clone)] -/// An unassigned generic parameter—e.g. `T` in `fn foo(t: T)`. -pub struct Generic { - /// The name of the generic parameter, e.g. `"T"`. - pub name: String, -} - -impl Debug for Generic { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.name) - } -} diff --git a/rustdoc/rustdoc_ir/src/generic_argument.rs b/rustdoc/rustdoc_ir/src/generic_argument.rs deleted file mode 100644 index a196cc52c..000000000 --- a/rustdoc/rustdoc_ir/src/generic_argument.rs +++ /dev/null @@ -1,160 +0,0 @@ -use std::fmt::{Debug, Display, Formatter}; - -use bimap::BiHashMap; -use guppy::PackageId; - -use crate::Type; -use crate::named_lifetime::NamedLifetime; -use crate::render::{LifetimeStyle, PathStyle, RenderConfig}; - -/// A single generic argument supplied to a generic type or function. -#[derive(serde::Serialize, serde::Deserialize, Eq, PartialEq, Hash, Clone)] -pub enum GenericArgument { - /// A generic type parameter, e.g. `u32` in `Vec` or `T` in `HashSet`. - TypeParameter(Type), - /// A lifetime parameter, e.g. `'a` in `Cow<'a, str>`. - Lifetime(GenericLifetimeParameter), - /// A const generic argument with a concrete evaluated value, e.g. `8` in `Size<8>`. - Const(ConstGenericArgument), -} - -/// A const generic argument with a concrete evaluated value, e.g. `8` in `Size<8>`. -#[derive(serde::Serialize, serde::Deserialize, Eq, PartialEq, Hash, Clone)] -pub struct ConstGenericArgument { - /// The evaluated value as a string, e.g. "8", "true", "'a'". - pub value: String, -} - -/// A lifetime used as a generic argument—e.g. `'a` in `Cow<'a, str>`. -#[derive(serde::Serialize, serde::Deserialize, PartialEq, Eq, Hash, Clone)] -pub enum GenericLifetimeParameter { - /// A named (non-static) lifetime, e.g. `'a`. - Named(NamedLifetime), - /// The `'static` lifetime. - Static, - /// An inferred lifetime, i.e. `'_`. - Inferred, -} - -impl GenericLifetimeParameter { - /// Construct from a lifetime name, with or without the leading `'`. - /// - /// Routes `"_"` → `Inferred`, `"static"` → `Static`, everything else → `Named`. - pub fn from_name(name: impl Into) -> Self { - let mut name = name.into(); - if let Some(stripped) = name.strip_prefix('\'') { - name = stripped.to_owned(); - } - match name.as_str() { - "_" => GenericLifetimeParameter::Inferred, - "static" => GenericLifetimeParameter::Static, - _ => GenericLifetimeParameter::Named(NamedLifetime::new(name)), - } - } -} - -impl GenericArgument { - /// Render this generic argument preserving named lifetimes as-is. - pub fn render_type_into(&self, id2name: &BiHashMap, buffer: &mut String) { - let config = RenderConfig { - path: PathStyle::CrateLookup(id2name), - lifetime: LifetimeStyle::Preserve, - }; - self.render_into(&config, buffer); - } - - /// Render this generic argument, replacing named lifetimes with `'_`. - pub fn render_with_inferred_lifetimes_into( - &self, - id2name: &BiHashMap, - buffer: &mut String, - ) { - let config = RenderConfig { - path: PathStyle::CrateLookup(id2name), - lifetime: LifetimeStyle::Erase, - }; - self.render_into(&config, buffer); - } - - /// Render this generic argument for error messages. - pub fn display_for_error_into(&self, buffer: &mut W) { - let config = RenderConfig { - path: PathStyle::Direct, - lifetime: LifetimeStyle::Preserve, - }; - self.render_into(&config, buffer); - } - - /// Render this generic argument into `buffer` according to `config`. - /// - /// This is the single implementation behind [`GenericArgument::render_type_into`], - /// [`GenericArgument::render_with_inferred_lifetimes_into`], and - /// [`GenericArgument::display_for_error_into`]. - pub(crate) fn render_into( - &self, - config: &RenderConfig<'_>, - buffer: &mut W, - ) { - match self { - GenericArgument::TypeParameter(t) => { - t.render_into(config, buffer); - } - GenericArgument::Lifetime(l) => match config.lifetime { - LifetimeStyle::Preserve => { - write!(buffer, "{l}").unwrap(); - } - LifetimeStyle::Erase => match l { - GenericLifetimeParameter::Static => { - write!(buffer, "'static").unwrap(); - } - GenericLifetimeParameter::Named(_) | GenericLifetimeParameter::Inferred => { - write!(buffer, "'_").unwrap(); - } - }, - }, - GenericArgument::Const(c) => { - write!(buffer, "{}", c.value).unwrap(); - } - } - } -} - -impl Display for GenericLifetimeParameter { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - match self { - GenericLifetimeParameter::Named(l) => write!(f, "'{l}"), - GenericLifetimeParameter::Static => write!(f, "'static"), - GenericLifetimeParameter::Inferred => write!(f, "'_"), - } - } -} - -impl Display for GenericArgument { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - match self { - GenericArgument::TypeParameter(t) => write!(f, "{t}"), - GenericArgument::Lifetime(l) => write!(f, "{l}"), - GenericArgument::Const(c) => write!(f, "{}", c.value), - } - } -} - -impl Debug for GenericArgument { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - match self { - GenericArgument::TypeParameter(r) => write!(f, "{r:?}"), - GenericArgument::Lifetime(l) => write!(f, "{l:?}"), - GenericArgument::Const(c) => write!(f, "{}", c.value), - } - } -} - -impl Debug for GenericLifetimeParameter { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - match self { - GenericLifetimeParameter::Named(name) => write!(f, "'{name}"), - GenericLifetimeParameter::Static => write!(f, "'static"), - GenericLifetimeParameter::Inferred => write!(f, "'_"), - } - } -} diff --git a/rustdoc/rustdoc_ir/src/generics_equivalence.rs b/rustdoc/rustdoc_ir/src/generics_equivalence.rs deleted file mode 100644 index 621dfe759..000000000 --- a/rustdoc/rustdoc_ir/src/generics_equivalence.rs +++ /dev/null @@ -1,42 +0,0 @@ -use ahash::{HashMap, HashMapExt}; - -/// To make the comparison easier, we assign a monotonically increasing unique id to all -/// unassigned generic parameters. -/// If the ids match, we know that the two sequences of unassigned generic parameters are equivalent. -pub(crate) struct UnassignedIdGenerator<'a> { - next_id: usize, - known_ids: HashMap<&'a str, usize>, -} - -impl<'a> UnassignedIdGenerator<'a> { - pub(crate) fn new() -> Self { - Self { - next_id: 0, - known_ids: HashMap::new(), - } - } - - pub(crate) fn id<'b>(&'b mut self, name: &'a str) -> usize - where - 'a: 'b, - { - if let Some(id) = self.known_ids.get(&name) { - *id - } else { - let id = self.next_id; - self.next_id += 1; - self.known_ids.insert(name, id); - id - } - } - - /// Iterate over the known ids, sorted by their assigned ID (i.e. insertion order). - /// - /// This is important because `HashMap` iteration order is arbitrary, - /// and callers rely on pairing entries by position across two generators. - pub(crate) fn into_sorted_iter(self) -> impl Iterator { - let mut entries: Vec<_> = self.known_ids.into_iter().collect(); - entries.sort_by_key(|(_, id)| *id); - entries.into_iter() - } -} diff --git a/rustdoc/rustdoc_ir/src/lib.rs b/rustdoc/rustdoc_ir/src/lib.rs deleted file mode 100644 index 668bd6455..000000000 --- a/rustdoc/rustdoc_ir/src/lib.rs +++ /dev/null @@ -1,67 +0,0 @@ -mod array; -mod callable; -mod callable_path; -mod canonical_path_resolver; -pub mod function_pointer; -mod generic; -mod generic_argument; -pub(crate) mod generics_equivalence; -mod lifetime; -mod named_lifetime; -mod path_type; -mod raw_pointer; -pub(crate) mod render; -mod scalar_primitive; -mod slice; -mod tuple; -mod type_; -mod type_reference; - -pub use array::Array; -pub use callable::{ - Callable, CallableInput, EnumVariantInit, FnHeader, FreeFunction, InherentMethod, - RustIdentifier, StructLiteralInit, TraitMethod, -}; -pub use callable_path::{ - EnumVariantConstructorPath, FreeFunctionPath, InherentMethodPath, StructLiteralPath, - TraitMethodPath, -}; -pub use canonical_path_resolver::{CanonicalPathResolver, NoOpResolver}; -pub use function_pointer::{FunctionPointer, FunctionPointerInput}; -pub use generic::Generic; -pub use generic_argument::{ConstGenericArgument, GenericArgument, GenericLifetimeParameter}; -pub use lifetime::Lifetime; -pub use named_lifetime::NamedLifetime; -pub use path_type::PathType; -pub use raw_pointer::RawPointer; -pub use scalar_primitive::{ScalarPrimitive, UnknownPrimitive}; -pub use slice::Slice; -pub use tuple::Tuple; -pub use type_::CanonicalType; -pub use type_reference::TypeReference; - -/// A Rust type. -#[derive(serde::Serialize, serde::Deserialize, Eq, PartialEq, Hash, Clone)] -pub enum Type { - /// A named type identified by its fully-qualified path, e.g. `std::vec::Vec`. - Path(PathType), - /// A reference type, e.g. `&str` or `&'a mut Vec`. - Reference(TypeReference), - /// A tuple type, e.g. `(u8, u16)` or the unit type `()`. - Tuple(Tuple), - /// A scalar primitive type, e.g. `u32`, `bool`, or `str`. - ScalarPrimitive(ScalarPrimitive), - /// A slice type, e.g. `[u8]`. - Slice(Slice), - /// A fixed-size array type, e.g. `[u8; 4]`. - Array(Array), - /// A raw pointer type, e.g. `*const u8` or `*mut u8`. - RawPointer(RawPointer), - /// A function pointer type, e.g. `fn(u32) -> u8`. - FunctionPointer(FunctionPointer), - /// An unassigned generic type parameter, e.g. `T`. - Generic(Generic), - /// A type alias, preserving the alias identity rather than resolving through. - /// Contains the alias's path (package, base_type, generic arguments). - TypeAlias(PathType), -} diff --git a/rustdoc/rustdoc_ir/src/lifetime.rs b/rustdoc/rustdoc_ir/src/lifetime.rs deleted file mode 100644 index f2e551d79..000000000 --- a/rustdoc/rustdoc_ir/src/lifetime.rs +++ /dev/null @@ -1,79 +0,0 @@ -use std::fmt::{Debug, Formatter}; - -use crate::GenericLifetimeParameter; -use crate::named_lifetime::NamedLifetime; - -#[derive(serde::Serialize, serde::Deserialize, PartialEq, Eq, Hash, Clone)] -pub enum Lifetime { - /// The `'static` lifetime. - Static, - /// A named lifetime, e.g. `'a` in `&'a str`. - Named(NamedLifetime), - /// An inferred lifetime, i.e. `'_`. - Inferred, - /// A lifetime that is omitted from the source thanks to lifetime elision - /// (see https://doc.rust-lang.org/nomicon/lifetime-elision.html). - /// - /// E.g. `&str`. - Elided, -} - -impl From for Lifetime { - fn from(l: GenericLifetimeParameter) -> Self { - match l { - GenericLifetimeParameter::Static => Lifetime::Static, - GenericLifetimeParameter::Named(n) => Lifetime::Named(n), - GenericLifetimeParameter::Inferred => Lifetime::Inferred, - } - } -} - -impl From> for Lifetime { - fn from(s: Option) -> Self { - match s { - Some(s) => Lifetime::from_name(s), - None => Lifetime::Elided, - } - } -} - -impl Lifetime { - /// Construct from a lifetime name, with or without the leading `'`. - /// - /// Routes `"_"` → `Inferred`, `"static"` → `Static`, everything else → `Named`. - pub fn from_name(name: impl Into) -> Self { - let mut name = name.into(); - if let Some(stripped) = name.strip_prefix('\'') { - name = stripped.to_owned(); - } - match name.as_str() { - "_" => Lifetime::Inferred, - "static" => Lifetime::Static, - _ => Lifetime::Named(NamedLifetime::new(name)), - } - } - - /// Returns `true` if this is the `'static` lifetime. - pub fn is_static(&self) -> bool { - match self { - Lifetime::Named(_) | Lifetime::Elided | Lifetime::Inferred => false, - Lifetime::Static => true, - } - } - - /// Returns `true` if this lifetime was elided or inferred (`'_`). - pub fn is_elided(&self) -> bool { - matches!(self, Lifetime::Elided | Lifetime::Inferred) - } -} - -impl Debug for Lifetime { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - match self { - Lifetime::Static => write!(f, "'static"), - Lifetime::Named(name) => write!(f, "'{name}"), - Lifetime::Inferred => write!(f, "'_"), - Lifetime::Elided => Ok(()), - } - } -} diff --git a/rustdoc/rustdoc_ir/src/named_lifetime.rs b/rustdoc/rustdoc_ir/src/named_lifetime.rs deleted file mode 100644 index 88d469879..000000000 --- a/rustdoc/rustdoc_ir/src/named_lifetime.rs +++ /dev/null @@ -1,43 +0,0 @@ -use std::fmt::{Debug, Display, Formatter}; - -/// A named lifetime (without leading `'`). Cannot be `"_"` or `"static"`. -#[derive(serde::Serialize, serde::Deserialize, PartialEq, Eq, Hash, Clone)] -pub struct NamedLifetime(String); - -impl NamedLifetime { - pub fn new(name: impl Into) -> Self { - let mut name = name.into(); - if let Some(stripped) = name.strip_prefix('\'') { - name = stripped.to_owned(); - } - assert!( - name != "_", - "Use GenericLifetimeParameter::Inferred for inferred lifetimes ('_')" - ); - assert!( - name != "static", - "Use GenericLifetimeParameter::Static for 'static" - ); - Self(name) - } - - pub fn as_str(&self) -> &str { - &self.0 - } - - pub fn into_inner(self) -> String { - self.0 - } -} - -impl Display for NamedLifetime { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.0) - } -} - -impl Debug for NamedLifetime { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.0) - } -} diff --git a/rustdoc/rustdoc_ir/src/path_type.rs b/rustdoc/rustdoc_ir/src/path_type.rs deleted file mode 100644 index cf2c43355..000000000 --- a/rustdoc/rustdoc_ir/src/path_type.rs +++ /dev/null @@ -1,192 +0,0 @@ -use std::fmt::{Debug, Formatter}; - -use guppy::PackageId; - -use crate::generics_equivalence::UnassignedIdGenerator; -use crate::render::{deserialize_package_id, serialize_package_id}; -use crate::{GenericArgument, Type}; - -/// A named type identified by its fully-qualified path—e.g. `std::vec::Vec`. -#[derive(serde::Serialize, serde::Deserialize, PartialEq, Eq, Hash, Clone)] -pub struct PathType { - #[serde(serialize_with = "serialize_package_id")] - #[serde(deserialize_with = "deserialize_package_id")] - // `PackageId` doesn't implement serde::Deserialize/serde::Serialize, therefore we must - // manually specify deserializer and serializer to make the whole `PathType` - // (de)serializable. - /// The id of the package that defines this type. - pub package_id: PackageId, - /// The id associated with this type within the (JSON) docs for `package_id`. - /// - /// The id is optional to allow for flexible usage patterns—e.g. to leverage [`Type`] - /// to work with types that we want to code-generate into a new crate. - pub rustdoc_id: Option, - /// The fully-qualified path segments for this type, e.g. `["std", "vec", "Vec"]`. - pub base_type: Vec, - /// The generic arguments applied to this type, e.g. `[u32]` in `Vec`. - pub generic_arguments: Vec, -} - -impl PathType { - pub(crate) fn _is_a_resolved_path_type_template_for( - &self, - concrete_type: &PathType, - bindings: &mut ahash::HashMap, - ) -> bool { - // We destructure ALL fields to make sure that the compiler reminds us to update - // this function if we add new fields to `PathType`. - let PathType { - package_id: concrete_package_id, - rustdoc_id: _, - base_type: concrete_base_type, - generic_arguments: concrete_generic_arguments, - } = concrete_type; - let PathType { - package_id: templated_package_id, - rustdoc_id: _, - base_type: templated_base_type, - generic_arguments: templated_generic_arguments, - } = self; - if concrete_package_id != templated_package_id - || concrete_base_type != templated_base_type - || concrete_generic_arguments.len() != templated_generic_arguments.len() - { - return false; - } - for (concrete_arg, templated_arg) in concrete_generic_arguments - .iter() - .zip(templated_generic_arguments.iter()) - { - use GenericArgument::*; - match (concrete_arg, templated_arg) { - // Both sides are generic — bind the template's generic to the concrete's generic. - ( - TypeParameter(Type::Generic(concrete_generic)), - TypeParameter(Type::Generic(template_generic)), - ) => { - let concrete_type = Type::Generic(concrete_generic.clone()); - let previous = - bindings.insert(template_generic.name.clone(), concrete_type.clone()); - if let Some(previous) = previous - && previous != concrete_type - { - tracing::trace!( - "Type parameter `{:?}` was already assigned to `{:?}` but is now being assigned to `{:?}`", - template_generic, - previous, - concrete_type - ); - return false; - } - } - // Concrete side has a generic but template side doesn't — can't specialize. - (TypeParameter(Type::Generic(_)), _) => { - return false; - } - (TypeParameter(assigned), TypeParameter(Type::Generic(unassigned))) => { - // The unassigned type parameter can be assigned to the concrete type - // we expect, so it is a specialization. - let previous_assignment = - bindings.insert(unassigned.name.clone(), assigned.clone()); - if let Some(previous_assignment) = previous_assignment - && &previous_assignment != assigned - { - tracing::trace!( - "Type parameter `{:?}` was already assigned to `{:?}` but is now being assigned to `{:?}`", - unassigned, - previous_assignment, - assigned - ); - return false; - } - } - (TypeParameter(concrete_arg_type), TypeParameter(templated_arg_type)) => { - if !templated_arg_type._is_a_template_for(concrete_arg_type, bindings) { - return false; - } - } - (Lifetime(_), Lifetime(_)) => { - // Lifetimes are not relevant for specialization (yet). - } - (Const(a), Const(b)) => { - if a != b { - return false; - } - } - (TypeParameter(_), Lifetime(_)) - | (Lifetime(_), TypeParameter(_)) - | (Const(_), TypeParameter(_)) - | (TypeParameter(_), Const(_)) - | (Const(_), Lifetime(_)) - | (Lifetime(_), Const(_)) => { - return false; - } - } - } - true - } - - pub(crate) fn _is_equivalent_to<'a, 'b>( - &'a self, - other: &'b PathType, - self_id_gen: &mut UnassignedIdGenerator<'a>, - other_id_gen: &mut UnassignedIdGenerator<'b>, - ) -> bool { - if (self.package_id != other.package_id) - || (self.rustdoc_id != other.rustdoc_id) - || (self.base_type != other.base_type) - { - return false; - } - let self_args = &self.generic_arguments; - let other_args = &other.generic_arguments; - if self_args.len() != other_args.len() { - return false; - } - for (self_arg, other_arg) in self_args.iter().zip(other_args) { - use GenericArgument::*; - use Type::*; - match (self_arg, other_arg) { - (TypeParameter(Generic(first)), TypeParameter(Generic(second))) => { - let first_id = self_id_gen.id(&first.name); - let second_id = other_id_gen.id(&second.name); - if first_id != second_id { - return false; - } - } - (TypeParameter(first), TypeParameter(second)) => { - if !first._is_equivalent_to(second, self_id_gen, other_id_gen) { - return false; - } - } - (Lifetime(_), Lifetime(_)) => { - // Lifetimes are not relevant for specialization (yet). - } - (first, second) => { - if first != second { - return false; - } - } - } - } - true - } -} - -impl Debug for PathType { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.base_type.join("::"))?; - if !self.generic_arguments.is_empty() { - write!(f, "<")?; - let mut arguments = self.generic_arguments.iter().peekable(); - while let Some(argument) = arguments.next() { - write!(f, "{argument:?}")?; - if arguments.peek().is_some() { - write!(f, ", ")?; - } - } - write!(f, ">")?; - } - Ok(()) - } -} diff --git a/rustdoc/rustdoc_ir/src/raw_pointer.rs b/rustdoc/rustdoc_ir/src/raw_pointer.rs deleted file mode 100644 index b0c195c76..000000000 --- a/rustdoc/rustdoc_ir/src/raw_pointer.rs +++ /dev/null @@ -1,23 +0,0 @@ -use std::fmt::{Debug, Formatter}; - -use crate::Type; - -#[derive(serde::Serialize, serde::Deserialize, Eq, PartialEq, Hash, Clone)] -/// A Rust raw pointer—e.g. `*const u8` or `*mut Vec`. -pub struct RawPointer { - /// `true` if this is a `*mut T` pointer, `false` if `*const T`. - pub is_mutable: bool, - /// The type being pointed to. - pub inner: Box, -} - -impl Debug for RawPointer { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - if self.is_mutable { - write!(f, "*mut ")?; - } else { - write!(f, "*const ")?; - } - write!(f, "{:?}", self.inner) - } -} diff --git a/rustdoc/rustdoc_ir/src/render.rs b/rustdoc/rustdoc_ir/src/render.rs deleted file mode 100644 index 8e2149833..000000000 --- a/rustdoc/rustdoc_ir/src/render.rs +++ /dev/null @@ -1,255 +0,0 @@ -use std::fmt; - -use anyhow::Context; -use bimap::BiHashMap; -use guppy::PackageId; -use serde::{Deserializer, Serializer}; - -use crate::function_pointer::write_fn_pointer_prefix; -use crate::{Lifetime, Type}; - -/// Configuration for rendering types and generic arguments. -/// -/// The two dimensions—[`PathStyle`] and [`LifetimeStyle`]—capture the -/// differences between codegen rendering, inferred-lifetime rendering, -/// and error-message rendering, allowing a single traversal to serve -/// all three use cases. -#[derive(Clone, Copy)] -pub(crate) struct RenderConfig<'a> { - /// How to render the crate-qualified portion of a path type. - pub path: PathStyle<'a>, - /// How to render named lifetime parameters. - pub lifetime: LifetimeStyle, -} - -/// Controls how the crate prefix of a [`Type::Path`] is rendered. -#[derive(Clone, Copy)] -pub(crate) enum PathStyle<'a> { - /// Look up the crate name from a package-id-to-name mapping, - /// then join the remaining segments with `::`. - CrateLookup(&'a BiHashMap), - /// Join all segments of `base_type` directly with `::`. - Direct, -} - -/// Controls how named lifetimes are rendered. -#[derive(Clone, Copy)] -pub(crate) enum LifetimeStyle { - /// Preserve named lifetimes as-is (e.g. `'a` stays `'a`). - Preserve, - /// Replace named and inferred lifetimes with `'_`. - Erase, -} - -/// Serialize a [`PackageId`] as its string representation. -pub(crate) fn serialize_package_id( - package_id: &PackageId, - serializer: S, -) -> Result -where - S: Serializer, -{ - serializer.serialize_str(package_id.repr()) -} - -/// Deserialize a [`PackageId`] from its string representation. -pub(crate) fn deserialize_package_id<'de, D>(deserializer: D) -> Result -where - D: Deserializer<'de>, -{ - let s: &str = serde::de::Deserialize::deserialize(deserializer)?; - Ok(PackageId::new(s)) -} - -impl Type { - /// Parse this type into a [`syn::Type`], using the provided package-id-to-crate-name mapping. - /// - /// Named lifetimes are preserved as-is. Call sites that need `'_` should use - /// [`Type::rename_lifetime_parameters`] beforehand to replace named lifetimes. - pub fn syn_type(&self, id2name: &BiHashMap) -> syn::Type { - let type_ = self.render_type(id2name); - syn::parse_str(&type_).unwrap() - } - - /// Render this type as a Rust source string, preserving named lifetimes as-is. - pub fn render_type(&self, id2name: &BiHashMap) -> String { - let mut buffer = String::new(); - self.render_type_into(id2name, &mut buffer); - buffer - } - - /// Like [`Type::render_type`], but writes into an existing buffer instead of allocating. - pub fn render_type_into(&self, id2name: &BiHashMap, buffer: &mut String) { - let config = RenderConfig { - path: PathStyle::CrateLookup(id2name), - lifetime: LifetimeStyle::Preserve, - }; - self.render_into(&config, buffer); - } - - /// Render this type as a Rust source string, replacing named lifetimes with `'_`. - /// - /// Use this when the type appears in a codegen expression context where the - /// original named lifetimes are not in scope. - pub fn render_with_inferred_lifetimes(&self, id2name: &BiHashMap) -> String { - let mut buffer = String::new(); - self.render_with_inferred_lifetimes_into(id2name, &mut buffer); - buffer - } - - /// Like [`Type::render_with_inferred_lifetimes`], but writes into an existing buffer - /// instead of allocating. - pub fn render_with_inferred_lifetimes_into( - &self, - id2name: &BiHashMap, - buffer: &mut String, - ) { - let config = RenderConfig { - path: PathStyle::CrateLookup(id2name), - lifetime: LifetimeStyle::Erase, - }; - self.render_into(&config, buffer); - } - - /// Format this type for display in user-facing error messages. - pub fn display_for_error(&self) -> String { - let mut s = String::new(); - self.display_for_error_into(&mut s); - s - } - - /// Like [`Type::display_for_error`], but writes into an existing buffer instead of - /// allocating. - pub fn display_for_error_into(&self, buffer: &mut W) { - let config = RenderConfig { - path: PathStyle::Direct, - lifetime: LifetimeStyle::Preserve, - }; - self.render_into(&config, buffer); - } - - /// Render this type into `buffer` according to `config`. - /// - /// This is the single implementation behind [`Type::render_type_into`], - /// [`Type::render_with_inferred_lifetimes_into`], and [`Type::display_for_error_into`]. - pub(crate) fn render_into(&self, config: &RenderConfig<'_>, buffer: &mut W) { - match self { - Type::Path(t) | Type::TypeAlias(t) => { - match config.path { - PathStyle::CrateLookup(id2name) => { - let crate_name = id2name - .get_by_left(&t.package_id) - .with_context(|| { - format!( - "The package id '{}' is missing from the id<>name mapping for crates.", - t.package_id - ) - }) - .unwrap(); - write!(buffer, "{crate_name}").unwrap(); - write!(buffer, "::{}", t.base_type[1..].join("::")).unwrap(); - } - PathStyle::Direct => { - write!(buffer, "{}", t.base_type.join("::")).unwrap(); - } - } - if !t.generic_arguments.is_empty() { - write!(buffer, "<").unwrap(); - let mut arguments = t.generic_arguments.iter().peekable(); - while let Some(argument) = arguments.next() { - argument.render_into(config, buffer); - if arguments.peek().is_some() { - write!(buffer, ", ").unwrap(); - } - } - write!(buffer, ">").unwrap(); - } - } - Type::Reference(r) => { - write!(buffer, "&").unwrap(); - match &r.lifetime { - Lifetime::Static => { - write!(buffer, "'static ").unwrap(); - } - Lifetime::Named(l) => match config.lifetime { - LifetimeStyle::Preserve => { - write!(buffer, "'{} ", l.as_str()).unwrap(); - } - LifetimeStyle::Erase => { - write!(buffer, "'_ ").unwrap(); - } - }, - Lifetime::Inferred => { - write!(buffer, "'_ ").unwrap(); - } - Lifetime::Elided => {} - } - if r.is_mutable { - write!(buffer, "mut ").unwrap(); - } - r.inner.render_into(config, buffer); - } - Type::Tuple(t) => { - write!(buffer, "(").unwrap(); - let mut elements = t.elements.iter().peekable(); - while let Some(element) = elements.next() { - element.render_into(config, buffer); - if elements.peek().is_some() { - write!(buffer, ", ").unwrap(); - } - } - write!(buffer, ")").unwrap(); - } - Type::ScalarPrimitive(s) => { - write!(buffer, "{s}").unwrap(); - } - Type::Slice(s) => { - write!(buffer, "[").unwrap(); - s.element_type.render_into(config, buffer); - write!(buffer, "]").unwrap(); - } - Type::Array(a) => { - write!(buffer, "[").unwrap(); - a.element_type.render_into(config, buffer); - write!(buffer, "; {}]", a.len).unwrap(); - } - Type::RawPointer(r) => { - if r.is_mutable { - write!(buffer, "*mut ").unwrap(); - } else { - write!(buffer, "*const ").unwrap(); - } - r.inner.render_into(config, buffer); - } - Type::FunctionPointer(fp) => { - write_fn_pointer_prefix(buffer, &fp.abi, fp.is_unsafe).unwrap(); - write!(buffer, "fn(").unwrap(); - let mut inputs = fp.inputs.iter().peekable(); - while let Some(input) = inputs.next() { - if let Some(name) = &input.name { - write!(buffer, "{name}: ").unwrap(); - } - input.type_.render_into(config, buffer); - if inputs.peek().is_some() { - write!(buffer, ", ").unwrap(); - } - } - write!(buffer, ")").unwrap(); - if let Some(output) = &fp.output { - write!(buffer, " -> ").unwrap(); - output.render_into(config, buffer); - } - } - Type::Generic(t) => { - write!(buffer, "{}", t.name).unwrap(); - } - } - } -} - -impl fmt::Display for Type { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.display_for_error_into(f); - Ok(()) - } -} diff --git a/rustdoc/rustdoc_ir/src/scalar_primitive.rs b/rustdoc/rustdoc_ir/src/scalar_primitive.rs deleted file mode 100644 index a138a666c..000000000 --- a/rustdoc/rustdoc_ir/src/scalar_primitive.rs +++ /dev/null @@ -1,100 +0,0 @@ -use std::fmt::{Debug, Display, Formatter}; - -/// A Rust scalar primitive type (e.g. `u32`, `bool`, `str`). -#[derive(serde::Serialize, serde::Deserialize, Eq, PartialEq, Hash, Clone)] -pub enum ScalarPrimitive { - Usize, - U8, - U16, - U32, - U64, - U128, - Isize, - I8, - I16, - I32, - I64, - I128, - F32, - F64, - Bool, - Char, - Str, -} - -impl ScalarPrimitive { - /// Returns the primitive type name as a string slice (e.g. `"u32"`, `"bool"`). - pub fn as_str(&self) -> &'static str { - match self { - Self::Usize => "usize", - Self::U8 => "u8", - Self::U16 => "u16", - Self::U32 => "u32", - Self::U64 => "u64", - Self::U128 => "u128", - Self::Isize => "isize", - Self::I8 => "i8", - Self::I16 => "i16", - Self::I32 => "i32", - Self::I64 => "i64", - Self::I128 => "i128", - Self::F32 => "f32", - Self::F64 => "f64", - Self::Bool => "bool", - Self::Char => "char", - Self::Str => "str", - } - } -} - -impl Debug for ScalarPrimitive { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.as_str()) - } -} - -impl Display for ScalarPrimitive { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.as_str()) - } -} - -/// Error returned when trying to convert an unrecognized string into a [`ScalarPrimitive`]. -#[derive(thiserror::Error, Debug)] -#[error("Unknown primitive type, `{name}`")] -pub struct UnknownPrimitive { - /// The unrecognized primitive type name. - pub name: String, -} - -impl TryFrom<&str> for ScalarPrimitive { - type Error = UnknownPrimitive; - - fn try_from(value: &str) -> Result { - let v = match value { - "usize" => Self::Usize, - "u8" => Self::U8, - "u16" => Self::U16, - "u32" => Self::U32, - "u64" => Self::U64, - "u128" => Self::U128, - "isize" => Self::Isize, - "i8" => Self::I8, - "i16" => Self::I16, - "i32" => Self::I32, - "i64" => Self::I64, - "i128" => Self::I128, - "f32" => Self::F32, - "f64" => Self::F64, - "bool" => Self::Bool, - "char" => Self::Char, - "str" => Self::Str, - _ => { - return Err(UnknownPrimitive { - name: value.to_string(), - }); - } - }; - Ok(v) - } -} diff --git a/rustdoc/rustdoc_ir/src/slice.rs b/rustdoc/rustdoc_ir/src/slice.rs deleted file mode 100644 index d31db9b57..000000000 --- a/rustdoc/rustdoc_ir/src/slice.rs +++ /dev/null @@ -1,16 +0,0 @@ -use std::fmt::{Debug, Formatter}; - -use crate::Type; - -#[derive(serde::Serialize, serde::Deserialize, Eq, PartialEq, Hash, Clone)] -/// A Rust slice—e.g. `[u16]`. -pub struct Slice { - /// The type of each element in the slice. - pub element_type: Box, -} - -impl Debug for Slice { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "[{:?}]", self.element_type) - } -} diff --git a/rustdoc/rustdoc_ir/src/tuple.rs b/rustdoc/rustdoc_ir/src/tuple.rs deleted file mode 100644 index 89daf3ac6..000000000 --- a/rustdoc/rustdoc_ir/src/tuple.rs +++ /dev/null @@ -1,25 +0,0 @@ -use std::fmt::{Debug, Formatter}; - -use crate::Type; - -#[derive(serde::Serialize, serde::Deserialize, Eq, PartialEq, Hash, Clone)] -/// A Rust tuple—e.g. `(u8, u16, u32)`. -pub struct Tuple { - /// The types of each element in the tuple. An empty vector represents the unit type `()`. - pub elements: Vec, -} - -impl Debug for Tuple { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "(")?; - let mut elements = self.elements.iter().peekable(); - while let Some(element) = elements.next() { - write!(f, "{element:?}")?; - if elements.peek().is_some() { - write!(f, ", ")?; - } - } - write!(f, ")")?; - Ok(()) - } -} diff --git a/rustdoc/rustdoc_ir/src/type_.rs b/rustdoc/rustdoc_ir/src/type_.rs deleted file mode 100644 index 1f0f11dff..000000000 --- a/rustdoc/rustdoc_ir/src/type_.rs +++ /dev/null @@ -1,982 +0,0 @@ -use std::fmt::{Debug, Formatter}; - -use ahash::{HashMap, HashMapExt}; -use indexmap::{IndexMap, IndexSet}; -use rustdoc_ext::GlobalItemId; - -use crate::generics_equivalence::UnassignedIdGenerator; -use crate::{ - Array, CanonicalPathResolver, FunctionPointer, FunctionPointerInput, Generic, GenericArgument, - GenericLifetimeParameter, Lifetime, NamedLifetime, PathType, RawPointer, Slice, Tuple, Type, - TypeReference, -}; - -/// A `Type` with canonicalized names for lifetimes and unassigned generic type parameters. -/// -/// - Non-static lifetimes: each occurrence gets a fresh positional name ('a, 'b, ...), -/// ignoring identity (two occurrences of `'x` become two distinct canonical names). -/// - Unassigned generic type parameters: renamed with fresh positional names (A, B, ...), -/// **preserving** identity (two occurrences of `T` both become `A`). -/// -/// Only constructible via [`Type::canonicalize()`]. -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct CanonicalType(Type); - -impl CanonicalType { - /// Access the underlying `Type`. - pub fn inner(&self) -> &Type { - &self.0 - } - - /// Consume the wrapper and return the underlying `Type`. - pub fn into_inner(self) -> Type { - self.0 - } -} - -impl AsRef for Type { - fn as_ref(&self) -> &Type { - self - } -} - -impl Type { - /// The unit type `()`, represented as an empty tuple. - pub const UNIT_TYPE: Type = Type::Tuple(Tuple { elements: vec![] }); - - /// Returns `true` if `t` is a `Result` type. - pub fn is_result(&self) -> bool { - let Type::Path(t) = self else { - return false; - }; - t.base_type == ["core", "result", "Result"] - || t.base_type == ["core", "prelude", "rust_2015", "Result"] - || t.base_type == ["core", "prelude", "rust_2018", "Result"] - || t.base_type == ["core", "prelude", "rust_2021", "Result"] - } - - /// Replace unassigned generic type parameters in `templated_type` with the concrete generic type - /// parameters defined in `bindings`. - /// - /// This function can also be used to _partially_ bind the unassigned generic type parameters in - /// `t`. You are not required to bind all of them. - pub fn bind_generic_type_parameters(&self, bindings: &HashMap) -> Type { - match self { - Type::Path(t) | Type::TypeAlias(t) => { - let is_alias = matches!(self, Type::TypeAlias(_)); - let mut bound_generics = Vec::with_capacity(t.generic_arguments.len()); - for generic in &t.generic_arguments { - let bound_generic = match generic { - GenericArgument::TypeParameter(t) => { - GenericArgument::TypeParameter(t.bind_generic_type_parameters(bindings)) - } - GenericArgument::Lifetime(_) | GenericArgument::Const(_) => { - generic.to_owned() - } - }; - bound_generics.push(bound_generic); - } - let path = PathType { - package_id: t.package_id.clone(), - // Should we set this to `None`? - rustdoc_id: t.rustdoc_id, - base_type: t.base_type.clone(), - generic_arguments: bound_generics, - }; - if is_alias { - Type::TypeAlias(path) - } else { - Type::Path(path) - } - } - Type::Reference(r) => Type::Reference(TypeReference { - is_mutable: r.is_mutable, - inner: Box::new(r.inner.bind_generic_type_parameters(bindings)), - lifetime: r.lifetime.clone(), - }), - Type::Tuple(t) => { - let mut bound_elements = Vec::with_capacity(t.elements.len()); - for inner in &t.elements { - bound_elements.push(inner.bind_generic_type_parameters(bindings)); - } - Type::Tuple(Tuple { - elements: bound_elements, - }) - } - Type::ScalarPrimitive(s) => Type::ScalarPrimitive(s.clone()), - Type::Slice(s) => Type::Slice(Slice { - element_type: Box::new(s.element_type.bind_generic_type_parameters(bindings)), - }), - Type::Array(a) => Type::Array(Array { - element_type: Box::new(a.element_type.bind_generic_type_parameters(bindings)), - len: a.len, - }), - Type::RawPointer(r) => Type::RawPointer(RawPointer { - is_mutable: r.is_mutable, - inner: Box::new(r.inner.bind_generic_type_parameters(bindings)), - }), - Type::FunctionPointer(fp) => Type::FunctionPointer(FunctionPointer { - inputs: fp - .inputs - .iter() - .map(|input| FunctionPointerInput { - name: input.name.clone(), - type_: input.type_.bind_generic_type_parameters(bindings), - }) - .collect(), - output: fp - .output - .as_ref() - .map(|t| Box::new(t.bind_generic_type_parameters(bindings))), - abi: fp.abi.clone(), - is_unsafe: fp.is_unsafe, - }), - Type::Generic(g) => { - if let Some(bound_type) = bindings.get(&g.name) { - bound_type.clone() - } else { - Type::Generic(g.to_owned()) - } - } - } - } - - /// Check if a type can be used as a "template"—i.e. if it has any unassigned generic parameters. - pub fn is_a_template(&self) -> bool { - match self { - Type::Path(path) | Type::TypeAlias(path) => { - path.generic_arguments.iter().any(|arg| match arg { - GenericArgument::TypeParameter(g) => g.is_a_template(), - GenericArgument::Lifetime( - GenericLifetimeParameter::Static - | GenericLifetimeParameter::Named(_) - | GenericLifetimeParameter::Inferred, - ) => false, - GenericArgument::Const(_) => false, - }) - } - Type::Reference(r) => r.inner.is_a_template(), - Type::Tuple(t) => t.elements.iter().any(|t| t.is_a_template()), - Type::ScalarPrimitive(_) => false, - Type::Slice(s) => s.element_type.is_a_template(), - Type::Array(a) => a.element_type.is_a_template(), - Type::RawPointer(r) => r.inner.is_a_template(), - Type::FunctionPointer(fp) => { - fp.inputs.iter().any(|input| input.type_.is_a_template()) - || fp.output.as_ref().is_some_and(|t| t.is_a_template()) - } - Type::Generic(_) => true, - } - } - - /// Returns the set of all unassigned generic type parameters in this type. - /// - /// E.g. `[T]` for `Json` or `[T, V]` for `Json`. - pub fn unassigned_generic_type_parameters(&self) -> IndexSet { - let mut set = IndexSet::new(); - self._unassigned_generic_type_parameters(&mut set); - set - } - - fn _unassigned_generic_type_parameters(&self, set: &mut IndexSet) { - match self { - Type::Path(path) | Type::TypeAlias(path) => { - for arg in &path.generic_arguments { - match arg { - GenericArgument::TypeParameter(g) => { - g._unassigned_generic_type_parameters(set); - } - GenericArgument::Lifetime(_) | GenericArgument::Const(_) => {} - } - } - } - Type::Reference(r) => r.inner._unassigned_generic_type_parameters(set), - Type::Tuple(t) => { - for inner in &t.elements { - inner._unassigned_generic_type_parameters(set); - } - } - Type::ScalarPrimitive(_) => {} - Type::Slice(s) => s.element_type._unassigned_generic_type_parameters(set), - Type::Array(a) => a.element_type._unassigned_generic_type_parameters(set), - Type::RawPointer(r) => r.inner._unassigned_generic_type_parameters(set), - Type::FunctionPointer(fp) => { - for input in &fp.inputs { - input.type_._unassigned_generic_type_parameters(set); - } - if let Some(output) = &fp.output { - output._unassigned_generic_type_parameters(set); - } - } - Type::Generic(t) => { - set.insert(t.name.clone()); - } - } - } - - /// Check if a type can be considered a "template" for another. - /// - /// I.e. if by replacing the unassigned generic type parameters of `self` with the - /// concrete generic type parameters of `concrete_type`, `self` would be equal to `concrete_type`. - /// - /// If possible, this function will return a map associating each unassigned generic parameter - /// in `self` with the type it must be set to in order to match `concrete_type`. - /// If impossible, this function will return `None`. - pub fn is_a_template_for(&self, concrete_type: &Type) -> Option> { - let mut bindings = HashMap::new(); - if self._is_a_template_for(concrete_type, &mut bindings) { - Some(bindings) - } else { - None - } - } - - pub(crate) fn _is_a_template_for( - &self, - concrete_type: &Type, - bindings: &mut HashMap, - ) -> bool { - if concrete_type == self { - return true; - } - use Type::*; - match (concrete_type, self) { - (Path(concrete_path), Path(templated_path)) - | (TypeAlias(concrete_path), TypeAlias(templated_path)) => { - templated_path._is_a_resolved_path_type_template_for(concrete_path, bindings) - } - (Slice(concrete_slice), Slice(templated_slice)) => templated_slice - .element_type - ._is_a_template_for(&concrete_slice.element_type, bindings), - (Array(concrete_array), Array(templated_array)) => { - concrete_array.len == templated_array.len - && templated_array - .element_type - ._is_a_template_for(&concrete_array.element_type, bindings) - } - (Reference(concrete_reference), Reference(templated_reference)) => templated_reference - .inner - ._is_a_template_for(&concrete_reference.inner, bindings), - (Tuple(concrete_tuple), Tuple(templated_tuple)) => { - if concrete_tuple.elements.len() != templated_tuple.elements.len() { - return false; - } - concrete_tuple - .elements - .iter() - .zip(templated_tuple.elements.iter()) - .all(|(concrete_type, templated_type)| { - templated_type._is_a_template_for(concrete_type, bindings) - }) - } - (ScalarPrimitive(concrete_primitive), ScalarPrimitive(templated_primitive)) => { - concrete_primitive == templated_primitive - } - (RawPointer(concrete_ptr), RawPointer(templated_ptr)) => { - concrete_ptr.is_mutable == templated_ptr.is_mutable - && templated_ptr - .inner - ._is_a_template_for(&concrete_ptr.inner, bindings) - } - (FunctionPointer(concrete_fp), FunctionPointer(templated_fp)) => { - if concrete_fp.abi != templated_fp.abi - || concrete_fp.is_unsafe != templated_fp.is_unsafe - { - return false; - } - if concrete_fp.inputs.len() != templated_fp.inputs.len() { - return false; - } - let inputs_match = concrete_fp - .inputs - .iter() - .zip(templated_fp.inputs.iter()) - .all(|(concrete, templated)| { - templated - .type_ - ._is_a_template_for(&concrete.type_, bindings) - }); - if !inputs_match { - return false; - } - match (&concrete_fp.output, &templated_fp.output) { - (Some(concrete_out), Some(templated_out)) => { - templated_out._is_a_template_for(concrete_out, bindings) - } - (None, None) => true, - _ => false, - } - } - (_, Generic(parameter)) => { - let previous = bindings.insert(parameter.name.clone(), concrete_type.clone()); - if let Some(previous) = previous - && &previous != concrete_type - { - return false; - } - true - } - (_, _) => false, - } - } - - /// Check if, by renaming the unassigned generic type parameters of `self` (via a bijection!), - /// `self` would be equal to `other`. - /// If possible, this function will return a map associating each unassigned generic parameter - /// in `self` with the name it must be renamed to in order to match `other`. - /// If impossible, this function will return `None`. - pub fn is_equivalent_to<'a, 'b>( - &'a self, - other: &'b Type, - ) -> Option> { - let mut self_id_gen = UnassignedIdGenerator::new(); - let mut other_id_gen = UnassignedIdGenerator::new(); - if self._is_equivalent_to(other, &mut self_id_gen, &mut other_id_gen) { - Some( - self_id_gen - .into_sorted_iter() - .zip(other_id_gen.into_sorted_iter()) - .map(|((self_name, _), (other_name, _))| (self_name, other_name)) - .collect(), - ) - } else { - None - } - } - - pub(crate) fn _is_equivalent_to<'a, 'b>( - &'a self, - other: &'b Type, - self_id_gen: &mut UnassignedIdGenerator<'a>, - other_id_gen: &mut UnassignedIdGenerator<'b>, - ) -> bool { - use Type::*; - match (self, other) { - (Path(self_path), Path(other_path)) | (TypeAlias(self_path), TypeAlias(other_path)) => { - self_path._is_equivalent_to(other_path, self_id_gen, other_id_gen) - } - (Slice(self_slice), Slice(other_slice)) => self_slice.element_type._is_equivalent_to( - &other_slice.element_type, - self_id_gen, - other_id_gen, - ), - (Array(self_array), Array(other_array)) => { - self_array.len == other_array.len - && self_array.element_type._is_equivalent_to( - &other_array.element_type, - self_id_gen, - other_id_gen, - ) - } - (Reference(self_reference), Reference(other_reference)) => self_reference - .inner - ._is_equivalent_to(&other_reference.inner, self_id_gen, other_id_gen), - (Tuple(self_tuple), Tuple(other_tuple)) => { - if self_tuple.elements.len() != other_tuple.elements.len() { - return false; - } - self_tuple - .elements - .iter() - .zip(other_tuple.elements.iter()) - .all(|(self_type, other_type)| { - self_type._is_equivalent_to(other_type, self_id_gen, other_id_gen) - }) - } - (ScalarPrimitive(self_p), ScalarPrimitive(other_p)) => self_p == other_p, - (RawPointer(self_ptr), RawPointer(other_ptr)) => { - self_ptr.is_mutable == other_ptr.is_mutable - && self_ptr - .inner - ._is_equivalent_to(&other_ptr.inner, self_id_gen, other_id_gen) - } - (FunctionPointer(self_fp), FunctionPointer(other_fp)) => { - if self_fp.abi != other_fp.abi || self_fp.is_unsafe != other_fp.is_unsafe { - return false; - } - if self_fp.inputs.len() != other_fp.inputs.len() { - return false; - } - let inputs_match = - self_fp - .inputs - .iter() - .zip(other_fp.inputs.iter()) - .all(|(s, o)| { - s.type_ - ._is_equivalent_to(&o.type_, self_id_gen, other_id_gen) - }); - if !inputs_match { - return false; - } - match (&self_fp.output, &other_fp.output) { - (Some(s), Some(o)) => s._is_equivalent_to(o, self_id_gen, other_id_gen), - (None, None) => true, - _ => false, - } - } - (Generic(self_g), Generic(other_g)) => { - let first_id = self_id_gen.id(&self_g.name); - let second_id = other_id_gen.id(&other_g.name); - first_id == second_id - } - (_, _) => false, - } - } - - /// Return `true` if there is at least one elided lifetime parameter in this type. - /// - /// E.g. `&'_ str` and `&str` would both return `true`. `&'static str` or `&'a str` wouldn't. - pub fn has_implicit_lifetime_parameters(&self) -> bool { - match self { - Type::Path(path) | Type::TypeAlias(path) => { - path.generic_arguments.iter().any(|arg| match arg { - GenericArgument::TypeParameter(g) => g.has_implicit_lifetime_parameters(), - GenericArgument::Lifetime(GenericLifetimeParameter::Inferred) => true, - GenericArgument::Lifetime( - GenericLifetimeParameter::Named(_) | GenericLifetimeParameter::Static, - ) => false, - GenericArgument::Const(_) => false, - }) - } - Type::Reference(r) => { - match &r.lifetime { - Lifetime::Inferred => { - return true; - } - Lifetime::Elided => { - return true; - } - Lifetime::Named(_) | Lifetime::Static => {} - } - r.inner.has_implicit_lifetime_parameters() - } - Type::Tuple(t) => t - .elements - .iter() - .any(|t| t.has_implicit_lifetime_parameters()), - Type::ScalarPrimitive(_) => false, - Type::Slice(s) => s.element_type.has_implicit_lifetime_parameters(), - Type::Array(a) => a.element_type.has_implicit_lifetime_parameters(), - Type::RawPointer(r) => r.inner.has_implicit_lifetime_parameters(), - Type::FunctionPointer(fp) => { - fp.inputs - .iter() - .any(|input| input.type_.has_implicit_lifetime_parameters()) - || fp - .output - .as_ref() - .is_some_and(|t| t.has_implicit_lifetime_parameters()) - } - Type::Generic(_) => false, - } - } - - /// Replace all implicit lifetimes (e.g. `&'_ str` or the elided lifetime in `&str`) to - /// the provided named lifetime. - pub fn set_implicit_lifetimes(&mut self, inferred_lifetime: String) { - match self { - Type::Path(path) | Type::TypeAlias(path) => { - for arg in path.generic_arguments.iter_mut() { - match arg { - GenericArgument::Lifetime(lifetime) => { - if matches!(lifetime, GenericLifetimeParameter::Inferred) { - *lifetime = - GenericLifetimeParameter::from_name(inferred_lifetime.clone()); - } - } - GenericArgument::TypeParameter(t) => { - t.set_implicit_lifetimes(inferred_lifetime.clone()); - } - GenericArgument::Const(_) => {} - } - } - } - Type::Reference(r) => { - match &r.lifetime { - Lifetime::Inferred => { - r.lifetime = Lifetime::from_name(inferred_lifetime.clone()); - } - Lifetime::Elided => { - r.lifetime = Lifetime::from_name(inferred_lifetime.clone()); - } - Lifetime::Static | Lifetime::Named(_) => {} - } - r.inner.set_implicit_lifetimes(inferred_lifetime); - } - Type::Tuple(t) => t - .elements - .iter_mut() - .for_each(|e| e.set_implicit_lifetimes(inferred_lifetime.clone())), - Type::Slice(s) => s.element_type.set_implicit_lifetimes(inferred_lifetime), - Type::Array(a) => a.element_type.set_implicit_lifetimes(inferred_lifetime), - Type::RawPointer(r) => r.inner.set_implicit_lifetimes(inferred_lifetime), - Type::FunctionPointer(fp) => { - for input in fp.inputs.iter_mut() { - input - .type_ - .set_implicit_lifetimes(inferred_lifetime.clone()); - } - if let Some(output) = fp.output.as_mut() { - output.set_implicit_lifetimes(inferred_lifetime); - } - } - Type::Generic(_) | Type::ScalarPrimitive(_) => {} - } - } - - /// Rename named lifetime parameters in this type according to the provided mapping. - /// - /// You don't need to provide a mapping for lifetimes that you don't want to rename. - pub fn rename_lifetime_parameters(&mut self, original2renamed: &IndexMap) { - match self { - Type::Path(t) | Type::TypeAlias(t) => { - for arg in t.generic_arguments.iter_mut() { - match arg { - GenericArgument::TypeParameter(tp) => { - tp.rename_lifetime_parameters(original2renamed); - } - GenericArgument::Lifetime(l) => { - if let GenericLifetimeParameter::Named(named) = l - && let Some(new_name) = original2renamed.get(named.as_str()) - { - *l = GenericLifetimeParameter::from_name(new_name.clone()); - } - } - GenericArgument::Const(_) => {} - } - } - } - Type::Reference(r) => { - match &r.lifetime { - Lifetime::Named(l) => { - if let Some(new_name) = original2renamed.get(l.as_str()) { - r.lifetime = Lifetime::from_name(new_name.clone()); - } - } - Lifetime::Static | Lifetime::Elided | Lifetime::Inferred => {} - } - r.inner.rename_lifetime_parameters(original2renamed); - } - Type::Tuple(t) => { - for e in t.elements.iter_mut() { - e.rename_lifetime_parameters(original2renamed); - } - } - Type::Slice(s) => { - s.element_type.rename_lifetime_parameters(original2renamed); - } - Type::Array(a) => { - a.element_type.rename_lifetime_parameters(original2renamed); - } - Type::RawPointer(r) => { - r.inner.rename_lifetime_parameters(original2renamed); - } - Type::FunctionPointer(fp) => { - for input in fp.inputs.iter_mut() { - input.type_.rename_lifetime_parameters(original2renamed); - } - if let Some(output) = fp.output.as_mut() { - output.rename_lifetime_parameters(original2renamed); - } - } - Type::Generic(_) | Type::ScalarPrimitive(_) => {} - } - } - - /// Return the set of all lifetime parameters for this type. - pub fn lifetime_parameters(&self) -> IndexSet { - let mut set = IndexSet::new(); - self._lifetime_parameters(&mut set); - set - } - - fn _lifetime_parameters(&self, set: &mut IndexSet) { - match self { - Type::Path(path) | Type::TypeAlias(path) => { - for arg in &path.generic_arguments { - match arg { - GenericArgument::TypeParameter(g) => { - g._lifetime_parameters(set); - } - GenericArgument::Lifetime(l) => { - set.insert(l.clone().into()); - } - GenericArgument::Const(_) => {} - } - } - } - Type::Reference(r) => { - set.insert(r.lifetime.clone()); - r.inner._lifetime_parameters(set) - } - Type::Tuple(t) => { - for inner in &t.elements { - inner._lifetime_parameters(set); - } - } - Type::Slice(s) => s.element_type._lifetime_parameters(set), - Type::Array(a) => a.element_type._lifetime_parameters(set), - Type::RawPointer(r) => r.inner._lifetime_parameters(set), - Type::FunctionPointer(fp) => { - for input in &fp.inputs { - input.type_._lifetime_parameters(set); - } - if let Some(output) = &fp.output { - output._lifetime_parameters(set); - } - } - Type::ScalarPrimitive(_) | Type::Generic(_) => {} - } - } - - /// Return the set of free lifetime parameters (i.e. non `'static`) for this type. - pub fn named_lifetime_parameters(&self) -> IndexSet { - let mut set = IndexSet::new(); - self._named_lifetime_parameters(&mut set); - set - } - - fn _named_lifetime_parameters(&self, set: &mut IndexSet) { - match self { - Type::Path(path) | Type::TypeAlias(path) => { - for arg in &path.generic_arguments { - match arg { - GenericArgument::TypeParameter(g) => { - g._named_lifetime_parameters(set); - } - GenericArgument::Lifetime( - GenericLifetimeParameter::Static | GenericLifetimeParameter::Inferred, - ) => {} - GenericArgument::Lifetime(GenericLifetimeParameter::Named(l)) => { - set.insert(l.as_str().to_owned()); - } - GenericArgument::Const(_) => {} - } - } - } - Type::Reference(r) => { - match &r.lifetime { - Lifetime::Named(l) => { - set.insert(l.as_str().to_owned()); - } - Lifetime::Static | Lifetime::Elided | Lifetime::Inferred => {} - } - r.inner._named_lifetime_parameters(set) - } - Type::Tuple(t) => { - for inner in &t.elements { - inner._named_lifetime_parameters(set); - } - } - Type::Slice(s) => s.element_type._named_lifetime_parameters(set), - Type::Array(a) => a.element_type._named_lifetime_parameters(set), - Type::RawPointer(r) => r.inner._named_lifetime_parameters(set), - Type::FunctionPointer(fp) => { - for input in &fp.inputs { - input.type_._named_lifetime_parameters(set); - } - if let Some(output) = &fp.output { - output._named_lifetime_parameters(set); - } - } - Type::ScalarPrimitive(_) | Type::Generic(_) => {} - } - } - - /// Returns a canonicalized copy of this type, wrapped in [`CanonicalType`]. - /// - /// - Non-static lifetimes: each occurrence gets a fresh canonical name ('a, 'b, 'c, ...), - /// regardless of whether it shares a name with another occurrence. - /// - Unassigned generic type parameters: renamed with fresh positional names (A, B, ...), - /// **preserving** identity (two occurrences of `T` both become `A`). - /// - Static lifetimes and scalar primitives are preserved as-is. - /// - For [`Type::Path`] and [`Type::TypeAlias`], when a `rustdoc_id` is available, the - /// `base_type` is rewritten to the canonical shortest importable path reported by - /// the supplied [`CanonicalPathResolver`]. This ensures that the same type reached - /// via different exports canonicalizes to the same value. - pub fn canonicalize(&self, resolver: &dyn CanonicalPathResolver) -> CanonicalType { - let mut lifetime_counter = 0usize; - let mut generic_counter = 0usize; - let mut generic_name_map: HashMap = HashMap::new(); - CanonicalType(self._canonicalize( - resolver, - &mut lifetime_counter, - &mut generic_counter, - &mut generic_name_map, - )) - } - - fn _canonicalize( - &self, - resolver: &dyn CanonicalPathResolver, - lifetime_counter: &mut usize, - generic_counter: &mut usize, - generic_name_map: &mut HashMap, - ) -> Self { - fn next_lifetime_name(counter: &mut usize) -> String { - // Produce "a", "b", ..., "z", "aa", "ab", ... - let mut n = *counter; - *counter += 1; - let mut name = String::new(); - loop { - name.insert(0, (b'a' + (n % 26) as u8) as char); - if n < 26 { - break; - } - n = n / 26 - 1; - } - name - } - - fn next_generic_name(counter: &mut usize) -> String { - // Produce "A", "B", ..., "Z", "AA", "AB", ... - let mut n = *counter; - *counter += 1; - let mut name = String::new(); - loop { - name.insert(0, (b'A' + (n % 26) as u8) as char); - if n < 26 { - break; - } - n = n / 26 - 1; - } - name - } - - fn canonicalize_lifetime(lifetime: &Lifetime, counter: &mut usize) -> Lifetime { - match lifetime { - Lifetime::Static => Lifetime::Static, - Lifetime::Named(_) | Lifetime::Elided | Lifetime::Inferred => { - Lifetime::Named(NamedLifetime::new(next_lifetime_name(counter))) - } - } - } - - fn canonicalize_generic_lifetime( - lifetime: &GenericLifetimeParameter, - counter: &mut usize, - ) -> GenericLifetimeParameter { - match lifetime { - GenericLifetimeParameter::Static => GenericLifetimeParameter::Static, - GenericLifetimeParameter::Named(_) | GenericLifetimeParameter::Inferred => { - GenericLifetimeParameter::Named(NamedLifetime::new(next_lifetime_name(counter))) - } - } - } - - match self { - Type::Path(t) | Type::TypeAlias(t) => { - let is_alias = matches!(self, Type::TypeAlias(_)); - let generic_arguments = t - .generic_arguments - .iter() - .map(|arg| match arg { - GenericArgument::TypeParameter(inner) => { - GenericArgument::TypeParameter(inner._canonicalize( - resolver, - lifetime_counter, - generic_counter, - generic_name_map, - )) - } - GenericArgument::Lifetime(l) => GenericArgument::Lifetime( - canonicalize_generic_lifetime(l, lifetime_counter), - ), - GenericArgument::Const(_) => arg.clone(), - }) - .collect(); - let base_type = if let Some(rustdoc_id) = t.rustdoc_id { - let id = GlobalItemId::new(rustdoc_id, t.package_id.clone()); - resolver - .canonical_path(&id) - .unwrap_or_else(|| t.base_type.clone()) - } else { - t.base_type.clone() - }; - let path = PathType { - package_id: t.package_id.clone(), - rustdoc_id: t.rustdoc_id, - base_type, - generic_arguments, - }; - if is_alias { - Type::TypeAlias(path) - } else { - Type::Path(path) - } - } - Type::Reference(r) => Type::Reference(TypeReference { - is_mutable: r.is_mutable, - lifetime: canonicalize_lifetime(&r.lifetime, lifetime_counter), - inner: Box::new(r.inner._canonicalize( - resolver, - lifetime_counter, - generic_counter, - generic_name_map, - )), - }), - Type::Tuple(t) => Type::Tuple(Tuple { - elements: t - .elements - .iter() - .map(|e| { - e._canonicalize( - resolver, - lifetime_counter, - generic_counter, - generic_name_map, - ) - }) - .collect(), - }), - Type::Slice(s) => Type::Slice(Slice { - element_type: Box::new(s.element_type._canonicalize( - resolver, - lifetime_counter, - generic_counter, - generic_name_map, - )), - }), - Type::Array(a) => Type::Array(Array { - element_type: Box::new(a.element_type._canonicalize( - resolver, - lifetime_counter, - generic_counter, - generic_name_map, - )), - len: a.len, - }), - Type::RawPointer(r) => Type::RawPointer(RawPointer { - is_mutable: r.is_mutable, - inner: Box::new(r.inner._canonicalize( - resolver, - lifetime_counter, - generic_counter, - generic_name_map, - )), - }), - Type::FunctionPointer(fp) => Type::FunctionPointer(FunctionPointer { - inputs: fp - .inputs - .iter() - .map(|input| FunctionPointerInput { - name: None, - type_: input.type_._canonicalize( - resolver, - lifetime_counter, - generic_counter, - generic_name_map, - ), - }) - .collect(), - output: fp.output.as_ref().map(|t| { - Box::new(t._canonicalize( - resolver, - lifetime_counter, - generic_counter, - generic_name_map, - )) - }), - abi: fp.abi.clone(), - is_unsafe: fp.is_unsafe, - }), - Type::ScalarPrimitive(_) => self.clone(), - Type::Generic(g) => { - let canonical_name = generic_name_map - .entry(g.name.clone()) - .or_insert_with(|| next_generic_name(generic_counter)) - .clone(); - Type::Generic(Generic { - name: canonical_name, - }) - } - } - } -} - -impl Debug for Type { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - match self { - Type::Path(t) | Type::TypeAlias(t) => write!(f, "{t:?}"), - Type::Reference(r) => write!(f, "{r:?}"), - Type::Tuple(t) => write!(f, "{t:?}"), - Type::ScalarPrimitive(s) => write!(f, "{s:?}"), - Type::Slice(s) => write!(f, "{s:?}"), - Type::Array(a) => write!(f, "{a:?}"), - Type::RawPointer(r) => write!(f, "{r:?}"), - Type::FunctionPointer(fp) => write!(f, "{fp:?}"), - Type::Generic(g) => write!(f, "{g:?}"), - } - } -} - -impl From for Type { - fn from(value: Tuple) -> Self { - Self::Tuple(value) - } -} - -impl From for Type { - fn from(value: PathType) -> Self { - Self::Path(value) - } -} - -impl From for Type { - fn from(value: TypeReference) -> Self { - Self::Reference(value) - } -} - -impl From for Type { - fn from(value: RawPointer) -> Self { - Self::RawPointer(value) - } -} - -impl From for Type { - fn from(value: Array) -> Self { - Self::Array(value) - } -} - -impl From for Type { - fn from(value: FunctionPointer) -> Self { - Self::FunctionPointer(value) - } -} - -#[cfg(test)] -mod tests { - use crate::{GenericLifetimeParameter, Lifetime, NamedLifetime}; - - #[test] - fn named_lifetimes_are_structurally_compared() { - // Different names are not equal. - assert_ne!( - Lifetime::Named(NamedLifetime::new("a")), - Lifetime::Named(NamedLifetime::new("b")), - ); - // Named is not equal to Elided or Inferred. - assert_ne!(Lifetime::Named(NamedLifetime::new("a")), Lifetime::Elided,); - assert_ne!(Lifetime::Named(NamedLifetime::new("a")), Lifetime::Inferred,); - // Same name is equal. - assert_eq!( - Lifetime::Named(NamedLifetime::new("a")), - Lifetime::Named(NamedLifetime::new("a")), - ); - } - - #[test] - fn named_generic_lifetimes_are_structurally_compared() { - assert_ne!( - GenericLifetimeParameter::Named(NamedLifetime::new("a")), - GenericLifetimeParameter::Named(NamedLifetime::new("b")), - ); - assert_eq!( - GenericLifetimeParameter::Named(NamedLifetime::new("a")), - GenericLifetimeParameter::Named(NamedLifetime::new("a")), - ); - } -} diff --git a/rustdoc/rustdoc_ir/src/type_reference.rs b/rustdoc/rustdoc_ir/src/type_reference.rs deleted file mode 100644 index 9839ff243..000000000 --- a/rustdoc/rustdoc_ir/src/type_reference.rs +++ /dev/null @@ -1,29 +0,0 @@ -use std::fmt::{Debug, Formatter}; - -use crate::{Lifetime, Type}; - -#[derive(serde::Serialize, serde::Deserialize, PartialEq, Eq, Hash, Clone)] -/// A Rust reference—e.g. `&mut u32` or `&'static mut Vec`. -pub struct TypeReference { - /// `true` if this is a mutable reference (`&mut T`). - pub is_mutable: bool, - /// The lifetime of this reference. - pub lifetime: Lifetime, - /// The type being referenced. - pub inner: Box, -} - -impl Debug for TypeReference { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "&")?; - if !self.lifetime.is_elided() { - write!(f, "{:?} ", self.lifetime)?; - } - - if self.is_mutable { - write!(f, "mut ")?; - } - write!(f, "{:?}", self.inner)?; - Ok(()) - } -} diff --git a/rustdoc/rustdoc_processor/Cargo.toml b/rustdoc/rustdoc_processor/Cargo.toml deleted file mode 100644 index 3c663cb46..000000000 --- a/rustdoc/rustdoc_processor/Cargo.toml +++ /dev/null @@ -1,56 +0,0 @@ -[package] -name = "rustdoc_processor" -description = "Compute, cache, index, and query rustdoc JSON documentation" -edition.workspace = true -repository.workspace = true -homepage.workspace = true -license.workspace = true -version.workspace = true - -[lints] -clippy = { large_enum_variant = "allow", result_large_err = "allow" } - -[dependencies] -# Core dependencies -rustdoc-types = { workspace = true } -rustdoc_ext = { path = "../rustdoc_ext" } -rustdoc_ir = { workspace = true } - -# Package graph -guppy = { workspace = true } -camino = { workspace = true } - -# SQLite cache -rusqlite = { workspace = true, features = ["bundled"] } -r2d2_sqlite = { workspace = true } -r2d2 = { workspace = true } - -# Serialization -bincode = { workspace = true, features = ["serde"] } -rkyv = { workspace = true } -serde = { workspace = true, features = ["derive"] } -serde_json = { workspace = true, features = ["unbounded_depth"] } -serde_stacker = { workspace = true } - -# Compute -semver = { workspace = true } -indexmap = { workspace = true } -rayon = { workspace = true } - -# In-memory frozen collections -elsa = { workspace = true } - -# Utilities -anyhow = { workspace = true } -fs-err = { workspace = true } -xxhash-rust = { workspace = true, features = ["xxh64"] } -globwalk = { workspace = true } -tracing = { workspace = true, default-features = true } -tracing_log_error = { workspace = true } -num_cpus = { workspace = true } -itertools = { workspace = true } -thiserror = { workspace = true } -rustc-hash = { workspace = true } -ahash = { workspace = true } -toml = { workspace = true } -once_cell = { workspace = true } diff --git a/rustdoc/rustdoc_processor/src/cache/checksum.rs b/rustdoc/rustdoc_processor/src/cache/checksum.rs deleted file mode 100644 index 1d0d1e778..000000000 --- a/rustdoc/rustdoc_processor/src/cache/checksum.rs +++ /dev/null @@ -1,89 +0,0 @@ -use core::str; -use std::{collections::BTreeSet, path::PathBuf}; - -use anyhow::Context; -use camino::Utf8Path; - -/// Checksum the contents of the crate at the given path in order -/// to verify that the contents have not changed. -/// -/// There are two steps: -/// -/// 1. Determine which files are in scope. -/// 2. Calculate the checksum of everything that was discovered, including the file names. -#[tracing::instrument("Checksum crate files", level = tracing::Level::DEBUG)] -pub(crate) fn checksum_crate(root_path: &Utf8Path) -> Result { - let paths = get_file_paths(root_path)?; - - let mut hasher = xxhash_rust::xxh64::Xxh64::new(24); - for path in paths { - let contents = std::fs::read(&path) - .with_context(|| format!("Failed to read file at `{}`", path.display()))?; - hasher.update(&contents); - } - Ok(hasher.digest()) -} - -/// Resolves the files included in the Cargo package. -/// -/// `root_dir` is the path to the directory that contains the `Cargo.toml` file. -/// -/// The "canonical" way of determining the files included in the package is to run `cargo package --list`, -/// but it appears to be orders of magnitude slower than just doing "the work" ourselves. -fn get_file_paths(root_dir: &Utf8Path) -> Result, anyhow::Error> { - #[derive(serde::Deserialize)] - struct CargoManifest { - package: Package, - } - - #[derive(serde::Deserialize)] - struct Package { - include: Option>, - exclude: Option>, - } - - // Read and parse Cargo.toml - let root_dir = root_dir - .canonicalize() - .context("Failed to canonicalize the path to the root directory")?; - let toml_content = fs_err::read_to_string(root_dir.join("Cargo.toml"))?; - let manifest: CargoManifest = toml::from_str(&toml_content)?; - - // Default inclusions - let default_include = vec![ - "src/**".to_string(), - "Cargo.toml".to_string(), - // A few other files would be included (e.g. README), - // but we don't care about them for the purpose of generating - // the JSON docs of the crate. - ]; - - let CargoManifest { - package: Package { include, exclude }, - } = manifest; - - // Determine include and exclude patterns - let include_patterns = include.unwrap_or(default_include); - let exclude_patterns = exclude.unwrap_or_default(); - - let patterns: Vec<_> = include_patterns - .into_iter() - .chain(exclude_patterns.into_iter().map(|p| format!("!{p}"))) - .collect(); - - let glob_walker = globwalk::GlobWalkerBuilder::from_patterns(&root_dir, &patterns).build()?; - - let included_files: BTreeSet = glob_walker - .into_iter() - .filter_map(|entry| { - let Ok(entry) = entry else { - return None; - }; - if !entry.file_type().is_file() { - return None; - } - Some(entry.into_path()) - }) - .collect(); - Ok(included_files) -} diff --git a/rustdoc/rustdoc_processor/src/cache/entry.rs b/rustdoc/rustdoc_processor/src/cache/entry.rs deleted file mode 100644 index e4875d192..000000000 --- a/rustdoc/rustdoc_processor/src/cache/entry.rs +++ /dev/null @@ -1,140 +0,0 @@ -//! Types for serialized cache entries. - -use std::borrow::Cow; - -use super::utils::RkyvCowBytes; - -/// Data that can be computed starting from the raw JSON documentation for a crate, -/// without having to re-invoke `rustdoc`. -#[derive(Debug)] -pub struct SecondaryIndexes<'a> { - pub import_index: Cow<'a, [u8]>, - pub annotated_items: Option>, - pub import_path2id: RkyvCowBytes<'a>, - pub re_exports: Cow<'a, [u8]>, -} - -/// The serialized form of a crate's documentation, as stored in the cache. -#[derive(Debug)] -pub struct CacheEntry<'a> { - pub root_item_id: u32, - pub external_crates: Cow<'a, [u8]>, - pub paths: RkyvCowBytes<'a>, - pub format_version: i64, - pub items: RkyvCowBytes<'a>, - pub secondary_indexes: Option>, -} - -impl<'a> CacheEntry<'a> { - /// Create a cache entry from a crate, including secondary indexes. - pub fn from_crate( - krate: &'a crate::queries::Crate, - annotations: &'a A, - ) -> Result, anyhow::Error> { - use rkyv::rancor::Panic; - use rkyv::util::AlignedVec; - - let external_crates = bincode::serde::encode_to_vec( - &krate.core.krate.external_crates, - bincode::config::standard(), - )?; - - let paths: AlignedVec = match &krate.core.krate.paths { - crate::crate_data::CrateItemPaths::Eager(crate::crate_data::EagerCrateItemPaths { - paths, - }) => rkyv::to_bytes::(paths)?, - crate::crate_data::CrateItemPaths::Lazy(lazy) => lazy.bytes.clone(), - }; - - let items: AlignedVec = match &krate.core.krate.index { - crate::crate_data::CrateItemIndex::Eager(crate::crate_data::EagerCrateItemIndex { - index, - }) => rkyv::to_bytes::(index)?, - crate::crate_data::CrateItemIndex::Lazy(lazy) => lazy.bytes.clone(), - }; - - let import_path2id: AlignedVec = match &krate.import_path2id { - crate::indexing::ImportPath2Id::Eager(crate::indexing::EagerImportPath2Id(m)) => { - rkyv::to_bytes::(m)? - } - crate::indexing::ImportPath2Id::Lazy(lazy) => lazy.0.clone(), - }; - - let import_index = - bincode::serde::encode_to_vec(&krate.import_index, bincode::config::standard())?; - let annotated_items = - bincode::serde::encode_to_vec(annotations, bincode::config::standard())?; - let re_exports = - bincode::serde::encode_to_vec(&krate.external_re_exports, bincode::config::standard())?; - - let secondary_indexes = SecondaryIndexes { - import_index: Cow::Owned(import_index), - annotated_items: Some(Cow::Owned(annotated_items)), - import_path2id: RkyvCowBytes::Owned(import_path2id), - re_exports: Cow::Owned(re_exports), - }; - - Ok(CacheEntry { - root_item_id: krate.core.krate.root_item_id.0, - external_crates: Cow::Owned(external_crates), - paths: RkyvCowBytes::Owned(paths), - format_version: krate.core.krate.format_version as i64, - items: RkyvCowBytes::Owned(items), - secondary_indexes: Some(secondary_indexes), - }) - } - - /// Create a raw cache entry from a crate (no secondary indexes). - pub fn from_crate_raw( - krate: &'a crate::queries::Crate, - ) -> Result, anyhow::Error> { - use rkyv::rancor::Panic; - use rkyv::util::AlignedVec; - - let external_crates = bincode::serde::encode_to_vec( - &krate.core.krate.external_crates, - bincode::config::standard(), - )?; - - let paths: AlignedVec = match &krate.core.krate.paths { - crate::crate_data::CrateItemPaths::Eager(crate::crate_data::EagerCrateItemPaths { - paths, - }) => rkyv::to_bytes::(paths)?, - crate::crate_data::CrateItemPaths::Lazy(lazy) => lazy.bytes.clone(), - }; - - let items: AlignedVec = match &krate.core.krate.index { - crate::crate_data::CrateItemIndex::Eager(crate::crate_data::EagerCrateItemIndex { - index, - }) => rkyv::to_bytes::(index)?, - crate::crate_data::CrateItemIndex::Lazy(lazy) => lazy.bytes.clone(), - }; - - Ok(CacheEntry { - root_item_id: krate.core.krate.root_item_id.0, - external_crates: Cow::Owned(external_crates), - paths: RkyvCowBytes::Owned(paths), - format_version: krate.core.krate.format_version as i64, - items: RkyvCowBytes::Owned(items), - secondary_indexes: None, - }) - } -} - -/// The key used to store and retrieve a crate's documentation from the cache. -/// -/// It tries to capture all the information that can influence the output of the -/// relevant `rustdoc` command. -#[derive(Debug)] -pub(crate) struct ThirdPartyCrateCacheKey<'a> { - pub(crate) crate_name: &'a str, - pub(crate) crate_source: Cow<'a, str>, - pub(crate) crate_version: String, - /// The hash of the crate's source code. - /// It is only populated for path dependencies. - pub(crate) crate_hash: Option, - pub(crate) cargo_fingerprint: &'a str, - pub(crate) rustdoc_options: String, - pub(crate) default_feature_is_enabled: bool, - pub(crate) active_named_features: String, -} diff --git a/rustdoc/rustdoc_processor/src/cache/mod.rs b/rustdoc/rustdoc_processor/src/cache/mod.rs deleted file mode 100644 index c20e85ece..000000000 --- a/rustdoc/rustdoc_processor/src/cache/mod.rs +++ /dev/null @@ -1,355 +0,0 @@ -//! SQLite-based caching for rustdoc JSON documentation. - -pub(crate) mod checksum; -pub mod entry; -mod third_party; -mod toolchain; -pub mod utils; - -pub use entry::{CacheEntry, SecondaryIndexes}; -use serde::de::DeserializeOwned; -pub use utils::RkyvCowBytes; - -use std::collections::BTreeSet; -use std::marker::PhantomData; - -use anyhow::Context; -use guppy::PackageId; -use guppy::graph::{PackageGraph, PackageMetadata}; -use itertools::Itertools; -use r2d2_sqlite::SqliteConnectionManager; -use rusqlite::params; - -use crate::TOOLCHAIN_CRATES; -use crate::crate_data::CrateData; -use crate::indexing::{ExternalReExports, ImportIndex, ImportPath2Id}; - -use third_party::ThirdPartyCrateCache; -use toolchain::ToolchainCache; - -pub(crate) static BINCODE_CONFIG: bincode::config::Configuration = bincode::config::standard(); - -/// A cache for storing and retrieving pre-computed JSON documentation generated by `rustdoc`. -/// -/// The cache is shared across all projects of the current user. -/// It is stored on disk, using a SQLite database. -/// -/// The type parameter `A` represents the annotation type associated with cached entries. -#[derive(Debug)] -pub struct RustdocGlobalFsCache { - cargo_fingerprint: String, - third_party_cache: ThirdPartyCrateCache, - toolchain_cache: ToolchainCache, - connection_pool: r2d2::Pool, - _annotation: PhantomData, -} - -impl Clone for RustdocGlobalFsCache { - fn clone(&self) -> Self { - Self { - cargo_fingerprint: self.cargo_fingerprint.clone(), - third_party_cache: self.third_party_cache.clone(), - toolchain_cache: self.toolchain_cache.clone(), - connection_pool: self.connection_pool.clone(), - _annotation: PhantomData, - } - } -} - -pub enum RustdocCacheKey<'a> { - ThirdPartyCrate(PackageMetadata<'a>), - ToolchainCrate(&'a str), -} - -impl std::fmt::Debug for RustdocCacheKey<'_> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - RustdocCacheKey::ThirdPartyCrate(metadata) => f - .debug_struct("ThirdPartyCrate") - .field("id", &metadata.id()) - .field("name", &metadata.name()) - .field("version", &metadata.version()) - .finish(), - RustdocCacheKey::ToolchainCrate(name) => f - .debug_struct("ToolchainCrate") - .field("name", name) - .finish(), - } - } -} - -/// An entry retrieved from the on-disk cache. -pub enum HydratedCacheEntry { - /// Only the "raw" output returned by `rustdoc` was stored in the cache. - /// - /// This happens when the indexing phase emitted one or more diagnostics, - /// thus forcing to go through that step (and report those errors) - /// every single time we attempt a compilation. - Raw(CrateData), - /// The cache holds both the raw `rustdoc` output and our secondary indexes. - /// It's ready to be used as is! - Processed(ProcessedCacheEntry), -} - -/// A fully processed cache entry with all secondary indexes. -pub struct ProcessedCacheEntry { - pub package_id: PackageId, - pub crate_data: CrateData, - pub import_path2id: ImportPath2Id, - pub import_index: ImportIndex, - pub external_re_exports: ExternalReExports, - pub annotated_items: A, -} - -impl ProcessedCacheEntry { - /// Convert this cache entry into a `Crate` and its associated annotation data. - pub fn into_crate(self) -> (crate::queries::Crate, A) { - let krate = crate::queries::Crate::new( - crate::queries::CrateCore { - package_id: self.package_id, - krate: self.crate_data, - }, - self.import_path2id, - self.external_re_exports, - self.import_index, - ); - (krate, self.annotated_items) - } -} - -impl<'a> RustdocCacheKey<'a> { - pub fn new(package_id: &'a PackageId, package_graph: &'a PackageGraph) -> RustdocCacheKey<'a> { - if TOOLCHAIN_CRATES.contains(&package_id.repr()) { - RustdocCacheKey::ToolchainCrate(package_id.repr()) - } else { - RustdocCacheKey::ThirdPartyCrate(package_graph.metadata(package_id).unwrap()) - } - } -} - -impl RustdocGlobalFsCache { - /// Initialize a new instance of the cache. - /// - /// The `cache_fingerprint` is used to determine the database file name. - /// It should change whenever the caching logic changes. - /// - /// `cache_dir` is the directory where the SQLite database file will be stored. - #[tracing::instrument(name = "Initialize on-disk rustdoc cache", skip_all)] - pub fn new( - cache_fingerprint: &str, - toolchain_name: &str, - cache_workspace_package_docs: bool, - package_graph: &PackageGraph, - cache_dir: &std::path::Path, - ) -> Result { - std::thread::scope(|scope| { - let handle = scope.spawn(|| cargo_fingerprint(toolchain_name)); - - let pool = Self::setup_database(cache_fingerprint, cache_dir)?; - let connection = pool.get()?; - let third_party_cache = ThirdPartyCrateCache::new( - &connection, - cache_workspace_package_docs, - package_graph, - )?; - let toolchain_cache = ToolchainCache::new(&connection)?; - let cargo_fingerprint = handle - .join() - .expect("Failed to compute on `cargo`'s fingerprint")?; - Ok(Self { - cargo_fingerprint, - connection_pool: pool, - third_party_cache, - toolchain_cache, - _annotation: PhantomData, - }) - }) - } - - /// Retrieve the cached documentation for a given package, if available. - pub fn get( - &self, - cache_key: &RustdocCacheKey, - package_graph: &PackageGraph, - ) -> Result>, anyhow::Error> { - let connection = self.connection_pool.get()?; - match cache_key { - RustdocCacheKey::ThirdPartyCrate(metadata) => self.third_party_cache.get::( - metadata, - &self.cargo_fingerprint, - &connection, - package_graph, - ), - RustdocCacheKey::ToolchainCrate(name) => { - self.toolchain_cache - .get::(name, &self.cargo_fingerprint, &connection) - } - } - } -} - -impl RustdocGlobalFsCache { - /// Store the JSON documentation for a crate in the cache. - pub fn insert( - &self, - cache_key: &RustdocCacheKey, - cache_entry: CacheEntry, - package_graph: &PackageGraph, - ) -> Result<(), anyhow::Error> { - let connection = self.connection_pool.get()?; - match cache_key { - RustdocCacheKey::ThirdPartyCrate(metadata) => { - let Some(key) = self.third_party_cache.cache_key( - metadata, - &self.cargo_fingerprint, - package_graph, - ) else { - return Ok(()); - }; - self.third_party_cache.insert(key, &connection, cache_entry) - } - RustdocCacheKey::ToolchainCrate(name) => { - self.toolchain_cache - .insert(name, cache_entry, &self.cargo_fingerprint, &connection) - } - } - } - - #[tracing::instrument(skip_all, level = "trace")] - /// Persist the list of package IDs that were accessed during the processing of the - /// application blueprint for this project. - pub fn persist_access_log( - &self, - package_ids: &BTreeSet, - project_fingerprint: &str, - ) -> Result<(), anyhow::Error> { - let connection = self.connection_pool.get()?; - - let mut stmt = connection.prepare_cached( - "INSERT INTO project2package_id_access_log ( - project_fingerprint, - package_ids - ) VALUES (?, ?) - ON CONFLICT(project_fingerprint) DO UPDATE SET package_ids=excluded.package_ids; - ", - )?; - stmt.execute(params![ - project_fingerprint, - bincode::encode_to_vec( - package_ids.iter().map(|s| s.repr()).collect_vec(), - BINCODE_CONFIG - )? - ])?; - - Ok(()) - } - - #[tracing::instrument(skip_all, level = "trace")] - /// Retrieve the list of package IDs that were accessed during the last time we processed the - /// application blueprint for this project. - /// - /// Returns an empty set if no access log is found for the given project fingerprint. - pub fn get_access_log( - &self, - project_fingerprint: &str, - ) -> Result, anyhow::Error> { - let connection = self.connection_pool.get()?; - - let mut stmt = connection.prepare_cached( - "SELECT package_ids FROM project2package_id_access_log WHERE project_fingerprint = ?", - )?; - let mut rows = stmt.query(params![project_fingerprint])?; - let Some(row) = rows.next()? else { - return Ok(BTreeSet::new()); - }; - - let package_ids: Vec<&str> = - bincode::borrow_decode_from_slice(row.get_ref_unwrap(0).as_bytes()?, BINCODE_CONFIG)?.0; - Ok(package_ids.into_iter().map(PackageId::new).collect()) - } - - /// Initialize the database, creating the file and the relevant tables if they don't exist yet. - fn setup_database( - cache_fingerprint: &str, - cache_dir: &std::path::Path, - ) -> Result, anyhow::Error> { - fs_err::create_dir_all(cache_dir).with_context(|| { - format!( - "Failed to create the cache directory at {}", - cache_dir.to_string_lossy() - ) - })?; - - // For the sake of simplicity, we use a different SQLite database for each version of the - // cache crate. This ensures that we don't have to worry about schema migrations. - // The cost we pay: the user will have to re-generate the documentation for all their crates - // when they upgrade the tool using this cache. - let cache_path = cache_dir.join(format!("{cache_fingerprint}.db")); - - #[derive(Debug)] - struct SqlitePragmas; - - impl r2d2::CustomizeConnection for SqlitePragmas { - fn on_acquire(&self, conn: &mut rusqlite::Connection) -> Result<(), rusqlite::Error> { - conn.execute_batch( - // 250MB memory-mapped, more than enough. - "PRAGMA mmap_size=262144000;", - )?; - Ok(()) - } - } - - let manager = SqliteConnectionManager::file(cache_path); - let pool = r2d2::Pool::builder() - .max_size(num_cpus::get() as u32) - .connection_customizer(Box::new(SqlitePragmas)) - .build(manager) - .context("Failed to open/create a SQLite database to store the rustdoc cache")?; - - let connection = pool.get()?; - connection.execute_batch( - "PRAGMA journal_mode=WAL; - PRAGMA synchronous=NORMAL;", - )?; - connection.execute( - "CREATE TABLE IF NOT EXISTS project2package_id_access_log ( - project_fingerprint TEXT NOT NULL, - package_ids BLOB NOT NULL, - PRIMARY KEY (project_fingerprint) - )", - [], - )?; - - Ok(pool) - } -} - -/// Return the output of `cargo --verbose --version` for the nightly toolchain, -/// which can be used to fingerprint the toolchain used by Pavex. -pub(crate) fn cargo_fingerprint(toolchain_name: &str) -> Result { - let err_msg = || { - format!( - "Failed to run `cargo --verbose --version` on `{toolchain_name}`.\n\ -Is the `{toolchain_name}` toolchain installed?\n\ -If not, invoke\n - - rustup toolchain install {toolchain_name} -c rust-docs-json - -to fix it.", - ) - }; - let mut cmd = std::process::Command::new("rustup"); - cmd.arg("run") - .arg(toolchain_name) - .arg("cargo") - .arg("--verbose") - .arg("--version"); - let output = cmd.output().with_context(err_msg)?; - if !output.status.success() { - anyhow::bail!(err_msg()); - } - let output = String::from_utf8(output.stdout).with_context(|| { - format!("An invocation of `cargo --verbose --version` for the `{toolchain_name}` toolchain returned non-UTF8 data as output.") - })?; - Ok(output) -} diff --git a/rustdoc/rustdoc_processor/src/cache/third_party.rs b/rustdoc/rustdoc_processor/src/cache/third_party.rs deleted file mode 100644 index 948df0e31..000000000 --- a/rustdoc/rustdoc_processor/src/cache/third_party.rs +++ /dev/null @@ -1,375 +0,0 @@ -//! Cache for third-party crates. - -use std::borrow::Cow; - -use anyhow::Context; -use camino::Utf8Path; -use guppy::graph::feature::StandardFeatures; -use guppy::graph::{PackageGraph, PackageMetadata}; -use rusqlite::params; -use serde::de::DeserializeOwned; -use tracing::instrument; -use tracing_log_error::log_error; - -use super::checksum::checksum_crate; -use super::entry::{CacheEntry, SecondaryIndexes, ThirdPartyCrateCacheKey}; -use super::utils::RkyvCowBytes; -use crate::rustdoc_options; - -use super::HydratedCacheEntry; - -#[derive(Debug, Clone)] -#[non_exhaustive] -pub(super) struct ThirdPartyCrateCache { - pub(super) cache_workspace_packages: bool, -} - -impl ThirdPartyCrateCache { - pub(super) fn new( - connection: &rusqlite::Connection, - cache_workspace_packages: bool, - package_graph: &PackageGraph, - ) -> Result { - Self::setup_table(connection)?; - // Force the creation of the feature graph ahead of our queries. - // It'll be cached internally by the `package_graph`. - let _ = package_graph.feature_graph(); - Ok(Self { - cache_workspace_packages, - }) - } - - /// Retrieve the cached documentation for a given package, if available. - #[instrument(name = "Retrieve third-party crate docs from disk cache", - skip_all, - level=tracing::Level::DEBUG, - fields(crate.id = %package_metadata.id(), cache_key = tracing::field::Empty, hit = tracing::field::Empty) - )] - pub(super) fn get( - &self, - package_metadata: &PackageMetadata, - cargo_fingerprint: &str, - connection: &rusqlite::Connection, - package_graph: &PackageGraph, - ) -> Result>, anyhow::Error> { - fn _get( - package_metadata: &PackageMetadata, - cargo_fingerprint: &str, - connection: &rusqlite::Connection, - cache_workspace_packages: bool, - package_graph: &PackageGraph, - ) -> Result>, anyhow::Error> { - let Some(cache_key) = ThirdPartyCrateCacheKey::build( - package_graph, - package_metadata, - cargo_fingerprint, - cache_workspace_packages, - ) else { - return Ok(None); - }; - tracing::Span::current().record("cache_key", tracing::field::debug(&cache_key)); - // Retrieve from rustdoc's output from cache, if available. - let mut stmt = connection.prepare_cached( - "SELECT - root_item_id, - external_crates, - paths, - format_version, - items, - import_index, - import_path2id, - re_exports, - annotated_items - FROM rustdoc_3d_party_crates_cache - WHERE crate_name = ? AND - crate_source = ? AND - crate_version = ? AND - crate_hash = ? AND - cargo_fingerprint = ? AND - rustdoc_options = ? AND - default_feature_is_enabled = ? AND - active_named_features = ?", - )?; - let span = tracing::trace_span!("Execute query"); - let guard = span.enter(); - let mut rows = stmt.query(params![ - cache_key.crate_name, - cache_key.crate_source, - cache_key.crate_version, - // `NULL` values are considered to be distinct from all other values - // by SQLite, including other `NULL`s. Therefore we use an empty - // string as a placeholder for `NULL` values. - cache_key.crate_hash.unwrap_or_default(), - cache_key.cargo_fingerprint, - cache_key.rustdoc_options, - cache_key.default_feature_is_enabled, - cache_key.active_named_features - ])?; - let Some(row) = rows.next().context("Failed to fetch next row")? else { - return Ok(None); - }; - drop(guard); - - let root_item_id = row.get_ref_unwrap(0).as_i64()?.try_into()?; - let external_crates = row.get_ref_unwrap(1).as_bytes()?; - let paths = row.get_ref_unwrap(2).as_bytes()?; - let format_version = row.get_ref_unwrap(3).as_i64()?; - let items = row.get_ref_unwrap(4).as_bytes()?; - let import_index = row.get_ref_unwrap(5).as_bytes_or_null()?; - let import_path2id = row.get_ref_unwrap(6).as_bytes_or_null()?; - let re_exports = row.get_ref_unwrap(7).as_bytes_or_null()?; - let annotated_items = row.get_ref_unwrap(8).as_bytes_or_null()?; - - let secondary_indexes = - match (import_index, import_path2id, re_exports, annotated_items) { - ( - Some(import_index), - Some(import_path2id), - Some(re_exports), - Some(annotated_items), - ) => Some(SecondaryIndexes { - import_index: Cow::Borrowed(import_index), - import_path2id: RkyvCowBytes::Borrowed(import_path2id), - re_exports: Cow::Borrowed(re_exports), - annotated_items: Some(Cow::Borrowed(annotated_items)), - }), - _ => None, - }; - - let krate = CacheEntry { - root_item_id, - external_crates: Cow::Borrowed(external_crates), - paths: RkyvCowBytes::Borrowed(paths), - format_version, - items: RkyvCowBytes::Borrowed(items), - secondary_indexes, - } - .hydrate::(package_metadata.id().to_owned()) - .context("Failed to re-hydrate the stored docs")?; - - Ok(Some(krate)) - } - let outcome = _get::( - package_metadata, - cargo_fingerprint, - connection, - self.cache_workspace_packages, - package_graph, - ); - match &outcome { - Ok(Some(_)) => { - tracing::Span::current().record("hit", true); - } - Ok(None) => { - tracing::Span::current().record("hit", false); - } - _ => {} - } - outcome - } - - /// Compute the cache key for a given package. - pub(super) fn cache_key<'a>( - &self, - package_metadata: &'a PackageMetadata, - cargo_fingerprint: &'a str, - package_graph: &PackageGraph, - ) -> Option> { - ThirdPartyCrateCacheKey::build( - package_graph, - package_metadata, - cargo_fingerprint, - self.cache_workspace_packages, - ) - } - - /// Store the JSON documentation generated by `rustdoc` in the cache. - #[instrument( - name = "Stored cache data for third-party crate docs to disk", - skip_all, - level=tracing::Level::DEBUG, - fields(cache_key = tracing::field::Empty)) - ] - pub(super) fn insert( - &self, - cache_key: ThirdPartyCrateCacheKey<'_>, - connection: &rusqlite::Connection, - cached_data: CacheEntry<'_>, - ) -> Result<(), anyhow::Error> { - tracing::Span::current().record("cache_key", tracing::field::debug(&cache_key)); - let mut stmt = connection.prepare_cached( - "INSERT INTO rustdoc_3d_party_crates_cache ( - crate_name, - crate_source, - crate_version, - crate_hash, - cargo_fingerprint, - rustdoc_options, - default_feature_is_enabled, - active_named_features, - root_item_id, - external_crates, - paths, - format_version, - items, - import_index, - import_path2id, - re_exports, - annotated_items - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", - )?; - stmt.execute(params![ - cache_key.crate_name, - cache_key.crate_source, - cache_key.crate_version, - // `NULL` values are considered to be distinct from all other values - // by SQLite, including other `NULL`s. Therefore we use an empty - // string as a placeholder for `NULL` values. - cache_key.crate_hash.unwrap_or_default(), - cache_key.cargo_fingerprint, - cache_key.rustdoc_options, - cache_key.default_feature_is_enabled, - cache_key.active_named_features, - cached_data.root_item_id, - cached_data.external_crates, - cached_data.paths, - cached_data.format_version, - cached_data.items, - cached_data - .secondary_indexes - .as_ref() - .map(|i| i.import_index.as_ref()), - cached_data - .secondary_indexes - .as_ref() - .map(|indexes| indexes.import_path2id.as_ref()), - cached_data - .secondary_indexes - .as_ref() - .map(|indexes| indexes.re_exports.as_ref()), - cached_data - .secondary_indexes - .as_ref() - .map(|indexes| indexes.annotated_items.as_ref()) - ])?; - Ok(()) - } - - fn setup_table(connection: &rusqlite::Connection) -> Result<(), anyhow::Error> { - connection.execute( - "CREATE TABLE IF NOT EXISTS rustdoc_3d_party_crates_cache ( - crate_name TEXT NOT NULL, - crate_source TEXT NOT NULL, - crate_version TEXT NOT NULL, - crate_hash TEXT NOT NULL, - cargo_fingerprint TEXT NOT NULL, - rustdoc_options TEXT NOT NULL, - default_feature_is_enabled INTEGER NOT NULL, - active_named_features TEXT NOT NULL, - root_item_id INTEGER NOT NULL, - external_crates BLOB NOT NULL, - paths BLOB NOT NULL, - format_version INTEGER NOT NULL, - items BLOB NOT NULL, - annotated_items BLOB, - import_index BLOB, - import_path2id BLOB, - re_exports BLOB, - PRIMARY KEY (crate_name, crate_source, crate_version, crate_hash, cargo_fingerprint, rustdoc_options, default_feature_is_enabled, active_named_features) - )", - [] - )?; - Ok(()) - } -} - -impl<'a> ThirdPartyCrateCacheKey<'a> { - /// Compute the cache key for a given package. - pub fn build( - package_graph: &PackageGraph, - package_metadata: &'a PackageMetadata<'a>, - cargo_fingerprint: &'a str, - cache_workspace_packages: bool, - ) -> Option> { - enum PathOrId<'a> { - Path(Cow<'a, Utf8Path>), - Id(&'a str), - } - - impl<'a> From> for Cow<'a, str> { - fn from(val: PathOrId<'a>) -> Self { - match val { - PathOrId::Path(cow) => match cow { - Cow::Owned(path) => Cow::Owned(path.to_string()), - Cow::Borrowed(path) => Cow::Borrowed(path.as_str()), - }, - PathOrId::Id(id) => Cow::Borrowed(id), - } - } - } - - let source = match package_metadata.source() { - guppy::graph::PackageSource::Workspace(p) => { - if !cache_workspace_packages { - return None; - } - let p = package_graph.workspace().root().join(p); - PathOrId::Path(Cow::Owned(p)) - } - guppy::graph::PackageSource::Path(p) => PathOrId::Path(Cow::Borrowed(p)), - guppy::graph::PackageSource::External(e) => PathOrId::Id(e), - }; - let crate_hash = if let PathOrId::Path(package_path) = &source { - let package_path = if package_path.is_relative() { - package_graph.workspace().root().join(package_path) - } else { - package_path.clone().into_owned() - }; - // We need to compute the hash of the package's contents, - // to invalidate the cache when the package changes. - // This is only relevant for path dependencies. - // We don't need to do this for external dependencies, - // since they are assumed to be immutable. - let hash = match checksum_crate(&package_path) { - Ok(hash) => hash, - Err(e) => { - log_error!( - *e, - "Failed to compute the hash of the package at {}. \ - I won't cache its JSON documentation to avoid serving stale data.", - package_metadata.id().repr() - ); - return None; - } - }; - Some(hash.to_string()) - } else { - None - }; - let feature_graph = package_graph.feature_graph(); - let feature_set = feature_graph - .query_workspace(StandardFeatures::Default) - .resolve(); - let features = feature_set - .features_for(package_metadata.id()) - .expect("Failed to determine cargo features"); - let (default_feature_is_enabled, mut active_named_features) = match features { - Some(f) => (f.has_base(), f.named_features().collect()), - None => (false, vec![]), - }; - active_named_features.sort(); - let cache_key = ThirdPartyCrateCacheKey { - crate_name: package_metadata.name(), - crate_source: source.into(), - crate_version: package_metadata.version().to_string(), - crate_hash, - cargo_fingerprint, - default_feature_is_enabled, - // SQLite doesn't support arrays, so we have to serialize these two collections as strings. - // This is well defined, since we sorted features and the order of options is well-defined. - rustdoc_options: rustdoc_options().join(" "), - active_named_features: active_named_features.join(" "), - }; - Some(cache_key) - } -} diff --git a/rustdoc/rustdoc_processor/src/cache/toolchain.rs b/rustdoc/rustdoc_processor/src/cache/toolchain.rs deleted file mode 100644 index d92a5f3e4..000000000 --- a/rustdoc/rustdoc_processor/src/cache/toolchain.rs +++ /dev/null @@ -1,227 +0,0 @@ -//! Cache for toolchain crates (std, core, alloc). - -use std::borrow::Cow; - -use guppy::PackageId; -use rusqlite::params; -use serde::de::DeserializeOwned; -use tracing::instrument; - -use super::entry::{CacheEntry, SecondaryIndexes}; -use super::utils::RkyvCowBytes; -use crate::crate_data::{ - CrateData, CrateItemIndex, CrateItemPaths, LazyCrateItemIndex, LazyCrateItemPaths, -}; -use crate::indexing::{ImportPath2Id, LazyImportPath2Id}; - -use super::{BINCODE_CONFIG, HydratedCacheEntry, ProcessedCacheEntry}; - -#[derive(Debug, Clone)] -#[non_exhaustive] -pub(super) struct ToolchainCache {} - -impl ToolchainCache { - pub(super) fn new(connection: &rusqlite::Connection) -> Result { - Self::setup_table(connection)?; - Ok(Self {}) - } - - /// Retrieve the cached documentation for a given toolchain crate, if available. - #[instrument(name = "Retrieve cached toolchain docs from disk", - skip_all, - level=tracing::Level::DEBUG, - fields(crate.name = %name) - )] - pub(super) fn get( - &self, - name: &str, - cargo_fingerprint: &str, - connection: &rusqlite::Connection, - ) -> Result>, anyhow::Error> { - // Retrieve from rustdoc's output from cache, if available. - let mut stmt = connection.prepare_cached( - "SELECT - root_item_id, - external_crates, - paths, - format_version, - items, - import_index, - import_path2id, - re_exports - FROM rustdoc_toolchain_crates_cache - WHERE name = ? AND cargo_fingerprint = ?", - )?; - - let span = tracing::trace_span!("Execute query"); - let guard = span.enter(); - let mut rows = stmt.query(params![name, cargo_fingerprint])?; - let Some(row) = rows.next()? else { - return Ok(None); - }; - drop(guard); - - let root_item_id = row.get_ref_unwrap(0).as_i64()?.try_into()?; - let external_crates = row.get_ref_unwrap(1).as_bytes()?; - let paths = row.get_ref_unwrap(2).as_bytes()?; - let format_version = row.get_ref_unwrap(3).as_i64()?; - - let items = row.get_ref_unwrap(4).as_bytes()?; - - let import_index = row.get_ref_unwrap(5).as_bytes()?; - let import_path2id = row.get_ref_unwrap(6).as_bytes()?; - let re_exports = row.get_ref_unwrap(7).as_bytes()?; - - let krate = CacheEntry { - root_item_id, - external_crates: Cow::Borrowed(external_crates), - paths: RkyvCowBytes::Borrowed(paths), - format_version, - items: RkyvCowBytes::Borrowed(items), - secondary_indexes: Some(SecondaryIndexes { - import_index: Cow::Borrowed(import_index), - // Standard library crates don't have annotations. - annotated_items: None, - import_path2id: RkyvCowBytes::Borrowed(import_path2id), - re_exports: Cow::Borrowed(re_exports), - }), - } - .hydrate::(PackageId::new(name))?; - - Ok(Some(krate)) - } - - /// Store the JSON documentation for a toolchain crate in the cache. - #[instrument(name = "Cache rustdoc output on disk", skip_all, level=tracing::Level::DEBUG, fields(crate.name = name))] - pub(super) fn insert( - &self, - name: &str, - cache_entry: CacheEntry<'_>, - cargo_fingerprint: &str, - connection: &rusqlite::Connection, - ) -> Result<(), anyhow::Error> { - let mut stmt = connection.prepare_cached( - "INSERT INTO rustdoc_toolchain_crates_cache ( - name, - cargo_fingerprint, - root_item_id, - external_crates, - paths, - format_version, - items, - import_index, - import_path2id, - re_exports - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", - )?; - stmt.execute(params![ - name, - cargo_fingerprint, - cache_entry.root_item_id, - cache_entry.external_crates, - cache_entry.paths, - cache_entry.format_version, - cache_entry.items, - cache_entry - .secondary_indexes - .as_ref() - .expect("Indexing never fails for toolchain crates") - .import_index, - cache_entry - .secondary_indexes - .as_ref() - .expect("Indexing never fails for toolchain crates") - .import_path2id, - cache_entry - .secondary_indexes - .as_ref() - .expect("Indexing never fails for toolchain crates") - .re_exports - ])?; - Ok(()) - } - - fn setup_table(connection: &rusqlite::Connection) -> Result<(), anyhow::Error> { - connection.execute( - "CREATE TABLE IF NOT EXISTS rustdoc_toolchain_crates_cache ( - name TEXT NOT NULL, - cargo_fingerprint TEXT NOT NULL, - root_item_id INTEGER NOT NULL, - external_crates BLOB NOT NULL, - paths BLOB NOT NULL, - format_version INTEGER NOT NULL, - items BLOB NOT NULL, - import_index BLOB NOT NULL, - import_path2id BLOB NOT NULL, - re_exports BLOB NOT NULL, - PRIMARY KEY (name, cargo_fingerprint) - )", - [], - )?; - Ok(()) - } -} - -impl CacheEntry<'_> { - /// Re-hydrate the documentation retrieved from the cache. - /// - /// We hydrate all mappings eagerly, but we avoid re-hydrating the item index eagerly, - /// since it can be quite large and deserialization can be slow for large crates. - /// The item index is stored as rkyv-serialized bytes for zero-copy access. - pub fn hydrate( - self, - package_id: PackageId, - ) -> Result, anyhow::Error> { - use anyhow::Context; - - let crate_data = CrateData { - root_item_id: rustdoc_types::Id(self.root_item_id.to_owned()), - external_crates: bincode::serde::decode_from_slice( - &self.external_crates, - BINCODE_CONFIG, - ) - .context("Failed to deserialize external_crates")? - .0, - paths: CrateItemPaths::Lazy(LazyCrateItemPaths { - bytes: self.paths.into_owned(), - }), - format_version: self.format_version.try_into()?, - index: CrateItemIndex::Lazy(LazyCrateItemIndex { - bytes: self.items.into_owned(), - }), - }; - let Some(secondary_indexes) = self.secondary_indexes else { - return Ok(HydratedCacheEntry::Raw(crate_data)); - }; - - let import_index = - bincode::serde::decode_from_slice(&secondary_indexes.import_index, BINCODE_CONFIG) - .context("Failed to deserialize import_index")? - .0; - - let re_exports = - bincode::serde::decode_from_slice(&secondary_indexes.re_exports, BINCODE_CONFIG) - .context("Failed to deserialize re-exports")? - .0; - - let annotated_items: A = if let Some(data) = secondary_indexes.annotated_items { - bincode::serde::decode_from_slice(&data, BINCODE_CONFIG) - .context("Failed to deserialize annotated_items")? - .0 - } else { - A::default() - }; - - let processed = ProcessedCacheEntry { - package_id, - crate_data, - import_path2id: ImportPath2Id::Lazy(LazyImportPath2Id( - secondary_indexes.import_path2id.into_owned(), - )), - external_re_exports: re_exports, - import_index, - annotated_items, - }; - Ok(HydratedCacheEntry::Processed(processed)) - } -} diff --git a/rustdoc/rustdoc_processor/src/cache/utils.rs b/rustdoc/rustdoc_processor/src/cache/utils.rs deleted file mode 100644 index fada30f74..000000000 --- a/rustdoc/rustdoc_processor/src/cache/utils.rs +++ /dev/null @@ -1,43 +0,0 @@ -//! Utility types for the cache module. - -use rkyv::util::AlignedVec; -use rusqlite::{ToSql, types::ToSqlOutput}; - -/// A `Cow` variant to work with `rkyv`'s `AlignedVec`. -#[derive(Debug)] -pub enum RkyvCowBytes<'a> { - Borrowed(&'a [u8]), - Owned(AlignedVec), -} - -impl ToSql for RkyvCowBytes<'_> { - fn to_sql(&self) -> rusqlite::Result> { - let s = match self { - RkyvCowBytes::Borrowed(items) => items, - RkyvCowBytes::Owned(s) => s.as_slice(), - }; - Ok(ToSqlOutput::Borrowed(rusqlite::types::ValueRef::Blob(s))) - } -} - -impl RkyvCowBytes<'_> { - pub fn into_owned(self) -> AlignedVec { - match self { - RkyvCowBytes::Borrowed(items) => { - let mut v = AlignedVec::with_capacity(items.len()); - v.extend_from_slice(items); - v - } - RkyvCowBytes::Owned(aligned_vec) => aligned_vec, - } - } -} - -impl AsRef<[u8]> for RkyvCowBytes<'_> { - fn as_ref(&self) -> &[u8] { - match self { - RkyvCowBytes::Borrowed(items) => items, - RkyvCowBytes::Owned(aligned_vec) => aligned_vec.as_slice(), - } - } -} diff --git a/rustdoc/rustdoc_processor/src/collection.rs b/rustdoc/rustdoc_processor/src/collection.rs deleted file mode 100644 index c026f9b1a..000000000 --- a/rustdoc/rustdoc_processor/src/collection.rs +++ /dev/null @@ -1,518 +0,0 @@ -use std::borrow::Cow; -use std::collections::BTreeSet; -use std::sync::Arc; - -use ahash::{HashMap, HashSet, HashSetExt}; -use elsa::FrozenMap; -use guppy::PackageId; -use guppy::graph::PackageGraph; -use rustdoc_types::Item; -use tracing::Span; -use tracing_log_error::log_error; - -use crate::TOOLCHAIN_CRATES; -use crate::cache::{CacheEntry, HydratedCacheEntry, RustdocCacheKey, RustdocGlobalFsCache}; -use crate::compute::{CannotGetCrateData, ComputeProgress, compute_crate_docs}; -use crate::indexing::CrateIndexer; -use crate::queries::Crate; -use rustdoc_ext::GlobalItemId; - -use rayon::iter::IntoParallelRefIterator; - -/// The main entrypoint for accessing the documentation of the crates -/// in a specific `PackageGraph`. -/// -/// It takes care of: -/// - Computing and caching the JSON documentation for crates in the graph; -/// - Execute queries that span the documentation of multiple crates (e.g. following crate -/// re-exports or star re-exports). -/// -/// # Performance -/// -/// It takes a bit of engineering to build a reasonably-fast tool on `rustdoc-json`. -/// On its own, `rustdoc-json` is slow. -/// Furthermore, retrieval of cached entries is expensive too, given the size -/// of the JSON documentation for some crates. -/// -/// `CrateCollection` uses a layered strategy to improve the performance of the -/// application using it. -/// -/// ## Lazy Computation -/// -/// To mitigate the performance impact, [`CrateCollection`] is _lazy_. It won't eagerly -/// compute the documentation for every single package in the graph. -/// Instead it'll populate entries whenever they are needed. -/// -/// ## Bootstrapping -/// -/// At the same time, computing entries one by one is significantly slower than having -/// `cargo` leveraging all available cores on the host machine. -/// To balance the two concerns, [`CrateCollection::bootstrap`] keeps track -/// of the crates which where needed the last time a collection was instantiated for the -/// workspace that's being analyzed. -/// -/// ## Batching -/// -/// Leverage [`CrateCollection::batch_compute_crates`] whenever you need to compute JSON -/// docs for multiple crates at once. -/// -/// ## Parallelism -/// -/// Internally, `CrateCollection` leverages `rayon` to offload CPU-intensive tasks to other -/// threads. E.g. deserialization of a raw JSON doc, chunks of the indexing pipeline, etc. -pub struct CrateCollection { - /// The graph underpinning this collection. - /// - /// Every single JSON document in this collection is attached - /// to a specific [`PackageId`] in this graph. - package_graph: PackageGraph, - /// A mechanism to extract items out of the "raw" JSON documentation - /// for a crate. - /// In particular, it allows different consumers of this collection - /// to extract project-specific annotations. Check out [`CrateIndexer`] - /// for more information. - indexer: I, - /// A map to associate each [`PackageId`] to its (parsed) JSON documentation. - package_id2krate: FrozenMap>, - /// Project-specific annotations extracted from items in each crate, - /// stored as a side map to keep [`Crate`] itself project-agnostic. - annotated_items: FrozenMap>, - /// A SQLite cache, storing pre-computed JSON docs. - /// - /// It amortizes the cost of invoking `rustdoc-json` when the same workspace - /// is analyzed a second time. - disk_cache: RustdocGlobalFsCache, - /// An opaque string that uniquely identifies the current project. - project_fingerprint: String, - /// This map keeps track of the packages that have been accessed while analyzing - /// the underlying [`PackageGraph`]. - /// - /// It is used to pre-compute/eagerly retrieve from the cache the docs for - /// this project the next it's analyzed. See [`CrateCollection`] documentation for - /// more information about performance optimization. - /// - /// # Implementation notes - /// - /// `elsa` doesn't expose a frozen BTreeSet yet, so we use a map with empty values - /// to emulate it. - access_log: FrozenMap>, - /// The name of the toolchain used to generate the JSON documentation of a crate. - /// It is assumed to be a toolchain available via `rustup`. - toolchain_name: String, - /// Progress reporter for documenting crates. - progress: Box, -} - -impl std::fmt::Debug for CrateCollection { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("CrateCollection") - .field("package_graph", &self.package_graph) - .finish_non_exhaustive() - } -} - -impl CrateCollection { - /// Create a new [`CrateCollection`] with the given indexer and pre-constructed cache. - pub fn new( - indexer: I, - toolchain_name: String, - package_graph: PackageGraph, - project_fingerprint: String, - disk_cache: RustdocGlobalFsCache, - progress: Box, - ) -> Self { - Self { - indexer, - package_id2krate: FrozenMap::new(), - annotated_items: FrozenMap::new(), - package_graph, - disk_cache, - project_fingerprint, - access_log: FrozenMap::new(), - toolchain_name, - progress, - } - } - - /// The graph underpinning this collection. - /// - /// Every single JSON document in this collection is attached - /// to a specific [`PackageId`] in this graph. - pub fn package_graph(&self) -> &PackageGraph { - &self.package_graph - } - - /// Bootstrap the crate collection by either fetching from the cache or computing - /// on the fly the documentation of all the crates whose JSON docs are likely to be - /// needed to process the application blueprint. - /// - /// We rely on: - /// - /// - Data from the previous run for this project, if available (the access log); - /// - Heuristics (toolchain crates); - /// - Data from the raw blueprint before any expensive processing has taken place (extra package ids). - /// - /// This method should only be called once. - #[tracing::instrument(skip_all, level = "trace")] - pub fn bootstrap(&self, extra_package_ids: Iter) -> Result<(), anyhow::Error> - where - Iter: Iterator, - { - let package_ids = self - .disk_cache - .get_access_log(&self.project_fingerprint) - .unwrap_or_else(|e| { - log_error!( - *e, - level: tracing::Level::WARN, - "Failed to retrieve the crate access log from the on-disk cache" - ); - // This is an optimisation, therefore we should not - // fail if the retrieval fails. - BTreeSet::new() - }); - let package_ids = package_ids - .into_iter() - .chain(extra_package_ids) - // Some package ids may not longer be part of the package graph, - // e.g. because the lockfile has been updated to use more recent versions - // or they have been removed as dependencies from one or more of the Cargo.toml - // manifests. - .filter(|id| self.package_graph.metadata(id).is_ok()) - // We always need the documentation for the toolchain crates. - .chain(TOOLCHAIN_CRATES.iter().map(|s| PackageId::new(*s))) - .collect::>(); - self.compute_batch(package_ids.into_iter()) - } - - /// Process a [`HydratedCacheEntry`] into a `(Crate, Annotations)` pair, - /// re-indexing if only the raw data was cached. - fn process_cache_entry( - &self, - entry: HydratedCacheEntry, - package_id: PackageId, - ) -> (Crate, I::Annotations) { - match entry { - HydratedCacheEntry::Processed(processed) => processed.into_crate(), - HydratedCacheEntry::Raw(crate_data) => { - let result = self.indexer.index(crate_data, package_id); - (result.krate, result.annotations) - } - } - } - - /// Compute the documentation for multiple crates given their [`PackageId`]s. - /// - /// They won't be computed again if they are already in [`CrateCollection`]'s internal cache. - #[tracing::instrument(skip_all, level = "trace")] - pub fn compute_batch(&self, package_ids: Iter) -> Result<(), anyhow::Error> - where - Iter: Iterator, - { - let missing_ids = package_ids - // First check if we already have the crate docs in the in-memory cache. - .filter(|package_id| self.get_crate_by_package_id(package_id).is_none()) - .collect::>(); - - // It can take a while to deserialize the JSON docs for a crate from the cache, - // so we parallelize the operation. - let package_graph = &self.package_graph; - let cache = &self.disk_cache; - let tracing_span = Span::current(); - let map_op = move |id: PackageId| { - tracing_span.in_scope(|| { - let cache_key = RustdocCacheKey::new(&id, package_graph); - match cache.get(&cache_key, package_graph) { - Ok(None) => (id, None), - Ok(Some(entry)) => (id, Some(entry)), - Err(e) => { - log_error!( - *e, - level: tracing::Level::WARN, - package_id = id.repr(), - "Failed to retrieve the documentation from the on-disk cache", - ); - (id, None) - } - } - }) - }; - - let mut to_be_computed = vec![]; - - use rayon::prelude::{IntoParallelIterator, ParallelIterator}; - for (package_id, cached) in missing_ids.into_par_iter().map(map_op).collect::>() { - if let Some(entry) = cached { - let (krate, annotations) = self.process_cache_entry(entry, package_id.clone()); - self.annotated_items - .insert(package_id.clone(), Box::new(annotations)); - self.package_id2krate.insert(package_id, Box::new(krate)); - continue; - } - to_be_computed.push(package_id); - } - - // The ones that are still missing need to be computed. - let results = compute_crate_docs( - &self.toolchain_name, - &self.package_graph, - to_be_computed.into_iter(), - self.package_graph.workspace().root().as_std_path(), - self.progress.as_ref(), - )?; - - // We then have to perform two more expensive operations: indexing of all the items in each - // crate and conversion of the "raw" JSON format into our optimised cache entry format. - // We perform both in parallel, since they're CPU-intensive. - // - // First indexing: - let indexer = &self.indexer; - let package_graph = self.package_graph(); - let indexed_krates = results - .into_par_iter() - .map(move |(package_id, krate)| { - let result = indexer.index_raw(krate, package_id.to_owned()); - ( - package_id, - result.krate, - result.annotations, - result.can_cache_indexes, - ) - }) - .collect::>(); - // Then conversion to the desired cache format: - let mut cache_entries: HashMap<_, _> = indexed_krates - .par_iter() - .filter_map(|(package_id, krate, annotations, cache_indexes)| { - let data = if *cache_indexes { - CacheEntry::from_crate(krate, annotations) - } else { - CacheEntry::from_crate_raw(krate) - }; - let cache_key = RustdocCacheKey::new(package_id, package_graph); - match data { - Ok(v) => Some((package_id, (cache_key, v))), - Err(e) => { - log_error!( - *e, - level: tracing::Level::WARN, - package_id = package_id.repr(), - "Failed to convert the computed JSON docs into the format used by the on-disk cache", - ); - None - } - } - }) - .collect(); - - let mut to_be_inserted = HashSet::with_capacity(indexed_krates.len()); - for (package_id, _, _, _) in &indexed_krates { - let Some((cache_key, cache_data)) = cache_entries.remove(&package_id) else { - continue; - }; - if let Err(e) = self - .disk_cache - .insert(&cache_key, cache_data, package_graph) - { - log_error!( - *e, - level: tracing::Level::WARN, - package_id = package_id.repr(), - "Failed to store the computed JSON docs in the on-disk cache", - ); - } - // If we tried to insert into the in-memory cache directly, we'd get a borrow-checker - // error since `cache_data` borrows from `krate`. - // We keep track of what needs to be inserted and do it later once the on-disk - // cache has been taken care of. - to_be_inserted.insert(package_id.to_owned()); - } - - for (package_id, krate, annotations, _) in indexed_krates { - if to_be_inserted.contains(&package_id) { - self.annotated_items - .insert(package_id.clone(), Box::new(annotations)); - self.package_id2krate.insert(package_id, Box::new(krate)); - } - } - Ok(()) - } - - /// Compute the documentation for the crate associated with a specific [`PackageId`]. - /// - /// It will be retrieved from [`CrateCollection`]'s internal cache if it was computed before. - pub fn get_or_compute(&self, package_id: &PackageId) -> Result<&Crate, CannotGetCrateData> { - self.access_log.insert(package_id.to_owned(), Box::new(())); - - // First check if we already have the crate docs in the in-memory cache. - if let Some(krate) = self.get_crate_by_package_id(package_id) { - return Ok(krate); - } - - // If not, let's try to retrieve them from the on-disk cache. - let cache_key = RustdocCacheKey::new(package_id, &self.package_graph); - match self.disk_cache.get(&cache_key, &self.package_graph) { - Ok(Some(entry)) => { - let (krate, annotations) = self.process_cache_entry(entry, package_id.clone()); - self.annotated_items - .insert(package_id.to_owned(), Box::new(annotations)); - self.package_id2krate - .insert(package_id.to_owned(), Box::new(krate)); - return Ok(self.get_crate_by_package_id(package_id).unwrap()); - } - Err(e) => { - log_error!(*e, level: tracing::Level::WARN, package_id = package_id.repr(), "Failed to retrieve the documentation from the on-disk cache"); - } - Ok(None) => {} - } - - // If we don't have them in the on-disk cache, we need to compute them. - let krate = compute_crate_docs( - &self.toolchain_name, - &self.package_graph, - std::iter::once(package_id.to_owned()), - self.package_graph.workspace().root().as_std_path(), - self.progress.as_ref(), - ) - .map_err(|e| CannotGetCrateData { - package_spec: package_id.to_string(), - source: Arc::new(e), - })? - .remove(package_id) - .unwrap(); - - let result = self.indexer.index_raw(krate, package_id.to_owned()); - - // Store in the on-disk cache for next time. - let cache_entry_data = if result.can_cache_indexes { - CacheEntry::from_crate(&result.krate, &result.annotations) - } else { - CacheEntry::from_crate_raw(&result.krate) - }; - match cache_entry_data { - Ok(cache_entry) => { - if let Err(e) = self - .disk_cache - .insert(&cache_key, cache_entry, &self.package_graph) - { - log_error!( - *e, - level: tracing::Level::WARN, - package_id = package_id.repr(), - "Failed to store the computed JSON docs in the on-disk cache", - ); - } - } - Err(e) => { - log_error!( - *e, - level: tracing::Level::WARN, - package_id = package_id.repr(), - "Failed to convert the computed JSON docs into the format used by the on-disk cache", - ); - } - } - - self.annotated_items - .insert(package_id.to_owned(), Box::new(result.annotations)); - self.package_id2krate - .insert(package_id.to_owned(), Box::new(result.krate)); - Ok(self.get_crate_by_package_id(package_id).unwrap()) - } - - /// Retrieve the documentation for the crate associated with [`PackageId`] from - /// [`CrateCollection`]'s internal cache if it was computed before. - /// - /// It returns `None` if no documentation is found for the specified [`PackageId`]. - pub fn get_crate_by_package_id(&self, package_id: &PackageId) -> Option<&Crate> { - self.package_id2krate.get(package_id) - } - - /// Retrieve the annotations for a specific package, if available. - pub fn get_annotated_items(&self, package_id: &PackageId) -> Option<&I::Annotations> { - self.annotated_items.get(package_id) - } - - /// Retrieve type information given its [`GlobalItemId`]. - /// - /// It panics if no item is found for the specified [`GlobalItemId`]. - pub fn get_item_by_global_type_id(&self, type_id: &GlobalItemId) -> Cow<'_, Item> { - let krate = self.get_or_compute(&type_id.package_id).unwrap(); - krate.get_item_by_local_type_id(&type_id.rustdoc_item_id) - } - - /// Retrieve the canonical path for a struct, enum or function given its [`GlobalItemId`]. - /// - /// It panics if no item is found for the specified [`GlobalItemId`]. - pub fn get_canonical_path_by_global_type_id( - &self, - type_id: &GlobalItemId, - ) -> Result<&[String], anyhow::Error> { - let krate = self.get_or_compute(&type_id.package_id).unwrap(); - krate.get_canonical_path(type_id) - } - - /// Retrieve the canonical path and the [`GlobalItemId`] for a struct, enum or function given - /// its **local** id. - pub fn get_canonical_path_by_local_type_id( - &self, - used_by_package_id: &PackageId, - item_id: &rustdoc_types::Id, - // The item might come from a transitive dependency via a re-export - // done by a direct dependency. - // We don't have a bulletproof way of finding the re-exporter name, but we can - // try to infer it (e.g. via the `name` property). - re_exporter_crate_name: Option<&str>, - ) -> Result<(GlobalItemId, &[String]), anyhow::Error> { - let (definition_package_id, path) = { - let used_by_krate = self.get_or_compute(used_by_package_id)?; - let local_type_summary = used_by_krate.get_summary_by_local_type_id(item_id)?; - ( - used_by_krate.compute_package_id_for_crate_id_with_hint( - local_type_summary.crate_id, - self, - // If the type was re-exported from another crate, the two names here should not match. - // The one coming from the summary is the name of the crate where the type was defined. - // The one coming from the `maybe_reexport_from` is the name of the crate where the type - // was re-exported from and used by the crate we are currently processing. - if local_type_summary.path.first().map(|s| s.as_str()) != re_exporter_crate_name - { - re_exporter_crate_name - } else { - None - }, - )?, - local_type_summary.path.clone(), - ) - }; - let definition_krate = self.get_or_compute(&definition_package_id)?; - let type_id = definition_krate.get_item_id_by_path(&path, self)??; - let canonical_path = self.get_canonical_path_by_global_type_id(&type_id)?; - Ok((type_id.clone(), canonical_path)) - } -} - -impl rustdoc_ir::CanonicalPathResolver for CrateCollection { - fn canonical_path(&self, id: &GlobalItemId) -> Option> { - self.get_canonical_path_by_global_type_id(id) - .ok() - .map(<[String]>::to_vec) - } -} - -impl Drop for CrateCollection { - fn drop(&mut self) { - let access_log = std::mem::take(&mut self.access_log); - let package_ids = access_log.into_map().into_keys().collect(); - if let Err(e) = self - .disk_cache - .persist_access_log(&package_ids, &self.project_fingerprint) - { - log_error!( - *e, - level: tracing::Level::WARN, - "Failed to persist the crate access log to the on-disk cache", - ); - } - } -} diff --git a/rustdoc/rustdoc_processor/src/compute/format.rs b/rustdoc/rustdoc_processor/src/compute/format.rs deleted file mode 100644 index cbfb27c90..000000000 --- a/rustdoc/rustdoc_processor/src/compute/format.rs +++ /dev/null @@ -1,24 +0,0 @@ -use std::io::Read; - -#[derive(serde::Deserialize)] -/// A minimal subset of the generated JSON docs for a crate, used to check the format version. -struct CrateMeta { - format_version: u32, -} - -/// Check that the JSON docs we are working with using the expected format version. -pub fn check_format(raw_json: R) -> Result<(), anyhow::Error> { - let Ok(min_krate) = serde_json::from_reader::(raw_json) else { - anyhow::bail!( - "Failed to deserialize the `format_version` of the generated JSON docs. Is it actually the JSON documentation for a crate?" - ); - }; - if min_krate.format_version != rustdoc_types::FORMAT_VERSION { - anyhow::bail!( - "The JSON docs use the `{}` format version, but `pavexc` expected `{}`.", - min_krate.format_version, - rustdoc_types::FORMAT_VERSION, - ); - } - Ok(()) -} diff --git a/rustdoc/rustdoc_processor/src/compute/mod.rs b/rustdoc/rustdoc_processor/src/compute/mod.rs deleted file mode 100644 index aac475d92..000000000 --- a/rustdoc/rustdoc_processor/src/compute/mod.rs +++ /dev/null @@ -1,10 +0,0 @@ -//! Orchestrate `cargo rustdoc` invocations to generate JSON documentation. - -mod format; -mod orchestration; -mod package_id_spec; -mod progress; -mod toolchain; - -pub use orchestration::{CannotGetCrateData, compute_crate_docs}; -pub use progress::{ComputeProgress, NoProgress}; diff --git a/rustdoc/rustdoc_processor/src/compute/orchestration.rs b/rustdoc/rustdoc_processor/src/compute/orchestration.rs deleted file mode 100644 index bcd650072..000000000 --- a/rustdoc/rustdoc_processor/src/compute/orchestration.rs +++ /dev/null @@ -1,344 +0,0 @@ -use std::io::{BufReader, Seek, SeekFrom}; -use std::path::{Path, PathBuf}; -use std::sync::Arc; - -use ahash::{HashMap, HashMapExt}; -use anyhow::Context; -use guppy::graph::{BuildTargetId, PackageGraph}; -use guppy::{PackageId, Version}; -use indexmap::IndexSet; -use itertools::Itertools as _; -use serde::Deserialize; - -use super::format::check_format; -use super::package_id_spec::PackageIdSpecification; -use super::progress::ComputeProgress; -use super::toolchain::get_toolchain_crate_docs; -use crate::TOOLCHAIN_CRATES; -use crate::rustdoc_options; -use crate::utils::normalize_crate_name; - -#[derive(Debug, thiserror::Error, Clone)] -#[error( - "I failed to retrieve information about the public types of a package in your dependency tree ('{package_spec}')." -)] -pub struct CannotGetCrateData { - pub package_spec: String, - #[source] - pub source: Arc, -} - -fn format_optional_version(v: &Option) -> Option> { - v.as_ref().map(|v| { - use std::fmt::Write; - let mut s = format!("v{}.{}.{}", v.major, v.minor, v.patch); - if !v.pre.is_empty() { - write!(&mut s, "-{}", v.pre).unwrap(); - } - tracing::field::display(s) - }) -} - -/// Returns the library target name for a package, falling back to the -/// normalized package name if no lib target exists. -/// -/// `cargo doc` names its output files after the lib target name, not the package name. -/// When a `Cargo.toml` sets `[lib] name = "foo"`, the output file is `foo.json`, -/// regardless of the package name. -fn lib_target_name(package_id: &PackageId, package_graph: &PackageGraph) -> String { - if let Ok(metadata) = package_graph.metadata(package_id) - && let Some(target) = metadata.build_target(&BuildTargetId::Library) - { - return target.name().to_owned(); - } - // Fallback: normalize the package name (hyphens → underscores), which - // is what cargo uses by default when no explicit [lib] name is set. - normalize_crate_name(package_graph.metadata(package_id).unwrap().name()) -} - -/// Return the JSON documentation for one or more crates. -/// Crates are singled out, within the current workspace, using a [`PackageIdSpecification`]. -/// -/// The documentation is computed on the fly for crates that are local to the current workspace. -/// The documentation is retrieved via `rustup` for toolchain crates (e.g. `std`). -pub fn compute_crate_docs( - toolchain_name: &str, - package_graph: &PackageGraph, - package_ids: I, - current_dir: &Path, - progress: &P, -) -> Result, anyhow::Error> -where - I: Iterator, - P: ComputeProgress + ?Sized, -{ - let mut to_be_computed = vec![]; - let mut results = HashMap::new(); - for package_id in package_ids { - // Some crates are not compiled as part of the dependency tree of the current workspace. - // They are instead bundled as part of Rust's toolchain and automatically available for import - // and usage in your crate: the standard library (`std`), `core` (a smaller subset of `std` - // that doesn't require an allocator), `alloc` (a smaller subset of `std` that assumes you - // can allocate). - // Since those crates are pre-compiled (and somewhat special), we can't generate their - // documentation on the fly. We assume that their JSON docs have been pre-computed and are - // available for us to look at. - if TOOLCHAIN_CRATES.contains(&package_id.repr()) { - let krate = get_toolchain_crate_docs(package_id.repr(), toolchain_name)?; - results.insert(package_id, krate); - continue; - } - - let package_spec = PackageIdSpecification::from_package_id(&package_id, package_graph)?; - to_be_computed.push((package_id, package_spec)); - } - - if to_be_computed.is_empty() { - return Ok(results); - } - - // We need to chunk the crates into batches, because `cargo rustdoc` can only compute the - // documentation for multiple crates at once if all the crate names are unique within the - // batch. - // - // That's due to the output naming scheme of `rustdoc`: the output file is `{crate_name}.json`. - // If we were to pass multiple crates with the same name to `cargo rustdoc`, the output file - // would be overwritten multiple times, and we would only be left with the documentation for - // the last crate. - let chunks = { - let mut chunks: Vec> = vec![]; - let mut chunk_id2names = HashMap::>::new(); - 'outer: for (package_id, package_spec) in to_be_computed { - let target_name = lib_target_name(&package_id, package_graph); - for (index, chunk) in chunks.iter_mut().enumerate() { - let chunk_names = chunk_id2names.get_mut(&index).unwrap(); - if chunk_names.insert(target_name.clone()) { - chunk.push((package_id, package_spec)); - continue 'outer; - } - } - // We need a new chunk! - let mut names = IndexSet::new(); - names.insert(target_name); - chunk_id2names.insert(chunks.len(), names); - chunks.push(vec![(package_id, package_spec)]); - } - chunks - }; - - let target_directory = package_graph.workspace().target_directory().as_std_path(); - for (i, chunk) in chunks.into_iter().enumerate() { - if i > 0 { - // All crates in the later chunks have at least another crate with the same name - // in the dependency graph (otherwise they would have been in the first chunk). - // We need to clean up the target directory to make sure that `cargo` doesn't - // mistakenly believe that the JSON docs it generated for another version of - // the crate can be reused. - for (id, _) in &chunk { - let lib_name = lib_target_name(id, package_graph); - let _ = fs_err::remove_file(json_doc_location(&lib_name, target_directory)); - } - } else { - // If it's the first chunk, we need to check package by package. - for (id, _) in &chunk { - let our_lib = lib_target_name(id, package_graph); - let has_duplicate = package_graph - .packages() - .any(|p| p.id() != id && lib_target_name(p.id(), package_graph) == our_lib); - if has_duplicate { - let _ = fs_err::remove_file(json_doc_location(&our_lib, target_directory)); - } - } - } - - let chunk_package_ids: Vec<_> = chunk.iter().map(|(id, _)| id.clone()).collect(); - progress.before_computing(package_graph, &chunk_package_ids); - let timer = std::time::Instant::now(); - - let outcome = _compute_crate_docs( - toolchain_name, - chunk.iter().map(|(_, spec)| spec), - current_dir, - ); - - let duration = timer.elapsed(); - progress.after_computed(package_graph, &chunk_package_ids, duration); - - outcome?; - - // It takes a while to deserialize the JSON output of `cargo rustdoc`, so we parallelize - // that part. - use rayon::prelude::{IntoParallelIterator, ParallelIterator}; - for (package_id, krate) in chunk - .into_par_iter() - .map(|(package_id, package_spec)| { - let lib_name = lib_target_name(&package_id, package_graph); - let krate = - load_json_docs(target_directory, &lib_name, &package_spec, toolchain_name); - (package_id, krate) - }) - .collect::>() - { - results.insert(package_id, krate?); - } - } - Ok(results) -} - -#[tracing::instrument(skip_all, fields(package_id_specs, cmd))] -fn _compute_crate_docs<'a, I>( - toolchain_name: &str, - package_id_specs: I, - current_dir: &Path, -) -> Result<(), anyhow::Error> -where - I: Iterator, -{ - let package_id_specs: Vec<_> = package_id_specs.collect(); - tracing::Span::current().record("package_id_specs", package_id_specs.iter().join(", ")); - if package_id_specs.len() == 1 { - _compute_single_crate_docs(toolchain_name, package_id_specs[0], current_dir) - } else { - _compute_multiple_crate_docs(toolchain_name, package_id_specs, current_dir) - } -} - -/// `cargo rustdoc` understands the structure of the expected output, so it won't -/// regenerate the JSON if it's already there and the crate hasn't changed. -/// Unfortunately, that's not the case for `cargo doc`, so we can't leverage the -/// same benefits in both the single and multi-crate case using the same command. -fn _compute_single_crate_docs( - toolchain_name: &str, - package_id_spec: &PackageIdSpecification, - current_dir: &Path, -) -> Result<(), anyhow::Error> { - let mut cmd = std::process::Command::new("rustup"); - cmd.arg("run") - .current_dir(current_dir) - .arg(toolchain_name) - .arg("cargo") - .arg("rustdoc") - .arg("-q") - .arg("--lib") - .arg("-p") - .arg(package_id_spec.to_string()) - .arg("-Zunstable-options") - .arg("--output-format") - .arg("json") - .arg("--") - .arg("--document-private-items") - .arg("--document-hidden-items"); - - tracing::Span::current().record("cmd", tracing::field::debug(&cmd)); - - let status = cmd - .status() - .with_context(|| format!("Failed to run `cargo rustdoc`.\n{cmd:?}"))?; - - if !status.success() { - anyhow::bail!( - "An invocation of `cargo rustdoc` exited with non-zero status code.\n{:?}", - cmd - ); - } - Ok(()) -} - -fn _compute_multiple_crate_docs( - toolchain_name: &str, - package_id_specs: Vec<&PackageIdSpecification>, - current_dir: &Path, -) -> Result<(), anyhow::Error> { - let mut cmd = std::process::Command::new("rustup"); - cmd.arg("run") - .current_dir(current_dir) - .arg(toolchain_name) - .arg("cargo") - .arg("doc") - .arg("--no-deps") - .arg("-q") - .arg("--lib"); - for package_id_spec in &package_id_specs { - cmd.arg("-p").arg(package_id_spec.to_string()); - } - - cmd.env("RUSTDOCFLAGS", rustdoc_options().join(" ")); - - tracing::Span::current().record("cmd", tracing::field::debug(&cmd)); - - let status = cmd - .status() - .with_context(|| format!("Failed to run `cargo doc`.\n{cmd:?}"))?; - - if !status.success() { - anyhow::bail!( - "An invocation of `cargo doc` exited with non-zero status code.\n{:?}", - cmd - ); - } - Ok(()) -} - -/// The path to the JSON file generated by `rustdoc`. -fn json_doc_location(lib_name: &str, target_directory: &Path) -> PathBuf { - target_directory - .join("doc") - .join(format!("{}.json", normalize_crate_name(lib_name))) -} - -#[tracing::instrument( - skip_all, - fields( - crate.name = package_id_spec.name, - crate.version = format_optional_version(&package_id_spec.version), - crate.source = package_id_spec.source, - crate.lib.name = lib_name, - ) -)] -fn load_json_docs( - target_directory: &Path, - lib_name: &str, - package_id_spec: &PackageIdSpecification, - toolchain_name: &str, -) -> Result { - let json_path = json_doc_location(lib_name, target_directory); - - let span = tracing::trace_span!("Read and deserialize JSON output"); - let guard = span.enter(); - let file = fs_err::File::open(&json_path).context( - "Failed to open the file containing the output of a `cargo rustdoc` invocation.", - )?; - let mut reader = BufReader::new(file); - let mut deserializer = serde_json::Deserializer::from_reader(&mut reader); - // The documentation for some crates (e.g. typenum) causes a "recursion limit exceeded" when - // deserializing their docs using the default recursion limit. - deserializer.disable_recursion_limit(); - let deserializer = serde_stacker::Deserializer::new(&mut deserializer); - match rustdoc_types::Crate::deserialize(deserializer) { - Ok(krate) => { - drop(guard); - Ok(krate) - } - Err(e) => { - // Reset the reader to the beginning of the file. - if reader.seek(SeekFrom::Start(0)).is_ok() - && let Err(format_err) = check_format(reader) - { - return Err(format_err).with_context(|| { - format!( - "The JSON docs at `{}` are not in the expected format. \ - Are you using the right version of the `nightly` toolchain, `{}`, to generate the JSON docs?", - json_path.display(), toolchain_name - ) - }); - } - - Err(e).with_context(|| { - format!( - "Failed to deserialize the output of a `cargo rustdoc` invocation (`{}`)", - json_path.display() - ) - }) - } - } -} diff --git a/rustdoc/rustdoc_processor/src/compute/package_id_spec.rs b/rustdoc/rustdoc_processor/src/compute/package_id_spec.rs deleted file mode 100644 index 607c49e66..000000000 --- a/rustdoc/rustdoc_processor/src/compute/package_id_spec.rs +++ /dev/null @@ -1,90 +0,0 @@ -use std::fmt::{Display, Formatter}; - -use anyhow::Context; -use guppy::graph::{PackageGraph, PackageMetadata, PackageSource}; -use guppy::{PackageId, Version}; - -/// A selector that follows the [package ID specification](https://doc.rust-lang.org/cargo/reference/pkgid-spec.html). -/// It is used as argument to the `-p`/`--package` flag in `cargo`'s commands. -#[derive(Debug, PartialEq, Hash, Eq, Clone)] -pub(crate) struct PackageIdSpecification { - pub(crate) source: Option, - pub(crate) name: String, - pub(crate) version: Option, -} - -impl PackageIdSpecification { - pub fn from_package_id( - package_id: &PackageId, - package_graph: &PackageGraph, - ) -> Result { - let package_metadata = package_graph.metadata(package_id).with_context(|| { - format!( - "`{}` doesn't appear in the package graph", - package_id.repr() - ) - })?; - - Ok(Self::from_package_metadata(&package_metadata)) - } - - pub fn from_package_metadata(metadata: &PackageMetadata) -> Self { - let source = match metadata.source() { - PackageSource::Workspace(source) | PackageSource::Path(source) => { - let source = source.strip_prefix("path+").unwrap_or(source); - if source.as_str().is_empty() { - source.to_string() - } else { - let source = if source.is_relative() { - metadata.graph().workspace().root().join(source).to_string() - } else { - source.to_string() - }; - format!("file://{source}") - } - } - PackageSource::External(source) => { - let s = if let Some(source) = source.strip_prefix("git+") { - source - } else if let Some(source) = source.strip_prefix("registry+") { - source - } else { - source - }; - // The source URL for the `git` repository can sometimes contain query parameters, - // e.g. `?rev=abcdef`. We need to strip them away, since the specification requires - // "hostname+path", no query params (see https://doc.rust-lang.org/cargo/reference/pkgid-spec.html). - s.split('?').next().unwrap().to_owned() - } - }; - let source = if source.is_empty() { - None - } else { - Some(source) - }; - let name = metadata.name().to_owned(); - let version = Some(metadata.version().to_owned()); - Self { - source, - name, - version, - } - } -} - -impl Display for PackageIdSpecification { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - if let Some(source) = &self.source { - // The source URL for the `git` repository can sometimes contain a fragment, - // e.g. `#`. - // We need to strip that away, otherwise there is ambiguity in the package ID. - let source = source.split('#').next().unwrap(); - write!(f, "{source}#")?; - } - write!(f, "{}", &self.name)?; - if let Some(version) = &self.version { - write!(f, "@{version}")?; - } - Ok(()) - } -} diff --git a/rustdoc/rustdoc_processor/src/compute/progress.rs b/rustdoc/rustdoc_processor/src/compute/progress.rs deleted file mode 100644 index caef4986c..000000000 --- a/rustdoc/rustdoc_processor/src/compute/progress.rs +++ /dev/null @@ -1,21 +0,0 @@ -use guppy::PackageId; -use guppy::graph::PackageGraph; - -/// Callback for reporting progress during `cargo rustdoc` invocations. -pub trait ComputeProgress { - fn before_computing(&self, package_graph: &PackageGraph, package_ids: &[PackageId]); - fn after_computed( - &self, - package_graph: &PackageGraph, - package_ids: &[PackageId], - duration: std::time::Duration, - ); -} - -/// No-op implementation. -pub struct NoProgress; - -impl ComputeProgress for NoProgress { - fn before_computing(&self, _: &PackageGraph, _: &[PackageId]) {} - fn after_computed(&self, _: &PackageGraph, _: &[PackageId], _: std::time::Duration) {} -} diff --git a/rustdoc/rustdoc_processor/src/compute/toolchain.rs b/rustdoc/rustdoc_processor/src/compute/toolchain.rs deleted file mode 100644 index a7f80252d..000000000 --- a/rustdoc/rustdoc_processor/src/compute/toolchain.rs +++ /dev/null @@ -1,104 +0,0 @@ -use anyhow::Context; -use once_cell::sync::OnceCell; -use rustdoc_types::ItemKind; -use std::path::PathBuf; - -use super::format::check_format; - -#[tracing::instrument( - skip_all, - fields( - crate.name = name, - ) -)] -pub(super) fn get_toolchain_crate_docs( - name: &str, - toolchain_name: &str, -) -> Result { - let root_folder = get_json_docs_root_folder_via_rustup(toolchain_name)?; - let json_path = root_folder.join(format!("{name}.json")); - let json = fs_err::read_to_string(json_path) - .with_context(|| format!("Failed to retrieve the JSON docs for {name}"))?; - let mut krate = match serde_json::from_str::(&json) { - Ok(krate) => krate, - Err(e) => { - return match check_format(std::io::Cursor::new(json)) { - Err(format_err) => Err(format_err).with_context(|| { - format!( - "The JSON docs for {name} are not in the expected format. \ - Are you using the right version of the `nightly` toolchain, `{toolchain_name}`, \ - to generate the JSON docs?" - ) - }), - _ => Err(e).with_context(|| format!("Failed to deserialize the JSON docs for {name}")), - }; - } - }; - - // Primitives, if using their fully qualified names, must be imported as `std::primitive::*`. - // Unfortunately, that `primitive` module doesn't exist in the JSON docs, so we have to - // manually add it. - if name == "std" || name == "core" { - krate.paths.values_mut().for_each(|summary| { - if summary.kind == ItemKind::Primitive { - summary.path.insert(1, "primitive".into()); - } - }) - } - - Ok(krate) -} - -fn get_json_docs_root_folder_via_rustup(toolchain_name: &str) -> Result { - let toolchain_root = get_toolchain_root_folder_via_rustup(toolchain_name)?; - Ok(toolchain_root.join("share/doc/rust/json")) -} - -/// In order to determine where all the components attached to a toolchain are stored, -/// we ask `rustup` to tell us the location of the `cargo` binary for that toolchain. -/// -/// Experiments seem to suggest that the path to the `cargo` binary is always structured as -/// `/bin/cargo`. Therefore we compute `` by chopping -/// off the final two components of the path returned by `rustup`. -fn get_toolchain_root_folder_via_rustup(name: &str) -> Result { - let cargo_path = get_cargo_via_rustup(name)?; - debug_assert!( - cargo_path.ends_with("bin/cargo"), - "The path to the `cargo` binary for `{name}` doesn't have the expected structure: {cargo_path:?}" - ); - Ok(cargo_path.parent().unwrap().parent().unwrap().to_path_buf()) -} - -/// The path to the `cargo` binary used by the toolchain we rely on to build JSON docs. -static DOCS_TOOLCHAIN_CARGO: OnceCell = OnceCell::new(); - -fn get_cargo_via_rustup(toolchain_name: &str) -> Result { - fn compute_cargo_via_rustup(toolchain_name: &str) -> Result { - let mut cmd = std::process::Command::new("rustup"); - cmd.arg("which") - .arg("--toolchain") - .arg(toolchain_name) - .arg("cargo"); - - let output = cmd.output().with_context(|| { - format!("Failed to run a `rustup` command. Is `rustup` installed?\n{cmd:?}") - })?; - - if !output.status.success() { - anyhow::bail!( - "An invocation of `rustup` exited with non-zero status code.\n{:?}", - cmd - ); - } - let path = std::str::from_utf8(&output.stdout) - .with_context(|| { - format!("An invocation of `rustup` returned non-UTF8 data as output.\n{cmd:?}") - })? - .trim(); - Ok(PathBuf::from(path)) - } - - DOCS_TOOLCHAIN_CARGO - .get_or_try_init(|| compute_cargo_via_rustup(toolchain_name)) - .map(ToOwned::to_owned) -} diff --git a/rustdoc/rustdoc_processor/src/crate_data/item_index.rs b/rustdoc/rustdoc_processor/src/crate_data/item_index.rs deleted file mode 100644 index 7f5d53b2a..000000000 --- a/rustdoc/rustdoc_processor/src/crate_data/item_index.rs +++ /dev/null @@ -1,77 +0,0 @@ -//! Index of all items in a crate. - -use std::borrow::Cow; - -use rkyv::collections::swiss_table::ArchivedHashMap; -use rkyv::rancor::Panic; -use rkyv::util::AlignedVec; -use rustc_hash::FxHashMap; -use rustdoc_types::{ArchivedId, ArchivedItem, Item}; - -/// The index of all the items in the crate. -/// -/// Since the index can be quite large, we try to avoid deserializing it all at once. -/// -/// The `Eager` variant contains the entire index, fully deserialized. This is what we get -/// when we have had to compute the documentation for the crate on the fly. -/// -/// The `Lazy` variant contains the index as a byte array. There is a mapping from the -/// id of an item to the start and end index of the item's bytes in the byte array. -/// We can therefore deserialize the item only if we need to access it. -/// Since we only access a tiny portion of the items in the index (especially for large crates), -/// this translates in a significant performance improvement. -#[derive(Debug, Clone)] -pub enum CrateItemIndex { - Eager(EagerCrateItemIndex), - Lazy(LazyCrateItemIndex), -} - -impl CrateItemIndex { - /// Retrieve an item from the index given its id. - pub fn get(&self, id: &rustdoc_types::Id) -> Option> { - match self { - Self::Eager(index) => index.index.get(id).map(Cow::Borrowed), - Self::Lazy(index) => { - let item = index.get_deserialized(id)?; - Some(Cow::Owned(item)) - } - } - } -} - -/// See [`CrateItemIndex`] for more information. -#[derive(Debug, Clone)] -pub struct EagerCrateItemIndex { - #[allow(clippy::disallowed_types)] - pub index: FxHashMap, -} - -/// See [`CrateItemIndex`] for more information. -/// -/// Stores rkyv-serialized bytes of a `HashMap` and provides zero-copy access. -#[derive(Debug, Clone)] -pub struct LazyCrateItemIndex { - /// The rkyv-serialized bytes containing a `HashMap`. - pub bytes: AlignedVec, -} - -impl LazyCrateItemIndex { - /// Get zero-copy access to the archived HashMap. - #[inline] - fn archived(&self) -> &ArchivedHashMap { - // SAFETY: The bytes were serialized by rkyv from a valid HashMap. - // We trust the cache to contain valid data. - unsafe { rkyv::access_unchecked::>(&self.bytes) } - } - - /// Get an item by its ID, returning a reference to the archived item. - pub fn get(&self, id: &rustdoc_types::Id) -> Option<&ArchivedItem> { - self.archived().get(&ArchivedId(id.0.into())) - } - - /// Deserialize an item by its ID. - pub fn get_deserialized(&self, id: &rustdoc_types::Id) -> Option { - let archived = self.get(id)?; - Some(rkyv::deserialize::(archived).unwrap()) - } -} diff --git a/rustdoc/rustdoc_processor/src/crate_data/item_paths.rs b/rustdoc/rustdoc_processor/src/crate_data/item_paths.rs deleted file mode 100644 index a7621acc7..000000000 --- a/rustdoc/rustdoc_processor/src/crate_data/item_paths.rs +++ /dev/null @@ -1,128 +0,0 @@ -//! Mapping from item IDs to their paths. - -use std::borrow::Cow; - -use rkyv::collections::swiss_table::ArchivedHashMap; -use rkyv::hash::FxHasher64; -use rkyv::rancor::Panic; -use rkyv::util::AlignedVec; -use rustc_hash::FxHashMap; -use rustdoc_types::{ArchivedId, ArchivedItemSummary, ItemKind, ItemSummary}; - -/// A mapping from the id of a type to its fully qualified path. -/// -/// Primarily useful for foreign items that are being re-exported by this crate. -#[derive(Debug, Clone)] -pub enum CrateItemPaths { - Eager(EagerCrateItemPaths), - Lazy(LazyCrateItemPaths), -} - -impl CrateItemPaths { - /// Retrieve an item summary from the index given its id. - pub fn get(&self, id: &rustdoc_types::Id) -> Option> { - match self { - Self::Eager(m) => m.paths.get(id).map(Cow::Borrowed), - Self::Lazy(m) => { - let item = m.get_deserialized(id)?; - Some(Cow::Owned(item)) - } - } - } - - pub fn iter(&self) -> impl Iterator)> { - match self { - CrateItemPaths::Eager(paths) => CrateItemPathsIter::Eager(paths.paths.iter()), - CrateItemPaths::Lazy(paths) => CrateItemPathsIter::Lazy(paths.archived().iter()), - } - } -} - -pub enum CrateItemPathsIter<'a> { - Eager(std::collections::hash_map::Iter<'a, rustdoc_types::Id, ItemSummary>), - Lazy( - rkyv::collections::swiss_table::map::Iter<'a, ArchivedId, ArchivedItemSummary, FxHasher64>, - ), -} - -pub enum ItemSummaryRef<'a> { - Eager(&'a ItemSummary), - Lazy(&'a ArchivedItemSummary), -} - -impl<'a> ItemSummaryRef<'a> { - pub fn crate_id(&self) -> u32 { - match self { - ItemSummaryRef::Eager(s) => s.crate_id, - ItemSummaryRef::Lazy(s) => s.crate_id.to_native(), - } - } - - pub fn kind(&self) -> ItemKind { - match self { - ItemSummaryRef::Eager(s) => s.kind, - ItemSummaryRef::Lazy(s) => { - // Safe to do since the enum is repr(u8) - rkyv::deserialize::<_, rkyv::rancor::Infallible>(&s.kind).unwrap() - } - } - } - - pub fn path(&self) -> Cow<'_, [String]> { - match self { - ItemSummaryRef::Eager(s) => Cow::Borrowed(&s.path), - ItemSummaryRef::Lazy(s) => { - Cow::Owned(s.path.iter().map(|s| s.as_str().to_owned()).collect()) - } - } - } -} - -impl<'a> Iterator for CrateItemPathsIter<'a> { - type Item = (rustdoc_types::Id, ItemSummaryRef<'a>); - - fn next(&mut self) -> Option { - match self { - Self::Eager(iter) => iter.next().map(|(k, v)| (*k, ItemSummaryRef::Eager(v))), - Self::Lazy(iter) => iter - .next() - .map(|(k, v)| (rustdoc_types::Id(k.0.to_native()), ItemSummaryRef::Lazy(v))), - } - } -} - -/// See [`CrateItemPaths`] for more information. -#[derive(Debug, Clone)] -pub struct EagerCrateItemPaths { - #[allow(clippy::disallowed_types)] - pub paths: FxHashMap, -} - -/// See [`CrateItemPaths`] for more information. -#[derive(Debug, Clone)] -pub struct LazyCrateItemPaths { - pub bytes: AlignedVec, -} - -impl LazyCrateItemPaths { - /// Get zero-copy access to the archived HashMap. - #[inline] - fn archived(&self) -> &ArchivedHashMap { - // SAFETY: The bytes were serialized by rkyv from a valid HashMap. - // We trust the cache to contain valid data. - unsafe { - rkyv::access_unchecked::>(&self.bytes) - } - } - - /// Get an item by its ID, returning a reference to the archived summary. - pub fn get(&self, id: &rustdoc_types::Id) -> Option<&ArchivedItemSummary> { - self.archived().get(&ArchivedId(id.0.into())) - } - - /// Deserialize a summary by its ID. - pub fn get_deserialized(&self, id: &rustdoc_types::Id) -> Option { - let archived = self.get(id)?; - Some(rkyv::deserialize::(archived).unwrap()) - } -} diff --git a/rustdoc/rustdoc_processor/src/crate_data/mod.rs b/rustdoc/rustdoc_processor/src/crate_data/mod.rs deleted file mode 100644 index 5b4afcf86..000000000 --- a/rustdoc/rustdoc_processor/src/crate_data/mod.rs +++ /dev/null @@ -1,27 +0,0 @@ -//! Core rustdoc data types. - -mod item_index; -mod item_paths; - -pub use item_index::{CrateItemIndex, EagerCrateItemIndex, LazyCrateItemIndex}; -pub use item_paths::{CrateItemPaths, EagerCrateItemPaths, LazyCrateItemPaths}; - -use rustc_hash::FxHashMap; -use rustdoc_types::ExternalCrate; - -/// The JSON documentation for a crate. -#[derive(Debug, Clone)] -pub struct CrateData { - /// The id of the root item for the crate. - pub root_item_id: rustdoc_types::Id, - /// A mapping from the id of an external crate to the information about it. - #[allow(clippy::disallowed_types)] - pub external_crates: FxHashMap, - /// A mapping from the id of a type to its fully qualified path. - /// Primarily useful for foreign items that are being re-exported by this crate. - pub paths: CrateItemPaths, - /// The version of the JSON format used by rustdoc. - pub format_version: u32, - /// The index of all the items in the crate. - pub index: CrateItemIndex, -} diff --git a/rustdoc/rustdoc_processor/src/indexing/import_index.rs b/rustdoc/rustdoc_processor/src/indexing/import_index.rs deleted file mode 100644 index 8d7676eab..000000000 --- a/rustdoc/rustdoc_processor/src/indexing/import_index.rs +++ /dev/null @@ -1,139 +0,0 @@ -//! Index of importable items in a crate. - -use std::cmp::Ordering; -use std::collections::BTreeSet; - -use ahash::HashMap; - -/// An index of all importable items in a crate. -#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)] -pub struct ImportIndex { - /// A mapping that keeps track of all modules defined in the current crate. - /// - /// We track modules separately because their names are allowed to collide with - /// type and function names. - pub modules: HashMap, - /// A mapping that keeps track of traits, structs, enums and functions - /// defined in the current crate. - pub items: HashMap, - /// A mapping that associates the id of each re-export (`pub use ...`) to the id - /// of the module it was re-exported from. - pub re_export2parent_module: HashMap, -} - -/// An entry in [`ImportIndex`]. -#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)] -pub struct ImportIndexEntry { - /// All the public paths that can be used to import the item. - pub public_paths: BTreeSet, - /// All the private paths that can be used to import the item. - pub private_paths: BTreeSet, - /// The path where the item was originally defined. - /// - /// It may be set to `None` if we can't access the original definition. - /// E.g. an item defined in a private module of `std`, where we only have access - /// to the public API. - pub defined_at: Option>, -} - -/// The visibility of a path inside [`ImportIndexEntry`]. -pub enum EntryVisibility { - /// The item can be imported from outside the crate where it was defined. - Public, - /// The item can only be imported from within the crate where it was defined. - Private, -} - -impl ImportIndexEntry { - /// A private constructor. - pub fn empty() -> Self { - Self { - public_paths: BTreeSet::new(), - private_paths: BTreeSet::new(), - defined_at: None, - } - } - - /// Create a new entry from a path. - pub fn new(path: Vec, visibility: EntryVisibility, is_definition: bool) -> Self { - let mut entry = Self::empty(); - if is_definition { - entry.defined_at = Some(path.clone()); - } - match visibility { - EntryVisibility::Public => entry.public_paths.insert(SortablePath(path)), - EntryVisibility::Private => entry.private_paths.insert(SortablePath(path)), - }; - entry - } - - /// Add a new private path for this item. - pub fn insert_private(&mut self, path: Vec) { - self.private_paths.insert(SortablePath(path)); - } - - /// Add a new path for this item. - pub fn insert(&mut self, path: Vec, visibility: EntryVisibility) { - match visibility { - EntryVisibility::Public => self.public_paths.insert(SortablePath(path)), - EntryVisibility::Private => self.private_paths.insert(SortablePath(path)), - }; - } - - /// Types can be exposed under multiple paths. - /// This method returns a "canonical" importable path—i.e. the shortest importable path - /// pointing at the type you specified. - /// - /// If the type is public, this method returns the shortest public path. - /// If the type is private, this method returns the shortest private path. - pub fn canonical_path(&self) -> &[String] { - if let Some(SortablePath(p)) = self.public_paths.first() { - return p; - } - if let Some(SortablePath(p)) = self.private_paths.first() { - return p; - } - unreachable!("There must be at least one path associated to an import index entry") - } - - /// Returns all paths associated with the type, both public and private. - pub fn paths(&self) -> impl Iterator { - self.public_paths - .iter() - .map(|SortablePath(p)| p.as_slice()) - .chain( - self.private_paths - .iter() - .map(|SortablePath(p)| p.as_slice()), - ) - } -} - -#[derive( - Debug, - Clone, - Eq, - PartialEq, - serde::Serialize, - serde::Deserialize, - bincode::Encode, - bincode::Decode, -)] -#[serde(transparent)] -pub struct SortablePath(pub Vec); - -impl Ord for SortablePath { - fn cmp(&self, other: &Self) -> Ordering { - match self.0.len().cmp(&other.0.len()) { - // Compare lexicographically if lengths are equal - Ordering::Equal => self.0.cmp(&other.0), - other => other, - } - } -} - -impl PartialOrd for SortablePath { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} diff --git a/rustdoc/rustdoc_processor/src/indexing/import_path.rs b/rustdoc/rustdoc_processor/src/indexing/import_path.rs deleted file mode 100644 index 8e63d53c8..000000000 --- a/rustdoc/rustdoc_processor/src/indexing/import_path.rs +++ /dev/null @@ -1,63 +0,0 @@ -//! Mapping from import paths to item IDs. - -use ahash::HashMap; -use rkyv::collections::swiss_table::ArchivedHashMap; -use rkyv::rancor::Panic; -use rkyv::string::ArchivedString; -use rkyv::util::AlignedVec; -use rkyv::vec::ArchivedVec; -use rustdoc_types::ArchivedId; - -/// A mapping from import paths to the id of the item they point to. -/// -/// The `Eager` variant contains the entire mapping, fully deserialized. -/// -/// The `Lazy` variant contains the index as a byte array, with entries deserialized on demand. -#[derive(Debug, Clone)] -pub enum ImportPath2Id { - Eager(EagerImportPath2Id), - Lazy(LazyImportPath2Id), -} - -impl ImportPath2Id { - pub fn get(&self, path: &[String]) -> Option { - match self { - ImportPath2Id::Eager(m) => m.0.get(path).cloned(), - ImportPath2Id::Lazy(m) => m.get_deserialized(path), - } - } -} - -/// See [`ImportPath2Id`] for more information. -#[derive(Debug, Clone)] -pub struct EagerImportPath2Id(pub HashMap, rustdoc_types::Id>); - -/// See [`ImportPath2Id`] for more information. -/// -/// Stores rkyv-serialized bytes of a `HashMap, Id>` and provides zero-copy access. -#[derive(Debug, Clone)] -pub struct LazyImportPath2Id(pub AlignedVec); - -impl LazyImportPath2Id { - #[inline] - fn archived(&self) -> &ArchivedHashMap, ArchivedId> { - unsafe { - rkyv::access_unchecked::, ArchivedId>>( - &self.0, - ) - } - } - - pub fn get(&self, path: &[String]) -> Option<&ArchivedId> { - let path_vec: Vec = path.to_vec(); - let bytes = rkyv::to_bytes::(&path_vec).ok()?; - - let archived_key = unsafe { rkyv::access_unchecked::>(&bytes) }; - self.archived().get(archived_key) - } - - pub fn get_deserialized(&self, path: &[String]) -> Option { - let archived = self.get(path)?; - Some(rkyv::deserialize::<_, Panic>(archived).unwrap()) - } -} diff --git a/rustdoc/rustdoc_processor/src/indexing/mod.rs b/rustdoc/rustdoc_processor/src/indexing/mod.rs deleted file mode 100644 index 2c520f2e4..000000000 --- a/rustdoc/rustdoc_processor/src/indexing/mod.rs +++ /dev/null @@ -1,352 +0,0 @@ -//! Build secondary indexes from raw `rustdoc` JSON output. - -mod import_index; -mod import_path; -mod re_exports; - -pub use import_index::ImportIndex; -pub(crate) use import_index::{EntryVisibility, ImportIndexEntry}; -pub use import_path::{EagerImportPath2Id, ImportPath2Id, LazyImportPath2Id}; -pub(crate) use re_exports::ExternalReExport; -pub use re_exports::ExternalReExports; - -use guppy::PackageId; -use indexmap::IndexSet; -use rustdoc_types::{ItemEnum, Visibility}; - -use crate::crate_data::CrateData; -use crate::queries::Crate; - -/// Provides the indexing strategy for a collection of crates: how to turn raw -/// rustdoc JSON into a `(Crate, Annotations)` pair. -/// -/// Different consumers supply different implementations—e.g. Pavex extracts -/// `#[pavex(...)]` attributes, while a plain consumer may use `()` annotations. -pub trait CrateIndexer: Send + Sync { - /// The annotation payload stored alongside each indexed crate. - type Annotations: Default + Send + Sync + serde::Serialize + serde::de::DeserializeOwned; - - /// Index a freshly-computed rustdoc JSON crate. - fn index_raw( - &self, - krate: rustdoc_types::Crate, - package_id: PackageId, - ) -> IndexResult; - - /// Index pre-parsed crate data (e.g. from a partial cache hit). - fn index(&self, crate_data: CrateData, package_id: PackageId) - -> IndexResult; -} - -/// The result of indexing a single crate. -pub struct IndexResult { - pub krate: Crate, - pub annotations: A, - /// If `false`, the secondary indexes (including annotations) should not - /// be persisted to the disk cache—e.g. because diagnostics were emitted - /// during indexing and need to be re-reported on next run. - pub can_cache_indexes: bool, -} - -/// A no-op indexer that produces no annotations. -pub struct NoAnnotations; - -impl CrateIndexer for NoAnnotations { - type Annotations = (); - - fn index_raw(&self, krate: rustdoc_types::Crate, package_id: PackageId) -> IndexResult<()> { - use crate::crate_data::{ - CrateItemIndex, CrateItemPaths, EagerCrateItemIndex, EagerCrateItemPaths, - }; - let crate_data = CrateData { - root_item_id: krate.root, - index: CrateItemIndex::Eager(EagerCrateItemIndex { index: krate.index }), - external_crates: krate.external_crates, - format_version: krate.format_version, - paths: CrateItemPaths::Eager(EagerCrateItemPaths { paths: krate.paths }), - }; - self.index(crate_data, package_id) - } - - fn index(&self, crate_data: CrateData, package_id: PackageId) -> IndexResult<()> { - let krate = Crate::index_without_visitor(crate_data, package_id); - IndexResult { - krate, - annotations: (), - can_cache_indexes: true, - } - } -} - -/// Visitor invoked during crate indexing to handle item-specific hooks. -pub trait IndexingVisitor { - /// Called for every item discovered during traversal. - fn on_item_discovered(&mut self, item: &rustdoc_types::Item, item_id: rustdoc_types::Id); - /// Called for items added to the import index (struct/enum/trait/fn/type_alias/primitive/static/union). - fn on_type_indexed(&mut self, item_id: rustdoc_types::Id); -} - -/// No-op visitor for consumers that don't need hooks. -pub(crate) struct NoopVisitor; - -impl IndexingVisitor for NoopVisitor { - fn on_item_discovered(&mut self, _item: &rustdoc_types::Item, _item_id: rustdoc_types::Id) {} - fn on_type_indexed(&mut self, _item_id: rustdoc_types::Id) {} -} - -#[allow(clippy::too_many_arguments)] -pub(crate) fn index_local_types<'a, V: IndexingVisitor>( - krate: &'a CrateData, - package_id: &'a PackageId, - // The ordered set of modules we navigated to reach this item. - // It used to detect infinite loops. - mut navigation_history: IndexSet, - mut current_path: Vec, - import_index: &mut ImportIndex, - re_exports: &mut ExternalReExports, - visitor: &mut V, - current_item_id: &rustdoc_types::Id, - is_public: bool, - // Set when the current item has been re-exported via a `use` statement - // that includes an `as` rename. - renamed_to: Option, - // If `true`, we've encountered at least a `pub use`/`use` statement while - // navigating to this item. - encountered_use: bool, -) { - // TODO: the way we handle `current_path` is extremely wasteful, - // we can likely reuse the same buffer throughout. - let current_item = match krate.index.get(current_item_id) { - None => { - if let Some(summary) = krate.paths.get(current_item_id) - && summary.kind == rustdoc_types::ItemKind::Primitive - { - // This is a known bug—see https://github.com/rust-lang/rust/issues/104064 - return; - } - panic!( - "Failed to retrieve item id `{:?}` from the JSON `index` for package id `{}`.", - ¤t_item_id, - package_id.repr() - ) - } - Some(i) => i, - }; - - visitor.on_item_discovered(current_item.as_ref(), *current_item_id); - - let is_public = is_public && current_item.visibility == Visibility::Public; - - let mut add_to_import_index = |path: Vec, is_module: bool| { - let visibility = if is_public { - EntryVisibility::Public - } else { - EntryVisibility::Private - }; - let is_definition = !encountered_use; - let index = if is_module { - &mut import_index.modules - } else { - &mut import_index.items - }; - match index.get_mut(current_item_id) { - Some(entry) => { - entry.insert(path.clone(), visibility); - if is_definition { - entry.defined_at = Some(path); - } - } - None => { - index.insert( - *current_item_id, - ImportIndexEntry::new(path, visibility, is_definition), - ); - } - } - }; - - let current_item = current_item.as_ref(); - match ¤t_item.inner { - ItemEnum::Module(m) => { - let current_path_segment = renamed_to.unwrap_or_else(|| { - current_item - .name - .as_deref() - .expect("All 'module' items have a 'name' property") - .to_owned() - }); - current_path.push(current_path_segment); - - add_to_import_index( - current_path - .iter() - .map(|s| s.to_string()) - .collect::>(), - true, - ); - - navigation_history.insert(*current_item_id); - for item_id in &m.items { - index_local_types( - krate, - package_id, - navigation_history.clone(), - current_path.clone(), - import_index, - re_exports, - visitor, - item_id, - is_public, - None, - encountered_use, - ); - } - } - ItemEnum::Use(i) => { - let Some(imported_id) = &i.id else { - return; - }; - - import_index - .re_export2parent_module - .insert(current_item.id, *navigation_history.last().unwrap()); - - let Some(imported_item) = krate.index.get(imported_id) else { - // We are looking at a public re-export of another crate - // (e.g. `pub use hyper;`), one of its modules or one of its items. - // Due to how re-exports are handled in `rustdoc`, the re-exported - // items inside that foreign module will not be found in the `index` - // for this crate. - // We intentionally add foreign items to the index to get a "complete" - // picture of all the types available in this crate. - re_exports.insert(krate, current_item, ¤t_path); - return; - }; - if let ItemEnum::Module(re_exported_module) = &imported_item.inner { - if !i.is_glob { - current_path.push(i.name.clone()); - } - // In Rust it is possible to create infinite loops with local modules! - // Minimal example: - // ```rust - // pub struct A; - // mod inner { - // pub use crate as b; - // } - // ``` - // We use this check to detect if we're about to get stuck in an infinite - // loop, so that we can break early. - // It does mean that some paths that _would_ be valid won't be recognised, - // but this pattern is rarely used and for the time being we don't want to - // take the complexity hit of making visible paths lazily evaluated. - let infinite_loop = !navigation_history.insert(*imported_id); - if !infinite_loop { - for re_exported_item_id in &re_exported_module.items { - index_local_types( - krate, - package_id, - navigation_history.clone(), - current_path.clone(), - import_index, - re_exports, - visitor, - re_exported_item_id, - is_public, - None, - true, - ); - } - } - } else { - navigation_history.insert(*imported_id); - - if matches!( - imported_item.inner, - ItemEnum::Enum(_) - | ItemEnum::Struct(_) - | ItemEnum::Trait(_) - | ItemEnum::Function(_) - | ItemEnum::Primitive(_) - | ItemEnum::TypeAlias(_) - | ItemEnum::Static(_) - | ItemEnum::Union(_) - ) { - // We keep track of the source path in our indexes. - // This is useful, in particular, if we don't have - // access to the source module of the imported item. - // This can happen when working with `std`/`alloc`/`core` - // since the JSON output doesn't include private/doc-hidden - // items. - let mut normalized_source_path = vec![]; - let source_segments = i.source.split("::"); - for segment in source_segments { - if segment == "self" { - normalized_source_path - .extend(current_path.iter().map(|s| s.to_string())); - } else if segment == "crate" { - normalized_source_path.push(current_path[0].to_string()) - } else { - normalized_source_path.push(segment.to_string()); - } - } - // Assume it's private unless we find out otherwise later on - match import_index.items.get_mut(imported_id) { - Some(entry) => { - entry.insert_private(normalized_source_path); - } - None => { - import_index.items.insert( - *imported_id, - ImportIndexEntry::new( - normalized_source_path, - EntryVisibility::Private, - false, - ), - ); - } - } - } - - index_local_types( - krate, - package_id, - navigation_history, - current_path.clone(), - import_index, - re_exports, - visitor, - imported_id, - is_public, - Some(i.name.clone()), - true, - ); - } - } - ItemEnum::Trait(_) - | ItemEnum::Primitive(_) - | ItemEnum::Function(_) - | ItemEnum::Enum(_) - | ItemEnum::Struct(_) - | ItemEnum::TypeAlias(_) - | ItemEnum::Static(_) - | ItemEnum::Union(_) - | ItemEnum::Constant { .. } => { - let name = current_item.name.as_deref().expect( - "All 'struct', 'function', 'enum', 'type_alias', 'primitive', 'trait', 'static', 'union' and 'constant' items have a 'name' property", - ); - if matches!(current_item.inner, ItemEnum::Primitive(_)) { - // E.g. `std::bool` won't work, `std::primitive::bool` does work but the `primitive` module - // is not visible in the JSON docs for `std`/`core`. - // A hacky workaround, but it works. - current_path.push("primitive".into()); - } - current_path.push(renamed_to.unwrap_or_else(|| name.to_owned())); - let path: Vec<_> = current_path.into_iter().map(|s| s.to_string()).collect(); - add_to_import_index(path, false); - - // Even if the item itself may not be annotated, one of its impls may be. - visitor.on_type_indexed(*current_item_id); - } - _ => {} - } -} diff --git a/rustdoc/rustdoc_processor/src/indexing/re_exports.rs b/rustdoc/rustdoc_processor/src/indexing/re_exports.rs deleted file mode 100644 index 9300b22a6..000000000 --- a/rustdoc/rustdoc_processor/src/indexing/re_exports.rs +++ /dev/null @@ -1,144 +0,0 @@ -//! Tracking of external re-exports. - -use ahash::HashMap; - -use crate::CrateCollection; -use crate::compute::CannotGetCrateData; -use crate::crate_data::CrateData; -use crate::indexing::CrateIndexer; -use crate::queries::Crate; -use rustdoc_ext::GlobalItemId; - -/// Track re-exports of types (or entire modules!) from other crates. -#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)] -pub struct ExternalReExports { - /// Key: the path of the re-exported type in the current crate. - /// Value: the id of the `rustdoc` item of kind `use` that performed the re-export. - /// - /// E.g. `pub use hyper::server as sx;` in `lib.rs` would use `vec!["my_crate", "sx"]` - /// as key in this map. - pub(crate) target_path2use_id: HashMap, rustdoc_types::Id>, - /// Key: the id of the `rustdoc` item of kind `use` that performed the re-export. - /// Value: metadata about the re-export. - pub(crate) use_id2re_export: HashMap, -} - -impl ExternalReExports { - /// Iterate over the external re-exports that have been collected. - pub fn iter( - &self, - ) -> impl Iterator, rustdoc_types::Id, &ExternalReExport)> { - self.target_path2use_id - .iter() - .map(|(target_path, id)| (target_path, *id, &self.use_id2re_export[id])) - } - - /// Get metadata about a re-export given the use item id. - pub fn get(&self, use_id: &rustdoc_types::Id) -> Option<&ExternalReExport> { - self.use_id2re_export.get(use_id) - } - - /// Get the use item id for a given target path. - pub fn get_use_id(&self, target_path: &[String]) -> Option { - self.target_path2use_id.get(target_path).copied() - } - - /// Insert a re-export entry. - pub fn insert_entry( - &mut self, - target_path: Vec, - use_id: rustdoc_types::Id, - re_export: ExternalReExport, - ) { - self.target_path2use_id.insert(target_path, use_id); - self.use_id2re_export.insert(use_id, re_export); - } - - /// Retrieve the re-exported item from the crate it was defined into. - /// - /// # Panics - /// - /// Panics if the provided `use_id` doesn't exist as a key in the re-export registry. - pub fn get_target_item_id( - &self, - // The crate associated with these re-exports. - re_exported_from: &Crate, - collection: &CrateCollection, - use_id: rustdoc_types::Id, - ) -> Result, CannotGetCrateData> { - let re_export = self - .get(&use_id) - .expect("use_id not found in re-export registry"); - let source_package_id = re_exported_from - .core - .compute_package_id_for_crate_id( - re_export.external_crate_id, - collection.package_graph(), - None, - ) - .expect("Failed to compute the package id for a given external crate id"); - let source_krate = collection.get_or_compute(&source_package_id)?; - let Ok(Ok(source_id)) = - source_krate.get_item_id_by_path(&re_export.source_path, collection) - else { - return Ok(None); - }; - Ok(Some(source_id)) - } - - /// Add another re-export to the database. - pub fn insert( - &mut self, - krate: &CrateData, - use_item: &rustdoc_types::Item, - current_path: &[String], - ) { - let rustdoc_types::ItemEnum::Use(use_) = &use_item.inner else { - unreachable!() - }; - let imported_id = use_.id.expect("Import doesn't have an associated id"); - let Some(imported_summary) = krate.paths.get(&imported_id) else { - // TODO: this is firing for std's JSON docs. File a bug report. - // panic!("The imported id ({}) is not listed in the index nor in the path section of rustdoc's JSON output", imported_id.0) - return; - }; - debug_assert!(imported_summary.crate_id != 0); - // We are looking at a public re-export of another crate - // (e.g. `pub use hyper;`), one of its modules or one of its items. - // Due to how re-exports are handled in `rustdoc`, the re-exported - // items inside that foreign module will not be found in the `index` - // for this crate. - // We intentionally add foreign items to the index to get a "complete" - // picture of all the types available in this crate. - let external_crate_id = imported_summary.crate_id; - let source_path = imported_summary.path.to_owned(); - let re_exported_path = { - let mut p = current_path.to_owned(); - if !use_.is_glob { - p.push(use_.name.clone()); - } - p - }; - let re_export = ExternalReExport { - source_path, - external_crate_id, - }; - - self.target_path2use_id - .insert(re_exported_path, use_item.id); - self.use_id2re_export.insert(use_item.id, re_export); - } -} - -/// Information about a type (or module) re-exported from another crate. -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, bincode::Encode, bincode::Decode)] -pub struct ExternalReExport { - /// The path of the re-exported type in the crate it was re-exported from. - /// - /// E.g. `pub use hyper::server as sx;` in `lib.rs` would set `source_path` to - /// `vec!["hyper", "server"]`. - pub source_path: Vec, - /// The id of the source crate in the `external_crates` section of the JSON - /// documentation of the crate that re-exported it. - pub external_crate_id: u32, -} diff --git a/rustdoc/rustdoc_processor/src/lib.rs b/rustdoc/rustdoc_processor/src/lib.rs deleted file mode 100644 index 86a357502..000000000 --- a/rustdoc/rustdoc_processor/src/lib.rs +++ /dev/null @@ -1,59 +0,0 @@ -//! Compute, cache, index, and query `rustdoc` JSON documentation for crates -//! in a project's dependency graph. -//! -//! The pipeline: -//! -//! 1. **Compute** ([`compute_crate_docs`]): invoke `cargo rustdoc` to generate JSON docs. -//! 2. **Cache** ([`RustdocGlobalFsCache`]): persist raw docs and secondary indexes -//! in a SQLite database at `{cache_dir}/{fingerprint}.db`. -//! 3. **Index** ([`index_local_types`]): build secondary indexes — import paths, item lookups, -//! and external re-export tracking. -//! 4. **Query** ([`Crate`], [`CrateCollection`]): look up items by path, resolve -//! cross-crate references, and retrieve canonical import paths. - -pub mod cache; -mod collection; -pub mod compute; -pub mod crate_data; -pub mod indexing; -pub mod queries; -mod unknown_item_path; -mod utils; -mod version_matcher; - -// Cross-cutting types re-exported at crate root -pub use collection::CrateCollection; -pub use rustdoc_ext::GlobalItemId; -pub use unknown_item_path::UnknownItemPath; - -/// Crate version - used as part of cache fingerprint. -pub const CRATE_VERSION: &str = env!("CARGO_PKG_VERSION"); - -/// Standard library crate package ID representation. -pub const STD_PACKAGE_ID_REPR: &str = "std"; -/// Core crate package ID representation. -pub const CORE_PACKAGE_ID_REPR: &str = "core"; -/// Alloc crate package ID representation. -pub const ALLOC_PACKAGE_ID_REPR: &str = "alloc"; - -/// The set of toolchain crates that are bundled with Rust. -pub const TOOLCHAIN_CRATES: [&str; 3] = [ - STD_PACKAGE_ID_REPR, - CORE_PACKAGE_ID_REPR, - ALLOC_PACKAGE_ID_REPR, -]; - -/// Return the options to pass to `rustdoc` in order to generate JSON documentation. -/// -/// We isolate this logic in a separate function in order to be able to refer to these -/// options from various places in the codebase and maintain a single source of truth. -/// -/// In particular, they do affect our caching logic (see the `cache` module). -pub(crate) fn rustdoc_options() -> [&'static str; 4] { - [ - "--document-private-items", - "-Zunstable-options", - "-wjson", - "--document-hidden-items", - ] -} diff --git a/rustdoc/rustdoc_processor/src/queries/core.rs b/rustdoc/rustdoc_processor/src/queries/core.rs deleted file mode 100644 index 5430c25ee..000000000 --- a/rustdoc/rustdoc_processor/src/queries/core.rs +++ /dev/null @@ -1,46 +0,0 @@ -use guppy::PackageId; -use guppy::graph::PackageGraph; - -use super::resolution::compute_package_id_for_crate_id; -use crate::crate_data::CrateData; - -#[derive(Debug, Clone)] -pub struct CrateCore { - /// The `PackageId` for the corresponding crate within the dependency tree - /// for the workspace it belongs to. - pub package_id: PackageId, - /// The JSON documentation for the crate. - pub krate: CrateData, -} - -impl CrateCore { - /// Given a crate id, return the corresponding [`PackageId`]. - /// - /// # Disambiguation - /// - /// There might be multiple crates in the dependency graph with the same name, causing - /// disambiguation issues. - /// To help out, you can specify `maybe_dependent`: the name of a crate that you think - /// depends on the crate you're trying to resolve. - /// This can narrow down the portion of the dependency graph that we need to search, - /// thus removing ambiguity. - /// - /// # Panics - /// - /// It panics if the provided crate id doesn't appear in the JSON documentation - /// for this crate—i.e. if it's not `0` or assigned to one of its transitive dependencies. - pub fn compute_package_id_for_crate_id( - &self, - crate_id: u32, - package_graph: &PackageGraph, - maybe_dependent_crate_name: Option<&str>, - ) -> Result { - compute_package_id_for_crate_id( - &self.package_id, - &self.krate.external_crates, - crate_id, - maybe_dependent_crate_name, - package_graph, - ) - } -} diff --git a/rustdoc/rustdoc_processor/src/queries/mod.rs b/rustdoc/rustdoc_processor/src/queries/mod.rs deleted file mode 100644 index e70c2bed2..000000000 --- a/rustdoc/rustdoc_processor/src/queries/mod.rs +++ /dev/null @@ -1,339 +0,0 @@ -//! Query layer for looking up items across crates. - -pub(crate) mod core; -pub(crate) mod resolution; - -pub use self::core::CrateCore; - -use resolution::CrateIdNeedle; -use std::borrow::Cow; -use std::sync::{Arc, RwLock}; - -use ahash::HashMap; -use anyhow::anyhow; -use guppy::PackageId; -use guppy::graph::PackageGraph; -use rustdoc_types::Item; - -use crate::CrateCollection; -use crate::compute::CannotGetCrateData; -use crate::crate_data::CrateData; -use crate::indexing::{ - CrateIndexer, EagerImportPath2Id, ExternalReExport, ExternalReExports, ImportIndex, - ImportPath2Id, IndexingVisitor, NoopVisitor, index_local_types, -}; -use crate::unknown_item_path::UnknownItemPath; -use rustdoc_ext::GlobalItemId; - -/// Thin wrapper around [`rustdoc_types::Crate`] to: -/// - bundle derived indexes; -/// - provide query helpers with good error messages. -/// -/// It also records the `PackageId` for the corresponding crate within the dependency tree -/// for the workspace it belongs to. -#[derive(Debug, Clone)] -pub struct Crate { - pub core: CrateCore, - /// An index to lookup the id of a type given one of its import paths, either - /// public or private. - /// - /// The index does NOT contain macros, since macros and types live in two - /// different namespaces and can contain items with the same name. - /// E.g. `core::clone::Clone` is both a trait and a derive macro. - pub import_path2id: ImportPath2Id, - /// Types (or modules!) re-exported from other crates. - pub external_re_exports: ExternalReExports, - /// An in-memory index of all modules, traits, structs, enums, and functions that were defined in the current crate. - /// - /// It can be used to retrieve all publicly visible items as well as computing a "canonical path" - /// for each of them. - pub import_index: ImportIndex, - /// An internal cache to avoid traversing the package graph every time we need to - /// translate a crate id into a package id via [`Self::compute_package_id_for_crate_id`] - /// or [`Self::compute_package_id_for_crate_id_with_hint`]. - pub(crate) crate_id2package_id: Arc>>, -} - -impl Crate { - /// Build a fully indexed [`Crate`] from raw [`CrateData`]. - /// - /// Runs [`index_local_types`] with the provided visitor, builds the - /// `import_path2id` map, and assembles the result. - #[tracing::instrument(skip_all, name = "index_crate_docs", fields(package.id = package_id.repr()))] - pub fn index( - krate: CrateData, - package_id: PackageId, - visitor: &mut impl IndexingVisitor, - ) -> Self { - use indexmap::IndexSet; - use rustdoc_types::ItemKind; - - let mut import_path2id: HashMap<_, _> = krate - .paths - .iter() - .filter_map(|(id, summary)| { - // We only want types, no macros - if matches!(summary.kind(), ItemKind::Macro | ItemKind::ProcDerive) { - return None; - } - // We will index local items on our own. - // We don't get them from `paths` because it may include private items - // as well, and we don't have a way to figure out if an item is private - // or not from the summary info. - if summary.crate_id() == 0 { - return None; - } - - Some((summary.path().into_owned(), id.to_owned())) - }) - .collect(); - - let mut import_index = ImportIndex::default(); - let mut external_re_exports = Default::default(); - index_local_types( - &krate, - &package_id, - IndexSet::new(), - vec![], - &mut import_index, - &mut external_re_exports, - visitor, - &krate.root_item_id, - true, - None, - false, - ); - - import_path2id.reserve(import_index.items.len()); - for (id, entry) in import_index.items.iter() { - for path in entry.public_paths.iter().chain(entry.private_paths.iter()) { - if !import_path2id.contains_key(&path.0) { - import_path2id.insert(path.0.clone(), id.to_owned()); - } - } - } - - // Also index local items under their definition path from krate.paths. - // This handles types defined in private modules (e.g. core::ptr::non_null::NonNull) - // that are publicly re-exported under a shorter path (core::ptr::NonNull). - // The local crate's rustdoc JSON records the definition path, so we need - // both paths to be resolvable. - for (id, summary) in krate.paths.iter() { - if summary.crate_id() == 0 - && !matches!(summary.kind(), ItemKind::Macro | ItemKind::ProcDerive) - && import_index.items.contains_key(&id) - { - let path = summary.path().into_owned(); - import_path2id.entry(path).or_insert(id); - } - } - - Crate::new( - CrateCore { package_id, krate }, - ImportPath2Id::Eager(EagerImportPath2Id(import_path2id)), - external_re_exports, - import_index, - ) - } - - /// Index a `CrateData` without any visitor hooks. - pub fn index_without_visitor(krate: CrateData, package_id: PackageId) -> Self { - Self::index(krate, package_id, &mut NoopVisitor) - } - - /// Create a new `Crate` from its constituent parts. - pub fn new( - core: CrateCore, - import_path2id: ImportPath2Id, - external_re_exports: ExternalReExports, - import_index: ImportIndex, - ) -> Self { - Self { - core, - import_path2id, - external_re_exports, - import_index, - crate_id2package_id: Default::default(), - } - } - - /// The name of the crate. - pub fn crate_name(&self) -> String { - self.core - .krate - .index - .get(&self.core.krate.root_item_id) - .as_ref() - .expect("Can't find the crate root") - .name - .clone() - .expect("The crate root doesn't have a name") - } - - pub fn crate_version<'a>(&self, package_graph: &'a PackageGraph) -> &'a semver::Version { - let metadata = package_graph.metadata(&self.core.package_id).unwrap(); - metadata.version() - } - - /// Given a crate id, return the corresponding [`PackageId`]. - /// - /// It panics if the provided crate id doesn't appear in the JSON documentation - /// for this crate—i.e. if it's not `0` or assigned to one of its transitive dependencies. - pub fn compute_package_id_for_crate_id( - &self, - crate_id: u32, - collection: &CrateCollection, - ) -> Result { - self.compute_package_id_for_crate_id_with_hint(crate_id, collection, None) - } - - /// Given a crate id, return the corresponding [`PackageId`]. - /// - /// # Disambiguation - /// - /// There might be multiple crates in the dependency graph with the same name, causing - /// disambiguation issues. - /// To help out, you can specify `maybe_dependent`: the name of a crate that you think - /// depends on the crate you're trying to resolve. - /// This can narrow down the portion of the dependency graph that we need to search, - /// thus removing ambiguity. - /// - /// # Panics - /// - /// It panics if the provided crate id doesn't appear in the JSON documentation - /// for this crate—i.e. if it's not `0` or assigned to one of its transitive dependencies. - pub fn compute_package_id_for_crate_id_with_hint( - &self, - crate_id: u32, - collection: &CrateCollection, - maybe_dependent_crate_name: Option<&str>, - ) -> Result { - let needle = CrateIdNeedle { - crate_id, - maybe_dependent_crate_name: maybe_dependent_crate_name.map(|s| s.to_owned()), - }; - // Check the cache first. - if let Some(package_id) = self.crate_id2package_id.read().unwrap().get(&needle) { - return Ok(package_id.to_owned()); - } - - // If we don't have a cached entry, perform the graph traversal. - let outcome = self.core.compute_package_id_for_crate_id( - crate_id, - collection.package_graph(), - maybe_dependent_crate_name, - ); - - // If successful, cache the outcome. - if let Ok(outcome) = &outcome { - self.crate_id2package_id - .write() - .unwrap() - .insert(needle, outcome.to_owned()); - } - outcome - } - - pub fn get_item_id_by_path( - &self, - path: &[String], - collection: &CrateCollection, - ) -> Result, CannotGetCrateData> { - if let Some(id) = self.import_path2id.get(path) { - return Ok(Ok(GlobalItemId::new(id, self.core.package_id.to_owned()))); - } - - for ( - re_exported_path_prefix, - _, - ExternalReExport { - source_path: source_path_prefix, - external_crate_id, - }, - ) in self.external_re_exports.iter() - { - if re_exported_path_prefix - .iter() - .zip(path) - .all(|(a, b)| a == b) - { - let mut original_source_path = source_path_prefix.clone(); - for segment in path.iter().skip(re_exported_path_prefix.len()) { - original_source_path.push(segment.to_owned()); - } - - let source_package_id = self - .core - .compute_package_id_for_crate_id( - *external_crate_id, - collection.package_graph(), - None, - ) - .unwrap(); - let source_krate = collection.get_or_compute(&source_package_id).unwrap(); - if let Ok(source_id) = - source_krate.get_item_id_by_path(&original_source_path, collection) - { - return Ok(source_id); - } - } - } - - Ok(Err(UnknownItemPath { - path: path.to_owned(), - })) - } - - /// Return the crate_id, path and item kind for a **local** type id. - /// - /// It only works for structs, enums and functions. - /// It **will** fail if the id points to a method! - pub fn get_summary_by_local_type_id( - &self, - id: &rustdoc_types::Id, - ) -> Result, anyhow::Error> { - self.core.krate.paths.get(id).ok_or_else(|| { - anyhow!( - "Failed to look up the type id `{}` in the rustdoc's path index for `{}`. \ - This is likely to be a bug in rustdoc's JSON output.", - id.0, - self.core.package_id.repr() - ) - }) - } - - pub fn get_item_by_local_type_id(&self, id: &rustdoc_types::Id) -> Cow<'_, Item> { - let type_ = self.maybe_get_item_by_local_type_id(id); - if type_.is_none() { - panic!( - "Failed to look up the type id `{}` in the rustdoc's index for package `{}`.", - id.0, - self.core.package_id.repr() - ) - } - type_.unwrap() - } - - /// Same as `get_type_by_local_type_id`, but returns `None` instead of panicking - /// if the type is not found. - pub fn maybe_get_item_by_local_type_id(&self, id: &rustdoc_types::Id) -> Option> { - self.core.krate.index.get(id) - } - - /// Types can be exposed under multiple paths. - /// This method returns a "canonical" importable path—i.e. the shortest importable path - /// pointing at the type you specified. - pub fn get_canonical_path(&self, type_id: &GlobalItemId) -> Result<&[String], anyhow::Error> { - if type_id.package_id == self.core.package_id - && let Some(entry) = self.import_index.items.get(&type_id.rustdoc_item_id) - { - return Ok(entry.canonical_path()); - } - Err(anyhow::anyhow!( - "Failed to find an importable path for the type id `{:?}` in the index I computed for `{:?}`. \ - This is likely to be a bug in the handling of rustdoc's JSON output or in rustdoc itself.", - type_id, - self.core.package_id.repr() - )) - } -} diff --git a/rustdoc/rustdoc_processor/src/queries/resolution.rs b/rustdoc/rustdoc_processor/src/queries/resolution.rs deleted file mode 100644 index cca7fdb24..000000000 --- a/rustdoc/rustdoc_processor/src/queries/resolution.rs +++ /dev/null @@ -1,236 +0,0 @@ -use anyhow::{Context, anyhow}; -use guppy::graph::PackageGraph; -use guppy::{PackageId, Version}; -use indexmap::IndexSet; -use rustc_hash::FxHashMap; -use rustdoc_types::ExternalCrate; -use tracing_log_error::log_error; - -use crate::TOOLCHAIN_CRATES; -use crate::utils::normalize_crate_name; -use crate::version_matcher::VersionMatcher; - -/// The information used by [`Crate::compute_package_id_for_crate_id_with_hint`](super::Crate::compute_package_id_for_crate_id_with_hint) -/// to map a `crate_id` to a `package_id`. -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub(crate) struct CrateIdNeedle { - pub(crate) crate_id: u32, - pub(crate) maybe_dependent_crate_name: Option, -} - -fn get_external_crate_version(external_crate: &ExternalCrate) -> Option { - if let Some(url) = &external_crate.html_root_url { - url.trim_end_matches('/') - .split('/') - .next_back() - .map(Version::parse) - .and_then(|x| x.ok()) - } else { - None - } -} - -/// Given a crate id for an external crate, return the corresponding [`PackageId`]. -/// -/// It panics if the provided crate id doesn't appear in the JSON documentation -/// for this crate—i.e. if it's not `0` or assigned to one of its transitive dependencies. -#[allow(clippy::disallowed_types)] -pub(crate) fn compute_package_id_for_crate_id( - // The package id of the crate whose documentation we are currently processing. - package_id: &PackageId, - // The mapping from crate id to external crate object. - external_crate_index: &FxHashMap, - crate_id: u32, - // There might be multiple crates in the dependency graph with the same name, causing - // disambiguation issues. - // To help out, you can specify `maybe_dependent`: the name of a crate that you think - // depends on the crate you're trying to resolve. - // This can narrow down the portion of the dependency graph that we need to search, - // thus removing ambiguity. - maybe_dependent_crate_name: Option<&str>, - package_graph: &PackageGraph, -) -> Result { - #[derive(Debug, Hash, Eq, PartialEq)] - struct PackageLinkMetadata { - id: PackageId, - name: String, - version: Version, - } - - enum ResolvedDependency { - Found(PackageId), - Ambiguous(IndexSet), - NotFound, - } - - /// Find a transitive dependency of `search_root` given its name (and maybe the version). - /// It only returns `Some` if the dependency can be identified without ambiguity. - fn find_transitive_dependency( - package_graph: &PackageGraph, - search_root: &PackageId, - name: &str, - version: Option<&Version>, - ) -> Option { - match _find_transitive_dependency(package_graph, search_root, name, version) { - Ok(ResolvedDependency::Found(id)) => Some(id), - Ok(ResolvedDependency::Ambiguous(_) | ResolvedDependency::NotFound) => None, - Err(e) => { - log_error!( - *e, - level: tracing::Level::WARN, - external_crate.name = %name, - external_crate.version = ?version, - search_root = %search_root.repr(), - "Failed to find transitive dependency" - ); - None - } - } - } - - fn _find_transitive_dependency( - package_graph: &PackageGraph, - search_root: &PackageId, - name: &str, - version: Option<&Version>, - ) -> Result { - let transitive_dependencies = package_graph - .query_forward([search_root]) - .with_context(|| { - format!( - "`{}` doesn't appear in the package graph for the current workspace", - search_root.repr() - ) - })? - .resolve(); - let expected_link_name = normalize_crate_name(name); - let package_candidates: IndexSet<_> = transitive_dependencies - .links(guppy::graph::DependencyDirection::Forward) - .filter(|link| normalize_crate_name(link.to().name()) == expected_link_name) - .map(|link| { - let l = link.to(); - PackageLinkMetadata { - id: l.id().to_owned(), - name: l.name().to_owned(), - version: l.version().clone(), - } - }) - .collect(); - if package_candidates.is_empty() { - return Ok(ResolvedDependency::NotFound); - } - if package_candidates.len() == 1 { - return Ok(ResolvedDependency::Found( - package_candidates.into_iter().next().unwrap().id, - )); - } - - if let Some(expected_link_version) = version { - let version_matcher = VersionMatcher::new(expected_link_version); - let filtered_candidates: Vec<_> = package_candidates - .iter() - .filter(|l| version_matcher.matches(&l.version)) - .collect(); - if filtered_candidates.is_empty() { - let candidates = package_candidates - .iter() - .map(|l| format!("- {}@{}", l.name, l.version)) - .collect::>() - .join("\n"); - anyhow::bail!( - "Searching for `{expected_link_name}` among the transitive dependencies \ - of `{search_root}` led to multiple results:\n{candidates}\n\ - When the version ({expected_link_version}) was added to the search filters, \ - no results come up. Could the inferred version be incorrect?\n\ - This can happen if `{expected_link_name}` is using `#![doc(html_root_url = \"..\")]` \ - with a URL that points to the documentation for a different (older?) version of itself." - ) - } - if filtered_candidates.len() == 1 { - return Ok(ResolvedDependency::Found( - filtered_candidates.first().unwrap().id.to_owned(), - )); - } - } - - Ok(ResolvedDependency::Ambiguous(package_candidates)) - } - - if crate_id == 0 { - return Ok(package_id.clone()); - } - - let external_crate = external_crate_index.get(&crate_id).ok_or_else(|| { - anyhow!( - "There is no external crate associated with id `{}` in the JSON documentation for `{}`", - crate_id, - package_id.repr() - ) - })?; - if TOOLCHAIN_CRATES.contains(&external_crate.name.as_str()) { - return Ok(PackageId::new(external_crate.name.clone())); - } - let external_crate_version = get_external_crate_version(external_crate); - let ambiguous_candidates = match _find_transitive_dependency( - package_graph, - package_id, - &external_crate.name, - external_crate_version.as_ref(), - )? { - ResolvedDependency::Found(id) => return Ok(id), - ResolvedDependency::Ambiguous(candidates) => candidates, - ResolvedDependency::NotFound => { - return Err(anyhow!( - "I could not find any crate named `{}` \ - among the dependencies (either direct or transitive) of {}", - external_crate.name, - package_id.repr() - )); - } - }; - - // We have multiple packages with the same name. - // We need to disambiguate among them. - if let Some(maybe_dependent_crate_name) = maybe_dependent_crate_name { - let intermediate_crates: Vec<_> = external_crate_index - .values() - .filter(|c| c.name == maybe_dependent_crate_name) - .collect(); - if intermediate_crates.len() == 1 { - let intermediate_crate = intermediate_crates.first().unwrap(); - let intermediate_crate_version = get_external_crate_version(intermediate_crate); - if let Some(intermediate_package_id) = find_transitive_dependency( - package_graph, - package_id, - &intermediate_crate.name, - intermediate_crate_version.as_ref(), - ) && let Some(id) = find_transitive_dependency( - package_graph, - &intermediate_package_id, - &external_crate.name, - external_crate_version.as_ref(), - ) { - return Ok(id); - } - } - } - - let candidates_list = ambiguous_candidates - .iter() - .map(|l| format!("- {} v{} ({})", l.name, l.version, l.id.repr())) - .collect::>() - .join("\n"); - Err(anyhow!( - "There are multiple packages named `{}` among the dependencies of {}:\n{}\n\ - In order to disambiguate among them, I need to know their versions.\n\ - Unfortunately, I couldn't extract the expected version for `{}` from HTML root URL included in the \ - JSON documentation for `{}`.\n\ - This due to a limitation in `rustdoc` itself: follow https://github.com/rust-lang/compiler-team/issues/622 \ - to track progress on this issue.", - external_crate.name, - package_id.repr(), - candidates_list, - external_crate.name, - package_id.repr() - )) -} diff --git a/rustdoc/rustdoc_processor/src/unknown_item_path.rs b/rustdoc/rustdoc_processor/src/unknown_item_path.rs deleted file mode 100644 index 50d9fc23b..000000000 --- a/rustdoc/rustdoc_processor/src/unknown_item_path.rs +++ /dev/null @@ -1,16 +0,0 @@ -/// Error type representing a path that could not be found in the rustdoc index. -#[derive(thiserror::Error, Debug)] -pub struct UnknownItemPath { - pub path: Vec, -} - -impl std::fmt::Display for UnknownItemPath { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let path = self.path.join("::").replace(' ', ""); - let krate = self.path.first().unwrap(); - write!( - f, - "I could not find '{path}' in the auto-generated documentation for '{krate}'." - ) - } -} diff --git a/rustdoc/rustdoc_processor/src/utils.rs b/rustdoc/rustdoc_processor/src/utils.rs deleted file mode 100644 index ad1591285..000000000 --- a/rustdoc/rustdoc_processor/src/utils.rs +++ /dev/null @@ -1,4 +0,0 @@ -// Ensure that crate names are in canonical form! Damn automated hyphen substitution! -pub(crate) fn normalize_crate_name(s: &str) -> String { - s.replace('-', "_") -} diff --git a/rustdoc/rustdoc_processor/src/version_matcher.rs b/rustdoc/rustdoc_processor/src/version_matcher.rs deleted file mode 100644 index 41dc61966..000000000 --- a/rustdoc/rustdoc_processor/src/version_matcher.rs +++ /dev/null @@ -1,46 +0,0 @@ -use semver::{Comparator, Op, Version, VersionReq}; - -/// Given an exact semver version, returns a matcher that matches any semver-compatible -/// version. -/// In the end, `cargo` is in charge of resolving the actual version, so we don't need to -/// be too strict here. -/// -/// This is a good defense against crates setting a stale `#![doc(html_root_url = "...")]` -/// (e.g. https://github.com/hyperium/http/pull/688). -pub(crate) struct VersionMatcher { - req: VersionReq, -} - -impl VersionMatcher { - pub fn new(version: &Version) -> VersionMatcher { - let mut comparator = Comparator { - op: Op::Caret, - major: version.major, - minor: None, - patch: None, - pre: Default::default(), - }; - if version.pre.is_empty() { - if version.major == 0 { - comparator.minor = Some(version.minor); - if version.minor == 0 { - comparator.patch = Some(version.patch); - } - } - } else { - // We don't play loose with pre-releases. - comparator.minor = Some(version.minor); - comparator.patch = Some(version.patch); - comparator.pre = version.pre.clone(); - } - let req = VersionReq { - comparators: vec![comparator], - }; - - VersionMatcher { req } - } - - pub fn matches(&self, version: &Version) -> bool { - self.req.matches(version) - } -} diff --git a/rustdoc/rustdoc_resolver/Cargo.toml b/rustdoc/rustdoc_resolver/Cargo.toml deleted file mode 100644 index ecd2dfc6f..000000000 --- a/rustdoc/rustdoc_resolver/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -name = "rustdoc_resolver" -description = "Convert rustdoc_types into rustdoc_ir types" -edition.workspace = true -repository.workspace = true -homepage.workspace = true -license.workspace = true -version.workspace = true - -[lints] -clippy = { large_enum_variant = "allow", result_large_err = "allow" } - -[dependencies] -rustdoc-types = { workspace = true } -rustdoc_ir = { workspace = true } -rustdoc_processor = { path = "../rustdoc_processor" } -rustdoc_ext = { path = "../rustdoc_ext" } -guppy = { workspace = true } -anyhow = { workspace = true } -ahash = { workspace = true } -once_cell = { workspace = true } -tracing = { workspace = true, default-features = true } -thiserror = { workspace = true } diff --git a/rustdoc/rustdoc_resolver/src/errors.rs b/rustdoc/rustdoc_resolver/src/errors.rs deleted file mode 100644 index d8ee6f4a1..000000000 --- a/rustdoc/rustdoc_resolver/src/errors.rs +++ /dev/null @@ -1,191 +0,0 @@ -//! Error types produced during type and callable resolution. - -use std::sync::Arc; - -use rustdoc_types::Type as RustdocType; - -/// An error encountered while resolving a `rustdoc_types::Type` into a `rustdoc_ir::Type`. -#[derive(Debug)] -pub struct TypeResolutionError { - pub ty: RustdocType, - pub details: TypeResolutionErrorDetails, -} - -impl std::fmt::Display for TypeResolutionError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - writeln!(f, "Failed to resolve a type, {:?}.", self.ty)?; - match &self.details { - TypeResolutionErrorDetails::UnsupportedFnPointer(unsupported_fn_pointer) => { - write!( - f, - "It uses a function pointer with inputs {:?} and output {:?}, which isn't currently supported.", - unsupported_fn_pointer.inputs, unsupported_fn_pointer.output - ) - } - TypeResolutionErrorDetails::UnsupportedReturnTypeNotation => { - write!( - f, - "It uses return type notation, which isn't currently supported." - ) - } - TypeResolutionErrorDetails::UnsupportedTypeKind(unsupported_type_kind) => { - write!( - f, - "It is a `{}`, which isn't currently supported.", - unsupported_type_kind.kind - ) - } - TypeResolutionErrorDetails::UnsupportedArrayLength(unsupported_array_length) => { - write!( - f, - "It uses an array with length `{}`, which we can't evaluate at compile time.", - unsupported_array_length.len - ) - } - TypeResolutionErrorDetails::UnknownPrimitive(u) => { - write!(f, "{u}") - } - TypeResolutionErrorDetails::GenericKindMismatch(mismatch) => { - write!( - f, - "Expected a {} for the generic parameter `{}`, but found a {}.", - mismatch.expected_kind, mismatch.parameter_name, mismatch.found_kind - ) - } - TypeResolutionErrorDetails::ItemResolutionError(source) => { - write!(f, "{source}") - } - TypeResolutionErrorDetails::TypePartResolutionError(source) => { - write!(f, "Failed to resolve {}:\n{}", source.role, source.source) - } - TypeResolutionErrorDetails::AssociatedTypeResolutionError(e) => { - write!(f, "{e}") - } - } - } -} - -impl std::error::Error for TypeResolutionError {} - -/// The specific reason a type could not be resolved. -#[derive(Debug)] -pub enum TypeResolutionErrorDetails { - UnsupportedFnPointer(UnsupportedFnPointer), - UnsupportedReturnTypeNotation, - UnsupportedTypeKind(UnsupportedTypeKind), - UnsupportedArrayLength(UnsupportedArrayLength), - UnknownPrimitive(rustdoc_ir::UnknownPrimitive), - GenericKindMismatch(GenericKindMismatch), - ItemResolutionError(anyhow::Error), - TypePartResolutionError(Box), - /// We couldn't find a concrete `impl Trait for Type` block that defines the associated type. - /// This can happen when the implementation is provided via a blanket impl (e.g., - /// `impl Trait for T`), which we cannot resolve from rustdoc's JSON output. - AssociatedTypeResolutionError(AssociatedTypeResolutionError), -} - -/// A function pointer type was encountered, which is not yet supported. -#[derive(Debug)] -pub struct UnsupportedFnPointer { - /// The input types, enclosed in parentheses. - pub inputs: Vec, - /// The output type provided after the `->`, if present. - pub output: Option, -} - -/// A sub-component of a type failed to resolve. -#[derive(Debug)] -pub struct TypePartResolutionError { - pub role: String, - pub source: TypeResolutionError, -} - -/// A type kind that is not yet supported (e.g. `dyn Trait`, `impl Trait`). -#[derive(Debug)] -pub struct UnsupportedTypeKind { - pub kind: &'static str, -} - -/// An array length expression that cannot be evaluated at compile time. -#[derive(Debug)] -pub struct UnsupportedArrayLength { - pub len: String, -} - -/// We couldn't find a concrete `impl Trait for Type` block that defines the associated type. -#[derive(Debug)] -pub struct AssociatedTypeResolutionError { - /// The associated type name (e.g., "Numeric"). - pub assoc_type_name: String, - /// The trait path (e.g., ["enumflags2", "_internal", "RawBitFlags"]). - pub trait_path: Vec, - /// The concrete self type that we resolved. - pub self_type: rustdoc_ir::Type, -} - -impl std::fmt::Display for AssociatedTypeResolutionError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let trait_str = self.trait_path.join("::"); - write!( - f, - "Failed to resolve the associated type `{t}::{name}` for `{self_type:?}`. \ - No concrete `impl {t} for {self_type:?}` block was found that defines this \ - associated type. This can happen when the implementation is provided via a blanket \ - impl (e.g., `impl {t} for T`), which we cannot resolve.", - t = trait_str, - name = self.assoc_type_name, - self_type = self.self_type, - ) - } -} - -/// A generic argument did not match the expected kind (type vs lifetime vs const). -#[derive(Debug)] -pub struct GenericKindMismatch { - pub parameter_name: String, - pub expected_kind: &'static str, - pub found_kind: &'static str, -} - -/// An input parameter of a callable has a type that cannot be resolved. -#[derive(Debug, thiserror::Error, Clone)] -#[error("One of the input parameters for `{callable_path}` has a type that I can't handle.")] -pub struct InputParameterResolutionError { - pub callable_path: String, - pub callable_item: rustdoc_types::Item, - pub parameter_type: RustdocType, - pub parameter_index: usize, - #[source] - pub source: Arc, -} - -/// The `Self` type of a method could not be resolved. -#[derive(Debug, thiserror::Error, Clone)] -#[error("I can't handle the `Self` type for `{path}`.")] -pub struct SelfResolutionError { - pub path: String, - #[source] - pub source: Arc, -} - -/// The return type of a callable could not be resolved. -#[derive(Debug, thiserror::Error, Clone)] -#[error("I don't know how to handle the type returned by `{callable_path}`.")] -pub struct OutputTypeResolutionError { - pub callable_path: String, - pub callable_item: rustdoc_types::Item, - pub output_type: RustdocType, - #[source] - pub source: Arc, -} - -/// An error encountered while resolving a callable (function or method). -#[derive(thiserror::Error, Debug, Clone)] -pub enum CallableResolutionError { - #[error(transparent)] - SelfResolutionError(#[from] SelfResolutionError), - #[error(transparent)] - InputParameterResolutionError(#[from] InputParameterResolutionError), - #[error(transparent)] - OutputTypeResolutionError(#[from] OutputTypeResolutionError), -} diff --git a/rustdoc/rustdoc_resolver/src/free_fn.rs b/rustdoc/rustdoc_resolver/src/free_fn.rs deleted file mode 100644 index 06ff47814..000000000 --- a/rustdoc/rustdoc_resolver/src/free_fn.rs +++ /dev/null @@ -1,112 +0,0 @@ -//! Converter for free (non-method) functions. - -use std::sync::Arc; - -use rustdoc_types::{Item, ItemEnum}; - -use rustdoc_ext::GlobalItemId; -use rustdoc_ir::{CallableInput, FnHeader, FreeFunction, FreeFunctionPath, RustIdentifier}; -use rustdoc_processor::CrateCollection; -use rustdoc_processor::indexing::CrateIndexer; -use rustdoc_processor::queries::Crate; - -use crate::errors::*; -use crate::resolve_type::{TypeAliasResolution, resolve_type}; - -/// Convert a free function from `rustdoc_types` into a [`FreeFunction`]. -pub fn resolve_free_function( - item: &Item, - krate: &Crate, - krate_collection: &CrateCollection, - alias_resolution: TypeAliasResolution, -) -> Result { - let ItemEnum::Function(inner) = &item.inner else { - unreachable!("Expected a function item"); - }; - - let canonical_path_segments: Vec = - krate.import_index.items[&item.id].canonical_path().to_vec(); - // A representation of the path that will be used in error paths - let path_display = canonical_path_segments.join("::"); - - let mut inputs = Vec::new(); - for (parameter_index, (param_name, input_ty)) in inner.sig.inputs.iter().enumerate() { - match resolve_type( - input_ty, - &krate.core.package_id, - krate_collection, - &Default::default(), - alias_resolution, - ) { - Ok(t) => { - inputs.push(CallableInput { - name: RustIdentifier::new(param_name.clone()), - type_: t, - }); - } - Err(e) => { - return Err(InputParameterResolutionError { - callable_path: path_display, - callable_item: item.clone(), - parameter_type: input_ty.clone(), - parameter_index, - source: Arc::new(e.into()), - } - .into()); - } - } - } - - let output = match &inner.sig.output { - Some(output_ty) => { - match resolve_type( - output_ty, - &krate.core.package_id, - krate_collection, - &Default::default(), - alias_resolution, - ) { - Ok(t) => Some(t), - Err(e) => { - return Err(OutputTypeResolutionError { - callable_path: path_display, - callable_item: item.clone(), - output_type: output_ty.clone(), - source: Arc::new(e.into()), - } - .into()); - } - } - } - None => None, - }; - - let symbol_name = item.attrs.iter().find_map(|attr| match attr { - rustdoc_types::Attribute::NoMangle => item.name.clone(), - rustdoc_types::Attribute::ExportName(name) => Some(name.clone()), - _ => None, - }); - let n_segments = canonical_path_segments.len(); - Ok(FreeFunction { - path: FreeFunctionPath { - package_id: krate.core.package_id.clone(), - crate_name: canonical_path_segments[0].clone(), - module_path: canonical_path_segments[1..n_segments - 1].to_vec(), - function_name: canonical_path_segments[n_segments - 1].clone(), - function_generics: vec![], - }, - header: FnHeader { - output, - inputs, - is_async: inner.header.is_async, - abi: inner.header.abi.clone(), - is_unsafe: inner.header.is_unsafe, - is_c_variadic: inner.sig.is_c_variadic, - symbol_name, - }, - source_coordinates: Some(GlobalItemId { - rustdoc_item_id: item.id, - package_id: krate.core.package_id.clone(), - }), - }) -} diff --git a/rustdoc/rustdoc_resolver/src/lib.rs b/rustdoc/rustdoc_resolver/src/lib.rs deleted file mode 100644 index e2e9f8b4b..000000000 --- a/rustdoc/rustdoc_resolver/src/lib.rs +++ /dev/null @@ -1,62 +0,0 @@ -//! Convert `rustdoc_types` items into `rustdoc_ir` types and callables. -//! -//! This crate sits between `rustdoc_types`/`rustdoc_processor` (input) and -//! `rustdoc_ir` (output), providing the core type-resolution logic with no -//! framework-specific dependencies. - -mod errors; -mod free_fn; -mod method; -mod resolve_type; -mod type_def; - -pub use errors::*; -pub use free_fn::resolve_free_function; -pub use method::rustdoc_method2callable; -pub use resolve_type::{TypeAliasResolution, resolve_type}; -pub use type_def::{rustdoc_item_def2type, rustdoc_new_type_def2type, rustdoc_type_alias2type}; - -use ahash::HashMap; -use rustdoc_ir::Type; - -/// Maps generic parameter names to their resolved types or lifetimes during type resolution. -/// -/// Used to substitute generic parameters with concrete types when resolving -/// type aliases and generic instantiations. -#[derive(Default, Clone)] -pub struct GenericBindings { - /// Mapping from lifetime parameter names to their resolved lifetime names. - pub lifetimes: HashMap, - /// Mapping from type parameter names to their resolved types. - pub types: HashMap, - /// Mapping from const parameter names to their evaluated values. - pub consts: HashMap, -} - -impl std::fmt::Debug for GenericBindings { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "GenericBindings {{ ")?; - if !self.lifetimes.is_empty() { - write!(f, "lifetimes: {{ ")?; - for (name, value) in &self.lifetimes { - writeln!(f, "{name} -> {value}, ")?; - } - write!(f, "}}, ")?; - } - if !self.types.is_empty() { - write!(f, "types: {{ ")?; - for (name, value) in &self.types { - writeln!(f, "{} -> {}, ", name, value.display_for_error())?; - } - write!(f, "}}, ")?; - } - if !self.consts.is_empty() { - write!(f, "consts: {{ ")?; - for (name, value) in &self.consts { - writeln!(f, "{name} -> {value}, ")?; - } - write!(f, "}}, ")?; - } - write!(f, "}}") - } -} diff --git a/rustdoc/rustdoc_resolver/src/method.rs b/rustdoc/rustdoc_resolver/src/method.rs deleted file mode 100644 index 9b67b977e..000000000 --- a/rustdoc/rustdoc_resolver/src/method.rs +++ /dev/null @@ -1,268 +0,0 @@ -//! Converter for methods (both inherent and trait methods). - -use std::ops::Deref; -use std::sync::Arc; - -use rustdoc_types::{GenericArgs, Item, ItemEnum}; - -use rustdoc_ext::GlobalItemId; -use rustdoc_ir::{ - Callable, CallableInput, FnHeader, GenericArgument, GenericLifetimeParameter, InherentMethod, - InherentMethodPath, RustIdentifier, TraitMethod, TraitMethodPath, -}; -use rustdoc_processor::CrateCollection; -use rustdoc_processor::indexing::CrateIndexer; -use rustdoc_processor::queries::Crate; - -use crate::GenericBindings; -use crate::errors::*; -use crate::resolve_type::{TypeAliasResolution, resolve_type}; - -/// Convert a method item retrieved from `rustdoc`'s JSON output to Pavex's internal -/// representation for callables (i.e. methods and functions). -/// -/// # Input constraints -/// -/// - `method_item` belongs to `krate`. -/// - `impl_id` is local to `krate`. -/// - `attached_to` can either point to a trait or a type. -/// It'll always point to the `Self` type if we're working with an inherent method. -/// -/// `attached_to`, in the trait case, may not be local to `krate`. -/// E.g. the user is implementing a trait defined in another crate -/// for one of their local types. -pub fn rustdoc_method2callable( - attached_to: rustdoc_types::Id, - impl_id: rustdoc_types::Id, - method_item: &Item, - krate: &Crate, - krate_collection: &CrateCollection, - alias_resolution: TypeAliasResolution, -) -> Result { - let impl_item = krate.get_item_by_local_type_id(&impl_id); - let ItemEnum::Impl(impl_item) = &impl_item.inner else { - unreachable!("The impl item id doesn't point to an impl item") - }; - - let mut generic_bindings = GenericBindings::default(); - - let self_ty = match resolve_type( - &impl_item.for_, - &krate.core.package_id, - krate_collection, - &generic_bindings, - alias_resolution, - ) { - Ok(t) => t, - Err(e) => { - let path_display: String = krate.import_index.items[&attached_to] - .canonical_path() - .iter() - .cloned() - .chain(std::iter::once( - method_item.name.clone().expect("Method without a name"), - )) - .collect::>() - .join("::"); - return Err(SelfResolutionError { - path: path_display, - source: Arc::new(e.into()), - } - .into()); - } - }; - - generic_bindings - .types - .insert("Self".into(), self_ty.clone()); - - // Build path before resolving inputs/outputs so we can use it in error messages. - enum MethodPath { - Trait(TraitMethodPath), - Inherent(InherentMethodPath), - } - impl std::fmt::Display for MethodPath { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - MethodPath::Trait(p) => write!(f, "{p}"), - MethodPath::Inherent(p) => write!(f, "{p}"), - } - } - } - let callable_path = if let Some(trait_) = &impl_item.trait_ { - let (trait_global_id, trait_path) = krate_collection - .get_canonical_path_by_local_type_id(&krate.core.package_id, &trait_.id, None) - // FIXME: handle the error - .unwrap(); - let method_name = method_item.name.clone().expect("Method without a name"); - - let mut trait_generics = Vec::new(); - if let Some(args) = &trait_.args { - let GenericArgs::AngleBracketed { args, .. } = args.as_ref() else { - todo!(); - }; - for arg in args { - let ga = match arg { - rustdoc_types::GenericArg::Lifetime(l) => { - GenericArgument::Lifetime(GenericLifetimeParameter::from_name(l)) - } - rustdoc_types::GenericArg::Type(t) => { - let Ok(t) = resolve_type( - t, - &krate.core.package_id, - krate_collection, - &generic_bindings, - alias_resolution, - ) else { - todo!() - }; - GenericArgument::TypeParameter(t) - } - rustdoc_types::GenericArg::Const(constant) => { - let raw = constant.value.as_ref().unwrap_or(&constant.expr); - let value = generic_bindings - .consts - .get(raw) - .cloned() - .unwrap_or_else(|| raw.clone()); - GenericArgument::Const(rustdoc_ir::ConstGenericArgument { value }) - } - rustdoc_types::GenericArg::Infer => { - unreachable!() - } - }; - trait_generics.push(ga); - } - } - - let n = trait_path.len(); - MethodPath::Trait(TraitMethodPath { - package_id: trait_global_id.package_id.clone(), - crate_name: trait_path[0].clone(), - module_path: trait_path[1..n - 1].to_vec(), - trait_name: trait_path[n - 1].clone(), - trait_generics, - self_type: self_ty.clone(), - method_name, - method_generics: vec![], - }) - } else { - let canonical_path_segments: Vec = krate.import_index.items[&attached_to] - .canonical_path() - .to_vec(); - let method_name = method_item.name.clone().expect("Method without a name"); - let n = canonical_path_segments.len(); - MethodPath::Inherent(InherentMethodPath { - package_id: krate.core.package_id.clone(), - crate_name: canonical_path_segments[0].clone(), - module_path: canonical_path_segments[1..n - 1].to_vec(), - type_name: canonical_path_segments[n - 1].clone(), - type_generics: vec![], - method_name, - method_generics: vec![], - }) - }; - - let ItemEnum::Function(inner) = &method_item.inner else { - unreachable!("Expected a function item"); - }; - - let mut inputs = Vec::new(); - let mut takes_self_as_ref = false; - for (parameter_index, (param_name, parameter_type)) in inner.sig.inputs.iter().enumerate() { - if parameter_index == 0 { - // The first parameter might be `&self` or `&mut self`. - // This is important to know for carrying out further analysis doing the line, - // e.g. undoing lifetime elision. - if let rustdoc_types::Type::BorrowedRef { type_, .. } = parameter_type - && let rustdoc_types::Type::Generic(g) = type_.deref() - && g == "Self" - { - takes_self_as_ref = true; - } - } - - match resolve_type( - parameter_type, - &krate.core.package_id, - krate_collection, - &generic_bindings, - alias_resolution, - ) { - Ok(t) => { - inputs.push(CallableInput { - name: RustIdentifier::new(param_name.clone()), - type_: t, - }); - } - Err(e) => { - return Err(InputParameterResolutionError { - callable_path: callable_path.to_string(), - callable_item: method_item.clone(), - parameter_type: parameter_type.clone(), - parameter_index, - source: Arc::new(e.into()), - } - .into()); - } - } - } - - let output = match &inner.sig.output { - Some(output_ty) => { - match resolve_type( - output_ty, - &krate.core.package_id, - krate_collection, - &generic_bindings, - alias_resolution, - ) { - Ok(t) => Some(t), - Err(e) => { - return Err(OutputTypeResolutionError { - callable_path: callable_path.to_string(), - callable_item: method_item.clone(), - output_type: output_ty.clone(), - source: Arc::new(e.into()), - } - .into()); - } - } - } - None => None, - }; - - let symbol_name = method_item.attrs.iter().find_map(|attr| match attr { - rustdoc_types::Attribute::NoMangle => method_item.name.clone(), - rustdoc_types::Attribute::ExportName(name) => Some(name.clone()), - _ => None, - }); - - let fn_header = FnHeader { - output, - inputs, - is_async: inner.header.is_async, - abi: inner.header.abi.clone(), - is_unsafe: inner.header.is_unsafe, - is_c_variadic: inner.sig.is_c_variadic, - symbol_name, - }; - let source_coordinates = Some(GlobalItemId { - rustdoc_item_id: method_item.id, - package_id: krate.core.package_id.clone(), - }); - Ok(match callable_path { - MethodPath::Trait(path) => Callable::TraitMethod(TraitMethod { - path, - header: fn_header, - source_coordinates, - takes_self_as_ref, - }), - MethodPath::Inherent(path) => Callable::InherentMethod(InherentMethod { - path, - header: fn_header, - source_coordinates, - takes_self_as_ref, - }), - }) -} diff --git a/rustdoc/rustdoc_resolver/src/resolve_type.rs b/rustdoc/rustdoc_resolver/src/resolve_type.rs deleted file mode 100644 index e0e4cc115..000000000 --- a/rustdoc/rustdoc_resolver/src/resolve_type.rs +++ /dev/null @@ -1,1003 +0,0 @@ -//! Core type resolution logic: converting `rustdoc_types::Type` into `rustdoc_ir::Type`. - -use std::ops::Deref; - -use guppy::PackageId; -use once_cell::sync::OnceCell; -use rustdoc_types::{GenericArg, GenericArgs, GenericParamDefKind, ItemEnum, Type as RustdocType}; - -use rustdoc_ir::{ - Array, ConstGenericArgument, FunctionPointer, FunctionPointerInput, Generic, GenericArgument, - GenericLifetimeParameter, PathType, RawPointer, Slice, Tuple, Type, TypeReference, -}; -use rustdoc_processor::CrateCollection; -use rustdoc_processor::GlobalItemId; -use rustdoc_processor::indexing::CrateIndexer; - -use crate::GenericBindings; -use crate::errors::*; - -/// Controls whether `resolve_type` resolves through type aliases or preserves them. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum TypeAliasResolution { - /// Resolve through type aliases, returning the underlying type (current default). - ResolveThrough, - /// Stop at type aliases, returning `Type::TypeAlias(PathType)`. - Preserve, -} - -/// Convert a `rustdoc_types::Type` into a `rustdoc_ir::Type`, recursively resolving -/// through type aliases and substituting generic bindings. -pub fn resolve_type( - type_: &RustdocType, - // The package id where the type we are trying to process has been referenced (e.g. as an - // input/output parameter). - used_by_package_id: &PackageId, - krate_collection: &CrateCollection, - generic_bindings: &GenericBindings, - alias_resolution: TypeAliasResolution, -) -> Result { - _resolve_type( - type_, - used_by_package_id, - krate_collection, - generic_bindings, - alias_resolution, - ) - .map_err(|details| TypeResolutionError { - ty: type_.to_owned(), - details, - }) -} - -fn _resolve_type( - type_: &RustdocType, - // The package id where the type we are trying to process has been referenced (e.g. as an - // input/output parameter). - used_by_package_id: &PackageId, - krate_collection: &CrateCollection, - generic_bindings: &GenericBindings, - alias_resolution: TypeAliasResolution, -) -> Result { - match type_ { - RustdocType::ResolvedPath(rustdoc_types::Path { - id, - args, - path: name, - }) => { - let re_exporter_crate_name = { - let mut re_exporter = None; - if let Some(krate) = krate_collection.get_crate_by_package_id(used_by_package_id) - && let Some(item) = krate.maybe_get_item_by_local_type_id(id) - { - // 0 is the crate index of local types. - if item.crate_id == 0 { - re_exporter = Some(None); - } - } - if re_exporter.is_none() { - // It is not guaranteed that this type will be from a direct dependency of `used_by_package_id`. - // It might be a re-export from a transitive dependency, done by a direct dependency. - // Unfortunately, `rustdoc` does not provide the package id of the crate where the type - // was re-exported from, creating a "missing link". - // We try to infer it from the `name` property, which is usually the fully qualified - // name of the type, e.g. `std::collections::HashMap`. - re_exporter = Some(name.split("::").next()); - } - re_exporter.unwrap() - }; - let (global_type_id, base_type) = krate_collection - .get_canonical_path_by_local_type_id(used_by_package_id, id, re_exporter_crate_name) - .map_err(TypeResolutionErrorDetails::ItemResolutionError)?; - let type_item = krate_collection.get_item_by_global_type_id(&global_type_id); - // We want to remove any indirections (e.g. `type Foo = Bar;`) and get the actual type. - if let ItemEnum::TypeAlias(type_alias) = &type_item.inner { - let mut alias_generic_bindings = GenericBindings::default(); - // The generic arguments that have been passed to the type alias. - // E.g. `u32` in `Foo` for `type Foo = Bar;` - let generic_args = if let Some(args) = args { - if let GenericArgs::AngleBracketed { args, .. } = args.deref() { - Some(args) - } else { - None - } - } else { - None - }; - // The generic parameters that have been defined for the type alias. - // E.g. `T` in `type Foo = Bar;` - let generic_param_defs = &type_alias.generics.params; - - // When preserving, we need to resolve the generic arguments - // for the alias's own identity, but we don't resolve through. - let mut resolved_alias_generics = vec![]; - - for (i, generic_param_def) in generic_param_defs.iter().enumerate() { - match &generic_param_def.kind { - GenericParamDefKind::Type { default, .. } => { - let provided_arg = generic_args.and_then(|v| v.get(i)); - let generic_type = if let Some(provided_arg) = provided_arg { - if let GenericArg::Type(provided_arg) = provided_arg { - resolve_type( - provided_arg, - used_by_package_id, - krate_collection, - generic_bindings, - alias_resolution, - ) - .map_err(|source| { - TypeResolutionErrorDetails::TypePartResolutionError( - Box::new(TypePartResolutionError { - role: format!( - "generic argument ({})", - generic_param_def.name - ), - source, - }), - ) - })? - } else { - let found = match provided_arg { - GenericArg::Lifetime(_) => "lifetime", - GenericArg::Type(_) => "type", - GenericArg::Const(_) => "constant", - GenericArg::Infer => "inferred", - }; - return Err(TypeResolutionErrorDetails::GenericKindMismatch( - GenericKindMismatch { - expected_kind: "type", - parameter_name: generic_param_def.name.to_owned(), - found_kind: found, - }, - )); - } - } else if let Some(default) = default { - let default = resolve_type( - default, - &global_type_id.package_id, - krate_collection, - generic_bindings, - alias_resolution, - ) - .map_err(|source| { - TypeResolutionErrorDetails::TypePartResolutionError(Box::new( - TypePartResolutionError { - role: format!( - "default type for generic argument ({})", - generic_param_def.name - ), - source, - }, - )) - })?; - - if skip_default(krate_collection, &default) { - continue; - } - default - } else { - Type::Generic(Generic { - name: generic_param_def.name.clone(), - }) - }; - alias_generic_bindings - .types - .insert(generic_param_def.name.to_string(), generic_type.clone()); - resolved_alias_generics - .push(GenericArgument::TypeParameter(generic_type)); - } - GenericParamDefKind::Lifetime { .. } => { - let provided_arg = generic_args.and_then(|v| v.get(i)); - let lifetime = if let Some(provided_arg) = provided_arg { - if let GenericArg::Lifetime(provided_arg) = provided_arg { - provided_arg - } else { - let found = match provided_arg { - GenericArg::Lifetime(_) => "lifetime", - GenericArg::Type(_) => "type", - GenericArg::Const(_) => "constant", - GenericArg::Infer => "inferred", - }; - return Err(TypeResolutionErrorDetails::GenericKindMismatch( - GenericKindMismatch { - expected_kind: "lifetime", - parameter_name: generic_param_def.name.to_owned(), - found_kind: found, - }, - )); - } - } else { - &generic_param_def.name - } - .to_owned(); - alias_generic_bindings - .lifetimes - .insert(generic_param_def.name.to_string(), lifetime.clone()); - resolved_alias_generics.push(GenericArgument::Lifetime( - GenericLifetimeParameter::from_name(lifetime), - )); - } - GenericParamDefKind::Const { default, .. } => { - let provided_arg = generic_args.and_then(|v| v.get(i)); - let value = if let Some(GenericArg::Const(constant)) = provided_arg { - let raw = constant.value.as_ref().unwrap_or(&constant.expr); - generic_bindings - .consts - .get(raw) - .cloned() - .unwrap_or_else(|| raw.clone()) - } else if let Some(default) = default { - default.clone() - } else { - generic_param_def.name.clone() - }; - alias_generic_bindings - .consts - .insert(generic_param_def.name.to_string(), value.clone()); - resolved_alias_generics - .push(GenericArgument::Const(ConstGenericArgument { value })); - } - } - } - - if alias_resolution == TypeAliasResolution::Preserve { - let alias_path = PathType { - package_id: global_type_id.package_id().to_owned(), - rustdoc_id: Some(global_type_id.rustdoc_item_id), - base_type: base_type.to_vec(), - generic_arguments: resolved_alias_generics, - }; - return Ok(Type::TypeAlias(alias_path)); - } - - let type_ = resolve_type( - &type_alias.type_, - &global_type_id.package_id, - krate_collection, - &alias_generic_bindings, - alias_resolution, - ) - .map_err(|source| { - TypeResolutionErrorDetails::TypePartResolutionError(Box::new( - TypePartResolutionError { - role: "aliased type".into(), - source, - }, - )) - })?; - - Ok(type_) - } else { - let mut generics = vec![]; - if let Some(args) = args { - match &**args { - GenericArgs::AngleBracketed { args, .. } => { - // We fetch the name of the generic parameters as they appear - // in the definition of the type that we are processing. - // This is necessary because generic parameters can be elided - // when using the type as part of a function signature—e.g. - // `fn path(params: Params<'_, '_>) -> Result<_, _> { ... }` - // - // Can the two elided generic lifetime parameters be set to two - // different values? Or must they be the same? - // We need to check the definition of `Params` to find out. - let generic_arg_defs = match &type_item.inner { - ItemEnum::Struct(s) => &s.generics, - ItemEnum::Enum(e) => &e.generics, - ItemEnum::Union(u) => &u.generics, - i => { - unimplemented!( - "I don't know how to handle a `{:?}` yet, sorry!", - i - ) - } - } - .params - .as_slice(); - // Track resolved type parameters so that defaults for - // later parameters can reference earlier ones. - // E.g. `BitFlags::Numeric>` — - // when resolving N's default, T must already be bound. - let mut local_generic_bindings = generic_bindings.clone(); - for (i, arg_def) in generic_arg_defs.iter().enumerate() { - let generic_argument = match &arg_def.kind { - GenericParamDefKind::Lifetime { .. } => { - let mut lifetime_name = arg_def.name.clone(); - if let Some(GenericArg::Lifetime(l)) = args.get(i) { - lifetime_name = l.clone(); - } - GenericArgument::Lifetime( - GenericLifetimeParameter::from_name(lifetime_name), - ) - } - GenericParamDefKind::Type { default, .. } => { - let resolved = if let Some(GenericArg::Type(generic_type)) = - args.get(i) - { - if let RustdocType::Generic(generic) = generic_type { - if let Some(resolved_type) = - generic_bindings.types.get(generic) - { - resolved_type.to_owned() - } else { - Type::Generic(Generic { - name: generic.to_owned(), - }) - } - } else { - resolve_type( - generic_type, - used_by_package_id, - krate_collection, - generic_bindings, - alias_resolution, - ) - .map_err(|source| { - TypeResolutionErrorDetails::TypePartResolutionError( - Box::new(TypePartResolutionError { - role: format!( - "assigned type for generic parameter `{}`", - arg_def.name - ), - source, - }), - ) - })? - } - } else if let Some(default) = default { - let default = resolve_type( - default, - &global_type_id.package_id, - krate_collection, - &local_generic_bindings, - alias_resolution, - ) - .map_err(|source| { - TypeResolutionErrorDetails::TypePartResolutionError( - Box::new(TypePartResolutionError { - role: format!( - "default type for generic parameter `{}`", - arg_def.name - ), - source, - }), - ) - })?; - if skip_default(krate_collection, &default) { - continue; - } - default - } else { - Type::Generic(Generic { - name: arg_def.name.clone(), - }) - }; - local_generic_bindings - .types - .insert(arg_def.name.clone(), resolved.clone()); - GenericArgument::TypeParameter(resolved) - } - GenericParamDefKind::Const { default, .. } => { - let value = if let Some(GenericArg::Const(constant)) = - args.get(i) - { - let raw = - constant.value.as_ref().unwrap_or(&constant.expr); - local_generic_bindings - .consts - .get(raw) - .cloned() - .unwrap_or_else(|| raw.clone()) - } else if let Some(default) = default { - default.clone() - } else { - arg_def.name.clone() - }; - local_generic_bindings - .consts - .insert(arg_def.name.clone(), value.clone()); - GenericArgument::Const(ConstGenericArgument { value }) - } - }; - generics.push(generic_argument); - } - } - GenericArgs::Parenthesized { inputs, output } => { - return Err(TypeResolutionErrorDetails::UnsupportedFnPointer( - UnsupportedFnPointer { - inputs: inputs.to_owned(), - output: output.to_owned(), - }, - )); - } - GenericArgs::ReturnTypeNotation => { - return Err(TypeResolutionErrorDetails::UnsupportedReturnTypeNotation); - } - } - } - let t = PathType { - package_id: global_type_id.package_id().to_owned(), - rustdoc_id: Some(global_type_id.rustdoc_item_id), - base_type: base_type.to_vec(), - generic_arguments: generics, - }; - Ok(Type::Path(t)) - } - } - RustdocType::BorrowedRef { - lifetime, - type_, - is_mutable, - } => { - let resolved_type = resolve_type( - type_, - used_by_package_id, - krate_collection, - generic_bindings, - alias_resolution, - ) - .map_err(|source| { - TypeResolutionErrorDetails::TypePartResolutionError(Box::new( - TypePartResolutionError { - role: "referenced type".into(), - source, - }, - )) - })?; - let t = TypeReference { - is_mutable: *is_mutable, - lifetime: lifetime.to_owned().into(), - inner: Box::new(resolved_type), - }; - Ok(t.into()) - } - RustdocType::Generic(s) => { - if let Some(resolved_type) = generic_bindings.types.get(s) { - Ok(resolved_type.to_owned()) - } else { - Ok(Type::Generic(Generic { name: s.to_owned() })) - } - } - RustdocType::Tuple(t) => { - let mut types = Vec::with_capacity(t.len()); - for (i, type_) in t.iter().enumerate() { - let type_ = resolve_type( - type_, - used_by_package_id, - krate_collection, - generic_bindings, - alias_resolution, - ) - .map_err(|source| { - TypeResolutionErrorDetails::TypePartResolutionError(Box::new( - TypePartResolutionError { - role: format!("type of element {} in tuple", i + 1), - source, - }, - )) - })?; - types.push(type_); - } - Ok(Type::Tuple(Tuple { elements: types })) - } - RustdocType::Primitive(p) => Ok(Type::ScalarPrimitive( - p.as_str() - .try_into() - .map_err(TypeResolutionErrorDetails::UnknownPrimitive)?, - )), - RustdocType::Slice(type_) => { - let inner = resolve_type( - type_, - used_by_package_id, - krate_collection, - generic_bindings, - alias_resolution, - ) - .map_err(|source| { - TypeResolutionErrorDetails::TypePartResolutionError(Box::new( - TypePartResolutionError { - role: "slice type".into(), - source, - }, - )) - })?; - - Ok(Type::Slice(Slice { - element_type: Box::new(inner), - })) - } - RustdocType::Array { type_, len } => { - let resolved_len = generic_bindings - .consts - .get(len) - .map(|s| s.as_str()) - .unwrap_or(len); - let len: usize = resolved_len.parse().map_err(|_| { - TypeResolutionErrorDetails::UnsupportedArrayLength(UnsupportedArrayLength { - len: len.clone(), - }) - })?; - let resolved = resolve_type( - type_, - used_by_package_id, - krate_collection, - generic_bindings, - alias_resolution, - ) - .map_err(|source| { - TypeResolutionErrorDetails::TypePartResolutionError(Box::new( - TypePartResolutionError { - role: "array element type".into(), - source, - }, - )) - })?; - Ok(Type::Array(Array { - element_type: Box::new(resolved), - len, - })) - } - RustdocType::DynTrait(_) => Err(TypeResolutionErrorDetails::UnsupportedTypeKind( - UnsupportedTypeKind { kind: "dyn trait" }, - )), - RustdocType::FunctionPointer(fp) => { - if !fp.generic_params.is_empty() { - return Err(TypeResolutionErrorDetails::UnsupportedTypeKind( - UnsupportedTypeKind { - kind: "higher-ranked function pointer", - }, - )); - } - - let inputs = fp - .sig - .inputs - .iter() - .enumerate() - .map(|(i, (name, ty))| { - let resolved_ty = resolve_type( - ty, - used_by_package_id, - krate_collection, - generic_bindings, - alias_resolution, - ) - .map_err(|source| { - TypeResolutionErrorDetails::TypePartResolutionError(Box::new( - TypePartResolutionError { - role: format!("function pointer input {}", i), - source, - }, - )) - })?; - let name = match name.as_str() { - "" | "_" => None, - s => Some(s.to_owned()), - }; - Ok(FunctionPointerInput { - name, - type_: resolved_ty, - }) - }) - .collect::, _>>()?; - - let output = fp - .sig - .output - .as_ref() - .map(|ty| { - resolve_type( - ty, - used_by_package_id, - krate_collection, - generic_bindings, - alias_resolution, - ) - .map_err(|source| { - TypeResolutionErrorDetails::TypePartResolutionError(Box::new( - TypePartResolutionError { - role: "function pointer output".into(), - source, - }, - )) - }) - }) - .transpose()?; - - Ok(Type::FunctionPointer(FunctionPointer { - inputs, - output: output.map(Box::new), - abi: fp.header.abi.clone(), - is_unsafe: fp.header.is_unsafe, - })) - } - RustdocType::Pat { .. } => Err(TypeResolutionErrorDetails::UnsupportedTypeKind( - UnsupportedTypeKind { kind: "pattern" }, - )), - RustdocType::ImplTrait(..) => Err(TypeResolutionErrorDetails::UnsupportedTypeKind( - UnsupportedTypeKind { kind: "impl trait" }, - )), - RustdocType::Infer => Err(TypeResolutionErrorDetails::UnsupportedTypeKind( - UnsupportedTypeKind { - kind: "inferred type", - }, - )), - RustdocType::RawPointer { is_mutable, type_ } => { - let resolved = resolve_type( - type_, - used_by_package_id, - krate_collection, - generic_bindings, - alias_resolution, - ) - .map_err(|source| { - TypeResolutionErrorDetails::TypePartResolutionError(Box::new( - TypePartResolutionError { - role: "pointee type".into(), - source, - }, - )) - })?; - Ok(Type::RawPointer(RawPointer { - is_mutable: *is_mutable, - inner: Box::new(resolved), - })) - } - RustdocType::QualifiedPath { - name, - self_type, - trait_, - args: _, - } => { - // 1. Resolve self_type to a concrete type - let resolved_self = resolve_type( - self_type, - used_by_package_id, - krate_collection, - generic_bindings, - alias_resolution, - ) - .map_err(|source| { - TypeResolutionErrorDetails::TypePartResolutionError(Box::new( - TypePartResolutionError { - role: "self type of qualified path".into(), - source, - }, - )) - })?; - - // 2. Get the trait (if present) - let Some(trait_path) = trait_ else { - return Err(TypeResolutionErrorDetails::UnsupportedTypeKind( - UnsupportedTypeKind { - kind: "inherent associated type", - }, - )); - }; - - // 3. Find the associated type in the impl - resolve_associated_type( - &resolved_self, - trait_path, - name, - used_by_package_id, - krate_collection, - generic_bindings, - alias_resolution, - ) - } - } -} - -/// Resolve `::AssocType` by finding the concrete impl block -/// and looking up the associated type definition. -fn resolve_associated_type( - resolved_self: &Type, - trait_path: &rustdoc_types::Path, - assoc_type_name: &str, - used_by_package_id: &PackageId, - krate_collection: &CrateCollection, - generic_bindings: &GenericBindings, - alias_resolution: TypeAliasResolution, -) -> Result { - // Resolve the trait path to get its GlobalItemId and canonical path. - let (trait_global_id, trait_canonical_path) = krate_collection - .get_canonical_path_by_local_type_id(used_by_package_id, &trait_path.id, None) - .map_err(TypeResolutionErrorDetails::ItemResolutionError)?; - - // Try to find the associated type in the concrete type's impls first, - // then fall back to the trait's implementations list. - if let Some(result) = find_trait_assoc_type_in_type_impls( - resolved_self, - trait_canonical_path, - assoc_type_name, - krate_collection, - generic_bindings, - alias_resolution, - )? { - return Ok(result); - } - - if let Some(result) = find_assoc_type_in_trait_impls( - resolved_self, - &trait_global_id, - assoc_type_name, - krate_collection, - generic_bindings, - alias_resolution, - )? { - return Ok(result); - } - - Err(TypeResolutionErrorDetails::AssociatedTypeResolutionError( - AssociatedTypeResolutionError { - assoc_type_name: assoc_type_name.to_owned(), - trait_path: trait_canonical_path.to_vec(), - self_type: resolved_self.clone(), - }, - )) -} - -/// Search through the concrete type's `impls` list to find a matching trait impl -/// and extract the associated type. -fn find_trait_assoc_type_in_type_impls( - resolved_self: &Type, - trait_canonical_path: &[String], - assoc_type_name: &str, - krate_collection: &CrateCollection, - generic_bindings: &GenericBindings, - alias_resolution: TypeAliasResolution, -) -> Result, TypeResolutionErrorDetails> { - // This may not be general enough—i.e. does the self type in a qualified - // path need to be a `Type::Path`? - // Without looking too deep into it, my gut feeling is "no", but we - // can generalize later. - let Type::Path(self_path) = resolved_self else { - return Ok(None); - }; - - let type_package_id = &self_path.package_id; - let Some(type_crate) = krate_collection.get_crate_by_package_id(type_package_id) else { - return Ok(None); - }; - - // Find the type definition to get its impls list. - let Some(rustdoc_id) = &self_path.rustdoc_id else { - return Ok(None); - }; - let type_item = type_crate.get_item_by_local_type_id(rustdoc_id); - let impls = match &type_item.inner { - ItemEnum::Struct(s) => &s.impls, - ItemEnum::Enum(e) => &e.impls, - ItemEnum::Union(u) => &u.impls, - _ => return Ok(None), - }; - - search_impls_for_assoc_type( - impls, - type_package_id, - trait_canonical_path, - assoc_type_name, - krate_collection, - generic_bindings, - alias_resolution, - ) -} - -/// Search through the trait's `implementations` list to find a matching impl -/// and extract the associated type. -fn find_assoc_type_in_trait_impls( - resolved_self: &Type, - trait_global_id: &GlobalItemId, - assoc_type_name: &str, - krate_collection: &CrateCollection, - generic_bindings: &GenericBindings, - alias_resolution: TypeAliasResolution, -) -> Result, TypeResolutionErrorDetails> { - let trait_item = krate_collection.get_item_by_global_type_id(trait_global_id); - let ItemEnum::Trait(trait_def) = &trait_item.inner else { - return Ok(None); - }; - - let resolved_self = resolved_self.canonicalize(krate_collection); - - // The trait's implementations are local to the trait's crate. - let trait_package_id = &trait_global_id.package_id; - let Some(trait_crate) = krate_collection.get_crate_by_package_id(trait_package_id) else { - return Ok(None); - }; - for impl_id in &trait_def.implementations { - let Some(impl_item) = trait_crate.maybe_get_item_by_local_type_id(impl_id) else { - continue; - }; - let ItemEnum::Impl(impl_) = &impl_item.inner else { - continue; - }; - if impl_.is_negative { - continue; - } - // Skip generic/blanket impls — same reasoning as in `search_impls_for_assoc_type`. - if impl_ - .generics - .params - .iter() - .any(|p| matches!(p.kind, GenericParamDefKind::Type { .. })) - { - continue; - } - // Try to resolve the impl's `for_` type. If resolution fails, skip. - let Ok(resolved_for) = resolve_type( - &impl_.for_, - trait_package_id, - krate_collection, - &GenericBindings::default(), - alias_resolution, - ) else { - continue; - }; - if resolved_for.canonicalize(krate_collection) != resolved_self { - continue; - } - - // Found a matching impl, now look for the associated type. - if let Some(result) = extract_assoc_type_from_impl_items( - &impl_.items, - trait_package_id, - assoc_type_name, - krate_collection, - generic_bindings, - alias_resolution, - )? { - return Ok(Some(result)); - } - } - - Ok(None) -} - -/// Search a list of impl IDs for a matching trait impl and extract the associated type. -fn search_impls_for_assoc_type( - impl_ids: &[rustdoc_types::Id], - impl_crate_package_id: &PackageId, - trait_canonical_path: &[String], - assoc_type_name: &str, - krate_collection: &CrateCollection, - generic_bindings: &GenericBindings, - alias_resolution: TypeAliasResolution, -) -> Result, TypeResolutionErrorDetails> { - let Some(impl_crate) = krate_collection.get_crate_by_package_id(impl_crate_package_id) else { - return Ok(None); - }; - - for impl_id in impl_ids { - let Some(item) = impl_crate.maybe_get_item_by_local_type_id(impl_id) else { - continue; - }; - let ItemEnum::Impl(impl_) = &item.inner else { - continue; - }; - if impl_.is_negative { - continue; - } - // Skip generic/blanket impls (e.g., `impl Trait for T` or - // `impl Trait for T`). We can't verify trait bounds, - // and the associated type value may reference the impl's own - // generic parameters. - if impl_ - .generics - .params - .iter() - .any(|p| matches!(p.kind, GenericParamDefKind::Type { .. })) - { - continue; - } - // Check if this impl is for the right trait. - let Some(impl_trait) = &impl_.trait_ else { - continue; - }; - let Ok((_, impl_trait_path)) = krate_collection.get_canonical_path_by_local_type_id( - impl_crate_package_id, - &impl_trait.id, - None, - ) else { - continue; - }; - if impl_trait_path != trait_canonical_path { - continue; - } - - // Found a matching trait impl, look for the associated type. - if let Some(result) = extract_assoc_type_from_impl_items( - &impl_.items, - impl_crate_package_id, - assoc_type_name, - krate_collection, - generic_bindings, - alias_resolution, - )? { - return Ok(Some(result)); - } - } - - Ok(None) -} - -/// Extract an associated type by name from an impl block's items. -fn extract_assoc_type_from_impl_items( - items: &[rustdoc_types::Id], - package_id: &PackageId, - assoc_type_name: &str, - krate_collection: &CrateCollection, - generic_bindings: &GenericBindings, - alias_resolution: TypeAliasResolution, -) -> Result, TypeResolutionErrorDetails> { - let Some(crate_) = krate_collection.get_crate_by_package_id(package_id) else { - return Ok(None); - }; - - for item_id in items { - let Some(item) = crate_.maybe_get_item_by_local_type_id(item_id) else { - continue; - }; - if item.name.as_deref() != Some(assoc_type_name) { - continue; - } - if let ItemEnum::AssocType { - type_: Some(concrete_type), - .. - } = &item.inner - { - // Resolve the concrete type. It's defined in this crate. - let resolved = resolve_type( - concrete_type, - package_id, - krate_collection, - generic_bindings, - alias_resolution, - ) - .map_err(|source| { - TypeResolutionErrorDetails::TypePartResolutionError(Box::new( - TypePartResolutionError { - role: format!("associated type `{}`", assoc_type_name), - source, - }, - )) - })?; - return Ok(Some(resolved)); - } - } - - Ok(None) -} - -/// This is a gigantic hack to work around an issue with `std`'s collections: -/// they are all generic over the allocator type, but the default (`alloc::alloc::Global`) -/// is a nightly-only type. -/// If you spell it out, the code won't compile on stable, even though it does -/// exactly the same thing as omitting the parameter. -fn skip_default(krate_collection: &CrateCollection, default: &Type) -> bool { - static GLOBAL_ALLOCATOR_PATH: OnceCell> = OnceCell::new(); - - let Type::Path(path_type) = default else { - return false; - }; - - let expected_path = GLOBAL_ALLOCATOR_PATH.get_or_init(|| { - // Try to resolve `alloc::alloc::Global` via the collection to get - // the canonical path. Fall back to the well-known path if resolution - // fails (e.g. alloc docs not available). - let alloc_pid = PackageId::new(rustdoc_processor::ALLOC_PACKAGE_ID_REPR); - if let Some(krate) = krate_collection.get_crate_by_package_id(&alloc_pid) - && let Some(item_entry) = krate - .import_index - .items - .iter() - .find(|(_, entry)| entry.canonical_path() == ["alloc", "alloc", "Global"]) - { - return item_entry.1.canonical_path().to_vec(); - } - vec!["alloc".into(), "alloc".into(), "Global".into()] - }); - - path_type.base_type == *expected_path -} diff --git a/rustdoc/rustdoc_resolver/src/type_def.rs b/rustdoc/rustdoc_resolver/src/type_def.rs deleted file mode 100644 index d496540f2..000000000 --- a/rustdoc/rustdoc_resolver/src/type_def.rs +++ /dev/null @@ -1,161 +0,0 @@ -//! Converters for type definitions (structs, enums, unions) and type aliases. - -use rustdoc_types::{Item, ItemEnum}; - -use rustdoc_ext::RustdocKindExt; -use rustdoc_ir::{ - ConstGenericArgument, Generic, GenericArgument, GenericLifetimeParameter, PathType, Type, -}; -use rustdoc_processor::CrateCollection; -use rustdoc_processor::indexing::CrateIndexer; -use rustdoc_processor::queries::Crate; - -use crate::GenericBindings; -use crate::errors::TypeResolutionError; -use crate::resolve_type::{TypeAliasResolution, resolve_type}; - -/// Convert an enum, a struct, or a union definition from the JSON documentation -/// for a crate into our own representation for types. -/// -/// # Panics -/// -/// Panics if the item isn't of kind enum, struct, or union. -pub fn rustdoc_new_type_def2type(item: &Item, krate: &Crate) -> Type { - assert!( - matches!( - &item.inner, - ItemEnum::Struct(_) | ItemEnum::Enum(_) | ItemEnum::Union(_) - ), - "Unexpected item type, `{}`. Expected a struct, an enum, or a union.", - item.inner.kind() - ); - let path = krate.import_index.items[&item.id].canonical_path(); - - let mut generic_arguments = vec![]; - let params_def = match &item.inner { - ItemEnum::Struct(s) => &s.generics.params, - ItemEnum::Enum(e) => &e.generics.params, - ItemEnum::Union(u) => &u.generics.params, - _ => unreachable!(), - }; - for arg in params_def { - let arg = match &arg.kind { - rustdoc_types::GenericParamDefKind::Lifetime { .. } => { - GenericArgument::Lifetime(GenericLifetimeParameter::from_name(&arg.name)) - } - rustdoc_types::GenericParamDefKind::Type { .. } => { - // TODO: Use the default if available. - GenericArgument::TypeParameter(Type::Generic(Generic { - name: arg.name.clone(), - })) - } - rustdoc_types::GenericParamDefKind::Const { .. } => { - GenericArgument::Const(ConstGenericArgument { - value: arg.name.clone(), - }) - } - }; - generic_arguments.push(arg); - } - - Type::Path(PathType { - package_id: krate.core.package_id.clone(), - rustdoc_id: Some(item.id), - base_type: path.into(), - generic_arguments, - }) -} - -/// Convert an item definition (struct, enum, union, or type alias) from the JSON -/// documentation for a crate into our own representation for types. -/// -/// This is a dispatcher that routes to [`rustdoc_new_type_def2type`] for nominal types -/// and [`rustdoc_type_alias2type`] for type aliases. -/// -/// # Panics -/// -/// Panics if the item isn't of kind struct, enum, union, or type alias. -pub fn rustdoc_item_def2type( - item: &Item, - krate: &Crate, - krate_collection: &CrateCollection, - alias_resolution: TypeAliasResolution, -) -> Result { - match &item.inner { - ItemEnum::Struct(_) | ItemEnum::Enum(_) | ItemEnum::Union(_) => { - Ok(rustdoc_new_type_def2type(item, krate)) - } - ItemEnum::TypeAlias(_) => { - rustdoc_type_alias2type(item, krate, krate_collection, alias_resolution) - } - _ => unreachable!( - "Unexpected item type, `{}`. Expected a struct, an enum, a union, or a type alias.", - item.inner.kind() - ), - } -} - -/// Convert a type alias definition from the JSON documentation -/// for a crate into our own representation for types. -/// -/// With [`TypeAliasResolution::Resolve`], the alias is transparent and the -/// returned type is the (recursively-resolved) right-hand side. -/// With [`TypeAliasResolution::Preserve`], the alias identity is kept and the -/// returned type is a [`Type::TypeAlias`] pointing at `item` itself — mirroring -/// how [`rustdoc_new_type_def2type`] returns a [`Type::Path`] for nominal types. -/// -/// # Panics -/// -/// Panics if the item isn't a type alias. -pub fn rustdoc_type_alias2type( - item: &Item, - krate: &Crate, - krate_collection: &CrateCollection, - alias_resolution: TypeAliasResolution, -) -> Result { - let ItemEnum::TypeAlias(inner) = &item.inner else { - unreachable!( - "Unexpected item type, `{}`. Expected a a type alias.", - item.inner.kind() - ) - }; - let ty = if alias_resolution != TypeAliasResolution::Preserve { - resolve_type( - &inner.type_, - &krate.core.package_id, - krate_collection, - &GenericBindings::default(), - alias_resolution, - )? - } else { - // Don't resolve the aliased type if the resolution strategy is set to "preserve". - let path = krate.import_index.items[&item.id].canonical_path(); - let generic_arguments = inner - .generics - .params - .iter() - .map(|param| match ¶m.kind { - rustdoc_types::GenericParamDefKind::Lifetime { .. } => { - GenericArgument::Lifetime(GenericLifetimeParameter::from_name(¶m.name)) - } - rustdoc_types::GenericParamDefKind::Type { .. } => { - GenericArgument::TypeParameter(Type::Generic(Generic { - name: param.name.clone(), - })) - } - rustdoc_types::GenericParamDefKind::Const { .. } => { - GenericArgument::Const(ConstGenericArgument { - value: param.name.clone(), - }) - } - }) - .collect(); - Type::TypeAlias(PathType { - package_id: krate.core.package_id.clone(), - rustdoc_id: Some(item.id), - base_type: path.into(), - generic_arguments, - }) - }; - Ok(ty) -}