Remove `cargo-check-external-types` (#1820)

This tool now has its own repository:
https://github.com/awslabs/cargo-check-external-types
This commit is contained in:
John DiSanti 2022-10-11 12:46:13 -07:00 committed by GitHub
parent bf3fbc2e1d
commit 046edd54ab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 3 additions and 3232 deletions

View File

@ -51,6 +51,7 @@ ARG cargo_deny_version=0.12.2
ARG cargo_udeps_version=0.1.29
ARG cargo_hack_version=0.5.14
ARG cargo_minimal_versions_version=0.1.4
ARG cargo_check_external_types_version=0.1.4
ENV RUSTUP_HOME=/opt/rustup \
CARGO_HOME=/opt/cargo \
PATH=/opt/cargo/bin/:${PATH} \
@ -97,14 +98,14 @@ RUN set -eux; \
cargo install cargo-deny --locked --version ${cargo_deny_version}; \
cargo +${rust_nightly_version} install cargo-udeps --locked --version ${cargo_udeps_version}; \
cargo install cargo-hack --locked --version ${cargo_hack_version}; \
cargo install cargo-minimal-versions --version ${cargo_minimal_versions_version}; \
cargo install cargo-minimal-versions --locked --version ${cargo_minimal_versions_version}; \
cargo install cargo-check-external-types --locked --version ${cargo_check_external_types_version}; \
if [[ "${checkout_smithy_rs_tools}" == "true" ]]; then \
git clone https://github.com/awslabs/smithy-rs.git; \
cd smithy-rs; \
git checkout ${smithy_rs_commit_hash}; \
fi; \
cargo install --locked --path tools/publisher; \
cargo install --locked --path tools/cargo-check-external-types; \
cargo install --locked --path tools/changelogger; \
cargo install --locked --path tools/crate-hasher; \
cargo install --locked --path tools/sdk-lints; \

View File

@ -1,630 +0,0 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "ansi_term"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
dependencies = [
"winapi",
]
[[package]]
name = "anyhow"
version = "1.0.62"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1485d4d2cc45e7b201ee3767015c96faa5904387c9d87c6efdd0fb511f12d305"
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"libc",
"winapi",
]
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "camino"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88ad0e1e3e88dd237a156ab9f571021b8a158caa0ae44b1968a241efb5144c1e"
dependencies = [
"serde",
]
[[package]]
name = "cargo-check-external-types"
version = "0.1.3"
dependencies = [
"anyhow",
"cargo_metadata",
"clap",
"owo-colors",
"pest",
"pretty_assertions",
"rustdoc-types",
"serde",
"serde_json",
"test_bin",
"toml",
"tracing",
"tracing-attributes",
"tracing-subscriber",
"wildmatch",
]
[[package]]
name = "cargo-platform"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cbdb825da8a5df079a43676dbe042702f1707b1109f713a01420fbb4cc71fa27"
dependencies = [
"serde",
]
[[package]]
name = "cargo_metadata"
version = "0.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4acbb09d9ee8e23699b9634375c72795d095bf268439da88562cf9b501f181fa"
dependencies = [
"camino",
"cargo-platform",
"semver",
"serde",
"serde_json",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
version = "3.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2dbdf4bdacb33466e854ce889eee8dfd5729abf7ccd7664d0a2d60cd384440b"
dependencies = [
"atty",
"bitflags",
"clap_derive",
"clap_lex",
"indexmap",
"lazy_static",
"strsim",
"termcolor",
"textwrap",
]
[[package]]
name = "clap_derive"
version = "3.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25320346e922cffe59c0bbc5410c8d8784509efb321488971081313cb1e1a33c"
dependencies = [
"heck",
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "clap_lex"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
dependencies = [
"os_str_bytes",
]
[[package]]
name = "ctor"
version = "0.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdffe87e1d521a10f9696f833fe502293ea446d7f256c06128293a4119bdf4cb"
dependencies = [
"quote",
"syn",
]
[[package]]
name = "diff"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
[[package]]
name = "hashbrown"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]]
name = "heck"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
[[package]]
name = "hermit-abi"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
dependencies = [
"libc",
]
[[package]]
name = "indexmap"
version = "1.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
dependencies = [
"autocfg",
"hashbrown",
]
[[package]]
name = "is_ci"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "616cde7c720bb2bb5824a224687d8f77bfd38922027f01d825cd7453be5099fb"
[[package]]
name = "itoa"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754"
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.132"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5"
[[package]]
name = "log"
version = "0.4.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
dependencies = [
"cfg-if",
]
[[package]]
name = "matchers"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
dependencies = [
"regex-automata",
]
[[package]]
name = "once_cell"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "074864da206b4973b84eb91683020dbefd6a8c3f0f38e054d93954e891935e4e"
[[package]]
name = "os_str_bytes"
version = "6.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff"
[[package]]
name = "output_vt100"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "628223faebab4e3e40667ee0b2336d34a5b960ff60ea743ddfdbcf7770bcfb66"
dependencies = [
"winapi",
]
[[package]]
name = "owo-colors"
version = "3.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
dependencies = [
"supports-color",
]
[[package]]
name = "pest"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b0560d531d1febc25a3c9398a62a71256c0178f2e3443baedd9ad4bb8c9deb4"
dependencies = [
"thiserror",
"ucd-trie",
]
[[package]]
name = "pin-project-lite"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
[[package]]
name = "pretty_assertions"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c89f989ac94207d048d92db058e4f6ec7342b0971fc58d1271ca148b799b3563"
dependencies = [
"ansi_term",
"ctor",
"diff",
"output_vt100",
]
[[package]]
name = "proc-macro-error"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
dependencies = [
"proc-macro-error-attr",
"proc-macro2",
"quote",
"syn",
"version_check",
]
[[package]]
name = "proc-macro-error-attr"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
dependencies = [
"proc-macro2",
"quote",
"version_check",
]
[[package]]
name = "proc-macro2"
version = "1.0.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
dependencies = [
"proc-macro2",
]
[[package]]
name = "regex"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b"
dependencies = [
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
dependencies = [
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.6.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244"
[[package]]
name = "rustdoc-types"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3a260c376ebec8b6fcd30f518b253772873c5dd8564b45e384aad8a79c62aa6"
dependencies = [
"serde",
]
[[package]]
name = "ryu"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
[[package]]
name = "semver"
version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93f6841e709003d68bb2deee8c343572bf446003ec20a583e76f7b15cebf3711"
dependencies = [
"serde",
]
[[package]]
name = "serde"
version = "1.0.144"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.144"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94ed3a816fb1d101812f83e789f888322c34e291f894f19590dc310963e87a00"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.85"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "sharded-slab"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31"
dependencies = [
"lazy_static",
]
[[package]]
name = "smallvec"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1"
[[package]]
name = "strsim"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "supports-color"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4872ced36b91d47bae8a214a683fe54e7078875b399dfa251df346c9b547d1f9"
dependencies = [
"atty",
"is_ci",
]
[[package]]
name = "syn"
version = "1.0.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "termcolor"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
dependencies = [
"winapi-util",
]
[[package]]
name = "test_bin"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e7a7de15468c6e65dd7db81cf3822c1ec94c71b2a3c1a976ea8e4696c91115c"
[[package]]
name = "textwrap"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
[[package]]
name = "thiserror"
version = "1.0.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5f6586b7f764adc0231f4c79be7b920e766bb2f3e51b3661cdb263828f19994"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12bafc5b54507e0149cdf1b145a5d80ab80a90bcd9275df43d4fff68460f6c21"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "thread_local"
version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180"
dependencies = [
"once_cell",
]
[[package]]
name = "toml"
version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7"
dependencies = [
"serde",
]
[[package]]
name = "tracing"
version = "0.1.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fce9567bd60a67d08a16488756721ba392f24f29006402881e43b19aac64307"
dependencies = [
"cfg-if",
"pin-project-lite",
"tracing-attributes",
"tracing-core",
]
[[package]]
name = "tracing-attributes"
version = "0.1.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11c75893af559bc8e10716548bdef5cb2b983f8e637db9d0e15126b61b484ee2"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tracing-core"
version = "0.1.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5aeea4303076558a00714b823f9ad67d58a3bbda1df83d8827d21193156e22f7"
dependencies = [
"once_cell",
"valuable",
]
[[package]]
name = "tracing-log"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922"
dependencies = [
"lazy_static",
"log",
"tracing-core",
]
[[package]]
name = "tracing-subscriber"
version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60db860322da191b40952ad9affe65ea23e7dd6a5c442c2c42865810c6ab8e6b"
dependencies = [
"ansi_term",
"matchers",
"once_cell",
"regex",
"sharded-slab",
"smallvec",
"thread_local",
"tracing",
"tracing-core",
"tracing-log",
]
[[package]]
name = "ucd-trie"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89570599c4fe5585de2b388aab47e99f7fa4e9238a1399f707a02e356058141c"
[[package]]
name = "unicode-ident"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf"
[[package]]
name = "valuable"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "wildmatch"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee583bdc5ff1cf9db20e9db5bb3ff4c3089a8f6b8b31aff265c9aba85812db86"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

View File

@ -1,27 +0,0 @@
[package]
name = "cargo-check-external-types"
version = "0.1.3"
authors = ["AWS Rust SDK Team <aws-sdk-rust@amazon.com>", "John DiSanti <jdisanti@amazon.com>"]
description = "Static analysis tool to detect external types exposed in a library's public API."
edition = "2021"
license = "Apache-2.0"
repository = "https://github.com/awslabs/smithy-rs"
[dependencies]
anyhow = "1"
cargo_metadata = "0.14"
clap = { version = "~3.1.18", features = ["derive"] }
owo-colors = { version = "3", features = ["supports-colors"] }
pest = "2" # For pretty error formatting
rustdoc-types = "0.12"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
toml = "0.5"
tracing = "0.1"
tracing-attributes = "0.1"
tracing-subscriber = { version = "0.3.15", features = ["env-filter"] }
wildmatch = "2"
[dev-dependencies]
pretty_assertions = "1.1"
test_bin = "0.4"

View File

@ -1,175 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.

View File

@ -1,61 +0,0 @@
cargo-check-external-types
==========================
Static analysis tool that detects external types used in a Rust library's public API.
Configuration can be provided to allow certain external types so that this tool can
be used in continuous integration so that types don't unintentionally make it into
the library's API. It can also output a Markdown table of the external types it found.
Example Output
--------------
The test suite has a Rust library that [relies on some external types](test-workspace/test-crate/src/lib.rs).
When the tool is run against this library without any configuration,
[it emits errors](tests/default-config-expected-output.txt)
for each occurrence of an external type in the public API.
When [a config file](tests/allow-some-types.toml) is provided,
the allowed external types [no longer show up in the output](tests/allow-some-types-expected-output.txt).
When the output format is set to `markdown-table`, then
a [table of external types](tests/output-format-markdown-table-expected-output.md) is output.
How to Use
----------
_Important:_ This tool requires a nightly build of Rust to be installed since it relies on rustdoc JSON output.
It was last tested against nightly-2022-07-25.
To install, run the following from this README path:
```bash
cargo install --locked cargo-check-external-types
```
Then, in your library crate path, run:
```bash
cargo +nightly check-external-types
```
This will produce errors if any external types are used in a public API at all. That's not terribly useful
on its own, so the tool can be given a config file to allow certain types. For example, we can allow
any type in `bytes` with:
```toml
allowed_external_types = [
"bytes::*",
]
```
Save that file somewhere in your project (in this example, we choose the name `external-types.toml`), and then
run the command with:
```bash
cargo +nightly check-external-types --config external-types.toml
```
License
-------
This tool is distributed under the terms of Apache License Version 2.0.
See the [LICENSE](LICENSE) file for more information.

View File

@ -1,10 +0,0 @@
#!/bin/bash
#
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
set -e
cd "$(dirname "$0")"
cargo clippy
cargo "+${RUST_NIGHTLY_VERSION}" test

View File

@ -1,121 +0,0 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
use crate::here;
use anyhow::{bail, Context, Result};
use rustdoc_types::{Crate, FORMAT_VERSION};
use serde::Deserialize;
use std::fs;
use std::path::PathBuf;
use std::process::{Command, Output};
#[derive(Deserialize)]
struct CrateFormatVersion {
format_version: u32,
}
/// Runs the `cargo rustdoc` command required to produce Rustdoc's JSON output with a nightly compiler.
pub struct CargoRustDocJson {
/// Name of the crate (as specified in the Cargo.toml file)
crate_name: String,
/// Path of the crate to examine
crate_path: PathBuf,
/// Expected `target/` directory where the output will be
target_path: PathBuf,
/// Features to enable
features: Vec<String>,
}
impl CargoRustDocJson {
pub fn new(
crate_name: impl Into<String>,
crate_path: impl Into<PathBuf>,
target_path: impl Into<PathBuf>,
features: Vec<String>,
) -> Self {
CargoRustDocJson {
crate_name: crate_name.into(),
crate_path: crate_path.into(),
target_path: target_path.into(),
features,
}
}
pub fn run(&self) -> Result<Crate> {
let cargo = std::env::var("CARGO")
.ok()
.unwrap_or_else(|| "cargo".to_string());
let mut command = Command::new(&cargo);
command.current_dir(&self.crate_path).arg("rustdoc");
if !self.features.is_empty() {
command.arg("--no-default-features").arg("--features");
command.arg(&self.features.join(","));
}
command
.arg("--")
.arg("--document-private-items")
.arg("-Z")
.arg("unstable-options")
.arg("--output-format")
.arg("json");
let output = command
.output()
.context(here!("failed to run nightly rustdoc"))?;
handle_failure("rustdoc", &output)?;
let output_file_name = self
.target_path
.canonicalize()
.context(here!())?
.join(format!("doc/{}.json", self.crate_name.replace('-', "_")));
let json = fs::read_to_string(output_file_name).context(here!())?;
let format_version: CrateFormatVersion = serde_json::from_str(&json)
.context("Failed to find `format_version` in rustdoc JSON output.")
.context(here!())?;
if format_version.format_version != FORMAT_VERSION {
bail!(
"The version of rustdoc being used produces JSON format version {0}, but \
this tool requires format version {1}. This can happen if the locally \
installed version of rustdoc doesn't match the rustdoc JSON types from \
the `rustdoc-types` crate.\n\n\
If this occurs with the latest Rust nightly and the latest version of this \
tool, then this is a bug, and the tool needs to be upgraded to the latest \
format version.\n\n\
Otherwise, you'll need to determine a Rust nightly version that matches \
this tool's supported format version (or vice versa).",
format_version.format_version,
FORMAT_VERSION
);
}
let package: Crate = serde_json::from_str(&json)
.context("Failed to parse rustdoc output.")
.context(here!())?;
Ok(package)
}
}
pub fn handle_failure(operation_name: &str, output: &Output) -> Result<(), anyhow::Error> {
if !output.status.success() {
return Err(capture_error(operation_name, output));
}
Ok(())
}
pub fn capture_error(operation_name: &str, output: &Output) -> anyhow::Error {
let message = format!(
"Failed to {name}:\nStatus: {status}\nStdout: {stdout}\nStderr: {stderr}\n",
name = operation_name,
status = if let Some(code) = output.status.code() {
format!("{}", code)
} else {
"Killed by signal".to_string()
},
stdout = String::from_utf8_lossy(&output.stdout),
stderr = String::from_utf8_lossy(&output.stderr)
);
anyhow::Error::msg(message)
}

View File

@ -1,144 +0,0 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
use serde::de::{SeqAccess, Visitor};
use serde::{Deserialize, Deserializer};
use std::fmt;
use wildmatch::WildMatch;
/// Struct reprepsentation of the TOML config files that specify which external types are allowed.
#[derive(Debug, Deserialize)]
pub struct Config {
/// Whether or not to allow types from `alloc`. Defaults to true.
#[serde(default = "default_allow_std")]
pub allow_alloc: bool,
/// Whether or not to allow types from `core`. Defaults to true.
#[serde(default = "default_allow_std")]
pub allow_core: bool,
/// Whether or not to allow types from `std`. Defaults to true.
#[serde(default = "default_allow_std")]
pub allow_std: bool,
/// List of globs for allowed external types
///
/// For example, to allow every type in a crate:
/// ```toml
/// allowed_external_types = [
/// "crate_name::*"
/// ]
/// ```
///
/// Or, to selectively allow just a module of that crate
/// ```toml
/// allowed_external_types = [
/// "crate_name::path::to_module::*"
/// ]
/// ```
#[serde(deserialize_with = "deserialize_vec_wild_match")]
pub allowed_external_types: Vec<WildMatch>,
}
impl Config {
/// Returns true if the given `type_name` is allowed by this config for the given `root_crate_name`.
pub fn allows_type(&self, root_crate_name: &str, type_name: &str) -> bool {
let type_crate_name = &type_name[0..type_name.find("::").unwrap_or(type_name.len())];
match type_crate_name {
_ if type_crate_name == root_crate_name => true,
"alloc" => self.allow_alloc,
"core" => self.allow_core,
"std" => self.allow_std,
_ => self
.allowed_external_types
.iter()
.any(|glob| glob.matches(type_name)),
}
}
}
impl Default for Config {
fn default() -> Self {
Self {
allow_alloc: default_allow_std(),
allow_core: default_allow_std(),
allow_std: default_allow_std(),
allowed_external_types: Default::default(),
}
}
}
const fn default_allow_std() -> bool {
true
}
struct VecWildMatchDeserializer;
impl<'de> Visitor<'de> for VecWildMatchDeserializer {
type Value = Vec<WildMatch>;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("list of glob strings")
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
let mut result = Vec::new();
while let Some(value) = seq.next_element::<&str>()? {
result.push(WildMatch::new(value));
}
Ok(result)
}
}
fn deserialize_vec_wild_match<'de, D>(de: D) -> Result<Vec<WildMatch>, D::Error>
where
D: Deserializer<'de>,
{
de.deserialize_any(VecWildMatchDeserializer)
}
#[cfg(test)]
mod tests {
use super::Config;
use wildmatch::WildMatch;
#[test]
fn deserialize_config() {
let config = r#"
allow_std = false
allowed_external_types = [
"test::*",
"another_test::something::*::something",
]
"#;
let config: Config = toml::from_str(config).unwrap();
assert!(config.allow_alloc);
assert!(config.allow_core);
assert!(!config.allow_std);
assert!(config.allowed_external_types[0].matches("test::something"));
assert!(!config.allowed_external_types[0].matches("other::something"));
assert!(config.allowed_external_types[1].matches("another_test::something::foo::something"));
assert!(!config.allowed_external_types[1].matches("another_test::other::foo::something"));
}
#[test]
fn test_allows_type() {
let config = Config {
allowed_external_types: vec![WildMatch::new("one::*"), WildMatch::new("two::*")],
..Default::default()
};
assert!(config.allows_type("root", "alloc::System"));
assert!(config.allows_type("root", "std::vec::Vec"));
assert!(config.allows_type("root", "std::path::Path"));
assert!(config.allows_type("root", "root::thing"));
assert!(config.allows_type("root", "one::thing"));
assert!(config.allows_type("root", "two::thing"));
assert!(!config.allows_type("root", "three::thing"));
}
}

View File

@ -1,276 +0,0 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
use anyhow::{Context, Result};
use owo_colors::{OwoColorize, Stream};
use pest::Position;
use rustdoc_types::Span;
use std::cmp::Ordering;
use std::collections::HashMap;
use std::fmt;
use std::path::{Path, PathBuf};
/// Where the error occurred relative to the [`Path`](crate::path::Path).
///
/// For example, if the path is a path to a function, then this could point to something
/// specific about that function, such as a specific function argument that is in error.
///
/// There is overlap in this enum with [`ComponentType`](crate::path::ComponentType) since
/// some paths are specific enough to locate the external type.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum ErrorLocation {
AssocType,
ArgumentNamed(String),
ClosureInput,
ClosureOutput,
ConstGeneric,
Constant,
EnumTupleEntry,
GenericArg,
GenericDefaultBinding,
ImplementedTrait,
QualifiedSelfType,
QualifiedSelfTypeAsTrait,
ReExport,
ReturnValue,
Static,
StructField,
TraitBound,
TypeDef,
WhereBound,
}
impl fmt::Display for ErrorLocation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
Self::AssocType => "associated type",
Self::ArgumentNamed(name) => return write!(f, "argument named `{}` of", name),
Self::ClosureInput => "closure input of",
Self::ClosureOutput => "closure output of",
Self::ConstGeneric => "const generic of",
Self::Constant => "constant",
Self::EnumTupleEntry => "enum tuple entry of",
Self::GenericArg => "generic arg of",
Self::GenericDefaultBinding => "generic default binding of",
Self::ImplementedTrait => "implemented trait of",
Self::QualifiedSelfType => "qualified self type",
Self::QualifiedSelfTypeAsTrait => "qualified type `as` trait",
Self::ReExport => "re-export named",
Self::ReturnValue => "return value of",
Self::Static => "static value",
Self::StructField => "struct field of",
Self::TraitBound => "trait bound of",
Self::TypeDef => "typedef type of",
Self::WhereBound => "where bound of",
};
write!(f, "{}", s)
}
}
/// Error type for validation errors that get displayed to the user on the CLI.
#[derive(Debug)]
pub enum ValidationError {
UnapprovedExternalTypeRef {
type_name: String,
what: ErrorLocation,
in_what_type: String,
location: Option<Span>,
sort_key: String,
},
}
impl ValidationError {
pub fn unapproved_external_type_ref(
type_name: impl Into<String>,
what: &ErrorLocation,
in_what_type: impl Into<String>,
location: Option<&Span>,
) -> Self {
let type_name = type_name.into();
let in_what_type = in_what_type.into();
let sort_key = format!(
"{}:{}:{}:{}",
location_sort_key(location),
type_name,
what,
in_what_type
);
Self::UnapprovedExternalTypeRef {
type_name,
what: what.clone(),
in_what_type,
location: location.cloned(),
sort_key,
}
}
pub fn type_name(&self) -> &str {
match self {
Self::UnapprovedExternalTypeRef { type_name, .. } => type_name,
}
}
pub fn location(&self) -> Option<&Span> {
match self {
Self::UnapprovedExternalTypeRef { location, .. } => location.as_ref(),
}
}
fn sort_key(&self) -> &str {
match self {
Self::UnapprovedExternalTypeRef { sort_key, .. } => sort_key.as_ref(),
}
}
pub fn fmt_headline(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::UnapprovedExternalTypeRef { type_name, .. } => {
let inner = format!(
"Unapproved external type `{}` referenced in public API",
type_name
);
write!(
f,
"{} {}",
"error:"
.if_supports_color(Stream::Stdout, |text| text.red())
.if_supports_color(Stream::Stdout, |text| text.bold()),
inner.if_supports_color(Stream::Stdout, |text| text.bold())
)
}
}
}
pub fn subtext(&self) -> String {
match self {
Self::UnapprovedExternalTypeRef {
what, in_what_type, ..
} => format!("in {} `{}`", what, in_what_type),
}
}
}
fn location_sort_key(location: Option<&Span>) -> String {
if let Some(location) = location {
format!(
"{}:{:07}:{:07}",
location.filename.to_string_lossy(),
location.begin.0,
location.begin.1
)
} else {
"none".into()
}
}
impl Ord for ValidationError {
fn cmp(&self, other: &Self) -> Ordering {
self.partial_cmp(other).unwrap()
}
}
impl PartialOrd for ValidationError {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.sort_key().partial_cmp(other.sort_key())
}
}
impl Eq for ValidationError {}
impl PartialEq for ValidationError {
fn eq(&self, other: &Self) -> bool {
self.sort_key() == other.sort_key()
}
}
impl fmt::Display for ValidationError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.fmt_headline(f)
}
}
/// Pretty printer for error context.
///
/// This makes validation errors look similar to the compiler errors from rustc.
pub struct ErrorPrinter {
workspace_root: PathBuf,
file_cache: HashMap<PathBuf, String>,
}
impl ErrorPrinter {
pub fn new(workspace_root: impl Into<PathBuf>) -> Self {
Self {
workspace_root: workspace_root.into(),
file_cache: HashMap::new(),
}
}
fn get_file_contents(&mut self, path: &Path) -> Result<&str> {
if !self.file_cache.contains_key(path) {
let full_file_name = self.workspace_root.join(path).canonicalize()?;
let contents = std::fs::read_to_string(&full_file_name)
.context("failed to load source file for error context")
.context(full_file_name.to_string_lossy().to_string())?;
self.file_cache.insert(path.to_path_buf(), contents);
}
Ok(self.file_cache.get(path).unwrap())
}
pub fn pretty_print_error_context(&mut self, location: &Span, subtext: String) {
match self.get_file_contents(&location.filename) {
Ok(file_contents) => {
let begin = Self::position_from_line_col(file_contents, location.begin);
let end = Self::position_from_line_col(file_contents, location.end);
// HACK: Using Pest to do the pretty error context formatting for lack of
// knowledge of a smaller library tailored to this use-case
let variant = pest::error::ErrorVariant::<()>::CustomError { message: subtext };
let err_context = match (begin, end) {
(Some(b), Some(e)) => {
Some(pest::error::Error::new_from_span(variant, b.span(&e)))
}
(Some(b), None) => Some(pest::error::Error::new_from_pos(variant, b)),
_ => None,
};
if let Some(err_context) = err_context {
println!(
"{}\n",
err_context.with_path(&location.filename.to_string_lossy())
);
}
}
Err(err) => {
println!("error: {subtext}");
println!(
" --> {}:{}:{}",
location.filename.to_string_lossy(),
location.begin.0,
location.begin.1 + 1
);
println!(" | Failed to load {:?}", location.filename);
println!(" | relative to {:?}", self.workspace_root);
println!(" | to provide error message context.");
println!(" | Cause: {err:?}");
}
}
}
fn position_from_line_col(contents: &str, (line, col): (usize, usize)) -> Option<Position> {
let (mut cl, mut cc) = (1, 1);
let content_bytes = contents.as_bytes();
for (index, &byte) in content_bytes.iter().enumerate() {
if cl == line && cc == col {
return Position::new(contents, index);
}
cc += 1;
if byte == b'\n' {
cl += 1;
cc = 0;
}
}
None
}
}

View File

@ -1,22 +0,0 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
pub mod cargo;
pub mod config;
pub mod error;
pub mod path;
pub mod visitor;
/// A macro for attaching info to error messages pointing to the line of code responsible for the error.
/// [Thanks to dtolnay for this macro](https://github.com/dtolnay/anyhow/issues/22#issuecomment-542309452)
#[macro_export]
macro_rules! here {
() => {
concat!("error at ", file!(), ":", line!(), ":", column!())
};
($message:tt) => {
concat!($message, " (", here!(), ")")
};
}

View File

@ -1,232 +0,0 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
use anyhow::{anyhow, bail};
use anyhow::{Context, Result};
use cargo_check_external_types::cargo::CargoRustDocJson;
use cargo_check_external_types::error::ErrorPrinter;
use cargo_check_external_types::here;
use cargo_check_external_types::visitor::Visitor;
use cargo_metadata::{CargoOpt, Metadata};
use clap::Parser;
use owo_colors::{OwoColorize, Stream};
use std::borrow::Cow;
use std::fmt;
use std::fs;
use std::path::PathBuf;
use std::process;
use std::str::FromStr;
use tracing_subscriber::prelude::*;
use tracing_subscriber::EnvFilter;
#[derive(Debug)]
enum OutputFormat {
Errors,
MarkdownTable,
}
impl fmt::Display for OutputFormat {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Self::Errors => "errors",
Self::MarkdownTable => "markdown-table",
})
}
}
impl FromStr for OutputFormat {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"errors" => Ok(OutputFormat::Errors),
"markdown-table" => Ok(OutputFormat::MarkdownTable),
_ => Err(anyhow!(
"invalid output format: {}. Expected `errors` or `markdown-table`.",
s
)),
}
}
}
#[derive(clap::Args, Debug)]
struct CheckExternalTypesArgs {
/// Enables all crate features
#[clap(long)]
all_features: bool,
/// Disables default features
#[clap(long)]
no_default_features: bool,
/// Comma delimited list of features to enable in the crate
#[clap(long, use_value_delimiter = true)]
features: Option<Vec<String>>,
/// Path to the Cargo manifest
manifest_path: Option<PathBuf>,
/// Path to config toml to read
#[clap(long)]
config: Option<PathBuf>,
/// Enable verbose output for debugging
#[clap(short, long)]
verbose: bool,
/// Format to output results in
#[clap(long, default_value_t = OutputFormat::Errors)]
output_format: OutputFormat,
}
#[derive(Parser, Debug)]
#[clap(author, version, about, bin_name = "cargo")]
enum Args {
CheckExternalTypes(CheckExternalTypesArgs),
}
enum Error {
ValidationErrors,
Failure(anyhow::Error),
}
impl From<anyhow::Error> for Error {
fn from(err: anyhow::Error) -> Self {
Error::Failure(err)
}
}
fn main() {
process::exit(match run_main() {
Ok(_) => 0,
Err(Error::ValidationErrors) => 1,
Err(Error::Failure(err)) => {
println!("{:#}", dbg!(err));
2
}
})
}
fn run_main() -> Result<(), Error> {
let Args::CheckExternalTypes(args) = Args::parse();
if args.verbose {
let filter_layer = EnvFilter::try_from_default_env()
.or_else(|_| EnvFilter::try_new("debug"))
.unwrap();
let fmt_layer = tracing_subscriber::fmt::layer()
.without_time()
.with_ansi(true)
.with_level(true)
.with_target(false)
.pretty();
tracing_subscriber::registry()
.with(filter_layer)
.with(fmt_layer)
.init();
}
let config = if let Some(config_path) = &args.config {
let contents = fs::read_to_string(config_path).context("failed to read config file")?;
toml::from_str(&contents).context("failed to parse config file")?
} else {
Default::default()
};
let mut cargo_metadata_cmd = cargo_metadata::MetadataCommand::new();
if args.all_features {
cargo_metadata_cmd.features(CargoOpt::AllFeatures);
}
if args.no_default_features {
cargo_metadata_cmd.features(CargoOpt::NoDefaultFeatures);
}
if let Some(features) = args.features {
cargo_metadata_cmd.features(CargoOpt::SomeFeatures(features));
}
let crate_path = if let Some(manifest_path) = args.manifest_path {
cargo_metadata_cmd.manifest_path(&manifest_path);
manifest_path
.canonicalize()
.context(here!())?
.parent()
.expect("parent path")
.to_path_buf()
} else {
std::env::current_dir()
.context(here!())?
.canonicalize()
.context(here!())?
};
let cargo_metadata = cargo_metadata_cmd.exec().context(here!())?;
let cargo_features = resolve_features(&cargo_metadata)?;
eprintln!("Running rustdoc to produce json doc output...");
let package = CargoRustDocJson::new(
&*cargo_metadata
.root_package()
.as_ref()
.map(|package| Cow::Borrowed(package.name.as_str()))
.unwrap_or_else(|| crate_path.file_name().expect("file name").to_string_lossy()),
&crate_path,
&cargo_metadata.target_directory,
cargo_features,
)
.run()
.context(here!())?;
eprintln!("Examining all public types...");
let errors = Visitor::new(config, package)?.visit_all()?;
match args.output_format {
OutputFormat::Errors => {
let mut error_printer = ErrorPrinter::new(&cargo_metadata.workspace_root);
for error in &errors {
println!("{}", error);
if let Some(location) = error.location() {
error_printer.pretty_print_error_context(location, error.subtext())
}
}
if !errors.is_empty() {
println!(
"{} {} emitted",
errors.len(),
"errors".if_supports_color(Stream::Stdout, |text| text.red())
);
return Err(Error::ValidationErrors);
}
}
OutputFormat::MarkdownTable => {
println!("| Crate | Type | Used In |");
println!("| --- | --- | --- |");
let mut rows = Vec::new();
for error in &errors {
let type_name = error.type_name();
let crate_name = &type_name[0..type_name.find("::").unwrap_or(type_name.len())];
let location = error.location().unwrap();
rows.push(format!(
"| {} | {} | {}:{}:{} |",
crate_name,
type_name,
location.filename.to_string_lossy(),
location.begin.0,
location.begin.1
));
}
rows.sort();
rows.into_iter().for_each(|row| println!("{}", row));
}
}
Ok(())
}
fn resolve_features(metadata: &Metadata) -> Result<Vec<String>> {
let root_package = metadata
.root_package()
.ok_or_else(|| anyhow!("No root package found"))?;
if let Some(resolve) = &metadata.resolve {
let root_node = resolve
.nodes
.iter()
.find(|&n| n.id == root_package.id)
.ok_or_else(|| anyhow!("Failed to find node for root package"))?;
Ok(root_node.features.clone())
} else {
bail!("Cargo metadata didn't have resolved nodes");
}
}

View File

@ -1,93 +0,0 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
use rustdoc_types::{Item, Span};
use std::fmt;
/// Component type for components in a [`Path`].
#[derive(Copy, Clone, Debug)]
pub enum ComponentType {
AssocConst,
AssocType,
Constant,
Crate,
Enum,
EnumVariant,
Function,
Method,
Module,
ReExport,
Static,
Struct,
StructField,
Trait,
TypeDef,
Union,
}
/// Represents one component in a [`Path`].
#[derive(Clone, Debug)]
struct Component {
typ: ComponentType,
name: String,
span: Option<Span>,
}
impl Component {
fn new(typ: ComponentType, name: String, span: Option<Span>) -> Self {
Self { typ, name, span }
}
}
/// Represents the full path to an item being visited by [`Visitor`](crate::visitor::Visitor).
///
/// This is equivalent to the type path of that item, which has to be re-assembled since
/// it is lost in the flat structure of the Rustdoc JSON output.
#[derive(Clone, Debug)]
pub struct Path {
stack: Vec<Component>,
}
impl Path {
pub fn new(crate_name: &str) -> Self {
Self {
stack: vec![Component::new(
ComponentType::Crate,
crate_name.into(),
None,
)],
}
}
pub fn push(&mut self, typ: ComponentType, item: &Item) {
self.push_raw(typ, item.name.as_ref().expect("name"), item.span.as_ref());
}
pub fn push_raw(&mut self, typ: ComponentType, name: &str, span: Option<&Span>) {
self.stack
.push(Component::new(typ, name.into(), span.cloned()));
}
/// Returns the span (file + beginning and end positions) of the last [`Component`] in the stack.
pub fn last_span(&self) -> Option<&Span> {
self.stack.last().and_then(|c| c.span.as_ref())
}
/// Returns the [`ComponentType`] of the last [`Component`] in the path.
pub fn last_typ(&self) -> Option<ComponentType> {
self.stack.last().map(|c| c.typ)
}
}
impl fmt::Display for Path {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let names: Vec<&str> = self
.stack
.iter()
.map(|component| component.name.as_str())
.collect();
write!(f, "{}", names.join("::"))
}
}

View File

@ -1,579 +0,0 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
use crate::config::Config;
use crate::error::{ErrorLocation, ValidationError};
use crate::here;
use crate::path::{ComponentType, Path};
use anyhow::{anyhow, Context, Result};
use rustdoc_types::{
Crate, FnDecl, GenericArgs, GenericBound, GenericParamDef, GenericParamDefKind, Generics, Id,
Item, ItemEnum, ItemSummary, Struct, Term, Trait, Type, Union, Variant, Visibility,
WherePredicate,
};
use std::cell::RefCell;
use std::collections::{BTreeSet, HashMap};
use tracing::debug;
use tracing_attributes::instrument;
macro_rules! unstable_rust_feature {
($name:expr, $documentation_uri:expr) => {
panic!(
"unstable Rust feature '{}' (see {}) is not supported by cargo-check-external-types",
$name, $documentation_uri
)
};
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
enum VisibilityCheck {
/// Check to make sure the item is public before visiting it
Default,
/// Assume the item is public and examine it.
/// This is useful for visiting private items that are publically re-exported
AssumePublic,
}
/// Visits all items in the Rustdoc JSON output to discover external types in public APIs
/// and track them as validation errors if the [`Config`] doesn't allow them.
pub struct Visitor {
config: Config,
root_crate_id: u32,
root_crate_name: String,
index: HashMap<Id, Item>,
paths: HashMap<Id, ItemSummary>,
errors: RefCell<BTreeSet<ValidationError>>,
}
impl Visitor {
pub fn new(config: Config, package: Crate) -> Result<Self> {
Ok(Visitor {
config,
root_crate_id: Self::root_crate_id(&package)?,
root_crate_name: Self::root_crate_name(&package)?,
index: package.index,
paths: package.paths,
errors: RefCell::new(BTreeSet::new()),
})
}
/// This is the entry point for visiting the entire Rustdoc JSON tree, starting
/// from the root module (the only module where `is_crate` is true).
pub fn visit_all(self) -> Result<BTreeSet<ValidationError>> {
let root_path = Path::new(&self.root_crate_name);
let root_module = self
.index
.values()
.filter_map(|item| {
if let ItemEnum::Module(module) = &item.inner {
Some(module)
} else {
None
}
})
.find(|module| module.is_crate)
.ok_or_else(|| anyhow!("failed to find crate root module"))?;
for id in &root_module.items {
let item = self.item(id).context(here!())?;
self.visit_item(&root_path, item, VisibilityCheck::Default)?;
}
Ok(self.errors.take())
}
/// Returns true if the given item is public. In some cases, this must be determined
/// by examining the surrounding context. For example, enum variants are public if the
/// enum is public, even if their visibility is set to `Visibility::Default`.
fn is_public(path: &Path, item: &Item) -> bool {
match item.visibility {
Visibility::Public => true,
Visibility::Default => match &item.inner {
// Enum variants are public if the enum is public
ItemEnum::Variant(_) => matches!(path.last_typ(), Some(ComponentType::Enum)),
// Struct fields inside of enum variants are public if the enum is public
ItemEnum::StructField(_) => {
matches!(path.last_typ(), Some(ComponentType::EnumVariant))
}
// Trait items are public if the trait is public
_ => matches!(path.last_typ(), Some(ComponentType::Trait)),
},
_ => false,
}
}
#[instrument(level = "debug", skip(self, path, item), fields(path = %path, name = ?item.name, id = %item.id.0))]
fn visit_item(
&self,
path: &Path,
item: &Item,
visibility_check: VisibilityCheck,
) -> Result<()> {
if visibility_check == VisibilityCheck::Default && !Self::is_public(path, item) {
return Ok(());
}
let mut path = path.clone();
match &item.inner {
ItemEnum::AssocConst { type_, .. } => {
path.push(ComponentType::AssocConst, item);
self.visit_type(&path, &ErrorLocation::StructField, type_)
.context(here!())?;
}
ItemEnum::AssocType {
bounds,
default,
generics,
} => {
path.push(ComponentType::AssocType, item);
if let Some(typ) = default {
self.visit_type(&path, &ErrorLocation::AssocType, typ).context(here!())?;
}
self.visit_generic_bounds(&path, bounds).context(here!())?;
self.visit_generics(&path, generics).context(here!())?;
}
ItemEnum::Constant(constant) => {
path.push(ComponentType::Constant, item);
self.visit_type(&path, &ErrorLocation::Constant, &constant.type_).context(here!())?;
}
ItemEnum::Enum(enm) => {
path.push(ComponentType::Enum, item);
self.visit_generics(&path, &enm.generics).context(here!())?;
for id in &enm.impls {
self.visit_impl(&path, self.item(id).context(here!())?)?;
}
for id in &enm.variants {
self.visit_item(&path, self.item(id).context(here!())?, VisibilityCheck::Default).context(here!())?;
}
}
ItemEnum::ForeignType => unstable_rust_feature!(
"extern_types",
"https://doc.rust-lang.org/beta/unstable-book/language-features/extern-types.html"
),
ItemEnum::Function(function) => {
path.push(ComponentType::Function, item);
self.visit_fn_decl(&path, &function.decl).context(here!())?;
self.visit_generics(&path, &function.generics).context(here!())?;
}
ItemEnum::Import(import) => {
if let Some(target_id) = &import.id {
if self.in_root_crate(target_id) {
// Override the visibility check for re-exported items
self.visit_item(
&path,
self.item(target_id).context(here!())?,
VisibilityCheck::AssumePublic
).context(here!())?;
}
path.push_raw(ComponentType::ReExport, &import.name, item.span.as_ref());
self.check_external(&path, &ErrorLocation::ReExport, target_id)
.context(here!())?;
}
}
ItemEnum::Method(method) => {
path.push(ComponentType::Method, item);
self.visit_fn_decl(&path, &method.decl).context(here!())?;
self.visit_generics(&path, &method.generics).context(here!())?;
}
ItemEnum::Module(module) => {
if !module.is_crate {
path.push(ComponentType::Module, item);
}
for id in &module.items {
let module_item = self.item(id).context(here!())?;
// Re-exports show up twice in the doc json: once as an `ItemEnum::Import`,
// and once as the type as if it were originating from the root crate (but
// with a different crate ID). We only want to examine the `ItemEnum::Import`
// for re-exports since it includes the correct span where the re-export occurs,
// and we don't want to examine the innards of the re-export.
if module_item.crate_id == self.root_crate_id {
self.visit_item(&path, module_item, VisibilityCheck::Default).context(here!())?;
}
}
}
ItemEnum::OpaqueTy(_) => unstable_rust_feature!("type_alias_impl_trait", "https://doc.rust-lang.org/beta/unstable-book/language-features/type-alias-impl-trait.html"),
ItemEnum::Static(sttc) => {
path.push(ComponentType::Static, item);
self.visit_type(&path, &ErrorLocation::Static, &sttc.type_).context(here!())?;
}
ItemEnum::Struct(strct) => {
path.push(ComponentType::Struct, item);
self.visit_struct(&path, strct).context(here!())?;
}
ItemEnum::StructField(typ) => {
path.push(ComponentType::StructField, item);
self.visit_type(&path, &ErrorLocation::StructField, typ)
.context(here!())?;
}
ItemEnum::Trait(trt) => {
path.push(ComponentType::Trait, item);
self.visit_trait(&path, trt).context(here!())?;
}
ItemEnum::Typedef(typedef) => {
path.push(ComponentType::TypeDef, item);
self.visit_type(&path, &ErrorLocation::TypeDef, &typedef.type_)
.context(here!())?;
self.visit_generics(&path, &typedef.generics).context(here!())?;
}
ItemEnum::TraitAlias(_) => unstable_rust_feature!(
"trait_alias",
"https://doc.rust-lang.org/beta/unstable-book/language-features/trait-alias.html"
),
ItemEnum::Union(unn) => {
path.push(ComponentType::Union, item);
self.visit_union(&path, unn).context(here!())?;
}
ItemEnum::Variant(variant) => {
path.push(ComponentType::EnumVariant, item);
self.visit_variant(&path, variant).context(here!())?;
}
ItemEnum::ExternCrate { .. }
| ItemEnum::Impl(_)
| ItemEnum::Macro(_)
| ItemEnum::PrimitiveType(_)
| ItemEnum::ProcMacro(_) => {}
}
Ok(())
}
#[instrument(level = "debug", skip(self, path, strct), fields(path = %path))]
fn visit_struct(&self, path: &Path, strct: &Struct) -> Result<()> {
self.visit_generics(path, &strct.generics)?;
for id in &strct.fields {
let field = self.item(id).context(here!())?;
self.visit_item(path, field, VisibilityCheck::Default)?;
}
for id in &strct.impls {
self.visit_impl(path, self.item(id).context(here!())?)?;
}
Ok(())
}
#[instrument(level = "debug", skip(self, path, unn), fields(path = %path))]
fn visit_union(&self, path: &Path, unn: &Union) -> Result<()> {
self.visit_generics(path, &unn.generics)?;
for id in &unn.fields {
let field = self.item(id).context(here!())?;
self.visit_item(path, field, VisibilityCheck::Default)?;
}
for id in &unn.impls {
self.visit_impl(path, self.item(id).context(here!())?)?;
}
Ok(())
}
#[instrument(level = "debug", skip(self, path, trt), fields(path = %path))]
fn visit_trait(&self, path: &Path, trt: &Trait) -> Result<()> {
self.visit_generics(path, &trt.generics)?;
self.visit_generic_bounds(path, &trt.bounds)?;
for id in &trt.items {
let item = self.item(id).context(here!())?;
self.visit_item(path, item, VisibilityCheck::Default)?;
}
Ok(())
}
#[instrument(level = "debug", skip(self, path, item), fields(path = %path, id = %item.id.0))]
fn visit_impl(&self, path: &Path, item: &Item) -> Result<()> {
if let ItemEnum::Impl(imp) = &item.inner {
// Ignore blanket implementations
if imp.blanket_impl.is_some() {
return Ok(());
}
self.visit_generics(path, &imp.generics)?;
for id in &imp.items {
self.visit_item(
path,
self.item(id).context(here!())?,
VisibilityCheck::Default,
)?;
}
if let Some(trait_) = &imp.trait_ {
self.visit_type(path, &ErrorLocation::ImplementedTrait, trait_)
.context(here!())?;
}
} else {
unreachable!("should be passed an Impl item");
}
Ok(())
}
#[instrument(level = "debug", skip(self, path, decl), fields(path = %path))]
fn visit_fn_decl(&self, path: &Path, decl: &FnDecl) -> Result<()> {
for (index, (name, typ)) in decl.inputs.iter().enumerate() {
if index == 0 && name == "self" {
continue;
}
self.visit_type(path, &ErrorLocation::ArgumentNamed(name.into()), typ)
.context(here!())?;
}
if let Some(output) = &decl.output {
self.visit_type(path, &ErrorLocation::ReturnValue, output)
.context(here!())?;
}
Ok(())
}
#[instrument(level = "debug", skip(self, path, typ), fields(path = %path))]
fn visit_type(&self, path: &Path, what: &ErrorLocation, typ: &Type) -> Result<()> {
match typ {
Type::ResolvedPath {
id,
args,
param_names,
..
} => {
self.check_external(path, what, id).context(here!())?;
if let Some(args) = args {
self.visit_generic_args(path, args)?;
}
self.visit_generic_bounds(path, param_names)?;
}
Type::Generic(_) => {}
Type::Primitive(_) => {}
Type::FunctionPointer(fp) => {
self.visit_fn_decl(path, &fp.decl)?;
self.visit_generic_param_defs(path, &fp.generic_params)?;
}
Type::Tuple(types) => {
for typ in types {
self.visit_type(path, &ErrorLocation::EnumTupleEntry, typ)?;
}
}
Type::Slice(typ) => self.visit_type(path, what, typ).context(here!())?,
Type::Array { type_, .. } => self.visit_type(path, what, type_).context(here!())?,
Type::ImplTrait(impl_trait) => {
for bound in impl_trait {
match bound {
GenericBound::TraitBound {
trait_,
generic_params,
..
} => {
self.visit_type(path, what, trait_)?;
self.visit_generic_param_defs(path, generic_params)?;
}
GenericBound::Outlives(_) => {}
}
}
}
Type::Infer => {
unimplemented!(
"visit_type for Type::Infer: not sure what Rust code triggers this. \
If you encounter this, please report it with a link to the code it happens with."
)
}
Type::RawPointer { type_, .. } => {
self.visit_type(path, what, type_).context(here!())?
}
Type::BorrowedRef { type_, .. } => {
self.visit_type(path, what, type_).context(here!())?
}
Type::QualifiedPath {
self_type, trait_, ..
} => {
self.visit_type(path, &ErrorLocation::QualifiedSelfType, self_type)?;
self.visit_type(path, &ErrorLocation::QualifiedSelfTypeAsTrait, trait_)?;
}
}
Ok(())
}
#[instrument(level = "debug", skip(self, path, args), fields(path = %path))]
fn visit_generic_args(&self, path: &Path, args: &GenericArgs) -> Result<()> {
match args {
GenericArgs::AngleBracketed { args, bindings } => {
for arg in args {
match arg {
rustdoc_types::GenericArg::Type(typ) => {
self.visit_type(path, &ErrorLocation::GenericArg, typ)?
}
rustdoc_types::GenericArg::Lifetime(_)
| rustdoc_types::GenericArg::Const(_)
| rustdoc_types::GenericArg::Infer => {}
}
}
for binding in bindings {
match &binding.binding {
rustdoc_types::TypeBindingKind::Equality(term) => {
if let Term::Type(typ) = term {
self.visit_type(path, &ErrorLocation::GenericDefaultBinding, typ)
.context(here!())?;
}
}
rustdoc_types::TypeBindingKind::Constraint(bounds) => {
self.visit_generic_bounds(path, bounds)?;
}
}
}
}
GenericArgs::Parenthesized { inputs, output } => {
for input in inputs {
self.visit_type(path, &ErrorLocation::ClosureInput, input)
.context(here!())?;
}
if let Some(output) = output {
self.visit_type(path, &ErrorLocation::ClosureOutput, output)
.context(here!())?;
}
}
}
Ok(())
}
#[instrument(level = "debug", skip(self, path, bounds), fields(path = %path))]
fn visit_generic_bounds(&self, path: &Path, bounds: &[GenericBound]) -> Result<()> {
for bound in bounds {
if let GenericBound::TraitBound {
trait_,
generic_params,
..
} = bound
{
self.visit_type(path, &ErrorLocation::TraitBound, trait_)
.context(here!())?;
self.visit_generic_param_defs(path, generic_params)?;
}
}
Ok(())
}
#[instrument(level = "debug", skip(self, path, params), fields(path = %path))]
fn visit_generic_param_defs(&self, path: &Path, params: &[GenericParamDef]) -> Result<()> {
for param in params {
match &param.kind {
GenericParamDefKind::Type {
bounds,
default,
synthetic: _,
} => {
self.visit_generic_bounds(path, bounds)?;
if let Some(typ) = default {
self.visit_type(path, &ErrorLocation::GenericDefaultBinding, typ)
.context(here!())?;
}
}
GenericParamDefKind::Const { type_, .. } => {
self.visit_type(path, &ErrorLocation::ConstGeneric, type_)
.context(here!())?;
}
_ => {}
}
}
Ok(())
}
#[instrument(level = "debug", skip(self, path, generics), fields(path = %path))]
fn visit_generics(&self, path: &Path, generics: &Generics) -> Result<()> {
self.visit_generic_param_defs(path, &generics.params)?;
for where_pred in &generics.where_predicates {
match where_pred {
WherePredicate::BoundPredicate {
type_,
bounds,
generic_params,
} => {
self.visit_type(path, &ErrorLocation::WhereBound, type_)
.context(here!())?;
self.visit_generic_bounds(path, bounds)?;
self.visit_generic_param_defs(path, generic_params)?;
}
WherePredicate::RegionPredicate { bounds, .. } => {
self.visit_generic_bounds(path, bounds)?;
}
WherePredicate::EqPredicate { lhs, .. } => {
self.visit_type(path, &ErrorLocation::WhereBound, lhs)
.context(here!())?;
}
}
}
Ok(())
}
#[instrument(level = "debug", skip(self, path, variant), fields(path = %path))]
fn visit_variant(&self, path: &Path, variant: &Variant) -> Result<()> {
match variant {
Variant::Plain => {}
Variant::Tuple(types) => {
for typ in types {
self.visit_type(path, &ErrorLocation::EnumTupleEntry, typ)?;
}
}
Variant::Struct(ids) => {
for id in ids {
self.visit_item(
path,
self.item(id).context(here!())?,
VisibilityCheck::Default,
)?;
}
}
}
Ok(())
}
fn check_external(&self, path: &Path, what: &ErrorLocation, id: &Id) -> Result<()> {
if let Ok(type_name) = self.type_name(id) {
if !self.config.allows_type(&self.root_crate_name, &type_name) {
self.add_error(ValidationError::unapproved_external_type_ref(
self.type_name(id)?,
what,
path.to_string(),
path.last_span(),
));
}
}
// Crates like `pin_project` do some shenanigans to create and reference types that don't end up
// in the doc index, but that should only happen within the root crate.
else if !id.0.starts_with(&format!("{}:", self.root_crate_id)) {
unreachable!("A type is referencing another type that is not in the index, and that type is from another crate.");
}
Ok(())
}
fn add_error(&self, error: ValidationError) {
debug!("detected error {:?}", error);
self.errors.borrow_mut().insert(error);
}
fn item(&self, id: &Id) -> Result<&Item> {
self.index
.get(id)
.ok_or_else(|| anyhow!("Failed to find item in index for ID {:?}", id))
.context(here!())
}
fn item_summary(&self, id: &Id) -> Option<&ItemSummary> {
self.paths.get(id)
}
fn type_name(&self, id: &Id) -> Result<String> {
Ok(self.item_summary(id).context(here!())?.path.join("::"))
}
fn root_crate_id(package: &Crate) -> Result<u32> {
Ok(Self::root(package)?.crate_id)
}
/// Returns true if the given `id` belongs to the root crate
fn in_root_crate(&self, id: &Id) -> bool {
id.0.starts_with(&format!("{}:", self.root_crate_id))
}
fn root_crate_name(package: &Crate) -> Result<String> {
Ok(Self::root(package)?
.name
.as_ref()
.expect("root should always have a name")
.clone())
}
fn root(package: &Crate) -> Result<&Item> {
package
.index
.get(&package.root)
.ok_or_else(|| anyhow!("root not found in index"))
.context(here!())
}
}

View File

@ -1 +0,0 @@
Cargo.lock

View File

@ -1,2 +0,0 @@
[workspace]
members = ["external-lib", "test-crate", "test-reexports-crate"]

View File

@ -1,7 +0,0 @@
[package]
name = "external-lib"
version = "0.1.0"
edition = "2021"
publish = false
[dependencies]

View File

@ -1,42 +0,0 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
//! This crate exports a bunch of types for testing cargo-check-external-types against `test-crate`
pub struct SomeStruct;
pub struct SomeOtherStruct;
pub trait SimpleTrait {
fn something(&self) -> u32;
}
impl SimpleTrait for () {
fn something(&self) -> u32 {
0
}
}
pub trait SimpleGenericTrait<T> {
fn something(&self, thing: T) -> u32;
}
pub trait AssociatedGenericTrait {
type Input;
type Output;
type Error;
fn something(&self, input: Self::Input) -> Self::Output;
fn something_result(&self, input: Self::Input) -> Result<Self::Output, Self::Error>;
}
pub struct SimpleNewType(pub u32);
#[repr(C)]
#[derive(Copy, Clone)]
pub struct ReprCType {
i: i32,
f: f32,
}

View File

@ -1,8 +0,0 @@
[package]
name = "test-crate"
version = "0.1.0"
edition = "2021"
publish = false
[dependencies]
external-lib = { path = "../external-lib" }

View File

@ -1,159 +0,0 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#![feature(generic_associated_types)]
#![allow(dead_code)]
//! This crate is used to test the cargo-check-external-types by exercising the all possible
//! exposure of external types in a public API.
pub mod test_union;
use external_lib::{
AssociatedGenericTrait,
SimpleNewType,
SimpleTrait,
SomeOtherStruct,
SomeStruct,
// Remove this comment if more lines are needed for imports in the future to preserve line numbers
// Remove this comment if more lines are needed for imports in the future to preserve line numbers
// Remove this comment if more lines are needed for imports in the future to preserve line numbers
// Remove this comment if more lines are needed for imports in the future to preserve line numbers
// Remove this comment if more lines are needed for imports in the future to preserve line numbers
// Remove this comment if more lines are needed for imports in the future to preserve line numbers
// Remove this comment if more lines are needed for imports in the future to preserve line numbers
// Remove this comment if more lines are needed for imports in the future to preserve line numbers
};
pub struct LocalStruct;
pub fn fn_with_local_struct(_local: LocalStruct) -> LocalStruct {
unimplemented!()
}
fn not_pub_external_in_fn_input(_one: &SomeStruct, _two: impl SimpleTrait) {}
pub fn external_in_fn_input(_one: &SomeStruct, _two: impl SimpleTrait) {}
fn not_pub_external_in_fn_output() -> SomeStruct {
unimplemented!()
}
pub fn external_in_fn_output() -> SomeStruct {
unimplemented!()
}
pub fn external_opaque_type_in_output() -> impl SimpleTrait {
unimplemented!()
}
fn not_pub_external_in_fn_output_generic() -> Option<SomeStruct> {
unimplemented!()
}
pub fn external_in_fn_output_generic() -> Option<SomeStruct> {
unimplemented!()
}
// Try to trick cargo-check-external-types here by putting something in a private module and re-exporting it
mod private_module {
use external_lib::SomeStruct;
pub fn something(_one: &SomeStruct) {}
}
pub use private_module::something;
pub struct StructWithExternalFields {
pub field: SomeStruct,
pub optional_field: Option<SomeStruct>,
}
impl StructWithExternalFields {
pub fn new(_field: impl Into<SomeStruct>, _optional_field: Option<SomeOtherStruct>) -> Self {
unimplemented!()
}
}
pub trait TraitReferencingExternals {
fn something(&self, a: SomeStruct) -> LocalStruct;
fn optional_something(&self, a: Option<SomeStruct>) -> LocalStruct;
fn otherthing(&self) -> SomeStruct;
fn optional_otherthing(&self) -> Option<SomeStruct>;
}
pub enum EnumWithExternals<T = SomeStruct> {
NormalTuple(LocalStruct),
NormalStruct {
v: LocalStruct,
},
TupleEnum(SomeStruct, Box<dyn SimpleTrait>),
StructEnum {
some_struct: SomeStruct,
simple_trait: Box<dyn SimpleTrait>,
},
GenericTupleEnum(T),
GenericStructEnum {
t: T,
},
}
impl<T> EnumWithExternals<T> {
pub fn thing(_t: LocalStruct) -> Self {
unimplemented!()
}
pub fn another_thing<S: SimpleTrait>(_s: S) -> Self {
unimplemented!()
}
}
pub static SOME_STRUCT: SomeStruct = SomeStruct;
pub const SOME_CONST: SomeStruct = SomeStruct;
pub mod some_pub_mod {
use external_lib::SomeStruct;
pub static OPTIONAL_STRUCT: Option<SomeStruct> = None;
pub const OPTIONAL_CONST: Option<SomeStruct> = None;
}
pub type NotExternalReferencing = u32;
pub type ExternalReferencingTypedef = SomeStruct;
pub type OptionalExternalReferencingTypedef = Option<SomeStruct>;
pub type DynExternalReferencingTypedef = Box<dyn SimpleTrait>;
pub type ExternalReferencingRawPtr = *const SomeStruct;
pub fn fn_with_external_trait_bounds<I, O, E, T>(_thing: T)
where
I: Into<SomeStruct>,
O: Into<SomeOtherStruct>,
E: std::error::Error,
T: AssociatedGenericTrait<Input = I, Output = O, Error = E>,
{
}
pub trait SomeTraitWithExternalDefaultTypes {
type Thing: SimpleTrait;
type OtherThing: AssociatedGenericTrait<
Input = SomeStruct,
Output = u32,
Error = SomeOtherStruct,
>;
fn something(&self, input: Self::Thing) -> Self::OtherThing;
}
pub trait SomeTraitWithGenericAssociatedType {
type MyGAT<T>
where
T: SimpleTrait;
fn some_fn<T: SimpleTrait>(&self, thing: Self::MyGAT<T>);
}
pub struct AssocConstStruct;
impl AssocConstStruct {
pub const SOME_CONST: u32 = 5;
pub const OTHER_CONST: SimpleNewType = SimpleNewType(5);
}

View File

@ -1,24 +0,0 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
use external_lib::{ReprCType, SimpleTrait};
#[repr(C)]
pub union SimpleUnion {
pub repr_c: ReprCType,
pub something_else: u64,
}
impl SimpleUnion {
pub fn repr_c(&self) -> &ReprCType {
&self.repr_c
}
}
#[repr(C)]
pub union GenericUnion<T: Copy + SimpleTrait> {
pub repr_c: T,
pub something_else: u64,
}

View File

@ -1,8 +0,0 @@
[package]
name = "test-reexports-crate"
version = "0.1.0"
edition = "2021"
publish = false
[dependencies]
external-lib = { path = "../external-lib" }

View File

@ -1,16 +0,0 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
pub use external_lib::AssociatedGenericTrait;
pub use external_lib::ReprCType;
pub use external_lib::SimpleTrait;
pub mod Something {
pub use external_lib::SimpleGenericTrait;
pub use external_lib::SimpleNewType;
}
pub use external_lib::SomeOtherStruct;
pub use external_lib::SomeStruct;

View File

@ -1,39 +0,0 @@
error: Unapproved external type `external_lib::AssociatedGenericTrait` referenced in public API
--> test-crate/src/lib.rs:125:1
|
125 | pub fn fn_with_external_trait_bounds<I, O, E, T>(_thing: T)
| ...
132 | }␊
| ^
|
= in trait bound of `test_crate::fn_with_external_trait_bounds`
error: Unapproved external type `external_lib::AssociatedGenericTrait` referenced in public API
--> test-crate/src/lib.rs:136:5
|
136 | type OtherThing: AssociatedGenericTrait<
| ...
140 | >;␊
| ^^
|
= in trait bound of `test_crate::SomeTraitWithExternalDefaultTypes::OtherThing`
error: Unapproved external type `external_lib::ReprCType` referenced in public API
--> test-crate/src/test_union.rs:10:5
|
10 | pub repr_c: ReprCType,
| ^-------------------^
|
= in struct field of `test_crate::test_union::SimpleUnion::repr_c`
error: Unapproved external type `external_lib::ReprCType` referenced in public API
--> test-crate/src/test_union.rs:15:5
|
15 | pub fn repr_c(&self) -> &ReprCType {
| ...
17 | }␊
| ^
|
= in return value of `test_crate::test_union::SimpleUnion::repr_c`
4 errors emitted

View File

@ -1,6 +0,0 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
allowed_external_types = [
"external_lib::S*",
]

View File

@ -1,369 +0,0 @@
error: Unapproved external type `external_lib::SimpleTrait` referenced in public API
--> test-crate/src/lib.rs:38:1
|
38 | pub fn external_in_fn_input(_one: &SomeStruct, _two: impl SimpleTrait) {}
| ^-----------------------------------------------------------------------^
|
= in argument named `_two` of `test_crate::external_in_fn_input`
error: Unapproved external type `external_lib::SimpleTrait` referenced in public API
--> test-crate/src/lib.rs:38:1
|
38 | pub fn external_in_fn_input(_one: &SomeStruct, _two: impl SimpleTrait) {}
| ^-----------------------------------------------------------------------^
|
= in trait bound of `test_crate::external_in_fn_input`
error: Unapproved external type `external_lib::SomeStruct` referenced in public API
--> test-crate/src/lib.rs:38:1
|
38 | pub fn external_in_fn_input(_one: &SomeStruct, _two: impl SimpleTrait) {}
| ^-----------------------------------------------------------------------^
|
= in argument named `_one` of `test_crate::external_in_fn_input`
error: Unapproved external type `external_lib::SomeStruct` referenced in public API
--> test-crate/src/lib.rs:43:1
|
43 | pub fn external_in_fn_output() -> SomeStruct {
| ...
45 | }␊
| ^
|
= in return value of `test_crate::external_in_fn_output`
error: Unapproved external type `external_lib::SimpleTrait` referenced in public API
--> test-crate/src/lib.rs:47:1
|
47 | pub fn external_opaque_type_in_output() -> impl SimpleTrait {
| ...
49 | }␊
| ^
|
= in return value of `test_crate::external_opaque_type_in_output`
error: Unapproved external type `external_lib::SomeStruct` referenced in public API
--> test-crate/src/lib.rs:54:1
|
54 | pub fn external_in_fn_output_generic() -> Option<SomeStruct> {
| ...
56 | }␊
| ^
|
= in generic arg of `test_crate::external_in_fn_output_generic`
error: Unapproved external type `external_lib::SomeStruct` referenced in public API
--> test-crate/src/lib.rs:62:5
|
62 | pub fn something(_one: &SomeStruct) {}
| ^------------------------------------^
|
= in argument named `_one` of `test_crate::something`
error: Unapproved external type `external_lib::SomeStruct` referenced in public API
--> test-crate/src/lib.rs:67:5
|
67 | pub field: SomeStruct,
| ^-------------------^
|
= in struct field of `test_crate::StructWithExternalFields::field`
error: Unapproved external type `external_lib::SomeStruct` referenced in public API
--> test-crate/src/lib.rs:68:5
|
68 | pub optional_field: Option<SomeStruct>,
| ^------------------------------------^
|
= in generic arg of `test_crate::StructWithExternalFields::optional_field`
error: Unapproved external type `external_lib::SomeOtherStruct` referenced in public API
--> test-crate/src/lib.rs:72:5
|
72 | pub fn new(_field: impl Into<SomeStruct>, _optional_field: Option<SomeOtherStruct>) -> Self {
| ...
74 | }␊
| ^
|
= in generic arg of `test_crate::StructWithExternalFields::new`
error: Unapproved external type `external_lib::SomeStruct` referenced in public API
--> test-crate/src/lib.rs:72:5
|
72 | pub fn new(_field: impl Into<SomeStruct>, _optional_field: Option<SomeOtherStruct>) -> Self {
| ...
74 | }␊
| ^
|
= in generic arg of `test_crate::StructWithExternalFields::new`
error: Unapproved external type `external_lib::SomeStruct` referenced in public API
--> test-crate/src/lib.rs:78:5
|
78 | fn something(&self, a: SomeStruct) -> LocalStruct;
| ^------------------------------------------------^
|
= in argument named `a` of `test_crate::TraitReferencingExternals::something`
error: Unapproved external type `external_lib::SomeStruct` referenced in public API
--> test-crate/src/lib.rs:79:5
|
79 | fn optional_something(&self, a: Option<SomeStruct>) -> LocalStruct;
| ^-----------------------------------------------------------------^
|
= in generic arg of `test_crate::TraitReferencingExternals::optional_something`
error: Unapproved external type `external_lib::SomeStruct` referenced in public API
--> test-crate/src/lib.rs:80:5
|
80 | fn otherthing(&self) -> SomeStruct;
| ^---------------------------------^
|
= in return value of `test_crate::TraitReferencingExternals::otherthing`
error: Unapproved external type `external_lib::SomeStruct` referenced in public API
--> test-crate/src/lib.rs:81:5
|
81 | fn optional_otherthing(&self) -> Option<SomeStruct>;
| ^--------------------------------------------------^
|
= in generic arg of `test_crate::TraitReferencingExternals::optional_otherthing`
error: Unapproved external type `external_lib::SomeStruct` referenced in public API
--> test-crate/src/lib.rs:84:1
|
84 | pub enum EnumWithExternals<T = SomeStruct> {
| ...
98 | }␊
| ^
|
= in generic default binding of `test_crate::EnumWithExternals`
error: Unapproved external type `external_lib::SimpleTrait` referenced in public API
--> test-crate/src/lib.rs:89:5
|
89 | TupleEnum(SomeStruct, Box<dyn SimpleTrait>),
| ^-----------------------------------------^
|
= in generic arg of `test_crate::EnumWithExternals::TupleEnum`
error: Unapproved external type `external_lib::SomeStruct` referenced in public API
--> test-crate/src/lib.rs:89:5
|
89 | TupleEnum(SomeStruct, Box<dyn SimpleTrait>),
| ^-----------------------------------------^
|
= in enum tuple entry of `test_crate::EnumWithExternals::TupleEnum`
error: Unapproved external type `external_lib::SomeStruct` referenced in public API
--> test-crate/src/lib.rs:91:9
|
91 | some_struct: SomeStruct,
| ^---------------------^
|
= in struct field of `test_crate::EnumWithExternals::StructEnum::some_struct`
error: Unapproved external type `external_lib::SimpleTrait` referenced in public API
--> test-crate/src/lib.rs:92:9
|
92 | simple_trait: Box<dyn SimpleTrait>,
| ^--------------------------------^
|
= in generic arg of `test_crate::EnumWithExternals::StructEnum::simple_trait`
error: Unapproved external type `external_lib::SimpleTrait` referenced in public API
--> test-crate/src/lib.rs:104:5
|
104 | pub fn another_thing<S: SimpleTrait>(_s: S) -> Self {
| ...
106 | }␊
| ^
|
= in trait bound of `test_crate::EnumWithExternals::another_thing`
error: Unapproved external type `external_lib::SomeStruct` referenced in public API
--> test-crate/src/lib.rs:109:1
|
109 | pub static SOME_STRUCT: SomeStruct = SomeStruct;
| ^----------------------------------------------^
|
= in static value `test_crate::SOME_STRUCT`
error: Unapproved external type `external_lib::SomeStruct` referenced in public API
--> test-crate/src/lib.rs:110:1
|
110 | pub const SOME_CONST: SomeStruct = SomeStruct;
| ^--------------------------------------------^
|
= in constant `test_crate::SOME_CONST`
error: Unapproved external type `external_lib::SomeStruct` referenced in public API
--> test-crate/src/lib.rs:115:5
|
115 | pub static OPTIONAL_STRUCT: Option<SomeStruct> = None;
| ^----------------------------------------------------^
|
= in generic arg of `test_crate::some_pub_mod::OPTIONAL_STRUCT`
error: Unapproved external type `external_lib::SomeStruct` referenced in public API
--> test-crate/src/lib.rs:116:5
|
116 | pub const OPTIONAL_CONST: Option<SomeStruct> = None;
| ^--------------------------------------------------^
|
= in generic arg of `test_crate::some_pub_mod::OPTIONAL_CONST`
error: Unapproved external type `external_lib::SomeStruct` referenced in public API
--> test-crate/src/lib.rs:120:1
|
120 | pub type ExternalReferencingTypedef = SomeStruct;
| ^-----------------------------------------------^
|
= in typedef type of `test_crate::ExternalReferencingTypedef`
error: Unapproved external type `external_lib::SomeStruct` referenced in public API
--> test-crate/src/lib.rs:121:1
|
121 | pub type OptionalExternalReferencingTypedef = Option<SomeStruct>;
| ^---------------------------------------------------------------^
|
= in generic arg of `test_crate::OptionalExternalReferencingTypedef`
error: Unapproved external type `external_lib::SimpleTrait` referenced in public API
--> test-crate/src/lib.rs:122:1
|
122 | pub type DynExternalReferencingTypedef = Box<dyn SimpleTrait>;
| ^------------------------------------------------------------^
|
= in generic arg of `test_crate::DynExternalReferencingTypedef`
error: Unapproved external type `external_lib::SomeStruct` referenced in public API
--> test-crate/src/lib.rs:123:1
|
123 | pub type ExternalReferencingRawPtr = *const SomeStruct;
| ^-----------------------------------------------------^
|
= in typedef type of `test_crate::ExternalReferencingRawPtr`
error: Unapproved external type `external_lib::AssociatedGenericTrait` referenced in public API
--> test-crate/src/lib.rs:125:1
|
125 | pub fn fn_with_external_trait_bounds<I, O, E, T>(_thing: T)
| ...
132 | }␊
| ^
|
= in trait bound of `test_crate::fn_with_external_trait_bounds`
error: Unapproved external type `external_lib::SomeOtherStruct` referenced in public API
--> test-crate/src/lib.rs:125:1
|
125 | pub fn fn_with_external_trait_bounds<I, O, E, T>(_thing: T)
| ...
132 | }␊
| ^
|
= in generic arg of `test_crate::fn_with_external_trait_bounds`
error: Unapproved external type `external_lib::SomeStruct` referenced in public API
--> test-crate/src/lib.rs:125:1
|
125 | pub fn fn_with_external_trait_bounds<I, O, E, T>(_thing: T)
| ...
132 | }␊
| ^
|
= in generic arg of `test_crate::fn_with_external_trait_bounds`
error: Unapproved external type `external_lib::SimpleTrait` referenced in public API
--> test-crate/src/lib.rs:135:5
|
135 | type Thing: SimpleTrait;
| ^----------------------^
|
= in trait bound of `test_crate::SomeTraitWithExternalDefaultTypes::Thing`
error: Unapproved external type `external_lib::AssociatedGenericTrait` referenced in public API
--> test-crate/src/lib.rs:136:5
|
136 | type OtherThing: AssociatedGenericTrait<
| ...
140 | >;␊
| ^^
|
= in trait bound of `test_crate::SomeTraitWithExternalDefaultTypes::OtherThing`
error: Unapproved external type `external_lib::SomeOtherStruct` referenced in public API
--> test-crate/src/lib.rs:136:5
|
136 | type OtherThing: AssociatedGenericTrait<
| ...
140 | >;␊
| ^^
|
= in generic default binding of `test_crate::SomeTraitWithExternalDefaultTypes::OtherThing`
error: Unapproved external type `external_lib::SomeStruct` referenced in public API
--> test-crate/src/lib.rs:136:5
|
136 | type OtherThing: AssociatedGenericTrait<
| ...
140 | >;␊
| ^^
|
= in generic default binding of `test_crate::SomeTraitWithExternalDefaultTypes::OtherThing`
error: Unapproved external type `external_lib::SimpleTrait` referenced in public API
--> test-crate/src/lib.rs:146:5
|
146 | type MyGAT<T>
| ...
148 | T: SimpleTrait;␊
| ^-----------------^
|
= in trait bound of `test_crate::SomeTraitWithGenericAssociatedType::MyGAT`
error: Unapproved external type `external_lib::SimpleTrait` referenced in public API
--> test-crate/src/lib.rs:150:5
|
150 | fn some_fn<T: SimpleTrait>(&self, thing: Self::MyGAT<T>);
| ^-------------------------------------------------------^
|
= in trait bound of `test_crate::SomeTraitWithGenericAssociatedType::some_fn`
error: Unapproved external type `external_lib::SimpleNewType` referenced in public API
--> test-crate/src/lib.rs:158:5
|
158 | pub const OTHER_CONST: SimpleNewType = SimpleNewType(5);
| ^------------------------------------------------------^
|
= in struct field of `test_crate::AssocConstStruct::OTHER_CONST`
error: Unapproved external type `external_lib::ReprCType` referenced in public API
--> test-crate/src/test_union.rs:10:5
|
10 | pub repr_c: ReprCType,
| ^-------------------^
|
= in struct field of `test_crate::test_union::SimpleUnion::repr_c`
error: Unapproved external type `external_lib::ReprCType` referenced in public API
--> test-crate/src/test_union.rs:15:5
|
15 | pub fn repr_c(&self) -> &ReprCType {
| ...
17 | }␊
| ^
|
= in return value of `test_crate::test_union::SimpleUnion::repr_c`
error: Unapproved external type `external_lib::SimpleTrait` referenced in public API
--> test-crate/src/test_union.rs:21:1
|
21 | pub union GenericUnion<T: Copy + SimpleTrait> {
| ...
24 | }␊
| ^
|
= in trait bound of `test_crate::test_union::GenericUnion`
42 errors emitted

View File

@ -1,77 +0,0 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
use cargo_check_external_types::cargo::handle_failure;
use pretty_assertions::assert_str_eq;
use std::fs;
use std::path::Path;
use std::process::Output;
use test_bin::get_test_bin;
/// Returns (stdout, stderr)
pub fn output_text(output: &Output) -> (String, String) {
(
String::from_utf8_lossy(&output.stdout).to_string(),
String::from_utf8_lossy(&output.stderr).to_string(),
)
}
fn run_with_args(in_path: impl AsRef<Path>, args: &[&str]) -> String {
let mut cmd = get_test_bin("cargo-check-external-types");
cmd.current_dir(in_path.as_ref());
cmd.arg("check-external-types");
for &arg in args {
cmd.arg(arg);
}
let output = cmd
.output()
.expect("failed to start cargo-check-external-types");
match output.status.code() {
Some(1) => { /* expected */ }
_ => handle_failure("cargo-check-external-types", &output).unwrap(),
}
let (stdout, _) = output_text(&output);
stdout
}
#[test]
fn with_default_config() {
let expected_output = fs::read_to_string("tests/default-config-expected-output.txt").unwrap();
let actual_output = run_with_args("test-workspace/test-crate", &[]);
assert_str_eq!(expected_output, actual_output);
}
#[test]
fn with_some_allowed_types() {
let expected_output = fs::read_to_string("tests/allow-some-types-expected-output.txt").unwrap();
let actual_output = run_with_args(
"test-workspace/test-crate",
&["--config", "../../tests/allow-some-types.toml"],
);
assert_str_eq!(expected_output, actual_output);
}
#[test]
fn with_output_format_markdown_table() {
let expected_output =
fs::read_to_string("tests/output-format-markdown-table-expected-output.md").unwrap();
let actual_output = run_with_args(
"test-workspace/test-crate",
&["--output-format", "markdown-table"],
);
assert_str_eq!(expected_output, actual_output);
}
// Make sure that the visitor doesn't attempt to visit the inner items of re-exported external types.
// Rustdoc doesn't include these inner items in its JSON output, which leads to obtuse crashes if they're
// referenced. It's also just the wrong behavior to look into the type being re-exported, since if it's
// approved, then it doesn't matter what it referenced. If it's not approved, then the re-export itself
// is the violation.
#[test]
fn test_reexports() {
let expected_output = fs::read_to_string("tests/test-reexports-expected-output.md").unwrap();
let actual_output = run_with_args("test-workspace/test-reexports-crate", &[]);
assert_str_eq!(expected_output, actual_output);
}

View File

@ -1,44 +0,0 @@
| Crate | Type | Used In |
| --- | --- | --- |
| external_lib | external_lib::AssociatedGenericTrait | test-crate/src/lib.rs:125:0 |
| external_lib | external_lib::AssociatedGenericTrait | test-crate/src/lib.rs:136:4 |
| external_lib | external_lib::ReprCType | test-crate/src/test_union.rs:10:4 |
| external_lib | external_lib::ReprCType | test-crate/src/test_union.rs:15:4 |
| external_lib | external_lib::SimpleNewType | test-crate/src/lib.rs:158:4 |
| external_lib | external_lib::SimpleTrait | test-crate/src/lib.rs:104:4 |
| external_lib | external_lib::SimpleTrait | test-crate/src/lib.rs:122:0 |
| external_lib | external_lib::SimpleTrait | test-crate/src/lib.rs:135:4 |
| external_lib | external_lib::SimpleTrait | test-crate/src/lib.rs:146:4 |
| external_lib | external_lib::SimpleTrait | test-crate/src/lib.rs:150:4 |
| external_lib | external_lib::SimpleTrait | test-crate/src/lib.rs:38:0 |
| external_lib | external_lib::SimpleTrait | test-crate/src/lib.rs:38:0 |
| external_lib | external_lib::SimpleTrait | test-crate/src/lib.rs:47:0 |
| external_lib | external_lib::SimpleTrait | test-crate/src/lib.rs:89:4 |
| external_lib | external_lib::SimpleTrait | test-crate/src/lib.rs:92:8 |
| external_lib | external_lib::SimpleTrait | test-crate/src/test_union.rs:21:0 |
| external_lib | external_lib::SomeOtherStruct | test-crate/src/lib.rs:125:0 |
| external_lib | external_lib::SomeOtherStruct | test-crate/src/lib.rs:136:4 |
| external_lib | external_lib::SomeOtherStruct | test-crate/src/lib.rs:72:4 |
| external_lib | external_lib::SomeStruct | test-crate/src/lib.rs:109:0 |
| external_lib | external_lib::SomeStruct | test-crate/src/lib.rs:110:0 |
| external_lib | external_lib::SomeStruct | test-crate/src/lib.rs:115:4 |
| external_lib | external_lib::SomeStruct | test-crate/src/lib.rs:116:4 |
| external_lib | external_lib::SomeStruct | test-crate/src/lib.rs:120:0 |
| external_lib | external_lib::SomeStruct | test-crate/src/lib.rs:121:0 |
| external_lib | external_lib::SomeStruct | test-crate/src/lib.rs:123:0 |
| external_lib | external_lib::SomeStruct | test-crate/src/lib.rs:125:0 |
| external_lib | external_lib::SomeStruct | test-crate/src/lib.rs:136:4 |
| external_lib | external_lib::SomeStruct | test-crate/src/lib.rs:38:0 |
| external_lib | external_lib::SomeStruct | test-crate/src/lib.rs:43:0 |
| external_lib | external_lib::SomeStruct | test-crate/src/lib.rs:54:0 |
| external_lib | external_lib::SomeStruct | test-crate/src/lib.rs:62:4 |
| external_lib | external_lib::SomeStruct | test-crate/src/lib.rs:67:4 |
| external_lib | external_lib::SomeStruct | test-crate/src/lib.rs:68:4 |
| external_lib | external_lib::SomeStruct | test-crate/src/lib.rs:72:4 |
| external_lib | external_lib::SomeStruct | test-crate/src/lib.rs:78:4 |
| external_lib | external_lib::SomeStruct | test-crate/src/lib.rs:79:4 |
| external_lib | external_lib::SomeStruct | test-crate/src/lib.rs:80:4 |
| external_lib | external_lib::SomeStruct | test-crate/src/lib.rs:81:4 |
| external_lib | external_lib::SomeStruct | test-crate/src/lib.rs:84:0 |
| external_lib | external_lib::SomeStruct | test-crate/src/lib.rs:89:4 |
| external_lib | external_lib::SomeStruct | test-crate/src/lib.rs:91:8 |

View File

@ -1,57 +0,0 @@
error: Unapproved external type `external_lib::AssociatedGenericTrait` referenced in public API
--> test-reexports-crate/src/lib.rs:6:1
|
6 | pub use external_lib::AssociatedGenericTrait;
| ^-------------------------------------------^
|
= in re-export named `test_reexports_crate::AssociatedGenericTrait`
error: Unapproved external type `external_lib::ReprCType` referenced in public API
--> test-reexports-crate/src/lib.rs:7:1
|
7 | pub use external_lib::ReprCType;
| ^------------------------------^
|
= in re-export named `test_reexports_crate::ReprCType`
error: Unapproved external type `external_lib::SimpleTrait` referenced in public API
--> test-reexports-crate/src/lib.rs:8:1
|
8 | pub use external_lib::SimpleTrait;
| ^--------------------------------^
|
= in re-export named `test_reexports_crate::SimpleTrait`
error: Unapproved external type `external_lib::SimpleGenericTrait` referenced in public API
--> test-reexports-crate/src/lib.rs:11:5
|
11 | pub use external_lib::SimpleGenericTrait;
| ^---------------------------------------^
|
= in re-export named `test_reexports_crate::Something::SimpleGenericTrait`
error: Unapproved external type `external_lib::SimpleNewType` referenced in public API
--> test-reexports-crate/src/lib.rs:12:5
|
12 | pub use external_lib::SimpleNewType;
| ^----------------------------------^
|
= in re-export named `test_reexports_crate::Something::SimpleNewType`
error: Unapproved external type `external_lib::SomeOtherStruct` referenced in public API
--> test-reexports-crate/src/lib.rs:15:1
|
15 | pub use external_lib::SomeOtherStruct;
| ^------------------------------------^
|
= in re-export named `test_reexports_crate::SomeOtherStruct`
error: Unapproved external type `external_lib::SomeStruct` referenced in public API
--> test-reexports-crate/src/lib.rs:16:1
|
16 | pub use external_lib::SomeStruct;
| ^-------------------------------^
|
= in re-export named `test_reexports_crate::SomeStruct`
7 errors emitted

View File

@ -22,7 +22,6 @@ function test_tool {
popd &>/dev/null
}
test_tool "tools/cargo-check-external-types" "${RUST_NIGHTLY_VERSION}"
test_tool "tools/changelogger" "${RUST_STABLE_VERSION}"
test_tool "tools/ci-cdk/canary-runner" "${RUST_STABLE_VERSION}"
test_tool "tools/crate-hasher" "${RUST_STABLE_VERSION}"