From 6f3e9271b123f94f158b1f000c996a558351320f Mon Sep 17 00:00:00 2001 From: Chih-Hung Hsieh Date: Wed, 13 May 2020 16:08:03 -0700 Subject: Import 'pin-project' package version 0.4.16 * Add OWNERS and Android.bp Bug: 156165390 Test: make Change-Id: I8ace68f978b2725ad91e0f64282003fa453674ca --- .editorconfig | 16 + .gitattributes | 4 + .github/CODEOWNERS | 1 + .github/bors.toml | 2 + .github/workflows/ci.yml | 146 ++++ .gitignore | 6 + .rustfmt.toml | 19 + Android.bp | 18 + CHANGELOG.md | 461 ++++++++++++ Cargo.toml | 30 + Cargo.toml.orig | 31 + LICENSE | 1 + LICENSE-APACHE | 202 +++++ LICENSE-MIT | 23 + METADATA | 19 + MODULE_LICENSE_APACHE2 | 0 NOTICE | 1 + OWNERS | 1 + README.md | 77 ++ ci.sh | 29 + ci/install-component.sh | 22 + ci/install-rust.sh | 13 + compiletest.sh | 12 + examples/README.md | 35 + examples/enum-default-expanded.rs | 109 +++ examples/enum-default.rs | 13 + examples/not_unpin-expanded.rs | 125 ++++ examples/not_unpin.rs | 17 + examples/pinned_drop-expanded.rs | 154 ++++ examples/pinned_drop.rs | 22 + examples/project_replace-expanded.rs | 158 ++++ examples/project_replace.rs | 14 + examples/struct-default-expanded.rs | 157 ++++ examples/struct-default.rs | 14 + examples/unsafe_unpin-expanded.rs | 111 +++ examples/unsafe_unpin.rs | 16 + src/lib.rs | 239 ++++++ tests/cfg.rs | 242 ++++++ tests/compiletest.rs | 15 + tests/forbid_unsafe.rs | 108 +++ tests/lints.rs | 126 ++++ tests/overwriting_core_crate.rs | 116 +++ tests/pin_project.rs | 809 +++++++++++++++++++++ tests/pinned_drop.rs | 235 ++++++ tests/project.rs | 297 ++++++++ tests/project_if_attr.rs.in | 44 ++ tests/project_ref.rs | 174 +++++ tests/project_replace.rs | 98 +++ tests/repr_packed.rs | 50 ++ tests/ui/cfg/cfg_attr-resolve.rs | 11 + tests/ui/cfg/cfg_attr-resolve.stderr | 5 + tests/ui/cfg/cfg_attr-type-mismatch.rs | 24 + tests/ui/cfg/cfg_attr-type-mismatch.stderr | 23 + tests/ui/cfg/cfg_attr-unpin.rs | 21 + tests/ui/cfg/cfg_attr-unpin.stderr | 22 + tests/ui/cfg/packed_sneaky-span-issue-1.rs | 18 + tests/ui/cfg/packed_sneaky-span-issue-1.stderr | 1 + tests/ui/cfg/packed_sneaky-span-issue-2.rs | 18 + tests/ui/cfg/packed_sneaky-span-issue-2.stderr | 1 + tests/ui/cfg/packed_sneaky.rs | 12 + tests/ui/cfg/packed_sneaky.stderr | 7 + tests/ui/cfg/proper_unpin.rs | 28 + tests/ui/cfg/proper_unpin.stderr | 11 + tests/ui/cfg/unsupported.rs | 13 + tests/ui/cfg/unsupported.stderr | 1 + tests/ui/not_unpin/assert-not-unpin.rs | 40 + tests/ui/not_unpin/assert-not-unpin.stderr | 83 +++ tests/ui/not_unpin/conflict-unpin.rs | 30 + tests/ui/not_unpin/conflict-unpin.stderr | 26 + tests/ui/not_unpin/impl-unsafe-unpin.rs | 30 + tests/ui/not_unpin/impl-unsafe-unpin.stderr | 32 + tests/ui/pin_project/add-pin-attr-to-struct.rs | 19 + tests/ui/pin_project/add-pin-attr-to-struct.stderr | 15 + tests/ui/pin_project/add-pinned-field.rs | 23 + tests/ui/pin_project/add-pinned-field.stderr | 23 + tests/ui/pin_project/conflict-drop.rs | 31 + tests/ui/pin_project/conflict-drop.stderr | 19 + tests/ui/pin_project/conflict-unpin.rs | 37 + tests/ui/pin_project/conflict-unpin.stderr | 32 + tests/ui/pin_project/impl-unsafe-unpin.rs | 30 + tests/ui/pin_project/impl-unsafe-unpin.stderr | 32 + tests/ui/pin_project/invalid.rs | 199 +++++ tests/ui/pin_project/invalid.stderr | 228 ++++++ tests/ui/pin_project/overlapping_unpin_struct.rs | 18 + .../ui/pin_project/overlapping_unpin_struct.stderr | 11 + tests/ui/pin_project/packed-enum.rs | 20 + tests/ui/pin_project/packed-enum.stderr | 30 + tests/ui/pin_project/packed-name-value.rs | 20 + tests/ui/pin_project/packed-name-value.stderr | 17 + tests/ui/pin_project/packed.rs | 25 + tests/ui/pin_project/packed.stderr | 17 + tests/ui/pin_project/packed_sneaky-1.rs | 33 + tests/ui/pin_project/packed_sneaky-1.stderr | 23 + tests/ui/pin_project/packed_sneaky-2.rs | 12 + tests/ui/pin_project/packed_sneaky-2.stderr | 13 + tests/ui/pin_project/private_in_public-enum.rs | 23 + tests/ui/pin_project/private_in_public-enum.stderr | 17 + tests/ui/pin_project/proper_unpin.rs | 38 + tests/ui/pin_project/proper_unpin.stderr | 37 + tests/ui/pin_project/remove-attr-from-field.rs | 32 + tests/ui/pin_project/remove-attr-from-field.stderr | 21 + tests/ui/pin_project/remove-attr-from-struct.rs | 30 + .../ui/pin_project/remove-attr-from-struct.stderr | 63 ++ tests/ui/pin_project/safe_packed_borrows.rs | 21 + tests/ui/pin_project/safe_packed_borrows.stderr | 24 + tests/ui/pin_project/unpin_sneaky.rs | 11 + tests/ui/pin_project/unpin_sneaky.stderr | 11 + tests/ui/pin_project/visibility.rs | 52 ++ tests/ui/pin_project/visibility.stderr | 39 + tests/ui/pinned_drop/call-drop-inner.rs | 16 + tests/ui/pinned_drop/call-drop-inner.stderr | 10 + tests/ui/pinned_drop/conditional-drop-impl.rs | 26 + tests/ui/pinned_drop/conditional-drop-impl.stderr | 26 + tests/ui/pinned_drop/forget-pinned-drop-impl.rs | 9 + .../ui/pinned_drop/forget-pinned-drop-impl.stderr | 7 + tests/ui/pinned_drop/invalid-self.rs | 14 + tests/ui/pinned_drop/invalid-self.stderr | 25 + tests/ui/pinned_drop/invalid.rs | 207 ++++++ tests/ui/pinned_drop/invalid.stderr | 125 ++++ tests/ui/pinned_drop/pinned-drop-no-attr-arg.rs | 15 + .../ui/pinned_drop/pinned-drop-no-attr-arg.stderr | 8 + tests/ui/pinned_drop/self.rs | 28 + tests/ui/pinned_drop/self.stderr | 23 + tests/ui/pinned_drop/unsafe-call.rs | 17 + tests/ui/pinned_drop/unsafe-call.stderr | 7 + tests/ui/project/ambiguous-let.rs | 24 + tests/ui/project/ambiguous-let.stderr | 5 + tests/ui/project/invalid.rs | 190 +++++ tests/ui/project/invalid.stderr | 155 ++++ tests/ui/project/type-mismatch.rs | 74 ++ tests/ui/project/type-mismatch.stderr | 16 + tests/ui/project/use-public.rs | 15 + tests/ui/project/use-public.stderr | 7 + tests/ui/project/use.rs | 17 + tests/ui/project/use.stderr | 11 + tests/ui/unsafe_unpin/conflict-unpin.rs | 30 + tests/ui/unsafe_unpin/conflict-unpin.stderr | 35 + .../ui/unsafe_unpin/not-implement-unsafe-unpin.rs | 14 + .../unsafe_unpin/not-implement-unsafe-unpin.stderr | 11 + tests/ui/unsafe_unpin/proper_unpin.rs | 41 ++ tests/ui/unsafe_unpin/proper_unpin.stderr | 63 ++ tests/ui/unstable-features/README.md | 5 + .../marker_trait_attr-feature-gate.rs | 19 + .../marker_trait_attr-feature-gate.stderr | 10 + tests/ui/unstable-features/marker_trait_attr.rs | 25 + .../ui/unstable-features/marker_trait_attr.stderr | 10 + .../overlapping_marker_traits-feature-gate.rs | 19 + .../overlapping_marker_traits-feature-gate.stderr | 10 + .../unstable-features/overlapping_marker_traits.rs | 29 + .../overlapping_marker_traits.stderr | 18 + .../run-pass/stmt_expr_attributes.rs | 62 ++ .../stmt_expr_attributes-feature-gate.rs | 55 ++ .../stmt_expr_attributes-feature-gate.stderr | 35 + tests/ui/unstable-features/trivial_bounds-bug.rs | 33 + .../ui/unstable-features/trivial_bounds-bug.stderr | 10 + .../trivial_bounds-feature-gate.rs | 54 ++ .../trivial_bounds-feature-gate.stderr | 50 ++ tests/ui/unstable-features/trivial_bounds.rs | 34 + tests/ui/unstable-features/trivial_bounds.stderr | 17 + tests/unsafe_unpin.rs | 53 ++ 160 files changed, 8254 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitattributes create mode 100644 .github/CODEOWNERS create mode 100644 .github/bors.toml create mode 100644 .github/workflows/ci.yml create mode 100644 .gitignore create mode 100644 .rustfmt.toml create mode 100644 Android.bp create mode 100644 CHANGELOG.md create mode 100644 Cargo.toml create mode 100644 Cargo.toml.orig create mode 120000 LICENSE create mode 100644 LICENSE-APACHE create mode 100644 LICENSE-MIT create mode 100644 METADATA create mode 100644 MODULE_LICENSE_APACHE2 create mode 120000 NOTICE create mode 100644 OWNERS create mode 100644 README.md create mode 100644 ci.sh create mode 100644 ci/install-component.sh create mode 100644 ci/install-rust.sh create mode 100644 compiletest.sh create mode 100644 examples/README.md create mode 100644 examples/enum-default-expanded.rs create mode 100644 examples/enum-default.rs create mode 100644 examples/not_unpin-expanded.rs create mode 100644 examples/not_unpin.rs create mode 100644 examples/pinned_drop-expanded.rs create mode 100644 examples/pinned_drop.rs create mode 100644 examples/project_replace-expanded.rs create mode 100644 examples/project_replace.rs create mode 100644 examples/struct-default-expanded.rs create mode 100644 examples/struct-default.rs create mode 100644 examples/unsafe_unpin-expanded.rs create mode 100644 examples/unsafe_unpin.rs create mode 100644 src/lib.rs create mode 100644 tests/cfg.rs create mode 100644 tests/compiletest.rs create mode 100644 tests/forbid_unsafe.rs create mode 100644 tests/lints.rs create mode 100644 tests/overwriting_core_crate.rs create mode 100644 tests/pin_project.rs create mode 100644 tests/pinned_drop.rs create mode 100644 tests/project.rs create mode 100644 tests/project_if_attr.rs.in create mode 100644 tests/project_ref.rs create mode 100644 tests/project_replace.rs create mode 100644 tests/repr_packed.rs create mode 100644 tests/ui/cfg/cfg_attr-resolve.rs create mode 100644 tests/ui/cfg/cfg_attr-resolve.stderr create mode 100644 tests/ui/cfg/cfg_attr-type-mismatch.rs create mode 100644 tests/ui/cfg/cfg_attr-type-mismatch.stderr create mode 100644 tests/ui/cfg/cfg_attr-unpin.rs create mode 100644 tests/ui/cfg/cfg_attr-unpin.stderr create mode 100644 tests/ui/cfg/packed_sneaky-span-issue-1.rs create mode 100644 tests/ui/cfg/packed_sneaky-span-issue-1.stderr create mode 100644 tests/ui/cfg/packed_sneaky-span-issue-2.rs create mode 100644 tests/ui/cfg/packed_sneaky-span-issue-2.stderr create mode 100644 tests/ui/cfg/packed_sneaky.rs create mode 100644 tests/ui/cfg/packed_sneaky.stderr create mode 100644 tests/ui/cfg/proper_unpin.rs create mode 100644 tests/ui/cfg/proper_unpin.stderr create mode 100644 tests/ui/cfg/unsupported.rs create mode 100644 tests/ui/cfg/unsupported.stderr create mode 100644 tests/ui/not_unpin/assert-not-unpin.rs create mode 100644 tests/ui/not_unpin/assert-not-unpin.stderr create mode 100644 tests/ui/not_unpin/conflict-unpin.rs create mode 100644 tests/ui/not_unpin/conflict-unpin.stderr create mode 100644 tests/ui/not_unpin/impl-unsafe-unpin.rs create mode 100644 tests/ui/not_unpin/impl-unsafe-unpin.stderr create mode 100644 tests/ui/pin_project/add-pin-attr-to-struct.rs create mode 100644 tests/ui/pin_project/add-pin-attr-to-struct.stderr create mode 100644 tests/ui/pin_project/add-pinned-field.rs create mode 100644 tests/ui/pin_project/add-pinned-field.stderr create mode 100644 tests/ui/pin_project/conflict-drop.rs create mode 100644 tests/ui/pin_project/conflict-drop.stderr create mode 100644 tests/ui/pin_project/conflict-unpin.rs create mode 100644 tests/ui/pin_project/conflict-unpin.stderr create mode 100644 tests/ui/pin_project/impl-unsafe-unpin.rs create mode 100644 tests/ui/pin_project/impl-unsafe-unpin.stderr create mode 100644 tests/ui/pin_project/invalid.rs create mode 100644 tests/ui/pin_project/invalid.stderr create mode 100644 tests/ui/pin_project/overlapping_unpin_struct.rs create mode 100644 tests/ui/pin_project/overlapping_unpin_struct.stderr create mode 100644 tests/ui/pin_project/packed-enum.rs create mode 100644 tests/ui/pin_project/packed-enum.stderr create mode 100644 tests/ui/pin_project/packed-name-value.rs create mode 100644 tests/ui/pin_project/packed-name-value.stderr create mode 100644 tests/ui/pin_project/packed.rs create mode 100644 tests/ui/pin_project/packed.stderr create mode 100644 tests/ui/pin_project/packed_sneaky-1.rs create mode 100644 tests/ui/pin_project/packed_sneaky-1.stderr create mode 100644 tests/ui/pin_project/packed_sneaky-2.rs create mode 100644 tests/ui/pin_project/packed_sneaky-2.stderr create mode 100644 tests/ui/pin_project/private_in_public-enum.rs create mode 100644 tests/ui/pin_project/private_in_public-enum.stderr create mode 100644 tests/ui/pin_project/proper_unpin.rs create mode 100644 tests/ui/pin_project/proper_unpin.stderr create mode 100644 tests/ui/pin_project/remove-attr-from-field.rs create mode 100644 tests/ui/pin_project/remove-attr-from-field.stderr create mode 100644 tests/ui/pin_project/remove-attr-from-struct.rs create mode 100644 tests/ui/pin_project/remove-attr-from-struct.stderr create mode 100644 tests/ui/pin_project/safe_packed_borrows.rs create mode 100644 tests/ui/pin_project/safe_packed_borrows.stderr create mode 100644 tests/ui/pin_project/unpin_sneaky.rs create mode 100644 tests/ui/pin_project/unpin_sneaky.stderr create mode 100644 tests/ui/pin_project/visibility.rs create mode 100644 tests/ui/pin_project/visibility.stderr create mode 100644 tests/ui/pinned_drop/call-drop-inner.rs create mode 100644 tests/ui/pinned_drop/call-drop-inner.stderr create mode 100644 tests/ui/pinned_drop/conditional-drop-impl.rs create mode 100644 tests/ui/pinned_drop/conditional-drop-impl.stderr create mode 100644 tests/ui/pinned_drop/forget-pinned-drop-impl.rs create mode 100644 tests/ui/pinned_drop/forget-pinned-drop-impl.stderr create mode 100644 tests/ui/pinned_drop/invalid-self.rs create mode 100644 tests/ui/pinned_drop/invalid-self.stderr create mode 100644 tests/ui/pinned_drop/invalid.rs create mode 100644 tests/ui/pinned_drop/invalid.stderr create mode 100644 tests/ui/pinned_drop/pinned-drop-no-attr-arg.rs create mode 100644 tests/ui/pinned_drop/pinned-drop-no-attr-arg.stderr create mode 100644 tests/ui/pinned_drop/self.rs create mode 100644 tests/ui/pinned_drop/self.stderr create mode 100644 tests/ui/pinned_drop/unsafe-call.rs create mode 100644 tests/ui/pinned_drop/unsafe-call.stderr create mode 100644 tests/ui/project/ambiguous-let.rs create mode 100644 tests/ui/project/ambiguous-let.stderr create mode 100644 tests/ui/project/invalid.rs create mode 100644 tests/ui/project/invalid.stderr create mode 100644 tests/ui/project/type-mismatch.rs create mode 100644 tests/ui/project/type-mismatch.stderr create mode 100644 tests/ui/project/use-public.rs create mode 100644 tests/ui/project/use-public.stderr create mode 100644 tests/ui/project/use.rs create mode 100644 tests/ui/project/use.stderr create mode 100644 tests/ui/unsafe_unpin/conflict-unpin.rs create mode 100644 tests/ui/unsafe_unpin/conflict-unpin.stderr create mode 100644 tests/ui/unsafe_unpin/not-implement-unsafe-unpin.rs create mode 100644 tests/ui/unsafe_unpin/not-implement-unsafe-unpin.stderr create mode 100644 tests/ui/unsafe_unpin/proper_unpin.rs create mode 100644 tests/ui/unsafe_unpin/proper_unpin.stderr create mode 100644 tests/ui/unstable-features/README.md create mode 100644 tests/ui/unstable-features/marker_trait_attr-feature-gate.rs create mode 100644 tests/ui/unstable-features/marker_trait_attr-feature-gate.stderr create mode 100644 tests/ui/unstable-features/marker_trait_attr.rs create mode 100644 tests/ui/unstable-features/marker_trait_attr.stderr create mode 100644 tests/ui/unstable-features/overlapping_marker_traits-feature-gate.rs create mode 100644 tests/ui/unstable-features/overlapping_marker_traits-feature-gate.stderr create mode 100644 tests/ui/unstable-features/overlapping_marker_traits.rs create mode 100644 tests/ui/unstable-features/overlapping_marker_traits.stderr create mode 100644 tests/ui/unstable-features/run-pass/stmt_expr_attributes.rs create mode 100644 tests/ui/unstable-features/stmt_expr_attributes-feature-gate.rs create mode 100644 tests/ui/unstable-features/stmt_expr_attributes-feature-gate.stderr create mode 100644 tests/ui/unstable-features/trivial_bounds-bug.rs create mode 100644 tests/ui/unstable-features/trivial_bounds-bug.stderr create mode 100644 tests/ui/unstable-features/trivial_bounds-feature-gate.rs create mode 100644 tests/ui/unstable-features/trivial_bounds-feature-gate.stderr create mode 100644 tests/ui/unstable-features/trivial_bounds.rs create mode 100644 tests/ui/unstable-features/trivial_bounds.stderr create mode 100644 tests/unsafe_unpin.rs diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..c93ffc7 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,16 @@ +# EditorConfig configuration +# https://editorconfig.org + +# Top-most EditorConfig file +root = true + +[*] +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +charset = utf-8 +indent_style = space +indent_size = 4 + +[*.{json,yml,md}] +indent_size = 2 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..45bca84 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,4 @@ +[attr]rust text eol=lf whitespace=tab-in-indent,trailing-space,tabwidth=4 + +* text=auto eol=lf +*.rs rust diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..2fdc28f --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @taiki-e diff --git a/.github/bors.toml b/.github/bors.toml new file mode 100644 index 0000000..1779788 --- /dev/null +++ b/.github/bors.toml @@ -0,0 +1,2 @@ +status = ["ci"] +delete_merged_branches = true diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..89b20e6 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,146 @@ +name: CI + +on: + pull_request: + push: + branches: + - master + - staging + - trying + schedule: + - cron: '00 01 * * *' + +env: + RUSTFLAGS: -Dwarnings + RUST_BACKTRACE: 1 + +defaults: + run: + shell: bash + +jobs: + test: + name: test + strategy: + matrix: + include: + # This is the minimum supported Rust version of this crate. + # When updating this, the reminder to update the minimum supported + # Rust version in README.md. + - build: 1.34.0 + rust: 1.34.0 + - build: 1.36.0 + rust: 1.36.0 + - build: 1.37.0 + rust: 1.37.0 + - build: stable + rust: stable + - build: beta + rust: beta + - build: nightly + rust: nightly + # pin-project itself has no platform-dependent implementation. + # macOS is only used to check that pin-project can interoperate + # correctly with `#[cfg()]`. + - build: macos + os: macos + rust: nightly + # - build: windows + # os: windows + # rust: nightly + runs-on: ${{ matrix.os || 'ubuntu' }}-latest + steps: + - uses: actions/checkout@master + - name: Install Rust + run: | + . ./ci/install-rust.sh ${{ matrix.rust }} + - name: Install cargo-hack + if: matrix.rust == 'nightly' + run: | + cargo install cargo-hack + - name: Install cargo-expand + if: matrix.rust == 'nightly' && matrix.build != 'macos' + run: | + cargo install cargo-expand + - name: Add targets + if: matrix.rust == 'nightly' + run: | + rustup target add thumbv7m-none-eabi + - name: cargo test (stable) + if: matrix.rust != 'nightly' + run: | + cargo test --all --all-features --exclude expandtest + - name: cargo test (nightly) + if: matrix.rust == 'nightly' + run: | + cargo test --all --all-features -- -Zunstable-options --include-ignored + - name: cargo check --target thumbv7m-none-eabi + if: matrix.rust == 'nightly' + run: | + cargo check --target thumbv7m-none-eabi --manifest-path tests/no-std/Cargo.toml + # Refs: https://github.com/rust-lang/cargo/issues/5657 + - name: cargo check -Zminimal-versions + if: matrix.rust == 'nightly' + run: | + cargo update -Zminimal-versions + cargo hack check --all --all-features --no-dev-deps --ignore-private + + style: + name: style + strategy: + fail-fast: false + matrix: + component: + - clippy + - rustfmt + - rustdoc + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@master + - name: Install Rust + run: | + . ./ci/install-rust.sh + - name: Install component + if: matrix.component != 'rustdoc' + run: | + . ./ci/install-component.sh ${{ matrix.component }} + - name: cargo clippy + if: matrix.component == 'clippy' + run: | + cargo clippy --all --all-features --all-targets + - name: cargo fmt -- --check + if: matrix.component == 'rustfmt' + run: | + cargo fmt --all -- --check + - name: cargo doc + if: matrix.component == 'rustdoc' + env: + # TODO: once https://github.com/rust-lang/rust/issues/70814 fixed, remove '-Aunused_braces' + RUSTDOCFLAGS: -Dwarnings -Aunused_braces + run: | + cargo doc --no-deps --all --all-features + + # Refs: https://github.com/rust-lang/crater/blob/9ab6f9697c901c4a44025cf0a39b73ad5b37d198/.github/workflows/bors.yml#L125-L149 + # + # ALL THE PREVIOUS JOBS NEEDS TO BE ADDED TO THE `needs` SECTION OF THIS JOB! + + ci-success: + name: ci + if: github.event_name == 'push' && success() + needs: + - style + - test + runs-on: ubuntu-latest + steps: + - name: Mark the job as a success + run: exit 0 + ci-failure: + name: ci + if: github.event_name == 'push' && !success() + needs: + - style + - test + runs-on: ubuntu-latest + steps: + - name: Mark the job as a failure + run: exit 1 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..214d7a8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +target +Cargo.lock + +# For platform and editor specific settings, it is recommended to add to +# a global .gitignore file. +# Refs: https://help.github.com/en/articles/ignoring-files#create-a-global-gitignore diff --git a/.rustfmt.toml b/.rustfmt.toml new file mode 100644 index 0000000..dc49733 --- /dev/null +++ b/.rustfmt.toml @@ -0,0 +1,19 @@ +# Rustfmt configuration +# https://github.com/rust-lang/rustfmt/blob/master/Configurations.md + +# This is required for bug-fixes, which technically can't be made to the stable +# first version. +version = "Two" # Tracking issue: https://github.com/rust-lang/rustfmt/issues/3383 + +# Refs: https://internals.rust-lang.org/t/running-rustfmt-on-rust-lang-rust-and-other-rust-lang-repositories/8732/72 +use_small_heuristics = "Max" + +# Apply rustfmt to more places. +merge_imports = true # Tracking issue: https://github.com/rust-lang/rustfmt/issues/3362 +format_code_in_doc_comments = true # Tracking issue: https://github.com/rust-lang/rustfmt/issues/3348 + +# Set the default settings again to always apply the proper formatting without +# being affected by the editor settings. +# Refs: https://github.com/rust-lang/rls/issues/501#issuecomment-333717736 +edition = "2018" +tab_spaces = 4 diff --git a/Android.bp b/Android.bp new file mode 100644 index 0000000..de06908 --- /dev/null +++ b/Android.bp @@ -0,0 +1,18 @@ +// This file is generated by cargo2android.py. + +rust_library_host_rlib { + name: "libpin_project", + crate_name: "pin_project", + srcs: ["src/lib.rs"], + edition: "2018", + proc_macros: [ + "libpin_project_internal", + ], +} + +// dependent_library ["feature_list"] +// pin-project-internal-0.4.16 +// proc-macro2-1.0.12 "default,proc-macro" +// quote-1.0.4 "default,proc-macro" +// syn-1.0.19 "clone-impls,default,derive,full,parsing,printing,proc-macro,quote,visit-mut" +// unicode-xid-0.2.0 "default" diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..28f705c --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,461 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +This project adheres to [Semantic Versioning](https://semver.org). + +## [Unreleased] + +## [0.4.16] - 2020-05-11 + +* [Fixed an issue that users can call internal function generated by `#[pinned_drop]`.][223] + +[223]: https://github.com/taiki-e/pin-project/pull/223 + +## [0.4.15] - 2020-05-10 + +* [`#[project]` attribute can now handle all project* attributes in that scope with one wrapper attribute.][220] + +[220]: https://github.com/taiki-e/pin-project/pull/220 + +## [0.4.14] - 2020-05-09 + +* [Added `!Unpin` option to `#[pin_project]` attribute for guarantee the type is `!Unpin`.][219] + + ```rust + use pin_project::pin_project; + #[pin_project(!Unpin)] + struct Struct { + field: T, + } + ``` + + This is equivalent to use `#[pin]` attribute for `PhantomPinned` field. + + ```rust + use pin_project::pin_project; + use std::marker::PhantomPinned; + #[pin_project] + struct Struct { + field: T, + #[pin] // Note that using `PhantomPinned` without `#[pin]` attribute has no effect. + _pin: PhantomPinned, + } + ``` + + *[Note: This raises the minimum supported Rust version of this crate from rustc 1.33 to rustc 1.34.](https://github.com/taiki-e/pin-project/pull/219#pullrequestreview-408644187)* + +* [Fixed an issue where duplicate `#[project]` attributes were ignored.][218] + +* [Fixed compile error and warnings with HRTB.][217] + +* [Hide generated items from --document-private-items.][211] See [#211][211] for more details. + +* Improve documentation + +[211]: https://github.com/taiki-e/pin-project/pull/211 +[217]: https://github.com/taiki-e/pin-project/pull/217 +[218]: https://github.com/taiki-e/pin-project/pull/218 +[219]: https://github.com/taiki-e/pin-project/pull/219 + +## [0.4.13] - 2020-05-07 + +* [Fixed a regression in 0.4.11.][207] + + Changes from [0.4.10](https://github.com/taiki-e/pin-project/releases/tag/v0.4.10) and [0.4.12](https://github.com/taiki-e/pin-project/releases/tag/v0.4.12): + + * [Fixed an issue that `#[project]` on non-statement expression does not work without unstable features.][197] + + * [Support overwriting the name of core crate.][199] + + * Improve documentation + +[207]: https://github.com/taiki-e/pin-project/pull/207 + +## [0.4.12] - 2020-05-07 + +* A release to avoid [a regression in 0.4.11][206]. No code changes from [0.4.10](https://github.com/taiki-e/pin-project/releases/tag/v0.4.10). + +[206]: https://github.com/taiki-e/pin-project/issues/206 + +## [0.4.11] - 2020-05-07 - YANKED + +* [Fixed an issue that `#[project]` on non-statement expression does not work without unstable features.][197] + +* [Support overwriting the name of core crate.][199] + +* Improve documentation + +[197]: https://github.com/taiki-e/pin-project/pull/197 +[199]: https://github.com/taiki-e/pin-project/pull/199 + +## [0.4.10] - 2020-05-04 + +* [Added `project_replace` method and `#[project_replace]` attribute.][194] + `project_replace` method is optional and can be enabled by passing the `Replace` argument to `#[pin_project]` attribute. + See [the documentation](https://docs.rs/pin-project/0.4/pin_project/attr.pin_project.html#project_replace) for more details. + +[194]: https://github.com/taiki-e/pin-project/pull/194 + +## [0.4.9] - 2020-04-14 + +* [Fixed lifetime inference error when associated types are used in fields.][188] + +* [Fixed compile error with tuple structs with `where` clauses.][186] + +* [`#[project]` attribute can now be used for `if let` expressions.][181] + +[181]: https://github.com/taiki-e/pin-project/pull/181 +[186]: https://github.com/taiki-e/pin-project/pull/186 +[188]: https://github.com/taiki-e/pin-project/pull/188 + +## [0.4.8] - 2020-01-27 + +* [Ensured that users cannot implement `PinnedDrop` without proper attribute argument.][180] + +[180]: https://github.com/taiki-e/pin-project/pull/180 + +## [0.4.7] - 2020-01-20 + +* [Fixed support for lifetime bounds.][176] + +[176]: https://github.com/taiki-e/pin-project/pull/176 + +## [0.4.6] - 2019-11-20 + +* [Fixed compile error when there is `Self` in the where clause.][169] + +[169]: https://github.com/taiki-e/pin-project/pull/169 + +## [0.4.5] - 2019-10-21 + +* [Fixed compile error with `dyn` types.][158] + +[158]: https://github.com/taiki-e/pin-project/pull/158 + +## [0.4.4] - 2019-10-17 + +* [Fixed an issue where `PinnedDrop` implementations can call unsafe code without an unsafe block.][149] + +[149]: https://github.com/taiki-e/pin-project/pull/149 + +## [0.4.3] - 2019-10-15 - YANKED + +* [`#[pin_project]` can now interoperate with `#[cfg_attr()]`.][135] + +* [`#[pin_project]` can now interoperate with `#[cfg()]` on tuple structs and tuple variants.][135] + +* [Fixed support for DSTs(Dynamically Sized Types) on `#[pin_project(UnsafeUnpin)]`][120] + +[120]: https://github.com/taiki-e/pin-project/pull/120 +[135]: https://github.com/taiki-e/pin-project/pull/135 + +## [0.4.2] - 2019-09-29 - YANKED + +* [Fixed support for DSTs(Dynamically Sized Types).][113] + +[113]: https://github.com/taiki-e/pin-project/pull/113 + +## [0.4.1] - 2019-09-26 - YANKED + +* [Fixed an issue that caused an error when using `#[pin_project]` on a type that has `#[pin]` + `!Unpin` field with no generics or lifetime.][111] + +[111]: https://github.com/taiki-e/pin-project/pull/111 + +## [0.4.0] - 2019-09-25 - YANKED + +* [**Pin projection has become a safe operation.**][18] In the absence of other unsafe code that you write, it is impossible to cause undefined behavior. + +* `#[unsafe_project]` attribute has been replaced with `#[pin_project]` attribute. ([#18][18], [#33][33]) + +* [The `Unpin` argument has been removed - an `Unpin` impl is now generated by default.][18] + +* Drop impls must be specified with `#[pinned_drop]` instead of via a normal `Drop` impl. ([#18][18], [#33][33], [#86][86]) + +* [`Unpin` impls must be specified with an impl of `UnsafeUnpin`, instead of implementing the normal `Unpin` trait.][18] + +* [`#[pin_project]` attribute now determines the visibility of the projection type/method is based on the original type.][96] + +* [`#[pin_project]` can now be used for public type with private field types.][53] + +* [`#[pin_project]` can now interoperate with `#[cfg()]`.][77] + +* [Added `project_ref` method to `#[pin_project]` types.][93] + +* [Added `#[project_ref]` attribute.][93] + +* [Removed "project_attr" feature and always enable `#[project]` attribute.][94] + +* [`#[project]` attribute can now be used for `impl` blocks.][46] + +* [`#[project]` attribute can now be used for `use` statements.][85] + +* [`#[project]` attribute now supports `match` expressions at the position of the initializer expression of `let` expressions.][51] + +Changes since the 0.4.0-beta.1 release: + +* [Fixed an issue that caused an error when using `#[pin_project(UnsafeUnpin)]` and not providing a manual `UnsafeUnpin` implementation on a type with no generics or lifetime.][107] + +[18]: https://github.com/taiki-e/pin-project/pull/18 +[33]: https://github.com/taiki-e/pin-project/pull/107 +[107]: https://github.com/taiki-e/pin-project/pull/107 + +## [0.4.0-beta.1] - 2019-09-21 + +* [Changed the argument type of project method back to `self: Pin<&mut Self>`.][90] + +* [Removed "project_attr" feature and always enable `#[project]` attribute.][94] + +* [Removed "renamed" feature.][100] + +* [`#[project]` attribute can now be used for `use` statements.][85] + +* [Added `project_ref` method and `#[project_ref]` attribute.][93] + +* [`#[pin_project]` attribute now determines the visibility of the projection type/method is based on the original type.][96] + +[85]: https://github.com/taiki-e/pin-project/pull/85 +[90]: https://github.com/taiki-e/pin-project/pull/90 +[93]: https://github.com/taiki-e/pin-project/pull/93 +[94]: https://github.com/taiki-e/pin-project/pull/94 +[96]: https://github.com/taiki-e/pin-project/pull/96 +[100]: https://github.com/taiki-e/pin-project/pull/100 + +## [0.4.0-alpha.11] - 2019-09-11 + +* [Changed #[pinned_drop] to trait implementation.][86] + + ```rust + #[pinned_drop] + impl PinnedDrop for Foo<'_, T> { + fn drop(mut self: Pin<&mut Self>) { + **self.project().was_dropped = true; + } + } + ``` + +* Added some examples and generated code. + +* Improve error messages. + +[86]: https://github.com/taiki-e/pin-project/pull/86 + +## [0.4.0-alpha.10] - 2019-09-07 + +* [`#[pin_project]` can now interoperate with `#[cfg()]`.][77] + +* Improved documentation. + +[77]: https://github.com/taiki-e/pin-project/pull/77 + +## [0.4.0-alpha.9] - 2019-09-05 + +* [Added 'project_into' method to #[pin_project] types][69]. This can be useful when returning a pin projection from a method. + ```rust + fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut T> { + self.project_into().pinned + } + ``` + +* [Prevented UnpinStruct from appearing in the document by default.][71] See [#71][71] for more details. + +[69]: https://github.com/taiki-e/pin-project/pull/69 +[71]: https://github.com/taiki-e/pin-project/pull/69 + +## [0.4.0-alpha.8] - 2019-09-03 + +* [Improved document of generated code.][62]. Also added an option to control the document of generated code. See [#62][62] for more details. + +* [Improved error messages][61] + +[61]: https://github.com/taiki-e/pin-project/pull/61 +[62]: https://github.com/taiki-e/pin-project/pull/62 + +## [0.4.0-alpha.7] - 2019-09-02 + +* [Applied `#[allow(dead_code)]` to generated types.][57] + +[57]: https://github.com/taiki-e/pin-project/pull/57 + +## [0.4.0-alpha.6] - 2019-09-01 + +* [Allowed using `#[pin_project]` type with private field types][53] + +[53]: https://github.com/taiki-e/pin-project/pull/53 + +## [0.4.0-alpha.5] - 2019-08-24 + +* [`#[project]` attribute now supports `match` expressions at the position of the initializer expression of `let` expressions.][51] + +[51]: https://github.com/taiki-e/pin-project/pull/51 + +## [0.4.0-alpha.4] - 2019-08-23 + +* Avoided clippy::drop_bounds lint in generated code. + +## [0.4.0-alpha.3] - 2019-08-23 + +* [Changed `project` method generated by `#[pin_project]` attribute to take an `&mut Pin<&mut Self>` argument.][47] + +* [`#[project]` attribute can now be used for impl blocks.][46] + +* [`#[pin_project]` attribute can now detect that the type used does not have its own drop implementation without actually implementing drop.][48] This removed some restrictions. + +[46]: https://github.com/taiki-e/pin-project/pull/46 +[47]: https://github.com/taiki-e/pin-project/pull/47 +[48]: https://github.com/taiki-e/pin-project/pull/48 + +## [0.4.0-alpha.2] - 2019-08-13 + +* Updated `proc-macro2`, `syn`, and `quote` to 1.0. + +## [0.4.0-alpha.1] - 2019-08-11 + +* **Pin projection has become a safe operation.** + +* `#[unsafe_project]` has been replaced with `#[pin_project]`. + +* The `Unpin` argument has been removed - an `Unpin` impl is now generated by default. + +* Drop impls must be specified with `#[pinned_drop]` instead of via a normal `Drop` impl. + +* `Unpin` impls must be specified with an impl of `UnsafeUnpin`, instead of implementing the normal `Unpin` trait. + +* Made `#[project]` attribute disabled by default. + +See also [tracking issue for 0.4 release][21]. + +[21]: https://github.com/taiki-e/pin-project/issues/21 + +## [0.3.5] - 2019-08-14 + +* Updated `proc-macro2`, `syn`, and `quote` to 1.0. + +## [0.3.4] - 2019-07-21 + +* Improved error messages. + +## [0.3.3] - 2019-07-15 - YANKED + +* Improved error messages. + +## [0.3.2] - 2019-03-30 + +* Avoided suffixes on tuple index. + +## [0.3.1] - 2019-03-02 + +* Improved documentation. + +* Updated minimum `syn` version to 0.15.22. + +## [0.3.0] - 2019-02-20 + +* Removed `unsafe_fields` attribute. + +* Removed `unsafe_variants` attribute. + +## [0.2.2] - 2019-02-20 + +* Fixed a bug that generates incorrect code for the some structures with trait bounds on type generics. + +## [0.2.1] - 2019-02-20 + +* Fixed a bug that generates incorrect code for the structures with where clause and associated type fields. + +## [0.2.0] - 2019-02-11 + +* Made `unsafe_fields` optional. + +* Improved documentation. + +## [0.1.8] - 2019-02-02 + +* Added the feature to create projected enums to `unsafe_project`. + +* Added `project` attribute to support pattern matching. + +## [0.1.7] - 2019-01-19 + +* Fixed documentation. + +## [0.1.6] - 2019-01-19 + +* `unsafe_fields` can now opt-out. + +* Added `unsafe_variants` attribute. This attribute is available if pin-project is built with the "unsafe_variants" feature. + +## [0.1.5] - 2019-01-17 + +* Added support for tuple struct to `unsafe_project`. + +## [0.1.4] - 2019-01-12 + +* Added options for automatically implementing `Unpin` to both `unsafe_project` and `unsafe_fields`. + +## [0.1.3] - 2019-01-11 + +* Fixed dependencies. + +* Added `unsafe_fields` attribute. + +## [0.1.2] - 2019-01-09 + +* Improved documentation. + +## [0.1.1] - 2019-01-08 + +* Renamed from `unsafe_pin_project` to `unsafe_project`. + +## [0.1.0] - 2019-01-08 - YANKED + +Initial release + +[Unreleased]: https://github.com/taiki-e/pin-project/compare/v0.4.16...HEAD +[0.4.16]: https://github.com/taiki-e/pin-project/compare/v0.4.15...v0.4.16 +[0.4.15]: https://github.com/taiki-e/pin-project/compare/v0.4.14...v0.4.15 +[0.4.14]: https://github.com/taiki-e/pin-project/compare/v0.4.13...v0.4.14 +[0.4.13]: https://github.com/taiki-e/pin-project/compare/v0.4.11...v0.4.13 +[0.4.12]: https://github.com/taiki-e/pin-project/compare/v0.4.10...v0.4.12 +[0.4.11]: https://github.com/taiki-e/pin-project/compare/v0.4.10...v0.4.11 +[0.4.10]: https://github.com/taiki-e/pin-project/compare/v0.4.9...v0.4.10 +[0.4.9]: https://github.com/taiki-e/pin-project/compare/v0.4.8...v0.4.9 +[0.4.8]: https://github.com/taiki-e/pin-project/compare/v0.4.7...v0.4.8 +[0.4.7]: https://github.com/taiki-e/pin-project/compare/v0.4.6...v0.4.7 +[0.4.6]: https://github.com/taiki-e/pin-project/compare/v0.4.5...v0.4.6 +[0.4.5]: https://github.com/taiki-e/pin-project/compare/v0.4.4...v0.4.5 +[0.4.4]: https://github.com/taiki-e/pin-project/compare/v0.4.3...v0.4.4 +[0.4.3]: https://github.com/taiki-e/pin-project/compare/v0.4.2...v0.4.3 +[0.4.2]: https://github.com/taiki-e/pin-project/compare/v0.4.1...v0.4.2 +[0.4.1]: https://github.com/taiki-e/pin-project/compare/v0.4.0...v0.4.1 +[0.4.0]: https://github.com/taiki-e/pin-project/compare/v0.4.0-beta.1...v0.4.0 +[0.4.0-beta.1]: https://github.com/taiki-e/pin-project/compare/v0.4.0-alpha.11...v0.4.0-beta.1 +[0.4.0-alpha.11]: https://github.com/taiki-e/pin-project/compare/v0.4.0-alpha.10...v0.4.0-alpha.11 +[0.4.0-alpha.10]: https://github.com/taiki-e/pin-project/compare/v0.4.0-alpha.9...v0.4.0-alpha.10 +[0.4.0-alpha.9]: https://github.com/taiki-e/pin-project/compare/v0.4.0-alpha.8...v0.4.0-alpha.9 +[0.4.0-alpha.8]: https://github.com/taiki-e/pin-project/compare/v0.4.0-alpha.7...v0.4.0-alpha.8 +[0.4.0-alpha.7]: https://github.com/taiki-e/pin-project/compare/v0.4.0-alpha.6...v0.4.0-alpha.7 +[0.4.0-alpha.6]: https://github.com/taiki-e/pin-project/compare/v0.4.0-alpha.5...v0.4.0-alpha.6 +[0.4.0-alpha.5]: https://github.com/taiki-e/pin-project/compare/v0.4.0-alpha.4...v0.4.0-alpha.5 +[0.4.0-alpha.4]: https://github.com/taiki-e/pin-project/compare/v0.4.0-alpha.3...v0.4.0-alpha.4 +[0.4.0-alpha.3]: https://github.com/taiki-e/pin-project/compare/v0.4.0-alpha.2...v0.4.0-alpha.3 +[0.4.0-alpha.2]: https://github.com/taiki-e/pin-project/compare/v0.4.0-alpha.1...v0.4.0-alpha.2 +[0.4.0-alpha.1]: https://github.com/taiki-e/pin-project/compare/v0.3.5...v0.4.0-alpha.1 +[0.3.5]: https://github.com/taiki-e/pin-project/compare/v0.3.4...v0.3.5 +[0.3.4]: https://github.com/taiki-e/pin-project/compare/v0.3.3...v0.3.4 +[0.3.3]: https://github.com/taiki-e/pin-project/compare/v0.3.2...v0.3.3 +[0.3.2]: https://github.com/taiki-e/pin-project/compare/v0.3.1...v0.3.2 +[0.3.1]: https://github.com/taiki-e/pin-project/compare/v0.3.0...v0.3.1 +[0.3.0]: https://github.com/taiki-e/pin-project/compare/v0.2.2...v0.3.0 +[0.2.2]: https://github.com/taiki-e/pin-project/compare/v0.2.1...v0.2.2 +[0.2.1]: https://github.com/taiki-e/pin-project/compare/v0.2.0...v0.2.1 +[0.2.0]: https://github.com/taiki-e/pin-project/compare/v0.1.8...v0.2.0 +[0.1.8]: https://github.com/taiki-e/pin-project/compare/v0.1.7...v0.1.8 +[0.1.7]: https://github.com/taiki-e/pin-project/compare/v0.1.6...v0.1.7 +[0.1.6]: https://github.com/taiki-e/pin-project/compare/v0.1.5...v0.1.6 +[0.1.5]: https://github.com/taiki-e/pin-project/compare/v0.1.4...v0.1.5 +[0.1.4]: https://github.com/taiki-e/pin-project/compare/v0.1.3...v0.1.4 +[0.1.3]: https://github.com/taiki-e/pin-project/compare/v0.1.2...v0.1.3 +[0.1.2]: https://github.com/taiki-e/pin-project/compare/v0.1.1...v0.1.2 +[0.1.1]: https://github.com/taiki-e/pin-project/compare/v0.1.0...v0.1.1 +[0.1.0]: https://github.com/taiki-e/pin-project/releases/tag/v0.1.0 diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..39280ab --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,30 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies +# +# If you believe there's an error in this file please file an +# issue against the rust-lang/cargo repository. If you're +# editing this file be aware that the upstream Cargo.toml +# will likely look very different (and much more reasonable) + +[package] +edition = "2018" +name = "pin-project" +version = "0.4.16" +authors = ["Taiki Endo "] +description = "A crate for safe and ergonomic pin-projection.\n" +homepage = "https://github.com/taiki-e/pin-project" +documentation = "https://docs.rs/pin-project" +readme = "README.md" +keywords = ["pin", "macros", "attribute"] +categories = ["no-std", "rust-patterns"] +license = "Apache-2.0 OR MIT" +repository = "https://github.com/taiki-e/pin-project" +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] +[dependencies.pin-project-internal] +version = "=0.4.16" +default-features = false diff --git a/Cargo.toml.orig b/Cargo.toml.orig new file mode 100644 index 0000000..c886996 --- /dev/null +++ b/Cargo.toml.orig @@ -0,0 +1,31 @@ +[package] +name = "pin-project" +version = "0.4.16" +authors = ["Taiki Endo "] +edition = "2018" +license = "Apache-2.0 OR MIT" +repository = "https://github.com/taiki-e/pin-project" +homepage = "https://github.com/taiki-e/pin-project" +documentation = "https://docs.rs/pin-project" +keywords = ["pin", "macros", "attribute"] +categories = ["no-std", "rust-patterns"] +readme = "README.md" +description = """ +A crate for safe and ergonomic pin-projection. +""" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[workspace] +members = [ + "pin-project-internal", + "tests/ui/auxiliary", + "tests/doc", + "tests/expand", + "tests/rust-2015" +] + +[dependencies] +pin-project-internal = { version = "=0.4.16", path = "pin-project-internal", default-features = false } + diff --git a/LICENSE b/LICENSE new file mode 120000 index 0000000..6b579aa --- /dev/null +++ b/LICENSE @@ -0,0 +1 @@ +LICENSE-APACHE \ No newline at end of file diff --git a/LICENSE-APACHE b/LICENSE-APACHE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/LICENSE-APACHE @@ -0,0 +1,202 @@ + + 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. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/LICENSE-MIT b/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/METADATA b/METADATA new file mode 100644 index 0000000..7c1feb9 --- /dev/null +++ b/METADATA @@ -0,0 +1,19 @@ +name: "pin-project" +description: "A crate for safe and ergonomic pin-projection." +third_party { + url { + type: HOMEPAGE + value: "https://crates.io/crates/pin-project" + } + url { + type: GIT + value: "https://github.com/taiki-e/pin-project" + } + version: "0.4.16" + license_type: NOTICE + last_upgrade_date { + year: 2020 + month: 5 + day: 11 + } +} diff --git a/MODULE_LICENSE_APACHE2 b/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 diff --git a/NOTICE b/NOTICE new file mode 120000 index 0000000..7a694c9 --- /dev/null +++ b/NOTICE @@ -0,0 +1 @@ +LICENSE \ No newline at end of file diff --git a/OWNERS b/OWNERS new file mode 100644 index 0000000..46fc303 --- /dev/null +++ b/OWNERS @@ -0,0 +1 @@ +include platform/prebuilts/rust:/OWNERS diff --git a/README.md b/README.md new file mode 100644 index 0000000..840d1fa --- /dev/null +++ b/README.md @@ -0,0 +1,77 @@ +# pin-project + +[![crates-badge]][crates-url] +[![docs-badge]][docs-url] +[![license-badge]][license] +[![rustc-badge]][rustc-url] + +[crates-badge]: https://img.shields.io/crates/v/pin-project.svg +[crates-url]: https://crates.io/crates/pin-project +[docs-badge]: https://docs.rs/pin-project/badge.svg +[docs-url]: https://docs.rs/pin-project +[license-badge]: https://img.shields.io/crates/l/pin-project.svg +[license]: #license +[rustc-badge]: https://img.shields.io/badge/rustc-1.34+-lightgray.svg +[rustc-url]: https://blog.rust-lang.org/2019/04/11/Rust-1.34.0.html + +A crate for safe and ergonomic pin-projection. + +[Documentation][docs-url] + +[Examples](examples/README.md) + +## Usage + +Add this to your `Cargo.toml`: + +```toml +[dependencies] +pin-project = "0.4" +``` + +The current pin-project requires Rust 1.34 or later. + +## Examples + +[`pin_project`] attribute creates a projection struct covering all the fields. + +```rust +use pin_project::pin_project; +use std::pin::Pin; + +#[pin_project] +struct Struct { + #[pin] + pinned: T, + unpinned: U, +} + +impl Struct { + fn foo(self: Pin<&mut Self>) { + let this = self.project(); + let _: Pin<&mut T> = this.pinned; // Pinned reference to the field + let _: &mut U = this.unpinned; // Normal reference to the field + } +} +``` + +[Code like this will be generated](examples/struct-default-expanded.rs) + +See [API documentation][docs-url] for more details. + +Also, there are examples and generated code of each feature in [examples](examples/README.md) directory. + +[`pin_project`]: https://docs.rs/pin-project/0.4/pin_project/attr.pin_project.html + +## License + +Licensed under either of + +* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or ) +* MIT license ([LICENSE-MIT](LICENSE-MIT) or ) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. diff --git a/ci.sh b/ci.sh new file mode 100644 index 0000000..df0131e --- /dev/null +++ b/ci.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +# A script to run a simplified version of the checks done by CI. +# +# Usage +# +# ```sh +# . ./ci.sh +# ``` + +echo "Running 'cargo fmt -- --check'" +cargo +nightly fmt --all -- --check + +echo "Running 'cargo clippy'" +cargo +nightly clippy --all --all-features --all-targets + +echo "Running 'cargo test'" +cargo +nightly test --all --all-features + +echo "Running 'cargo doc'" +cargo +nightly doc --no-deps --all --all-features + +echo "Running 'compiletest'" +. ./compiletest.sh + +echo "Running 'expandtest'" +# See also https://docs.rs/macrotest/1/macrotest/#updating-expandedrs +# rm **/*.expanded.rs +cargo +nightly test --manifest-path tests/expand/Cargo.toml diff --git a/ci/install-component.sh b/ci/install-component.sh new file mode 100644 index 0000000..de08b8f --- /dev/null +++ b/ci/install-component.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +set -euo pipefail + +component="${1}" + +if ! rustup component add "${component}" 2>/dev/null; then + # If the component is unavailable on the latest nightly, + # use the latest toolchain with the component available. + # Refs: https://github.com/rust-lang/rustup-components-history#the-web-part + target=$(curl -sSf "https://rust-lang.github.io/rustup-components-history/x86_64-unknown-linux-gnu/${component}") + echo "'${component}' is unavailable on the default toolchain, use the toolchain 'nightly-${target}' instead" + + . ci/install-rust.sh "nightly-${target}" + + rustup component add "${component}" +fi + +case "${component}" in + rustfmt | rustdoc) "${component}" -V ;; + *) cargo "${component}" -V ;; +esac diff --git a/ci/install-rust.sh b/ci/install-rust.sh new file mode 100644 index 0000000..b6625b6 --- /dev/null +++ b/ci/install-rust.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +set -euo pipefail + +toolchain="${1:-nightly}" + +rustup set profile minimal +rustup update "${toolchain}" --no-self-update +rustup default "${toolchain}" + +rustup -V +rustc -V +cargo -V diff --git a/compiletest.sh b/compiletest.sh new file mode 100644 index 0000000..9af5a5f --- /dev/null +++ b/compiletest.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +# A script to run compile tests with the same condition of the checks done by CI. +# +# Usage +# +# ```sh +# . ./compiletest.sh +# ``` + +TRYBUILD=overwrite cargo +nightly test -p pin-project --all-features --test compiletest -- --ignored +# cargo +nightly test -p pin-project --all-features --test compiletest -- --ignored diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..94f49b5 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,35 @@ +# Examples and generated code of each feature of pin-project + +### Basic usage of `#[pin_project]` on structs + + * [example](struct-default.rs) + * [generated code](struct-default-expanded.rs) + +### Basic usage of `#[pin_project]` on enums + + * [example](enum-default.rs) + * [generated code](enum-default-expanded.rs) + +### Manual implementation of `Unpin` by `UnsafeUnpin` + + * [example](unsafe_unpin.rs) + * [generated code](unsafe_unpin-expanded.rs) + * [`UnsafeUnpin` documentation](https://docs.rs/pin-project/0.4/pin_project/trait.UnsafeUnpin.html) + +### Manual implementation of `Drop` by `#[pinned_drop]` + + * [example](pinned_drop.rs) + * [generated code](pinned_drop-expanded.rs) + * [`#[pinned_drop]` documentation](https://docs.rs/pin-project/0.4/pin_project/attr.pinned_drop.html) + +### `project_replace()` method + + * [example](project_replace.rs) + * [generated code](project_replace-expanded.rs) + * [`project_replace()` documentation](https://docs.rs/pin-project/0.4/pin_project/attr.pin_project.html#project_replace) + +### Ensure `!Unpin` by `#[pin_project(!Unpin)]` + + * [example](not_unpin.rs) + * [generated code](not_unpin-expanded.rs) + * [`!Unpin` documentation](https://docs.rs/pin-project/0.4/pin_project/attr.pin_project.html#unpin) diff --git a/examples/enum-default-expanded.rs b/examples/enum-default-expanded.rs new file mode 100644 index 0000000..036c01f --- /dev/null +++ b/examples/enum-default-expanded.rs @@ -0,0 +1,109 @@ +// Original code (./enum-default.rs): +// +// ```rust +// #![allow(dead_code)] +// +// use pin_project::pin_project; +// +// #[pin_project] +// enum Enum { +// Pinned(#[pin] T), +// Unpinned(U), +// } +// +// fn main() {} +// ``` + +#![allow(dead_code, unused_imports, unused_parens)] +#![allow(clippy::no_effect, clippy::just_underscores_and_digits)] + +use pin_project::pin_project; + +enum Enum { + Pinned(/* #[pin] */ T), + Unpinned(U), +} + +#[doc(hidden)] +#[allow(clippy::mut_mut)] // This lint warns `&mut &mut `. +#[allow(dead_code)] // This lint warns unused fields/variants. +#[allow(single_use_lifetimes)] +enum __EnumProjection<'pin, T, U> +where + Enum: 'pin, +{ + Pinned(::pin_project::__reexport::pin::Pin<&'pin mut (T)>), + Unpinned(&'pin mut (U)), +} +#[doc(hidden)] +#[allow(dead_code)] // This lint warns unused fields/variants. +#[allow(single_use_lifetimes)] +enum __EnumProjectionRef<'pin, T, U> +where + Enum: 'pin, +{ + Pinned(::pin_project::__reexport::pin::Pin<&'pin (T)>), + Unpinned(&'pin (U)), +} + +#[doc(hidden)] +#[allow(non_upper_case_globals)] +#[allow(single_use_lifetimes)] +const __SCOPE_Enum: () = { + impl Enum { + fn project<'pin>( + self: ::pin_project::__reexport::pin::Pin<&'pin mut Self>, + ) -> __EnumProjection<'pin, T, U> { + unsafe { + match self.get_unchecked_mut() { + Enum::Pinned(_0) => __EnumProjection::Pinned( + ::pin_project::__reexport::pin::Pin::new_unchecked(_0), + ), + Enum::Unpinned(_0) => __EnumProjection::Unpinned(_0), + } + } + } + fn project_ref<'pin>( + self: ::pin_project::__reexport::pin::Pin<&'pin Self>, + ) -> __EnumProjectionRef<'pin, T, U> { + unsafe { + match self.get_ref() { + Enum::Pinned(_0) => __EnumProjectionRef::Pinned( + ::pin_project::__reexport::pin::Pin::new_unchecked(_0), + ), + Enum::Unpinned(_0) => __EnumProjectionRef::Unpinned(_0), + } + } + } + } + + // Automatically create the appropriate conditional `Unpin` implementation. + // + // See ./struct-default-expanded.rs and https://github.com/taiki-e/pin-project/pull/53. + // for details. + struct __Enum<'pin, T, U> { + __pin_project_use_generics: ::pin_project::__private::AlwaysUnpin<'pin, (T, U)>, + __field0: T, + } + impl<'pin, T, U> ::pin_project::__reexport::marker::Unpin for Enum where + __Enum<'pin, T, U>: ::pin_project::__reexport::marker::Unpin + { + } + unsafe impl ::pin_project::UnsafeUnpin for Enum {} + + // Ensure that enum does not implement `Drop`. + // + // See ./struct-default-expanded.rs for details. + trait EnumMustNotImplDrop {} + #[allow(clippy::drop_bounds)] + impl EnumMustNotImplDrop for T {} + impl EnumMustNotImplDrop for Enum {} + impl ::pin_project::__private::PinnedDrop for Enum { + unsafe fn drop(self: ::pin_project::__reexport::pin::Pin<&mut Self>) {} + } + + // We don't need to check for `#[repr(packed)]`, + // since it does not apply to enums. +}; + +fn main() {} diff --git a/examples/enum-default.rs b/examples/enum-default.rs new file mode 100644 index 0000000..ab5a4bc --- /dev/null +++ b/examples/enum-default.rs @@ -0,0 +1,13 @@ +// See ./enum-default-expanded.rs for generated code. + +#![allow(dead_code)] + +use pin_project::pin_project; + +#[pin_project] +enum Enum { + Pinned(#[pin] T), + Unpinned(U), +} + +fn main() {} diff --git a/examples/not_unpin-expanded.rs b/examples/not_unpin-expanded.rs new file mode 100644 index 0000000..45f1321 --- /dev/null +++ b/examples/not_unpin-expanded.rs @@ -0,0 +1,125 @@ +// Original code (./not_unpin.rs): +// +// ```rust +// #![allow(dead_code)] +// +// use pin_project::pin_project; +// +// #[pin_project(!Unpin)] +// pub struct Struct { +// #[pin] +// pinned: T, +// unpinned: U, +// } +// +// fn main() { +// fn _is_unpin() {} +// // _is_unpin::>(); //~ ERROR `std::marker::PhantomPinned` cannot be unpinned +// } +// ``` + +#![allow(dead_code, unused_imports, unused_parens)] +#![allow(clippy::no_effect)] + +use pin_project::pin_project; + +pub struct Struct { + // #[pin] + pinned: T, + unpinned: U, +} + +#[doc(hidden)] +#[allow(clippy::mut_mut)] // This lint warns `&mut &mut `. +#[allow(dead_code)] // This lint warns unused fields/variants. +#[allow(single_use_lifetimes)] +pub(crate) struct __StructProjection<'pin, T, U> +where + Struct: 'pin, +{ + pinned: ::pin_project::__reexport::pin::Pin<&'pin mut (T)>, + unpinned: &'pin mut (U), +} +#[doc(hidden)] +#[allow(dead_code)] // This lint warns unused fields/variants. +#[allow(single_use_lifetimes)] +pub(crate) struct __StructProjectionRef<'pin, T, U> +where + Struct: 'pin, +{ + pinned: ::pin_project::__reexport::pin::Pin<&'pin (T)>, + unpinned: &'pin (U), +} + +#[doc(hidden)] +#[allow(non_upper_case_globals)] +#[allow(single_use_lifetimes)] +const __SCOPE_Struct: () = { + impl Struct { + pub(crate) fn project<'pin>( + self: ::pin_project::__reexport::pin::Pin<&'pin mut Self>, + ) -> __StructProjection<'pin, T, U> { + unsafe { + let Self { pinned, unpinned } = self.get_unchecked_mut(); + __StructProjection { + pinned: ::pin_project::__reexport::pin::Pin::new_unchecked(pinned), + unpinned, + } + } + } + pub(crate) fn project_ref<'pin>( + self: ::pin_project::__reexport::pin::Pin<&'pin Self>, + ) -> __StructProjectionRef<'pin, T, U> { + unsafe { + let Self { pinned, unpinned } = self.get_ref(); + __StructProjectionRef { + pinned: ::pin_project::__reexport::pin::Pin::new_unchecked(pinned), + unpinned, + } + } + } + } + + // Create `Unpin` impl that has trivial `Unpin` bounds. + // + // See https://github.com/taiki-e/pin-project/issues/102#issuecomment-540472282 + // for details. + impl<'pin, T, U> ::pin_project::__reexport::marker::Unpin for Struct where + ::pin_project::__private::Wrapper<'pin, ::pin_project::__reexport::marker::PhantomPinned>: + ::pin_project::__reexport::marker::Unpin + { + } + // A dummy impl of `UnsafeUnpin`, to ensure that the user cannot implement it. + // + // To ensure that users don't accidentally write a non-functional `UnsafeUnpin` + // impls, we emit one ourselves. If the user ends up writing a `UnsafeUnpin` impl, + // they'll get a "conflicting implementations of trait" error when coherence + // checks are run. + unsafe impl ::pin_project::UnsafeUnpin for Struct {} + + // Ensure that struct does not implement `Drop`. + // + // See ./struct-default-expanded.rs for details. + trait StructMustNotImplDrop {} + #[allow(clippy::drop_bounds)] + impl StructMustNotImplDrop for T {} + impl StructMustNotImplDrop for Struct {} + impl ::pin_project::__private::PinnedDrop for Struct { + unsafe fn drop(self: ::pin_project::__reexport::pin::Pin<&mut Self>) {} + } + + // Ensure that it's impossible to use pin projections on a #[repr(packed)] struct. + // + // See ./struct-default-expanded.rs and https://github.com/taiki-e/pin-project/pull/34 + // for details. + #[deny(safe_packed_borrows)] + fn __assert_not_repr_packed(val: &Struct) { + &val.pinned; + &val.unpinned; + } +}; + +fn main() { + fn _is_unpin() {} + // _is_unpin::>(); //~ ERROR `std::marker::PhantomPinned` cannot be unpinned +} diff --git a/examples/not_unpin.rs b/examples/not_unpin.rs new file mode 100644 index 0000000..2ad72a8 --- /dev/null +++ b/examples/not_unpin.rs @@ -0,0 +1,17 @@ +// See ./not_unpin-expanded.rs for generated code. + +#![allow(dead_code)] + +use pin_project::pin_project; + +#[pin_project(!Unpin)] +pub struct Struct { + #[pin] + pinned: T, + unpinned: U, +} + +fn main() { + fn _is_unpin() {} + // _is_unpin::>(); //~ ERROR `std::marker::PhantomPinned` cannot be unpinned +} diff --git a/examples/pinned_drop-expanded.rs b/examples/pinned_drop-expanded.rs new file mode 100644 index 0000000..15dde89 --- /dev/null +++ b/examples/pinned_drop-expanded.rs @@ -0,0 +1,154 @@ +// Original code (./pinned_drop.rs): +// +// ```rust +// #![allow(dead_code)] +// +// use pin_project::{pin_project, pinned_drop}; +// use std::pin::Pin; +// +// #[pin_project(PinnedDrop)] +// pub struct Struct<'a, T> { +// was_dropped: &'a mut bool, +// #[pin] +// field: T, +// } +// +// #[pinned_drop] +// fn drop_Struct(mut this: Pin<&mut Struct<'_, T>>) { +// **this.project().was_dropped = true; +// } +// +// fn main() {} +// ``` + +#![allow(dead_code, unused_imports, unused_parens)] +#![allow(clippy::no_effect)] + +use pin_project::{pin_project, pinned_drop}; +use std::pin::Pin; + +pub struct Struct<'a, T> { + was_dropped: &'a mut bool, + // #[pin] + field: T, +} + +#[doc(hidden)] +#[allow(clippy::mut_mut)] // This lint warns `&mut &mut `. +#[allow(dead_code)] // This lint warns unused fields/variants. +#[allow(single_use_lifetimes)] +pub(crate) struct __StructProjection<'pin, 'a, T> +where + Struct<'a, T>: 'pin, +{ + was_dropped: &'pin mut (&'a mut bool), + field: ::pin_project::__reexport::pin::Pin<&'pin mut (T)>, +} +#[doc(hidden)] +#[allow(dead_code)] // This lint warns unused fields/variants. +#[allow(single_use_lifetimes)] +pub(crate) struct __StructProjectionRef<'pin, 'a, T> +where + Struct<'a, T>: 'pin, +{ + was_dropped: &'pin (&'a mut bool), + field: ::pin_project::__reexport::pin::Pin<&'pin (T)>, +} + +#[doc(hidden)] +#[allow(non_upper_case_globals)] +#[allow(single_use_lifetimes)] +const __SCOPE_Struct: () = { + impl<'a, T> Struct<'a, T> { + pub(crate) fn project<'pin>( + self: ::pin_project::__reexport::pin::Pin<&'pin mut Self>, + ) -> __StructProjection<'pin, 'a, T> { + unsafe { + let Self { was_dropped, field } = self.get_unchecked_mut(); + __StructProjection { + was_dropped, + field: ::pin_project::__reexport::pin::Pin::new_unchecked(field), + } + } + } + pub(crate) fn project_ref<'pin>( + self: ::pin_project::__reexport::pin::Pin<&'pin Self>, + ) -> __StructProjectionRef<'pin, 'a, T> { + unsafe { + let Self { was_dropped, field } = self.get_ref(); + __StructProjectionRef { + was_dropped, + field: ::pin_project::__reexport::pin::Pin::new_unchecked(field), + } + } + } + } + + impl<'a, T> ::pin_project::__reexport::ops::Drop for Struct<'a, T> { + fn drop(&mut self) { + // Safety - we're in 'drop', so we know that 'self' will + // never move again. + let pinned_self = unsafe { ::pin_project::__reexport::pin::Pin::new_unchecked(self) }; + // We call `pinned_drop` only once. Since `PinnedDrop::drop` + // is an unsafe method and a private API, it is never called again in safe + // code *unless the user uses a maliciously crafted macro*. + unsafe { + ::pin_project::__private::PinnedDrop::drop(pinned_self); + } + } + } + + // Automatically create the appropriate conditional `Unpin` implementation. + // + // See ./struct-default-expanded.rs and https://github.com/taiki-e/pin-project/pull/53. + // for details. + pub struct __Struct<'pin, 'a, T> { + __pin_project_use_generics: ::pin_project::__private::AlwaysUnpin<'pin, (T)>, + __field0: T, + __lifetime0: &'a (), + } + impl<'pin, 'a, T> ::pin_project::__reexport::marker::Unpin for Struct<'a, T> where + __Struct<'pin, 'a, T>: ::pin_project::__reexport::marker::Unpin + { + } + unsafe impl<'a, T> ::pin_project::UnsafeUnpin for Struct<'a, T> {} + + // Ensure that it's impossible to use pin projections on a #[repr(packed)] struct. + // + // See ./struct-default-expanded.rs and https://github.com/taiki-e/pin-project/pull/34 + // for details. + #[deny(safe_packed_borrows)] + fn __assert_not_repr_packed<'a, T>(val: &Struct<'a, T>) { + &val.was_dropped; + &val.field; + } +}; + +// Implementing `PinnedDrop::drop` is safe, but calling it is not safe. +// This is because destructors can be called multiple times in safe code and +// [double dropping is unsound](https://github.com/rust-lang/rust/pull/62360). +// +// Ideally, it would be desirable to be able to forbid manual calls in +// the same way as `Drop::drop`, but the library cannot do it. So, by using +// macros and replacing them with private traits, we prevent users from +// calling `PinnedDrop::drop`. +// +// Users can implement [`Drop`] safely using `#[pinned_drop]` and can drop a +// type that implements `PinnedDrop` using the [`drop`] function safely. +// **Do not call or implement this trait directly.** +impl ::pin_project::__private::PinnedDrop for Struct<'_, T> { + // Since calling it twice on the same object would be UB, + // this method is unsafe. + unsafe fn drop(self: Pin<&mut Self>) { + #[allow(clippy::needless_pass_by_value)] + fn __drop_inner(__self: Pin<&mut Struct<'_, T>>) { + // A dummy `__drop_inner` function to prevent users call outer `__drop_inner`. + fn __drop_inner() {} + + **__self.project().was_dropped = true; + } + __drop_inner(self); + } +} + +fn main() {} diff --git a/examples/pinned_drop.rs b/examples/pinned_drop.rs new file mode 100644 index 0000000..4dfd228 --- /dev/null +++ b/examples/pinned_drop.rs @@ -0,0 +1,22 @@ +// See ./pinned_drop-expanded.rs for generated code. + +#![allow(dead_code)] + +use pin_project::{pin_project, pinned_drop}; +use std::pin::Pin; + +#[pin_project(PinnedDrop)] +pub struct Struct<'a, T> { + was_dropped: &'a mut bool, + #[pin] + field: T, +} + +#[pinned_drop] +impl PinnedDrop for Struct<'_, T> { + fn drop(self: Pin<&mut Self>) { + **self.project().was_dropped = true; + } +} + +fn main() {} diff --git a/examples/project_replace-expanded.rs b/examples/project_replace-expanded.rs new file mode 100644 index 0000000..ec9f00e --- /dev/null +++ b/examples/project_replace-expanded.rs @@ -0,0 +1,158 @@ +// Original code (./struct-default.rs): +// +// ```rust +// #![allow(dead_code)] +// +// use pin_project::pin_project; +// +// #[pin_project(Replace)] +// struct Struct { +// #[pin] +// pinned: T, +// unpinned: U, +// } +// +// fn main() {} +// ``` + +#![allow(dead_code, unused_imports, unused_parens)] +#![allow(clippy::no_effect)] + +use pin_project::pin_project; + +struct Struct { + // #[pin] + pinned: T, + unpinned: U, +} + +#[doc(hidden)] +#[allow(clippy::mut_mut)] // This lint warns `&mut &mut `. +#[allow(dead_code)] // This lint warns unused fields/variants. +#[allow(single_use_lifetimes)] +struct __StructProjection<'pin, T, U> +where + Struct: 'pin, +{ + pinned: ::pin_project::__reexport::pin::Pin<&'pin mut (T)>, + unpinned: &'pin mut (U), +} +#[doc(hidden)] +#[allow(dead_code)] // This lint warns unused fields/variants. +#[allow(single_use_lifetimes)] +struct __StructProjectionRef<'pin, T, U> +where + Struct: 'pin, +{ + pinned: ::pin_project::__reexport::pin::Pin<&'pin (T)>, + unpinned: &'pin (U), +} + +#[doc(hidden)] +#[allow(dead_code)] // This lint warns unused fields/variants. +#[allow(single_use_lifetimes)] +struct __StructProjectionOwned { + pinned: ::pin_project::__reexport::marker::PhantomData, + unpinned: U, +} + +#[doc(hidden)] +#[allow(non_upper_case_globals)] +#[allow(single_use_lifetimes)] +const __SCOPE_Struct: () = { + impl Struct { + fn project<'pin>( + self: ::pin_project::__reexport::pin::Pin<&'pin mut Self>, + ) -> __StructProjection<'pin, T, U> { + unsafe { + let Self { pinned, unpinned } = self.get_unchecked_mut(); + __StructProjection { + pinned: ::pin_project::__reexport::pin::Pin::new_unchecked(pinned), + unpinned, + } + } + } + fn project_ref<'pin>( + self: ::pin_project::__reexport::pin::Pin<&'pin Self>, + ) -> __StructProjectionRef<'pin, T, U> { + unsafe { + let Self { pinned, unpinned } = self.get_ref(); + __StructProjectionRef { + pinned: ::pin_project::__reexport::pin::Pin::new_unchecked(pinned), + unpinned, + } + } + } + fn project_replace( + self: ::pin_project::__reexport::pin::Pin<&mut Self>, + __replacement: Self, + ) -> __StructProjectionOwned { + unsafe { + let __self_ptr: *mut Self = self.get_unchecked_mut(); + let Self { pinned, unpinned } = &mut *__self_ptr; + + // First, extract all the unpinned fields + let __result = __StructProjectionOwned { + pinned: ::pin_project::__reexport::marker::PhantomData, + unpinned: ::pin_project::__reexport::ptr::read(unpinned), + }; + + // Destructors will run in reverse order, so next create a guard to overwrite + // `self` with the replacement value without calling destructors. + let __guard = ::pin_project::__private::UnsafeOverwriteGuard { + target: __self_ptr, + value: ::pin_project::__reexport::mem::ManuallyDrop::new(__replacement), + }; + + // Now create guards to drop all the pinned fields + // + // Due to a compiler bug (https://github.com/rust-lang/rust/issues/47949) + // this must be in its own scope, or else `__result` will not be dropped + // if any of the destructors panic. + { + let __guard = ::pin_project::__private::UnsafeDropInPlaceGuard(pinned); + } + + // Finally, return the result + __result + } + } + } + + // Automatically create the appropriate conditional `Unpin` implementation. + // + // See ./struct-default-expanded.rs and https://github.com/taiki-e/pin-project/pull/53. + // for details. + struct __Struct<'pin, T, U> { + __pin_project_use_generics: ::pin_project::__private::AlwaysUnpin<'pin, (T, U)>, + __field0: T, + } + impl<'pin, T, U> ::pin_project::__reexport::marker::Unpin for Struct where + __Struct<'pin, T, U>: ::pin_project::__reexport::marker::Unpin + { + } + unsafe impl ::pin_project::UnsafeUnpin for Struct {} + + // Ensure that struct does not implement `Drop`. + // + // See ./struct-default-expanded.rs for details. + trait StructMustNotImplDrop {} + #[allow(clippy::drop_bounds)] + impl StructMustNotImplDrop for T {} + impl StructMustNotImplDrop for Struct {} + impl ::pin_project::__private::PinnedDrop for Struct { + unsafe fn drop(self: ::pin_project::__reexport::pin::Pin<&mut Self>) {} + } + + // Ensure that it's impossible to use pin projections on a #[repr(packed)] struct. + // + // See ./struct-default-expanded.rs and https://github.com/taiki-e/pin-project/pull/34 + // for details. + #[deny(safe_packed_borrows)] + fn __assert_not_repr_packed(val: &Struct) { + &val.pinned; + &val.unpinned; + } +}; + +fn main() {} diff --git a/examples/project_replace.rs b/examples/project_replace.rs new file mode 100644 index 0000000..4482625 --- /dev/null +++ b/examples/project_replace.rs @@ -0,0 +1,14 @@ +// See ./struct-default-expanded.rs for generated code. + +#![allow(dead_code)] + +use pin_project::pin_project; + +#[pin_project(Replace)] +struct Struct { + #[pin] + pinned: T, + unpinned: U, +} + +fn main() {} diff --git a/examples/struct-default-expanded.rs b/examples/struct-default-expanded.rs new file mode 100644 index 0000000..2bf7edd --- /dev/null +++ b/examples/struct-default-expanded.rs @@ -0,0 +1,157 @@ +// Original code (./struct-default.rs): +// +// ```rust +// #![allow(dead_code)] +// +// use pin_project::pin_project; +// +// #[pin_project] +// struct Struct { +// #[pin] +// pinned: T, +// unpinned: U, +// } +// +// fn main() {} +// ``` + +#![allow(dead_code, unused_imports, unused_parens)] +#![allow(clippy::no_effect)] + +use pin_project::pin_project; + +struct Struct { + // #[pin] + pinned: T, + unpinned: U, +} + +#[doc(hidden)] +#[allow(clippy::mut_mut)] // This lint warns `&mut &mut `. +#[allow(dead_code)] // This lint warns unused fields/variants. +#[allow(single_use_lifetimes)] +struct __StructProjection<'pin, T, U> +where + Struct: 'pin, +{ + pinned: ::pin_project::__reexport::pin::Pin<&'pin mut (T)>, + unpinned: &'pin mut (U), +} +#[doc(hidden)] +#[allow(dead_code)] // This lint warns unused fields/variants. +#[allow(single_use_lifetimes)] +struct __StructProjectionRef<'pin, T, U> +where + Struct: 'pin, +{ + pinned: ::pin_project::__reexport::pin::Pin<&'pin (T)>, + unpinned: &'pin (U), +} + +#[doc(hidden)] +#[allow(non_upper_case_globals)] +#[allow(single_use_lifetimes)] +const __SCOPE_Struct: () = { + impl Struct { + fn project<'pin>( + self: ::pin_project::__reexport::pin::Pin<&'pin mut Self>, + ) -> __StructProjection<'pin, T, U> { + unsafe { + let Self { pinned, unpinned } = self.get_unchecked_mut(); + __StructProjection { + pinned: ::pin_project::__reexport::pin::Pin::new_unchecked(pinned), + unpinned, + } + } + } + fn project_ref<'pin>( + self: ::pin_project::__reexport::pin::Pin<&'pin Self>, + ) -> __StructProjectionRef<'pin, T, U> { + unsafe { + let Self { pinned, unpinned } = self.get_ref(); + __StructProjectionRef { + pinned: ::pin_project::__reexport::pin::Pin::new_unchecked(pinned), + unpinned, + } + } + } + } + + // Automatically create the appropriate conditional `Unpin` implementation. + // + // Basically this is equivalent to the following code: + // + // ```rust + // impl Unpin for Struct where T: Unpin {} + // ``` + // + // However, if struct is public and there is a private type field, + // this would cause an E0446 (private type in public interface). + // + // When RFC 2145 is implemented (rust-lang/rust#48054), + // this will become a lint, rather then a hard error. + // + // As a workaround for this, we generate a new struct, containing all of the pinned + // fields from our #[pin_project] type. This struct is declared within + // a function, which makes it impossible to be named by user code. + // This guarantees that it will use the default auto-trait impl for Unpin - + // that is, it will implement Unpin iff all of its fields implement Unpin. + // This type can be safely declared as 'public', satisfying the privacy + // checker without actually allowing user code to access it. + // + // This allows users to apply the #[pin_project] attribute to types + // regardless of the privacy of the types of their fields. + // + // See also https://github.com/taiki-e/pin-project/pull/53. + struct __Struct<'pin, T, U> { + __pin_project_use_generics: ::pin_project::__private::AlwaysUnpin<'pin, (T, U)>, + __field0: T, + } + impl<'pin, T, U> ::pin_project::__reexport::marker::Unpin for Struct where + __Struct<'pin, T, U>: ::pin_project::__reexport::marker::Unpin + { + } + // A dummy impl of `UnsafeUnpin`, to ensure that the user cannot implement it. + // + // To ensure that users don't accidentally write a non-functional `UnsafeUnpin` + // impls, we emit one ourselves. If the user ends up writing a `UnsafeUnpin` impl, + // they'll get a "conflicting implementations of trait" error when coherence + // checks are run. + unsafe impl ::pin_project::UnsafeUnpin for Struct {} + + // Ensure that struct does not implement `Drop`. + // + // If you attempt to provide an Drop impl, the blanket impl will + // then apply to your type, causing a compile-time error due to + // the conflict with the second impl. + trait StructMustNotImplDrop {} + #[allow(clippy::drop_bounds)] + impl StructMustNotImplDrop for T {} + impl StructMustNotImplDrop for Struct {} + // A dummy impl of `PinnedDrop`, to ensure that users don't accidentally + // write a non-functional `PinnedDrop` impls. + impl ::pin_project::__private::PinnedDrop for Struct { + unsafe fn drop(self: ::pin_project::__reexport::pin::Pin<&mut Self>) {} + } + + // Ensure that it's impossible to use pin projections on a #[repr(packed)] struct. + // + // Taking a reference to a packed field is unsafe, and applying + // #[deny(safe_packed_borrows)] makes sure that doing this without + // an 'unsafe' block (which we deliberately do not generate) + // is a hard error. + // + // If the struct ends up having #[repr(packed)] applied somehow, + // this will generate an (unfriendly) error message. Under all reasonable + // circumstances, we'll detect the #[repr(packed)] attribute, and generate + // a much nicer error above. + // + // See https://github.com/taiki-e/pin-project/pull/34 for more details. + #[deny(safe_packed_borrows)] + fn __assert_not_repr_packed(val: &Struct) { + &val.pinned; + &val.unpinned; + } +}; + +fn main() {} diff --git a/examples/struct-default.rs b/examples/struct-default.rs new file mode 100644 index 0000000..46808a5 --- /dev/null +++ b/examples/struct-default.rs @@ -0,0 +1,14 @@ +// See ./struct-default-expanded.rs for generated code. + +#![allow(dead_code)] + +use pin_project::pin_project; + +#[pin_project] +struct Struct { + #[pin] + pinned: T, + unpinned: U, +} + +fn main() {} diff --git a/examples/unsafe_unpin-expanded.rs b/examples/unsafe_unpin-expanded.rs new file mode 100644 index 0000000..7d8ad8e --- /dev/null +++ b/examples/unsafe_unpin-expanded.rs @@ -0,0 +1,111 @@ +// Original code (./unsafe_unpin.rs): +// +// ```rust +// #![allow(dead_code)] +// +// use pin_project::{pin_project, UnsafeUnpin}; +// +// #[pin_project(UnsafeUnpin)] +// pub struct Struct { +// #[pin] +// pinned: T, +// unpinned: U, +// } +// +// unsafe impl UnsafeUnpin for Struct {} +// +// fn main() {} +// ``` + +#![allow(dead_code, unused_imports, unused_parens)] +#![allow(clippy::no_effect)] + +use pin_project::{pin_project, UnsafeUnpin}; + +pub struct Struct { + // #[pin] + pinned: T, + unpinned: U, +} + +#[doc(hidden)] +#[allow(clippy::mut_mut)] // This lint warns `&mut &mut `. +#[allow(dead_code)] // This lint warns unused fields/variants. +#[allow(single_use_lifetimes)] +pub(crate) struct __StructProjection<'pin, T, U> +where + Struct: 'pin, +{ + pinned: ::pin_project::__reexport::pin::Pin<&'pin mut (T)>, + unpinned: &'pin mut (U), +} +#[doc(hidden)] +#[allow(dead_code)] // This lint warns unused fields/variants. +#[allow(single_use_lifetimes)] +pub(crate) struct __StructProjectionRef<'pin, T, U> +where + Struct: 'pin, +{ + pinned: ::pin_project::__reexport::pin::Pin<&'pin (T)>, + unpinned: &'pin (U), +} + +#[doc(hidden)] +#[allow(non_upper_case_globals)] +#[allow(single_use_lifetimes)] +const __SCOPE_Struct: () = { + impl Struct { + pub(crate) fn project<'pin>( + self: ::pin_project::__reexport::pin::Pin<&'pin mut Self>, + ) -> __StructProjection<'pin, T, U> { + unsafe { + let Self { pinned, unpinned } = self.get_unchecked_mut(); + __StructProjection { + pinned: ::pin_project::__reexport::pin::Pin::new_unchecked(pinned), + unpinned, + } + } + } + pub(crate) fn project_ref<'pin>( + self: ::pin_project::__reexport::pin::Pin<&'pin Self>, + ) -> __StructProjectionRef<'pin, T, U> { + unsafe { + let Self { pinned, unpinned } = self.get_ref(); + __StructProjectionRef { + pinned: ::pin_project::__reexport::pin::Pin::new_unchecked(pinned), + unpinned, + } + } + } + } + + impl<'pin, T, U> ::pin_project::__reexport::marker::Unpin for Struct where + ::pin_project::__private::Wrapper<'pin, Self>: ::pin_project::UnsafeUnpin + { + } + + // Ensure that struct does not implement `Drop`. + // + // See ./struct-default-expanded.rs for details. + trait StructMustNotImplDrop {} + #[allow(clippy::drop_bounds)] + impl StructMustNotImplDrop for T {} + impl StructMustNotImplDrop for Struct {} + impl ::pin_project::__private::PinnedDrop for Struct { + unsafe fn drop(self: ::pin_project::__reexport::pin::Pin<&mut Self>) {} + } + + // Ensure that it's impossible to use pin projections on a #[repr(packed)] struct. + // + // See ./struct-default-expanded.rs and https://github.com/taiki-e/pin-project/pull/34 + // for details. + #[deny(safe_packed_borrows)] + fn __assert_not_repr_packed(val: &Struct) { + &val.pinned; + &val.unpinned; + } +}; + +unsafe impl UnsafeUnpin for Struct {} + +fn main() {} diff --git a/examples/unsafe_unpin.rs b/examples/unsafe_unpin.rs new file mode 100644 index 0000000..5ec0cd2 --- /dev/null +++ b/examples/unsafe_unpin.rs @@ -0,0 +1,16 @@ +// See ./unsafe_unpin-expanded.rs for generated code. + +#![allow(dead_code)] + +use pin_project::{pin_project, UnsafeUnpin}; + +#[pin_project(UnsafeUnpin)] +pub struct Struct { + #[pin] + pinned: T, + unpinned: U, +} + +unsafe impl UnsafeUnpin for Struct {} + +fn main() {} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..45c58fa --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,239 @@ +//! A crate for safe and ergonomic pin-projection. +//! +//! ## Examples +//! +//! [`pin_project`] attribute creates a projection type covering all the fields of struct or enum. +//! +//! ```rust +//! use pin_project::pin_project; +//! use std::pin::Pin; +//! +//! #[pin_project] +//! struct Struct { +//! #[pin] +//! pinned: T, +//! unpinned: U, +//! } +//! +//! impl Struct { +//! fn foo(self: Pin<&mut Self>) { +//! let this = self.project(); +//! let _: Pin<&mut T> = this.pinned; // Pinned reference to the field +//! let _: &mut U = this.unpinned; // Normal reference to the field +//! } +//! } +//! ``` +//! +//! [Code like this will be generated](https://github.com/taiki-e/pin-project/blob/master/examples/struct-default-expanded.rs) +//! +//! See [`pin_project`] attribute for more details. +//! +//! Also, there are examples and generated code of each feature in [examples](https://github.com/taiki-e/pin-project/blob/master/examples/README.md) directory. +//! +//! [`pin_project`]: attr.pin_project.html + +#![no_std] +#![recursion_limit = "256"] +#![doc(html_root_url = "https://docs.rs/pin-project/0.4.16")] +#![doc(test( + no_crate_inject, + attr(deny(warnings, rust_2018_idioms, single_use_lifetimes), allow(dead_code)) +))] +#![warn(missing_docs, rust_2018_idioms, single_use_lifetimes, unreachable_pub)] +#![warn(clippy::all, clippy::default_trait_access)] +// mem::take requires Rust 1.40 +#![allow(clippy::mem_replace_with_default)] +#![allow(clippy::needless_doctest_main)] + +#[doc(inline)] +pub use pin_project_internal::pin_project; + +#[doc(inline)] +pub use pin_project_internal::pinned_drop; + +#[doc(inline)] +pub use pin_project_internal::project; + +#[doc(inline)] +pub use pin_project_internal::project_ref; + +#[doc(inline)] +pub use pin_project_internal::project_replace; + +/// A trait used for custom implementations of [`Unpin`]. +/// This trait is used in conjunction with the `UnsafeUnpin` +/// argument to [`pin_project`] +/// +/// The Rust [`Unpin`] trait is safe to implement - by itself, +/// implementing it cannot lead to undefined behavior. Undefined +/// behavior can only occur when other unsafe code is used. +/// +/// It turns out that using pin projections, which requires unsafe code, +/// imposes additional requirements on an [`Unpin`] impl. Normally, all of this +/// unsafety is contained within this crate, ensuring that it's impossible for +/// you to violate any of the guarantees required by pin projection. +/// +/// However, things change if you want to provide a custom [`Unpin`] impl +/// for your `#[pin_project]` type. As stated in [the Rust +/// documentation](https://doc.rust-lang.org/nightly/std/pin/index.html#projections-and-structural-pinning), +/// you must be sure to only implement [`Unpin`] when all of your `#[pin]` fields (i.e. structurally +/// pinned fields) are also [`Unpin`]. +/// +/// To help highlight this unsafety, the `UnsafeUnpin` trait is provided. +/// Implementing this trait is logically equivalent to implementing [`Unpin`] - +/// this crate will generate an [`Unpin`] impl for your type that 'forwards' to +/// your `UnsafeUnpin` impl. However, this trait is `unsafe` - since your type +/// uses structural pinning (otherwise, you wouldn't be using this crate!), +/// you must be sure that your `UnsafeUnpin` impls follows all of +/// the requirements for an [`Unpin`] impl of a structurally-pinned type. +/// +/// Note that if you specify `#[pin_project(UnsafeUnpin)]`, but do *not* +/// provide an impl of `UnsafeUnpin`, your type will never implement [`Unpin`]. +/// This is effectively the same thing as adding a [`PhantomPinned`] to your type +/// +/// Since this trait is `unsafe`, impls of it will be detected by the `unsafe_code` lint, +/// and by tools like `cargo geiger`. +/// +/// ## Examples +/// +/// An `UnsafeUnpin` impl which, in addition to requiring that structurally pinned +/// fields be [`Unpin`], imposes an additional requirement: +/// +/// ```rust +/// use pin_project::{pin_project, UnsafeUnpin}; +/// +/// #[pin_project(UnsafeUnpin)] +/// struct Foo { +/// #[pin] +/// field_1: K, +/// field_2: V, +/// } +/// +/// unsafe impl UnsafeUnpin for Foo where K: Unpin + Clone {} +/// ``` +/// +/// [`PhantomPinned`]: core::marker::PhantomPinned +/// [`pin_project`]: attr.pin_project.html +pub unsafe trait UnsafeUnpin {} + +// Not public API. +#[doc(hidden)] +pub mod __private { + use super::UnsafeUnpin; + use core::{marker::PhantomData, mem::ManuallyDrop, pin::Pin, ptr}; + + #[doc(hidden)] + pub use pin_project_internal::__PinProjectInternalDerive; + + // Implementing `PinnedDrop::drop` is safe, but calling it is not safe. + // This is because destructors can be called multiple times in safe code and + // [double dropping is unsound](https://github.com/rust-lang/rust/pull/62360). + // + // Ideally, it would be desirable to be able to forbid manual calls in + // the same way as [`Drop::drop`], but the library cannot do it. So, by using + // macros and replacing them with private traits, we prevent users from + // calling `PinnedDrop::drop`. + // + // Users can implement [`Drop`] safely using `#[pinned_drop]` and can drop a + // type that implements `PinnedDrop` using the [`drop`] function safely. + // **Do not call or implement this trait directly.** + #[doc(hidden)] + pub trait PinnedDrop { + #[doc(hidden)] + unsafe fn drop(self: Pin<&mut Self>); + } + + // This is an internal helper struct used by `pin-project-internal`. + // This allows us to force an error if the user tries to provide + // a regular `Unpin` impl when they specify the `UnsafeUnpin` argument. + // This is why we need Wrapper: + // + // Supposed we have the following code: + // + // #[pin_project(UnsafeUnpin)] + // struct MyStruct { + // #[pin] field: T + // } + // + // impl Unpin for MyStruct where MyStruct: UnsafeUnpin {} // generated by pin-project-internal + // impl Unpin for MyStruct where T: Copy // written by the user + // + // We want this code to be rejected - the user is completely bypassing `UnsafeUnpin`, + // and providing an unsound Unpin impl in safe code! + // + // Unfortunately, the Rust compiler will accept the above code. + // Because MyStruct is declared in the same crate as the user-provided impl, + // the compiler will notice that 'MyStruct: UnsafeUnpin' never holds. + // + // The solution is to introduce the 'Wrapper' struct, which is defined + // in the 'pin-project' crate. + // + // We now have code that looks like this: + // + // impl Unpin for MyStruct where Wrapper>: UnsafeUnpin {} // generated by pin-project-internal + // impl Unpin for MyStruct where T: Copy // written by the user + // + // We also have 'unsafe impl UnsafeUnpin for Wrapper where T: UnsafeUnpin {}' in the + // 'pin-project' crate. + // + // Now, our generated impl has a bound involving a type defined in another crate - Wrapper. + // This will cause rust to conservatively assume that 'Wrapper>: UnsafeUnpin' + // holds, in the interest of preserving forwards compatibility (in case such an impl is added + // for Wrapper in a new version of the crate). + // + // This will cause rust to reject any other Unpin impls for MyStruct, since it will + // assume that our generated impl could potentially apply in any situation. + // + // This achieves the desired effect - when the user writes `#[pin_project(UnsafeUnpin)]`, + // the user must either provide no impl of `UnsafeUnpin` (which is equivalent + // to making the type never implement Unpin), or provide an impl of `UnsafeUnpin`. + // It is impossible for them to provide an impl of `Unpin` + #[doc(hidden)] + pub struct Wrapper<'a, T: ?Sized>(PhantomData<&'a ()>, T); + + unsafe impl UnsafeUnpin for Wrapper<'_, T> where T: UnsafeUnpin {} + + // This is an internal helper struct used by `pin-project-internal`. + // + // See https://github.com/taiki-e/pin-project/pull/53 for more details. + #[doc(hidden)] + pub struct AlwaysUnpin<'a, T: ?Sized>(PhantomData<&'a ()>, PhantomData); + + impl Unpin for AlwaysUnpin<'_, T> {} + + // This is an internal helper used to ensure a value is dropped. + #[doc(hidden)] + pub struct UnsafeDropInPlaceGuard(pub *mut T); + + impl Drop for UnsafeDropInPlaceGuard { + fn drop(&mut self) { + unsafe { + ptr::drop_in_place(self.0); + } + } + } + + // This is an internal helper used to ensure a value is overwritten without + // its destructor being called. + #[doc(hidden)] + pub struct UnsafeOverwriteGuard { + pub value: ManuallyDrop, + pub target: *mut T, + } + + impl Drop for UnsafeOverwriteGuard { + fn drop(&mut self) { + unsafe { + ptr::write(self.target, ptr::read(&*self.value)); + } + } + } +} + +// Not public API. +// See tests/overwriting_core_crate.rs for more. +#[doc(hidden)] +pub mod __reexport { + #[doc(hidden)] + pub use core::{marker, mem, ops, pin, ptr}; +} diff --git a/tests/cfg.rs b/tests/cfg.rs new file mode 100644 index 0000000..20b8472 --- /dev/null +++ b/tests/cfg.rs @@ -0,0 +1,242 @@ +#![warn(rust_2018_idioms, single_use_lifetimes)] +#![allow(dead_code)] + +// Refs: https://doc.rust-lang.org/nightly/reference/attributes.html + +use pin_project::pin_project; +use std::{marker::PhantomPinned, pin::Pin}; + +fn is_unpin() {} + +#[cfg(target_os = "linux")] +struct Linux; +#[cfg(not(target_os = "linux"))] +struct Other; + +// Use this type to check that `cfg(any())` is working properly. +// If `cfg(any())` is not working properly, `is_unpin` will fail. +struct Any(PhantomPinned); + +#[test] +fn cfg() { + // structs + + #[pin_project(Replace)] + struct SameName { + #[cfg(target_os = "linux")] + #[pin] + inner: Linux, + #[cfg(not(target_os = "linux"))] + #[pin] + inner: Other, + #[cfg(any())] + #[pin] + any: Any, + } + + is_unpin::(); + + #[cfg(target_os = "linux")] + let _x = SameName { inner: Linux }; + #[cfg(not(target_os = "linux"))] + let _x = SameName { inner: Other }; + + #[pin_project(Replace)] + struct DifferentName { + #[cfg(target_os = "linux")] + #[pin] + l: Linux, + #[cfg(not(target_os = "linux"))] + #[pin] + o: Other, + #[cfg(any())] + #[pin] + a: Any, + } + + is_unpin::(); + + #[cfg(target_os = "linux")] + let _x = DifferentName { l: Linux }; + #[cfg(not(target_os = "linux"))] + let _x = DifferentName { o: Other }; + + #[pin_project(Replace)] + struct TupleStruct( + #[cfg(target_os = "linux")] + #[pin] + Linux, + #[cfg(not(target_os = "linux"))] + #[pin] + Other, + #[cfg(any())] + #[pin] + Any, + ); + + is_unpin::(); + + #[cfg(target_os = "linux")] + let _x = TupleStruct(Linux); + #[cfg(not(target_os = "linux"))] + let _x = TupleStruct(Other); + + // enums + + #[pin_project(Replace)] + enum Variant { + #[cfg(target_os = "linux")] + Inner(#[pin] Linux), + #[cfg(not(target_os = "linux"))] + Inner(#[pin] Other), + + #[cfg(target_os = "linux")] + Linux(#[pin] Linux), + #[cfg(not(target_os = "linux"))] + Other(#[pin] Other), + #[cfg(any())] + Any(#[pin] Any), + } + + is_unpin::(); + + #[cfg(target_os = "linux")] + let _x = Variant::Inner(Linux); + #[cfg(not(target_os = "linux"))] + let _x = Variant::Inner(Other); + + #[cfg(target_os = "linux")] + let _x = Variant::Linux(Linux); + #[cfg(not(target_os = "linux"))] + let _x = Variant::Other(Other); + + #[pin_project(Replace)] + enum Field { + SameName { + #[cfg(target_os = "linux")] + #[pin] + inner: Linux, + #[cfg(not(target_os = "linux"))] + #[pin] + inner: Other, + #[cfg(any())] + #[pin] + any: Any, + }, + DifferentName { + #[cfg(target_os = "linux")] + #[pin] + l: Linux, + #[cfg(not(target_os = "linux"))] + #[pin] + w: Other, + #[cfg(any())] + #[pin] + any: Any, + }, + TupleVariant( + #[cfg(target_os = "linux")] + #[pin] + Linux, + #[cfg(not(target_os = "linux"))] + #[pin] + Other, + #[cfg(any())] + #[pin] + Any, + ), + } + + is_unpin::(); + + #[cfg(target_os = "linux")] + let _x = Field::SameName { inner: Linux }; + #[cfg(not(target_os = "linux"))] + let _x = Field::SameName { inner: Other }; + + #[cfg(target_os = "linux")] + let _x = Field::DifferentName { l: Linux }; + #[cfg(not(target_os = "linux"))] + let _x = Field::DifferentName { w: Other }; + + #[cfg(target_os = "linux")] + let _x = Field::TupleVariant(Linux); + #[cfg(not(target_os = "linux"))] + let _x = Field::TupleVariant(Other); +} + +#[test] +fn cfg_attr() { + #[pin_project(Replace)] + struct SameCfg { + #[cfg(target_os = "linux")] + #[cfg_attr(target_os = "linux", pin)] + inner: Linux, + #[cfg(not(target_os = "linux"))] + #[cfg_attr(not(target_os = "linux"), pin)] + inner: Other, + #[cfg(any())] + #[cfg_attr(any(), pin)] + any: Any, + } + + is_unpin::(); + + #[cfg(target_os = "linux")] + let mut x = SameCfg { inner: Linux }; + #[cfg(not(target_os = "linux"))] + let mut x = SameCfg { inner: Other }; + + let x = Pin::new(&mut x).project(); + #[cfg(target_os = "linux")] + let _: Pin<&mut Linux> = x.inner; + #[cfg(not(target_os = "linux"))] + let _: Pin<&mut Other> = x.inner; + + #[pin_project(Replace)] + struct DifferentCfg { + #[cfg(target_os = "linux")] + #[cfg_attr(target_os = "linux", pin)] + inner: Linux, + #[cfg(not(target_os = "linux"))] + #[cfg_attr(target_os = "linux", pin)] + inner: Other, + #[cfg(any())] + #[cfg_attr(any(), pin)] + any: Any, + } + + is_unpin::(); + + #[cfg(target_os = "linux")] + let mut x = DifferentCfg { inner: Linux }; + #[cfg(not(target_os = "linux"))] + let mut x = DifferentCfg { inner: Other }; + + let x = Pin::new(&mut x).project(); + #[cfg(target_os = "linux")] + let _: Pin<&mut Linux> = x.inner; + #[cfg(not(target_os = "linux"))] + let _: &mut Other = x.inner; + + #[cfg_attr(not(any()), pin_project)] + struct Foo { + #[cfg_attr(not(any()), pin)] + inner: T, + } + + let mut x = Foo { inner: 0_u8 }; + let x = Pin::new(&mut x).project(); + let _: Pin<&mut u8> = x.inner; +} + +#[test] +fn cfg_attr_any_packed() { + // Since `cfg(any())` can never be true, it is okay for this to pass. + #[pin_project(Replace)] + #[cfg_attr(any(), repr(packed))] + struct Struct { + #[pin] + field: u32, + } +} diff --git a/tests/compiletest.rs b/tests/compiletest.rs new file mode 100644 index 0000000..078abaa --- /dev/null +++ b/tests/compiletest.rs @@ -0,0 +1,15 @@ +#![warn(rust_2018_idioms, single_use_lifetimes)] + +#[ignore] +#[test] +fn ui() { + let t = trybuild::TestCases::new(); + t.compile_fail("tests/ui/cfg/*.rs"); + t.compile_fail("tests/ui/not_unpin/*.rs"); + t.compile_fail("tests/ui/pin_project/*.rs"); + t.compile_fail("tests/ui/pinned_drop/*.rs"); + t.compile_fail("tests/ui/project/*.rs"); + t.compile_fail("tests/ui/unsafe_unpin/*.rs"); + t.compile_fail("tests/ui/unstable-features/*.rs"); + t.pass("tests/ui/unstable-features/run-pass/*.rs"); +} diff --git a/tests/forbid_unsafe.rs b/tests/forbid_unsafe.rs new file mode 100644 index 0000000..4b2e248 --- /dev/null +++ b/tests/forbid_unsafe.rs @@ -0,0 +1,108 @@ +#![forbid(unsafe_code)] +#![warn(rust_2018_idioms, single_use_lifetimes)] +#![allow(dead_code)] + +// default #[pin_project], PinnedDrop, Replace, and !Unpin are completely safe. + +use pin_project::{pin_project, pinned_drop}; +use std::pin::Pin; + +#[pin_project] +pub struct StructDefault { + #[pin] + pub pinned: T, + pub unpinned: U, +} + +#[pin_project(PinnedDrop)] +pub struct StructPinnedDrop { + #[pin] + pub pinned: T, + pub unpinned: U, +} + +#[pinned_drop] +impl PinnedDrop for StructPinnedDrop { + fn drop(self: Pin<&mut Self>) {} +} + +#[pin_project(Replace)] +pub struct StructReplace { + #[pin] + pub pinned: T, + pub unpinned: U, +} + +// UnsafeUnpin without UnsafeUnpin impl is also safe +#[pin_project(UnsafeUnpin)] +pub struct StructUnsafeUnpin { + #[pin] + pub pinned: T, + pub unpinned: U, +} + +#[pin_project(!Unpin)] +pub struct StructNotUnpin { + #[pin] + pub pinned: T, + pub unpinned: U, +} + +#[pin_project] +pub enum EnumDefault { + Struct { + #[pin] + pinned: T, + unpinned: U, + }, + Tuple(#[pin] T, U), +} + +#[pin_project(PinnedDrop)] +pub enum EnumPinnedDrop { + Struct { + #[pin] + pinned: T, + unpinned: U, + }, + Tuple(#[pin] T, U), +} + +#[pinned_drop] +impl PinnedDrop for EnumPinnedDrop { + fn drop(self: Pin<&mut Self>) {} +} + +#[pin_project(Replace)] +pub enum EnumReplace { + Struct { + #[pin] + pinned: T, + unpinned: U, + }, + Tuple(#[pin] T, U), +} + +// UnsafeUnpin without UnsafeUnpin impl is also safe +#[pin_project(UnsafeUnpin)] +pub enum EnumUnsafeUnpin { + Struct { + #[pin] + pinned: T, + unpinned: U, + }, + Tuple(#[pin] T, U), +} + +#[pin_project(!Unpin)] +pub enum EnumNotUnpin { + Struct { + #[pin] + pinned: T, + unpinned: U, + }, + Tuple(#[pin] T, U), +} + +#[test] +fn test() {} diff --git a/tests/lints.rs b/tests/lints.rs new file mode 100644 index 0000000..4009f55 --- /dev/null +++ b/tests/lints.rs @@ -0,0 +1,126 @@ +#![warn(rust_2018_idioms, single_use_lifetimes)] +#![warn(unused, future_incompatible)] +#![warn(clippy::all, clippy::pedantic, clippy::nursery)] + +use pin_project::{pin_project, pinned_drop, UnsafeUnpin}; +use std::pin::Pin; + +#[pin_project] +pub struct StructDefault { + #[pin] + pub pinned: T, + pub unpinned: U, +} + +#[pin_project(PinnedDrop)] +pub struct StructPinnedDrop { + #[pin] + pub pinned: T, + pub unpinned: U, +} + +#[pinned_drop] +impl PinnedDrop for StructPinnedDrop { + fn drop(self: Pin<&mut Self>) {} +} + +#[pin_project(Replace)] +pub struct StructReplace { + #[pin] + pub pinned: T, + pub unpinned: U, +} + +#[pin_project(UnsafeUnpin)] +pub struct StructUnsafeUnpin { + #[pin] + pub pinned: T, + pub unpinned: U, +} + +unsafe impl UnsafeUnpin for StructUnsafeUnpin {} + +#[pin_project(!Unpin)] +pub struct StructNotUnpin { + #[pin] + pub pinned: T, + pub unpinned: U, +} + +#[pin_project] +pub struct StructMutMut<'a, T, U> { + #[pin] + pub pinned: &'a mut T, + pub unpinned: &'a mut U, +} + +#[pin_project] +pub enum EnumDefault { + Struct { + #[pin] + pinned: T, + unpinned: U, + }, + Tuple(#[pin] T, U), +} + +#[pin_project(PinnedDrop)] +pub enum EnumPinnedDrop { + Struct { + #[pin] + pinned: T, + unpinned: U, + }, + Tuple(#[pin] T, U), +} + +#[pinned_drop] +impl PinnedDrop for EnumPinnedDrop { + fn drop(self: Pin<&mut Self>) {} +} + +#[pin_project(Replace)] +pub enum EnumReplace { + Struct { + #[pin] + pinned: T, + unpinned: U, + }, + Tuple(#[pin] T, U), +} + +#[pin_project(UnsafeUnpin)] +pub enum EnumUnsafeUnpin { + Struct { + #[pin] + pinned: T, + unpinned: U, + }, + Tuple(#[pin] T, U), +} + +unsafe impl UnsafeUnpin for EnumUnsafeUnpin {} + +#[pin_project(!Unpin)] +pub enum EnumNotUnpin { + Struct { + #[pin] + pinned: T, + unpinned: U, + }, + Tuple(#[pin] T, U), +} + +#[pin_project] +pub enum EnumMutMut<'a, T, U> { + Struct { + #[pin] + pinned: &'a mut T, + unpinned: &'a mut U, + }, + Tuple(#[pin] T, U), +} + +#[allow(clippy::missing_const_for_fn)] +#[test] +fn test() {} diff --git a/tests/overwriting_core_crate.rs b/tests/overwriting_core_crate.rs new file mode 100644 index 0000000..121104c --- /dev/null +++ b/tests/overwriting_core_crate.rs @@ -0,0 +1,116 @@ +#![warn(rust_2018_idioms, single_use_lifetimes)] + +// See https://github.com/rust-lang/pin-utils/pull/26#discussion_r344491597 +// +// Note: If the proc-macro does not depend on its own items, it may be preferable not to +// support overwriting the name of core/std crate for compatibility with reexport. +#[allow(unused_extern_crates)] +extern crate pin_project as core; + +// Dummy module to check that the expansion refers to the crate. +mod pin_project {} + +use ::pin_project::{pin_project, pinned_drop, UnsafeUnpin}; +use std::pin::Pin; + +#[pin_project] +pub struct StructDefault { + #[pin] + pub pinned: T, + pub unpinned: U, +} + +#[pin_project(PinnedDrop)] +pub struct StructPinnedDrop { + #[pin] + pub pinned: T, + pub unpinned: U, +} + +#[pinned_drop] +impl PinnedDrop for StructPinnedDrop { + fn drop(self: Pin<&mut Self>) {} +} + +#[pin_project(Replace)] +pub struct StructReplace { + #[pin] + pub pinned: T, + pub unpinned: U, +} + +#[pin_project(UnsafeUnpin)] +pub struct StructUnsafeUnpin { + #[pin] + pub pinned: T, + pub unpinned: U, +} + +unsafe impl UnsafeUnpin for StructUnsafeUnpin {} + +#[pin_project(!Unpin)] +pub struct StructNotUnpin { + #[pin] + pub pinned: T, + pub unpinned: U, +} + +#[pin_project] +pub enum EnumDefault { + Struct { + #[pin] + pinned: T, + unpinned: U, + }, + Tuple(#[pin] T, U), +} + +#[pin_project(PinnedDrop)] +pub enum EnumPinnedDrop { + Struct { + #[pin] + pinned: T, + unpinned: U, + }, + Tuple(#[pin] T, U), +} + +#[pinned_drop] +impl PinnedDrop for EnumPinnedDrop { + fn drop(self: Pin<&mut Self>) {} +} + +#[pin_project(Replace)] +pub enum EnumReplace { + Struct { + #[pin] + pinned: T, + unpinned: U, + }, + Tuple(#[pin] T, U), +} + +#[pin_project(UnsafeUnpin)] +pub enum EnumUnsafeUnpin { + Struct { + #[pin] + pinned: T, + unpinned: U, + }, + Tuple(#[pin] T, U), +} + +unsafe impl UnsafeUnpin for EnumUnsafeUnpin {} + +#[pin_project(!Unpin)] +pub enum EnumNotUnpin { + Struct { + #[pin] + pinned: T, + unpinned: U, + }, + Tuple(#[pin] T, U), +} + +#[test] +fn test() {} diff --git a/tests/pin_project.rs b/tests/pin_project.rs new file mode 100644 index 0000000..c84ca4b --- /dev/null +++ b/tests/pin_project.rs @@ -0,0 +1,809 @@ +#![warn(rust_2018_idioms, single_use_lifetimes)] +#![allow(dead_code)] + +use core::{ + marker::{PhantomData, PhantomPinned}, + pin::Pin, +}; +use pin_project::{pin_project, pinned_drop, UnsafeUnpin}; + +#[test] +fn projection() { + #[pin_project(Replace)] + struct Struct { + #[pin] + field1: T, + field2: U, + } + + let mut s = Struct { field1: 1, field2: 2 }; + let mut s_orig = Pin::new(&mut s); + let s = s_orig.as_mut().project(); + + let x: Pin<&mut i32> = s.field1; + assert_eq!(*x, 1); + + let y: &mut i32 = s.field2; + assert_eq!(*y, 2); + + assert_eq!(s_orig.as_ref().field1, 1); + assert_eq!(s_orig.as_ref().field2, 2); + + let mut s = Struct { field1: 1, field2: 2 }; + + let __StructProjection { field1, field2 } = Pin::new(&mut s).project(); + let _: Pin<&mut i32> = field1; + let _: &mut i32 = field2; + + let __StructProjectionRef { field1, field2 } = Pin::new(&s).project_ref(); + let _: Pin<&i32> = field1; + let _: &i32 = field2; + + let mut s = Pin::new(&mut s); + let __StructProjectionOwned { field1, field2 } = + s.as_mut().project_replace(Struct { field1: 3, field2: 4 }); + let _: PhantomData = field1; + let _: i32 = field2; + assert_eq!(field2, 2); + assert_eq!(s.field1, 3); + assert_eq!(s.field2, 4); + + #[pin_project(Replace)] + struct TupleStruct(#[pin] T, U); + + let mut s = TupleStruct(1, 2); + let s = Pin::new(&mut s).project(); + + let x: Pin<&mut i32> = s.0; + assert_eq!(*x, 1); + + let y: &mut i32 = s.1; + assert_eq!(*y, 2); + + #[pin_project(Replace)] + #[derive(Eq, PartialEq, Debug)] + enum Enum { + Variant1(#[pin] A, B), + Variant2 { + #[pin] + field1: C, + field2: D, + }, + None, + } + + let mut e = Enum::Variant1(1, 2); + let mut e_orig = Pin::new(&mut e); + let e = e_orig.as_mut().project(); + + match e { + __EnumProjection::Variant1(x, y) => { + let x: Pin<&mut i32> = x; + assert_eq!(*x, 1); + + let y: &mut i32 = y; + assert_eq!(*y, 2); + } + __EnumProjection::Variant2 { field1, field2 } => { + let _x: Pin<&mut i32> = field1; + let _y: &mut i32 = field2; + } + __EnumProjection::None => {} + } + + assert_eq!(Pin::into_ref(e_orig).get_ref(), &Enum::Variant1(1, 2)); + + let mut e = Enum::Variant2 { field1: 3, field2: 4 }; + let mut e = Pin::new(&mut e).project(); + + match &mut e { + __EnumProjection::Variant1(x, y) => { + let _x: &mut Pin<&mut i32> = x; + let _y: &mut &mut i32 = y; + } + __EnumProjection::Variant2 { field1, field2 } => { + let x: &mut Pin<&mut i32> = field1; + assert_eq!(**x, 3); + + let y: &mut &mut i32 = field2; + assert_eq!(**y, 4); + } + __EnumProjection::None => {} + } + + if let __EnumProjection::Variant2 { field1, field2 } = e { + let x: Pin<&mut i32> = field1; + assert_eq!(*x, 3); + + let y: &mut i32 = field2; + assert_eq!(*y, 4); + } +} + +#[test] +fn enum_project_set() { + #[pin_project(Replace)] + #[derive(Eq, PartialEq, Debug)] + enum Enum { + Variant1(#[pin] u8), + Variant2(bool), + } + + let mut e = Enum::Variant1(25); + let mut e_orig = Pin::new(&mut e); + let e_proj = e_orig.as_mut().project(); + + match e_proj { + __EnumProjection::Variant1(val) => { + let new_e = Enum::Variant2(val.as_ref().get_ref() == &25); + e_orig.set(new_e); + } + _ => unreachable!(), + } + + assert_eq!(e, Enum::Variant2(true)); +} + +#[test] +fn where_clause() { + #[pin_project] + struct Struct + where + T: Copy, + { + field: T, + } + + #[pin_project] + struct TupleStruct(T) + where + T: Copy; + + #[pin_project] + enum EnumWhere + where + T: Copy, + { + Variant(T), + } +} + +#[test] +fn where_clause_and_associated_type_field() { + #[pin_project(Replace)] + struct Struct1 + where + I: Iterator, + { + #[pin] + field1: I, + field2: I::Item, + } + + #[pin_project(Replace)] + struct Struct2 + where + I: Iterator, + { + #[pin] + field1: I, + field2: J, + } + + #[pin_project(Replace)] + struct Struct3 + where + T: 'static, + { + field: T, + } + + trait Static: 'static {} + + impl Static for Struct3 {} + + #[pin_project(Replace)] + struct TupleStruct(#[pin] I, I::Item) + where + I: Iterator; + + #[pin_project(Replace)] + enum Enum + where + I: Iterator, + { + Variant1(#[pin] I), + Variant2(I::Item), + } +} + +#[test] +fn derive_copy() { + #[pin_project(Replace)] + #[derive(Clone, Copy)] + struct Struct { + val: T, + } + + fn is_copy() {} + + is_copy::>(); +} + +#[test] +fn move_out() { + struct NotCopy; + + #[pin_project(Replace)] + struct Struct { + val: NotCopy, + } + + let x = Struct { val: NotCopy }; + let _val: NotCopy = x.val; + + #[pin_project(Replace)] + enum Enum { + Variant(NotCopy), + } + + let x = Enum::Variant(NotCopy); + #[allow(clippy::infallible_destructuring_match)] + let _val: NotCopy = match x { + Enum::Variant(val) => val, + }; +} + +#[test] +fn trait_bounds_on_type_generics() { + #[pin_project(Replace)] + pub struct Struct1<'a, T: ?Sized> { + field: &'a mut T, + } + + #[pin_project(Replace)] + pub struct Struct2<'a, T: ::core::fmt::Debug> { + field: &'a mut T, + } + + #[pin_project(Replace)] + pub struct Struct3<'a, T: core::fmt::Debug> { + field: &'a mut T, + } + + #[pin_project(Replace)] + pub struct Struct4<'a, T: core::fmt::Debug + core::fmt::Display> { + field: &'a mut T, + } + + #[pin_project(Replace)] + pub struct Struct5<'a, T: core::fmt::Debug + ?Sized> { + field: &'a mut T, + } + + #[pin_project(Replace)] + pub struct Struct6<'a, T: core::fmt::Debug = [u8; 16]> { + field: &'a mut T, + } + + let _: Struct6<'_> = Struct6 { field: &mut [0u8; 16] }; + + #[pin_project(Replace)] + pub struct Struct7 { + field: T, + } + + trait Static: 'static {} + + impl Static for Struct7 {} + + #[pin_project(Replace)] + pub struct Struct8<'a, 'b: 'a> { + field1: &'a u8, + field2: &'b u8, + } + + #[pin_project(Replace)] + pub struct TupleStruct<'a, T: ?Sized>(&'a mut T); + + #[pin_project(Replace)] + enum Enum<'a, T: ?Sized> { + Variant(&'a mut T), + } +} + +#[test] +fn overlapping_lifetime_names() { + #[pin_project(Replace)] + pub struct Struct1<'pin, T> { + #[pin] + field: &'pin mut T, + } + + #[pin_project(Replace)] + pub struct Struct2<'pin, 'pin_, 'pin__> { + #[pin] + field: &'pin &'pin_ &'pin__ (), + } + + pub trait A<'a> {} + + #[allow(single_use_lifetimes)] // https://github.com/rust-lang/rust/issues/55058 + #[pin_project(Replace)] + pub struct HRTB<'pin___, T> + where + for<'pin> &'pin T: Unpin, + T: for<'pin> A<'pin>, + for<'pin, 'pin_, 'pin__> &'pin &'pin_ &'pin__ T: Unpin, + { + #[pin] + field: &'pin___ mut T, + } +} + +#[test] +fn combine() { + #[pin_project(PinnedDrop, UnsafeUnpin)] + pub struct Struct1 { + #[pin] + field: T, + } + + #[pinned_drop] + impl PinnedDrop for Struct1 { + fn drop(self: Pin<&mut Self>) {} + } + + unsafe impl UnsafeUnpin for Struct1 {} + + #[pin_project(UnsafeUnpin, Replace)] + pub struct Struct2 { + #[pin] + field: T, + } + + unsafe impl UnsafeUnpin for Struct2 {} + + #[pin_project(PinnedDrop, !Unpin)] + pub struct Struct3 { + #[pin] + field: T, + } + + #[pinned_drop] + impl PinnedDrop for Struct3 { + fn drop(self: Pin<&mut Self>) {} + } + + #[pin_project(!Unpin, Replace)] + pub struct Struct4 { + #[pin] + field: T, + } +} + +#[test] +fn private_type_in_public_type() { + #[pin_project(Replace)] + pub struct PublicStruct { + #[pin] + inner: PrivateStruct, + } + + struct PrivateStruct(T); +} + +#[test] +fn lifetime_project() { + #[pin_project(Replace)] + struct Struct1 { + #[pin] + pinned: T, + unpinned: U, + } + + #[pin_project(Replace)] + struct Struct2<'a, T, U> { + #[pin] + pinned: &'a mut T, + unpinned: U, + } + + #[pin_project(Replace)] + enum Enum { + Variant { + #[pin] + pinned: T, + unpinned: U, + }, + } + + impl Struct1 { + fn get_pin_ref<'a>(self: Pin<&'a Self>) -> Pin<&'a T> { + self.project_ref().pinned + } + fn get_pin_mut<'a>(self: Pin<&'a mut Self>) -> Pin<&'a mut T> { + self.project().pinned + } + } + + impl<'b, T, U> Struct2<'b, T, U> { + fn get_pin_ref<'a>(self: Pin<&'a Self>) -> Pin<&'a &'b mut T> { + self.project_ref().pinned + } + fn get_pin_mut<'a>(self: Pin<&'a mut Self>) -> Pin<&'a mut &'b mut T> { + self.project().pinned + } + } + + impl Enum { + fn get_pin_ref<'a>(self: Pin<&'a Self>) -> Pin<&'a T> { + match self.project_ref() { + __EnumProjectionRef::Variant { pinned, .. } => pinned, + } + } + fn get_pin_mut<'a>(self: Pin<&'a mut Self>) -> Pin<&'a mut T> { + match self.project() { + __EnumProjection::Variant { pinned, .. } => pinned, + } + } + } +} + +#[rustversion::since(1.36)] // https://github.com/rust-lang/rust/pull/61207 +#[test] +fn lifetime_project_elided() { + #[pin_project(Replace)] + struct Struct1 { + #[pin] + pinned: T, + unpinned: U, + } + + #[pin_project(Replace)] + struct Struct2<'a, T, U> { + #[pin] + pinned: &'a mut T, + unpinned: U, + } + + #[pin_project(Replace)] + enum Enum { + Variant { + #[pin] + pinned: T, + unpinned: U, + }, + } + + impl Struct1 { + fn get_pin_ref(self: Pin<&Self>) -> Pin<&T> { + self.project_ref().pinned + } + fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut T> { + self.project().pinned + } + } + + impl<'b, T, U> Struct2<'b, T, U> { + fn get_pin_ref(self: Pin<&Self>) -> Pin<&&'b mut T> { + self.project_ref().pinned + } + fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut &'b mut T> { + self.project().pinned + } + } + + impl Enum { + fn get_pin_ref(self: Pin<&Self>) -> Pin<&T> { + match self.project_ref() { + __EnumProjectionRef::Variant { pinned, .. } => pinned, + } + } + fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut T> { + match self.project() { + __EnumProjection::Variant { pinned, .. } => pinned, + } + } + } +} + +mod visibility { + use pin_project::pin_project; + + #[pin_project(Replace)] + pub(crate) struct A { + pub b: u8, + } +} + +#[test] +fn visibility() { + let mut x = visibility::A { b: 0 }; + let x = Pin::new(&mut x); + let y = x.as_ref().project_ref(); + let _: &u8 = y.b; + let y = x.project(); + let _: &mut u8 = y.b; +} + +#[test] +fn trivial_bounds() { + #[pin_project(Replace)] + pub struct NoGenerics { + #[pin] + field: PhantomPinned, + } +} + +#[test] +fn dst() { + #[pin_project] + struct Struct1 { + x: T, + } + + let mut x = Struct1 { x: 0_u8 }; + let x: Pin<&mut Struct1> = Pin::new(&mut x as _); + let _y: &mut (dyn core::fmt::Debug) = x.project().x; + + #[pin_project] + struct Struct2 { + #[pin] + x: T, + } + + let mut x = Struct2 { x: 0_u8 }; + let x: Pin<&mut Struct2> = Pin::new(&mut x as _); + let _y: Pin<&mut (dyn core::fmt::Debug + Unpin)> = x.project().x; + + #[pin_project(UnsafeUnpin)] + struct Struct5 { + x: T, + } + + #[pin_project(UnsafeUnpin)] + struct Struct6 { + #[pin] + x: T, + } + + #[pin_project(PinnedDrop)] + struct Struct7 { + x: T, + } + + #[pinned_drop] + impl PinnedDrop for Struct7 { + fn drop(self: Pin<&mut Self>) {} + } + + #[pin_project(PinnedDrop)] + struct Struct8 { + #[pin] + x: T, + } + + #[pinned_drop] + impl PinnedDrop for Struct8 { + fn drop(self: Pin<&mut Self>) {} + } + + #[pin_project(!Unpin)] + struct Struct9 { + x: T, + } + + #[pin_project(!Unpin)] + struct Struct10 { + #[pin] + x: T, + } + + #[pin_project] + struct TupleStruct1(T); + + #[pin_project] + struct TupleStruct2(#[pin] T); + + #[pin_project(UnsafeUnpin)] + struct TupleStruct5(T); + + #[pin_project(UnsafeUnpin)] + struct TupleStruct6(#[pin] T); + + #[pin_project(PinnedDrop)] + struct TupleStruct7(T); + + #[pinned_drop] + impl PinnedDrop for TupleStruct7 { + fn drop(self: Pin<&mut Self>) {} + } + + #[pin_project(PinnedDrop)] + struct TupleStruct8(#[pin] T); + + #[pinned_drop] + impl PinnedDrop for TupleStruct8 { + fn drop(self: Pin<&mut Self>) {} + } + + #[pin_project(!Unpin)] + struct TupleStruct9(T); + + #[pin_project(!Unpin)] + struct TupleStruct10(#[pin] T); +} + +#[allow(explicit_outlives_requirements)] // https://github.com/rust-lang/rust/issues/60993 +#[test] +fn unsized_in_where_clause() { + #[pin_project] + struct Struct3 + where + T: ?Sized, + { + x: T, + } + + #[pin_project] + struct Struct4 + where + T: ?Sized, + { + #[pin] + x: T, + } + + #[pin_project] + struct TupleStruct3(T) + where + T: ?Sized; + + #[pin_project] + struct TupleStruct4(#[pin] T) + where + T: ?Sized; +} + +#[test] +fn dyn_type() { + #[pin_project] + struct Struct1 { + f: dyn core::fmt::Debug, + } + + #[pin_project] + struct Struct2 { + #[pin] + f: dyn core::fmt::Debug, + } + + #[pin_project] + struct Struct3 { + f: dyn core::fmt::Debug + Send, + } + + #[pin_project] + struct Struct4 { + #[pin] + f: dyn core::fmt::Debug + Send, + } + + #[pin_project] + struct TupleStruct1(dyn core::fmt::Debug); + + #[pin_project] + struct TupleStruct2(#[pin] dyn core::fmt::Debug); + + #[pin_project] + struct TupleStruct3(dyn core::fmt::Debug + Send); + + #[pin_project] + struct TupleStruct4(#[pin] dyn core::fmt::Debug + Send); +} + +#[test] +fn self_in_where_clause() { + pub trait Trait1 {} + + #[pin_project(Replace)] + pub struct Struct1 + where + Self: Trait1, + { + x: T, + } + + impl Trait1 for Struct1 {} + + pub trait Trait2 { + type Assoc; + } + + #[pin_project(Replace)] + pub struct Struct2 + where + Self: Trait2>, + ::Assoc: Trait1, + { + x: T, + } + + impl Trait2 for Struct2 { + type Assoc = Struct1; + } +} + +#[test] +fn no_infer_outlives() { + trait Bar { + type Y; + } + + struct Example(A); + + impl Bar for Example { + type Y = Option; + } + + #[pin_project(Replace)] + struct Foo { + _x: as Bar>::Y, + } +} + +// https://github.com/rust-lang/rust/issues/47949 +// https://github.com/taiki-e/pin-project/pull/194#discussion_r419098111 +#[test] +fn project_replace_panic() { + use std::panic; + + #[pin_project(Replace)] + struct S { + #[pin] + pinned: T, + unpinned: U, + } + + struct D<'a>(&'a mut bool, bool); + impl Drop for D<'_> { + fn drop(&mut self) { + *self.0 = true; + if self.1 { + panic!() + } + } + } + + let (mut a, mut b, mut c, mut d) = (false, false, false, false); + let res = panic::catch_unwind(panic::AssertUnwindSafe(|| { + let mut x = S { pinned: D(&mut a, true), unpinned: D(&mut b, false) }; + let _y = Pin::new(&mut x) + .project_replace(S { pinned: D(&mut c, false), unpinned: D(&mut d, false) }); + // Previous `x.pinned` was dropped and panicked when `project_replace` is called, so this is unreachable. + unreachable!(); + })); + assert!(res.is_err()); + assert!(a); + assert!(b); + assert!(c); + assert!(d); + + let (mut a, mut b, mut c, mut d) = (false, false, false, false); + let res = panic::catch_unwind(panic::AssertUnwindSafe(|| { + let mut x = S { pinned: D(&mut a, false), unpinned: D(&mut b, true) }; + { + let _y = Pin::new(&mut x) + .project_replace(S { pinned: D(&mut c, false), unpinned: D(&mut d, false) }); + // `_y` (previous `x.unpinned`) live to the end of this scope, so this is not unreachable, + // unreachable!(); + } + unreachable!(); + })); + assert!(res.is_err()); + assert!(a); + assert!(b); + assert!(c); + assert!(d); +} diff --git a/tests/pinned_drop.rs b/tests/pinned_drop.rs new file mode 100644 index 0000000..b0677e2 --- /dev/null +++ b/tests/pinned_drop.rs @@ -0,0 +1,235 @@ +#![warn(rust_2018_idioms, single_use_lifetimes)] +#![allow(dead_code)] + +use pin_project::{pin_project, pinned_drop}; +use std::pin::Pin; + +#[test] +fn safe_project() { + #[pin_project(PinnedDrop)] + pub struct Struct<'a> { + was_dropped: &'a mut bool, + #[pin] + field: u8, + } + + #[pinned_drop] + impl PinnedDrop for Struct<'_> { + fn drop(self: Pin<&mut Self>) { + **self.project().was_dropped = true; + } + } + + let mut was_dropped = false; + drop(Struct { was_dropped: &mut was_dropped, field: 42 }); + assert!(was_dropped); +} + +#[test] +fn mut_self_argument() { + #[pin_project(PinnedDrop)] + struct Struct { + data: usize, + } + + #[pinned_drop] + impl PinnedDrop for Struct { + fn drop(mut self: Pin<&mut Self>) { + let _: &mut _ = &mut self.data; + } + } +} + +#[test] +fn self_in_vec() { + #[pin_project(PinnedDrop)] + struct Struct { + data: usize, + } + + #[pinned_drop] + impl PinnedDrop for Struct { + fn drop(self: Pin<&mut Self>) { + let _: Vec<_> = vec![self.data]; + } + } +} + +#[test] +fn self_in_macro_containing_fn() { + #[pin_project(PinnedDrop)] + pub struct Struct { + data: usize, + } + + macro_rules! emit { + ($($tt:tt)*) => { + $($tt)* + }; + } + + #[pinned_drop] + impl PinnedDrop for Struct { + fn drop(self: Pin<&mut Self>) { + let _ = emit!({ + impl Struct { + pub fn f(self) {} + } + }); + let _ = self.data; + } + } +} + +#[test] +fn self_call() { + #[pin_project(PinnedDrop)] + pub struct Struct { + data: usize, + } + + trait Trait { + fn self_ref(&self) {} + fn self_pin_ref(self: Pin<&Self>) {} + fn self_mut(&mut self) {} + fn self_pin_mut(self: Pin<&mut Self>) {} + fn assoc_fn(_this: Pin<&mut Self>) {} + } + + impl Trait for Struct {} + + #[pinned_drop] + impl PinnedDrop for Struct { + fn drop(mut self: Pin<&mut Self>) { + self.self_ref(); + self.as_ref().self_pin_ref(); + self.self_mut(); + self.as_mut().self_pin_mut(); + Self::assoc_fn(self.as_mut()); + ::assoc_fn(self.as_mut()); + } + } +} + +#[test] +fn self_expr() { + #[pin_project(PinnedDrop)] + pub struct Struct { + x: usize, + } + + #[pinned_drop] + impl PinnedDrop for Struct { + fn drop(mut self: Pin<&mut Self>) { + let _: Self = Self { x: 0 }; + } + } + + #[pin_project(PinnedDrop)] + pub struct TupleStruct(usize); + + #[pinned_drop] + impl PinnedDrop for TupleStruct { + fn drop(mut self: Pin<&mut Self>) { + let _: Self = Self(0); + } + } +} + +#[rustversion::since(1.37)] +#[test] +fn self_expr_enum() { + #[pin_project(PinnedDrop)] + pub enum Enum { + StructVariant { x: usize }, + TupleVariant(usize), + } + + #[pinned_drop] + impl PinnedDrop for Enum { + fn drop(mut self: Pin<&mut Self>) { + let _: Self = Self::StructVariant { x: 0 }; + let _: Self = Self::TupleVariant(0); + } + } +} + +#[test] +fn self_pat() { + #[pin_project(PinnedDrop)] + pub struct Struct { + x: usize, + } + + #[pinned_drop] + impl PinnedDrop for Struct { + #[allow(irrefutable_let_patterns)] + #[allow(clippy::match_single_binding)] + fn drop(mut self: Pin<&mut Self>) { + match *self { + Self { x: _ } => {} + } + if let Self { x: _ } = *self {} + let Self { x: _ } = *self; + } + } + + #[pin_project(PinnedDrop)] + pub struct TupleStruct(usize); + + #[pinned_drop] + impl PinnedDrop for TupleStruct { + #[allow(irrefutable_let_patterns)] + fn drop(mut self: Pin<&mut Self>) { + match *self { + Self(_) => {} + } + if let Self(_) = *self {} + let Self(_) = *self; + } + } +} + +#[rustversion::since(1.37)] +#[test] +fn self_pat_enum() { + #[pin_project(PinnedDrop)] + pub enum Enum { + StructVariant { x: usize }, + TupleVariant(usize), + } + + #[pinned_drop] + impl PinnedDrop for Enum { + fn drop(mut self: Pin<&mut Self>) { + match *self { + Self::StructVariant { x: _ } => {} + Self::TupleVariant(_) => {} + } + if let Self::StructVariant { x: _ } = *self {} + if let Self::TupleVariant(_) = *self {} + } + } +} + +// See also `ui/pinned_drop/self.rs`. +#[rustversion::since(1.40)] // https://github.com/rust-lang/rust/pull/64690 +#[test] +fn self_in_macro_def() { + #[pin_project(PinnedDrop)] + pub struct Struct { + x: usize, + } + + #[pinned_drop] + impl PinnedDrop for Struct { + fn drop(self: Pin<&mut Self>) { + macro_rules! t { + () => {{ + let _ = self; + }}; + } + t!(); + } + } +} diff --git a/tests/project.rs b/tests/project.rs new file mode 100644 index 0000000..a0f8b07 --- /dev/null +++ b/tests/project.rs @@ -0,0 +1,297 @@ +#![warn(rust_2018_idioms, single_use_lifetimes)] +#![allow(dead_code)] + +// Ceurrently, `#[attr] if true {}` doesn't even *parse* on MSRV, +// which means that it will error even behind a `#[rustversion::since(..)]` +// +// This trick makes sure that we don't even attempt to parse +// the `#[project] if let _` test on MSRV. +#[rustversion::since(1.43)] +include!("project_if_attr.rs.in"); + +use pin_project::{pin_project, project, project_ref, project_replace}; +use std::pin::Pin; + +#[project] // Nightly does not need a dummy attribute to the function. +#[test] +fn project_stmt_expr() { + #[pin_project] + struct Struct { + #[pin] + field1: T, + field2: U, + } + + let mut s = Struct { field1: 1, field2: 2 }; + + #[project] + let Struct { field1, field2 } = Pin::new(&mut s).project(); + + let x: Pin<&mut i32> = field1; + assert_eq!(*x, 1); + + let y: &mut i32 = field2; + assert_eq!(*y, 2); + + #[pin_project] + struct TupleStruct(#[pin] T, U); + + let mut s = TupleStruct(1, 2); + + #[project] + let TupleStruct(x, y) = Pin::new(&mut s).project(); + + let x: Pin<&mut i32> = x; + assert_eq!(*x, 1); + + let y: &mut i32 = y; + assert_eq!(*y, 2); + + #[pin_project] + enum Enum { + Variant1(#[pin] A, B), + Variant2 { + #[pin] + field1: C, + field2: D, + }, + None, + } + + let mut e = Enum::Variant1(1, 2); + + let mut e = Pin::new(&mut e).project(); + + #[project] + match &mut e { + Enum::Variant1(x, y) => { + let x: &mut Pin<&mut i32> = x; + assert_eq!(**x, 1); + + let y: &mut &mut i32 = y; + assert_eq!(**y, 2); + } + Enum::Variant2 { field1, field2 } => { + let _x: &mut Pin<&mut i32> = field1; + let _y: &mut &mut i32 = field2; + } + Enum::None => {} + } + + #[project] + let val = match &mut e { + Enum::Variant1(_, _) => true, + Enum::Variant2 { .. } => false, + Enum::None => false, + }; + assert_eq!(val, true); +} + +#[test] +fn project_impl() { + #[pin_project] + struct HasGenerics { + #[pin] + field1: T, + field2: U, + } + + #[project] + impl HasGenerics { + fn a(self) { + let Self { field1, field2 } = self; + + let _x: Pin<&mut T> = field1; + let _y: &mut U = field2; + } + } + + #[pin_project] + struct NoneGenerics { + #[pin] + field1: i32, + field2: u32, + } + + #[project] + impl NoneGenerics {} + + #[pin_project] + struct HasLifetimes<'a, T, U> { + #[pin] + field1: &'a mut T, + field2: U, + } + + #[project] + impl HasLifetimes<'_, T, U> {} + + #[pin_project] + struct HasOverlappingLifetimes<'pin, T, U> { + #[pin] + field1: &'pin mut T, + field2: U, + } + + #[allow(single_use_lifetimes)] + #[project] + impl<'pin, T, U> HasOverlappingLifetimes<'pin, T, U> {} + + #[pin_project] + struct HasOverlappingLifetimes2 { + #[pin] + field1: T, + field2: U, + } + + #[allow(single_use_lifetimes)] + #[allow(clippy::needless_lifetimes)] + #[project] + impl HasOverlappingLifetimes2 { + fn foo<'pin>(&'pin self) {} + } +} + +#[pin_project] +struct A { + #[pin] + field: u8, +} + +mod project_use_1 { + use crate::A; + use core::pin::Pin; + use pin_project::project; + + #[project] + use crate::A; + + #[project] + #[test] + fn project_use() { + let mut x = A { field: 0 }; + #[project] + let A { field } = Pin::new(&mut x).project(); + let _: Pin<&mut u8> = field; + } +} + +mod project_use_2 { + #[project] + use crate::A; + use pin_project::project; + + #[project] + impl A { + fn project_use(self) {} + } +} + +#[allow(clippy::unnecessary_operation, clippy::unit_arg)] +#[test] +#[project] +fn non_stmt_expr_match() { + #[pin_project] + enum Enum { + Variant(#[pin] A), + } + + let mut x = Enum::Variant(1); + let x = Pin::new(&mut x).project(); + + Some( + #[project] + match x { + Enum::Variant(_x) => {} + }, + ); +} + +// https://github.com/taiki-e/pin-project/issues/206 +#[allow(clippy::unnecessary_operation, clippy::unit_arg)] +#[test] +#[project] +fn issue_206() { + #[pin_project] + enum Enum { + Variant(#[pin] A), + } + + let mut x = Enum::Variant(1); + let x = Pin::new(&mut x).project(); + + Some({ + #[project] + match &x { + Enum::Variant(_) => {} + } + }); + + #[allow(clippy::never_loop)] + loop { + let _ = { + #[project] + match &x { + Enum::Variant(_) => {} + } + }; + break; + } +} + +#[project] +#[test] +fn combine() { + #[pin_project(Replace)] + enum Enum { + V1(#[pin] A), + V2, + } + + let mut x = Enum::V1(1); + #[project] + match Pin::new(&mut x).project() { + Enum::V1(_) => {} + Enum::V2 => unreachable!(), + } + #[project_ref] + match Pin::new(&x).project_ref() { + Enum::V1(_) => {} + Enum::V2 => unreachable!(), + } + #[project_replace] + match Pin::new(&mut x).project_replace(Enum::V2) { + Enum::V1(_) => {} + Enum::V2 => unreachable!(), + } +} + +// FIXME: This should be denied, but allowed for compatibility at this time. +#[project] +#[project_ref] +#[project_replace] +#[test] +fn combine_compat() { + #[pin_project(Replace)] + enum Enum { + V1(#[pin] A), + V2, + } + + let mut x = Enum::V1(1); + #[project] + match Pin::new(&mut x).project() { + Enum::V1(_) => {} + Enum::V2 => unreachable!(), + } + #[project_ref] + match Pin::new(&x).project_ref() { + Enum::V1(_) => {} + Enum::V2 => unreachable!(), + } + #[project_replace] + match Pin::new(&mut x).project_replace(Enum::V2) { + Enum::V1(_) => {} + Enum::V2 => unreachable!(), + } +} diff --git a/tests/project_if_attr.rs.in b/tests/project_if_attr.rs.in new file mode 100644 index 0000000..a8ceeac --- /dev/null +++ b/tests/project_if_attr.rs.in @@ -0,0 +1,44 @@ +#[test] +#[project] +fn project_if_let() { + #[pin_project] + enum Foo { + Variant1(#[pin] A), + Variant2(u8), + Variant3 { + #[pin] field: B + } + } + + let mut x: Foo = Foo::Variant1(true); + let x = Pin::new(&mut x).project(); + + #[project] + if let Foo::Variant1(a) = x { + let a: Pin<&mut bool> = a; + assert_eq!(*a, true); + } else if let Foo::Variant2(_) = x { + unreachable!(); + } else if let Foo::Variant3 { .. } = x { + unreachable!(); + } +} + +#[allow(clippy::unnecessary_operation, clippy::unit_arg)] +#[test] +#[project] +fn non_stmt_expr_if_let() { + #[pin_project] + enum Enum { + Variant(#[pin] A), + } + + let mut x = Enum::Variant(1); + let x = Pin::new(&mut x).project(); + + #[allow(irrefutable_let_patterns)] + Some( + #[project] + if let Enum::Variant(_x) = x {}, + ); +} diff --git a/tests/project_ref.rs b/tests/project_ref.rs new file mode 100644 index 0000000..e38ef83 --- /dev/null +++ b/tests/project_ref.rs @@ -0,0 +1,174 @@ +#![warn(rust_2018_idioms, single_use_lifetimes)] +#![allow(dead_code)] + +use pin_project::{pin_project, project_ref}; +use std::pin::Pin; + +#[project_ref] // Nightly does not need a dummy attribute to the function. +#[test] +fn project_stmt_expr() { + #[pin_project] + struct Struct { + #[pin] + field1: T, + field2: U, + } + + let s = Struct { field1: 1, field2: 2 }; + + #[project_ref] + let Struct { field1, field2 } = Pin::new(&s).project_ref(); + + let x: Pin<&i32> = field1; + assert_eq!(*x, 1); + + let y: &i32 = field2; + assert_eq!(*y, 2); + + // tuple struct + + #[pin_project] + struct TupleStruct(#[pin] T, U); + + let s = TupleStruct(1, 2); + + #[project_ref] + let TupleStruct(x, y) = Pin::new(&s).project_ref(); + + let x: Pin<&i32> = x; + assert_eq!(*x, 1); + + let y: &i32 = y; + assert_eq!(*y, 2); + + #[pin_project] + enum Enum { + Variant1(#[pin] A, B), + Variant2 { + #[pin] + field1: C, + field2: D, + }, + None, + } + + let e = Enum::Variant1(1, 2); + + let e = Pin::new(&e).project_ref(); + + #[project_ref] + match &e { + Enum::Variant1(x, y) => { + let x: &Pin<&i32> = x; + assert_eq!(**x, 1); + + let y: &&i32 = y; + assert_eq!(**y, 2); + } + Enum::Variant2 { field1, field2 } => { + let _x: &Pin<&i32> = field1; + let _y: &&i32 = field2; + } + Enum::None => {} + } + + #[project_ref] + let val = match &e { + Enum::Variant1(_, _) => true, + Enum::Variant2 { .. } => false, + Enum::None => false, + }; + assert_eq!(val, true); +} + +#[test] +fn project_impl() { + #[pin_project] + struct HasGenerics { + #[pin] + field1: T, + field2: U, + } + + #[project_ref] + impl HasGenerics { + fn a(self) { + let Self { field1, field2 } = self; + + let _x: Pin<&T> = field1; + let _y: &U = field2; + } + } + + #[pin_project] + struct NoneGenerics { + #[pin] + field1: i32, + field2: u32, + } + + #[project_ref] + impl NoneGenerics {} + + #[pin_project] + struct HasLifetimes<'a, T, U> { + #[pin] + field1: &'a mut T, + field2: U, + } + + #[project_ref] + impl HasLifetimes<'_, T, U> {} + + #[pin_project] + struct HasOverlappingLifetimes<'pin, T, U> { + #[pin] + field1: &'pin mut T, + field2: U, + } + + #[allow(single_use_lifetimes)] + #[project_ref] + impl<'pin, T, U> HasOverlappingLifetimes<'pin, T, U> {} + + #[pin_project] + struct HasOverlappingLifetimes2 { + #[pin] + field1: T, + field2: U, + } + + #[allow(single_use_lifetimes)] + #[allow(clippy::needless_lifetimes)] + #[project_ref] + impl HasOverlappingLifetimes2 { + fn foo<'pin>(&'pin self) {} + } +} + +#[project_ref] +#[test] +fn combine() { + #[pin_project(Replace)] + enum Enum { + V1(#[pin] A), + V2, + } + + let mut x = Enum::V1(1); + #[project] + match Pin::new(&mut x).project() { + Enum::V1(_) => {} + Enum::V2 => unreachable!(), + } + #[project_ref] + match Pin::new(&x).project_ref() { + Enum::V1(_) => {} + Enum::V2 => unreachable!(), + } + #[project_replace] + match Pin::new(&mut x).project_replace(Enum::V2) { + Enum::V1(_) => {} + Enum::V2 => unreachable!(), + } +} diff --git a/tests/project_replace.rs b/tests/project_replace.rs new file mode 100644 index 0000000..9c8a5ab --- /dev/null +++ b/tests/project_replace.rs @@ -0,0 +1,98 @@ +#![warn(rust_2018_idioms, single_use_lifetimes)] +#![allow(dead_code)] + +use pin_project::{pin_project, project_replace}; +use std::{marker::PhantomData, pin::Pin}; + +#[project_replace] // Nightly does not need a dummy attribute to the function. +#[test] +fn project_replace_stmt_expr() { + #[pin_project(Replace)] + struct Struct { + #[pin] + field1: T, + field2: U, + } + + let mut s = Struct { field1: 1, field2: 2 }; + + #[project_replace] + let Struct { field1, field2 } = + Pin::new(&mut s).project_replace(Struct { field1: 42, field2: 43 }); + + let _x: PhantomData = field1; + + let y: i32 = field2; + assert_eq!(y, 2); + + // tuple struct + + #[pin_project(Replace)] + struct TupleStruct(#[pin] T, U); + + let mut s = TupleStruct(1, 2); + + #[project_replace] + let TupleStruct(x, y) = Pin::new(&mut s).project_replace(TupleStruct(42, 43)); + + let _x: PhantomData = x; + let y: i32 = y; + assert_eq!(y, 2); + + #[pin_project(Replace)] + enum Enum { + Variant1(#[pin] A, B), + Variant2 { + #[pin] + field1: C, + field2: D, + }, + None, + } + + let mut e = Enum::Variant1(1, 2); + + let e = Pin::new(&mut e).project_replace(Enum::None); + + #[project_replace] + match e { + Enum::Variant1(x, y) => { + let _x: PhantomData = x; + let y: i32 = y; + assert_eq!(y, 2); + } + Enum::Variant2 { field1, field2 } => { + let _x: PhantomData = field1; + let _y: i32 = field2; + panic!() + } + Enum::None => panic!(), + } +} + +#[project_replace] +#[test] +fn combine() { + #[pin_project(Replace)] + enum Enum { + V1(#[pin] A), + V2, + } + + let mut x = Enum::V1(1); + #[project] + match Pin::new(&mut x).project() { + Enum::V1(_) => {} + Enum::V2 => unreachable!(), + } + #[project_ref] + match Pin::new(&x).project_ref() { + Enum::V1(_) => {} + Enum::V2 => unreachable!(), + } + #[project_replace] + match Pin::new(&mut x).project_replace(Enum::V2) { + Enum::V1(_) => {} + Enum::V2 => unreachable!(), + } +} diff --git a/tests/repr_packed.rs b/tests/repr_packed.rs new file mode 100644 index 0000000..ca56959 --- /dev/null +++ b/tests/repr_packed.rs @@ -0,0 +1,50 @@ +#![warn(rust_2018_idioms, single_use_lifetimes)] +#![allow(dead_code)] +#![deny(safe_packed_borrows)] + +use std::cell::Cell; + +// Ensure that the compiler doesn't copy the fields +// of #[repr(packed)] types during drop, if the field has alignment 1 +// (that is, any reference to the field is guaranteed to have proper alignment) +// We are currently unable to statically prevent the usage of #[pin_project] +// on #[repr(packed)] types composed entirely of fields of alignment 1. +// This shouldn't lead to undefined behavior, as long as the compiler doesn't +// try to move the field anyway during drop. +// +// This tests validates that the compiler is doing what we expect. +#[test] +fn weird_repr_packed() { + // We keep track of the field address during + // drop using a thread local, to avoid changing + // the layout of our #[repr(packed)] type. + thread_local! { + static FIELD_ADDR: Cell = Cell::new(0); + } + + #[repr(packed)] + struct Foo { + field: u8, + } + + impl Drop for Foo { + fn drop(&mut self) { + FIELD_ADDR.with(|f| { + f.set(&self.field as *const u8 as usize); + }) + } + } + + #[allow(clippy::let_and_return)] + let field_addr = { + // We let this field drop by going out of scope, + // rather than explicitly calling drop(foo). + // Calling drop(foo) causes 'foo' to be moved + // into the 'drop' function, resulting in a different + // address. + let x = Foo { field: 27 }; + let field_addr = &x.field as *const u8 as usize; + field_addr + }; + assert_eq!(field_addr, FIELD_ADDR.with(|f| f.get())); +} diff --git a/tests/ui/cfg/cfg_attr-resolve.rs b/tests/ui/cfg/cfg_attr-resolve.rs new file mode 100644 index 0000000..e16f3e8 --- /dev/null +++ b/tests/ui/cfg/cfg_attr-resolve.rs @@ -0,0 +1,11 @@ +use std::pin::Pin; + +#[cfg_attr(any(), pin_project::pin_project)] +struct Foo { + inner: T, +} + +fn main() { + let mut x = Foo { inner: 0_u8 }; + let _x = Pin::new(&mut x).project(); //~ ERROR E0599 +} diff --git a/tests/ui/cfg/cfg_attr-resolve.stderr b/tests/ui/cfg/cfg_attr-resolve.stderr new file mode 100644 index 0000000..45af3ae --- /dev/null +++ b/tests/ui/cfg/cfg_attr-resolve.stderr @@ -0,0 +1,5 @@ +error[E0599]: no method named `project` found for struct `std::pin::Pin<&mut Foo>` in the current scope + --> $DIR/cfg_attr-resolve.rs:10:31 + | +10 | let _x = Pin::new(&mut x).project(); //~ ERROR E0599 + | ^^^^^^^ method not found in `std::pin::Pin<&mut Foo>` diff --git a/tests/ui/cfg/cfg_attr-type-mismatch.rs b/tests/ui/cfg/cfg_attr-type-mismatch.rs new file mode 100644 index 0000000..2807c87 --- /dev/null +++ b/tests/ui/cfg/cfg_attr-type-mismatch.rs @@ -0,0 +1,24 @@ +use pin_project::pin_project; +use std::pin::Pin; + +#[cfg_attr(not(any()), pin_project)] +struct Foo { + #[cfg_attr(any(), pin)] + inner: T, +} + +#[cfg_attr(not(any()), pin_project)] +struct Bar { + #[cfg_attr(not(any()), pin)] + inner: T, +} + +fn main() { + let mut x = Foo { inner: 0_u8 }; + let x = Pin::new(&mut x).project(); + let _: Pin<&mut u8> = x.inner; //~ ERROR E0308 + + let mut x = Bar { inner: 0_u8 }; + let x = Pin::new(&mut x).project(); + let _: &mut u8 = x.inner; //~ ERROR E0308 +} diff --git a/tests/ui/cfg/cfg_attr-type-mismatch.stderr b/tests/ui/cfg/cfg_attr-type-mismatch.stderr new file mode 100644 index 0000000..2868299 --- /dev/null +++ b/tests/ui/cfg/cfg_attr-type-mismatch.stderr @@ -0,0 +1,23 @@ +error[E0308]: mismatched types + --> $DIR/cfg_attr-type-mismatch.rs:19:27 + | +19 | let _: Pin<&mut u8> = x.inner; //~ ERROR E0308 + | ------------ ^^^^^^^ expected struct `std::pin::Pin`, found `&mut u8` + | | + | expected due to this + | + = note: expected struct `std::pin::Pin<&mut u8>` + found mutable reference `&mut u8` + +error[E0308]: mismatched types + --> $DIR/cfg_attr-type-mismatch.rs:23:22 + | +23 | let _: &mut u8 = x.inner; //~ ERROR E0308 + | ------- ^^^^^^^ + | | | + | | expected `&mut u8`, found struct `std::pin::Pin` + | | help: consider mutably borrowing here: `&mut x.inner` + | expected due to this + | + = note: expected mutable reference `&mut u8` + found struct `std::pin::Pin<&mut u8>` diff --git a/tests/ui/cfg/cfg_attr-unpin.rs b/tests/ui/cfg/cfg_attr-unpin.rs new file mode 100644 index 0000000..7b88205 --- /dev/null +++ b/tests/ui/cfg/cfg_attr-unpin.rs @@ -0,0 +1,21 @@ +use pin_project::pin_project; +use std::marker::PhantomPinned; + +#[cfg_attr(any(), pin_project)] +struct Foo { + inner: T, +} + +#[cfg_attr(not(any()), pin_project)] +struct Bar { + #[cfg_attr(not(any()), pin)] + inner: T, +} + +fn is_unpin() {} + +fn main() { + is_unpin::>(); // ERROR E0277 + is_unpin::>(); // Ok + is_unpin::>(); //~ ERROR E0277 +} diff --git a/tests/ui/cfg/cfg_attr-unpin.stderr b/tests/ui/cfg/cfg_attr-unpin.stderr new file mode 100644 index 0000000..ce31c24 --- /dev/null +++ b/tests/ui/cfg/cfg_attr-unpin.stderr @@ -0,0 +1,22 @@ +error[E0277]: `std::marker::PhantomPinned` cannot be unpinned + --> $DIR/cfg_attr-unpin.rs:18:5 + | +15 | fn is_unpin() {} + | ----- required by this bound in `is_unpin` +... +18 | is_unpin::>(); // ERROR E0277 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ within `Foo`, the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned` + | + = note: required because it appears within the type `Foo` + +error[E0277]: `std::marker::PhantomPinned` cannot be unpinned + --> $DIR/cfg_attr-unpin.rs:20:5 + | +15 | fn is_unpin() {} + | ----- required by this bound in `is_unpin` +... +20 | is_unpin::>(); //~ ERROR E0277 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ within `__SCOPE_Bar::__Bar<'_, std::marker::PhantomPinned>`, the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned` + | + = note: required because it appears within the type `__SCOPE_Bar::__Bar<'_, std::marker::PhantomPinned>` + = note: required because of the requirements on the impl of `std::marker::Unpin` for `Bar` diff --git a/tests/ui/cfg/packed_sneaky-span-issue-1.rs b/tests/ui/cfg/packed_sneaky-span-issue-1.rs new file mode 100644 index 0000000..3776dac --- /dev/null +++ b/tests/ui/cfg/packed_sneaky-span-issue-1.rs @@ -0,0 +1,18 @@ +use auxiliary_macros::hidden_repr; +use pin_project::pin_project; + +//~ ERROR may not be used on #[repr(packed)] types +// span is lost. +// Refs: https://github.com/rust-lang/rust/issues/43081 +#[pin_project] +#[hidden_repr(packed)] +struct Foo { + #[cfg(not(any()))] + #[pin] + field: u32, + #[cfg(any())] + #[pin] + field: u8, +} + +fn main() {} diff --git a/tests/ui/cfg/packed_sneaky-span-issue-1.stderr b/tests/ui/cfg/packed_sneaky-span-issue-1.stderr new file mode 100644 index 0000000..f4d7dee --- /dev/null +++ b/tests/ui/cfg/packed_sneaky-span-issue-1.stderr @@ -0,0 +1 @@ +error: #[pin_project] attribute may not be used on #[repr(packed)] types diff --git a/tests/ui/cfg/packed_sneaky-span-issue-2.rs b/tests/ui/cfg/packed_sneaky-span-issue-2.rs new file mode 100644 index 0000000..aa65d33 --- /dev/null +++ b/tests/ui/cfg/packed_sneaky-span-issue-2.rs @@ -0,0 +1,18 @@ +use auxiliary_macros::hidden_repr; +use pin_project::pin_project; + +//~ ERROR may not be used on #[repr(packed)] types +// span is lost. +// Refs: https://github.com/rust-lang/rust/issues/43081 +#[pin_project] +#[hidden_repr(packed)] +struct Foo { + #[cfg(any())] + #[pin] + field: u32, + #[cfg(not(any()))] + #[pin] + field: u8, +} + +fn main() {} diff --git a/tests/ui/cfg/packed_sneaky-span-issue-2.stderr b/tests/ui/cfg/packed_sneaky-span-issue-2.stderr new file mode 100644 index 0000000..f4d7dee --- /dev/null +++ b/tests/ui/cfg/packed_sneaky-span-issue-2.stderr @@ -0,0 +1 @@ +error: #[pin_project] attribute may not be used on #[repr(packed)] types diff --git a/tests/ui/cfg/packed_sneaky.rs b/tests/ui/cfg/packed_sneaky.rs new file mode 100644 index 0000000..3305ed3 --- /dev/null +++ b/tests/ui/cfg/packed_sneaky.rs @@ -0,0 +1,12 @@ +use auxiliary_macros::hidden_repr_cfg_not_any; +use pin_project::pin_project; + +// `#[hidden_repr_cfg_not_any(packed)]` generates `#[cfg_attr(not(any()), repr(packed))]`. +#[pin_project] +#[hidden_repr_cfg_not_any(packed)] //~ ERROR may not be used on #[repr(packed)] types +struct Foo { + #[pin] + field: u32, +} + +fn main() {} diff --git a/tests/ui/cfg/packed_sneaky.stderr b/tests/ui/cfg/packed_sneaky.stderr new file mode 100644 index 0000000..5910cf4 --- /dev/null +++ b/tests/ui/cfg/packed_sneaky.stderr @@ -0,0 +1,7 @@ +error: #[pin_project] attribute may not be used on #[repr(packed)] types + --> $DIR/packed_sneaky.rs:6:1 + | +6 | #[hidden_repr_cfg_not_any(packed)] //~ ERROR may not be used on #[repr(packed)] types + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/cfg/proper_unpin.rs b/tests/ui/cfg/proper_unpin.rs new file mode 100644 index 0000000..b7bb04d --- /dev/null +++ b/tests/ui/cfg/proper_unpin.rs @@ -0,0 +1,28 @@ +use pin_project::pin_project; +use std::marker::PhantomPinned; + +#[pin_project] +struct Foo { + #[cfg(any())] + #[pin] + inner: T, + #[cfg(not(any()))] + inner: T, +} + +#[pin_project] +struct Bar { + #[cfg(any())] + inner: T, + #[cfg(not(any()))] + #[pin] + inner: T, +} + +fn is_unpin() {} + +fn main() { + is_unpin::>(); // Ok + is_unpin::>(); // Ok + is_unpin::>(); //~ ERROR E0277 +} diff --git a/tests/ui/cfg/proper_unpin.stderr b/tests/ui/cfg/proper_unpin.stderr new file mode 100644 index 0000000..407d900 --- /dev/null +++ b/tests/ui/cfg/proper_unpin.stderr @@ -0,0 +1,11 @@ +error[E0277]: `std::marker::PhantomPinned` cannot be unpinned + --> $DIR/proper_unpin.rs:27:5 + | +22 | fn is_unpin() {} + | ----- required by this bound in `is_unpin` +... +27 | is_unpin::>(); //~ ERROR E0277 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ within `__SCOPE_Bar::__Bar<'_, std::marker::PhantomPinned>`, the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned` + | + = note: required because it appears within the type `__SCOPE_Bar::__Bar<'_, std::marker::PhantomPinned>` + = note: required because of the requirements on the impl of `std::marker::Unpin` for `Bar` diff --git a/tests/ui/cfg/unsupported.rs b/tests/ui/cfg/unsupported.rs new file mode 100644 index 0000000..5205307 --- /dev/null +++ b/tests/ui/cfg/unsupported.rs @@ -0,0 +1,13 @@ +use pin_project::pin_project; + +//~ ERROR may not be used on structs with zero fields +// span is lost. +// Refs: https://github.com/rust-lang/rust/issues/43081 +#[pin_project] +struct Struct { + #[cfg(any())] + #[pin] + f: u8, +} + +fn main() {} diff --git a/tests/ui/cfg/unsupported.stderr b/tests/ui/cfg/unsupported.stderr new file mode 100644 index 0000000..0ee8676 --- /dev/null +++ b/tests/ui/cfg/unsupported.stderr @@ -0,0 +1 @@ +error: #[pin_project] attribute may not be used on structs with zero fields diff --git a/tests/ui/not_unpin/assert-not-unpin.rs b/tests/ui/not_unpin/assert-not-unpin.rs new file mode 100644 index 0000000..b8f8238 --- /dev/null +++ b/tests/ui/not_unpin/assert-not-unpin.rs @@ -0,0 +1,40 @@ +use pin_project::pin_project; +use std::marker::PhantomPinned; + +struct Inner { + val: T, +} + +#[pin_project(!Unpin)] +struct Foo { + #[pin] + inner: Inner, + other: U, +} + +#[pin_project(!Unpin)] +struct TrivialBounds { + #[pin] + field1: PhantomPinned, +} + +#[pin_project(!Unpin)] +struct Bar<'a, T, U> { + #[pin] + inner: &'a mut Inner, + other: U, +} + +fn is_unpin() {} + +fn main() { + is_unpin::>(); //~ ERROR E0277 + is_unpin::>(); //~ ERROR E0277 + is_unpin::>(); //~ ERROR E0277 + is_unpin::>(); //~ ERROR E0277 + + is_unpin::(); //~ ERROR E0277 + + is_unpin::>(); //~ ERROR E0277 + is_unpin::>(); //~ ERROR E0277 +} diff --git a/tests/ui/not_unpin/assert-not-unpin.stderr b/tests/ui/not_unpin/assert-not-unpin.stderr new file mode 100644 index 0000000..5e323fc --- /dev/null +++ b/tests/ui/not_unpin/assert-not-unpin.stderr @@ -0,0 +1,83 @@ +error[E0277]: `std::marker::PhantomPinned` cannot be unpinned + --> $DIR/assert-not-unpin.rs:31:5 + | +28 | fn is_unpin() {} + | ----- required by this bound in `is_unpin` +... +31 | is_unpin::>(); //~ ERROR E0277 + | ^^^^^^^^^^^^^^^^^^^^^^^ within `pin_project::__private::Wrapper<'_, std::marker::PhantomPinned>`, the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned` + | + = note: required because it appears within the type `pin_project::__private::Wrapper<'_, std::marker::PhantomPinned>` + = note: required because of the requirements on the impl of `std::marker::Unpin` for `Foo<(), ()>` + +error[E0277]: `std::marker::PhantomPinned` cannot be unpinned + --> $DIR/assert-not-unpin.rs:32:5 + | +28 | fn is_unpin() {} + | ----- required by this bound in `is_unpin` +... +32 | is_unpin::>(); //~ ERROR E0277 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ within `pin_project::__private::Wrapper<'_, std::marker::PhantomPinned>`, the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned` + | + = note: required because it appears within the type `pin_project::__private::Wrapper<'_, std::marker::PhantomPinned>` + = note: required because of the requirements on the impl of `std::marker::Unpin` for `Foo` + +error[E0277]: `std::marker::PhantomPinned` cannot be unpinned + --> $DIR/assert-not-unpin.rs:33:5 + | +28 | fn is_unpin() {} + | ----- required by this bound in `is_unpin` +... +33 | is_unpin::>(); //~ ERROR E0277 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ within `pin_project::__private::Wrapper<'_, std::marker::PhantomPinned>`, the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned` + | + = note: required because it appears within the type `pin_project::__private::Wrapper<'_, std::marker::PhantomPinned>` + = note: required because of the requirements on the impl of `std::marker::Unpin` for `Foo<(), std::marker::PhantomPinned>` + +error[E0277]: `std::marker::PhantomPinned` cannot be unpinned + --> $DIR/assert-not-unpin.rs:34:5 + | +28 | fn is_unpin() {} + | ----- required by this bound in `is_unpin` +... +34 | is_unpin::>(); //~ ERROR E0277 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ within `pin_project::__private::Wrapper<'_, std::marker::PhantomPinned>`, the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned` + | + = note: required because it appears within the type `pin_project::__private::Wrapper<'_, std::marker::PhantomPinned>` + = note: required because of the requirements on the impl of `std::marker::Unpin` for `Foo` + +error[E0277]: `std::marker::PhantomPinned` cannot be unpinned + --> $DIR/assert-not-unpin.rs:36:5 + | +28 | fn is_unpin() {} + | ----- required by this bound in `is_unpin` +... +36 | is_unpin::(); //~ ERROR E0277 + | ^^^^^^^^^^^^^^^^^^^^^^^^^ within `pin_project::__private::Wrapper<'_, std::marker::PhantomPinned>`, the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned` + | + = note: required because it appears within the type `pin_project::__private::Wrapper<'_, std::marker::PhantomPinned>` + = note: required because of the requirements on the impl of `std::marker::Unpin` for `TrivialBounds` + +error[E0277]: `std::marker::PhantomPinned` cannot be unpinned + --> $DIR/assert-not-unpin.rs:38:5 + | +28 | fn is_unpin() {} + | ----- required by this bound in `is_unpin` +... +38 | is_unpin::>(); //~ ERROR E0277 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ within `pin_project::__private::Wrapper<'_, std::marker::PhantomPinned>`, the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned` + | + = note: required because it appears within the type `pin_project::__private::Wrapper<'_, std::marker::PhantomPinned>` + = note: required because of the requirements on the impl of `std::marker::Unpin` for `Bar<'_, (), ()>` + +error[E0277]: `std::marker::PhantomPinned` cannot be unpinned + --> $DIR/assert-not-unpin.rs:39:5 + | +28 | fn is_unpin() {} + | ----- required by this bound in `is_unpin` +... +39 | is_unpin::>(); //~ ERROR E0277 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ within `pin_project::__private::Wrapper<'_, std::marker::PhantomPinned>`, the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned` + | + = note: required because it appears within the type `pin_project::__private::Wrapper<'_, std::marker::PhantomPinned>` + = note: required because of the requirements on the impl of `std::marker::Unpin` for `Bar<'_, std::marker::PhantomPinned, std::marker::PhantomPinned>` diff --git a/tests/ui/not_unpin/conflict-unpin.rs b/tests/ui/not_unpin/conflict-unpin.rs new file mode 100644 index 0000000..f259f6c --- /dev/null +++ b/tests/ui/not_unpin/conflict-unpin.rs @@ -0,0 +1,30 @@ +use pin_project::pin_project; + +#[pin_project(!Unpin)] //~ ERROR E0119 +struct Foo { + #[pin] + future: T, + field: U, +} + +impl Unpin for Foo where T: Unpin {} + +#[pin_project(!Unpin)] //~ ERROR E0119 +struct Bar { + #[pin] + future: T, + field: U, +} + +impl Unpin for Bar {} + +#[pin_project(!Unpin)] //~ ERROR E0119 +struct Baz { + #[pin] + future: T, + field: U, +} + +impl Unpin for Baz {} + +fn main() {} diff --git a/tests/ui/not_unpin/conflict-unpin.stderr b/tests/ui/not_unpin/conflict-unpin.stderr new file mode 100644 index 0000000..7407bdf --- /dev/null +++ b/tests/ui/not_unpin/conflict-unpin.stderr @@ -0,0 +1,26 @@ +error[E0119]: conflicting implementations of trait `std::marker::Unpin` for type `Foo<_, _>`: + --> $DIR/conflict-unpin.rs:3:16 + | +3 | #[pin_project(!Unpin)] //~ ERROR E0119 + | ^^^^^ conflicting implementation for `Foo<_, _>` +... +10 | impl Unpin for Foo where T: Unpin {} + | --------------------------------------------- first implementation here + +error[E0119]: conflicting implementations of trait `std::marker::Unpin` for type `Bar<_, _>`: + --> $DIR/conflict-unpin.rs:12:16 + | +12 | #[pin_project(!Unpin)] //~ ERROR E0119 + | ^^^^^ conflicting implementation for `Bar<_, _>` +... +19 | impl Unpin for Bar {} + | ------------------------------ first implementation here + +error[E0119]: conflicting implementations of trait `std::marker::Unpin` for type `Baz<_, _>`: + --> $DIR/conflict-unpin.rs:21:16 + | +21 | #[pin_project(!Unpin)] //~ ERROR E0119 + | ^^^^^ conflicting implementation for `Baz<_, _>` +... +28 | impl Unpin for Baz {} + | -------------------------------------------- first implementation here diff --git a/tests/ui/not_unpin/impl-unsafe-unpin.rs b/tests/ui/not_unpin/impl-unsafe-unpin.rs new file mode 100644 index 0000000..625dc29 --- /dev/null +++ b/tests/ui/not_unpin/impl-unsafe-unpin.rs @@ -0,0 +1,30 @@ +use pin_project::{pin_project, UnsafeUnpin}; + +#[pin_project(!Unpin)] //~ ERROR E0119 +struct Foo { + #[pin] + future: T, + field: U, +} + +unsafe impl UnsafeUnpin for Foo where T: Unpin {} + +#[pin_project(!Unpin)] //~ ERROR E0119 +struct Bar { + #[pin] + future: T, + field: U, +} + +unsafe impl UnsafeUnpin for Bar {} + +#[pin_project(!Unpin)] //~ ERROR E0119 +struct Baz { + #[pin] + future: T, + field: U, +} + +unsafe impl UnsafeUnpin for Baz {} + +fn main() {} diff --git a/tests/ui/not_unpin/impl-unsafe-unpin.stderr b/tests/ui/not_unpin/impl-unsafe-unpin.stderr new file mode 100644 index 0000000..ba80d5e --- /dev/null +++ b/tests/ui/not_unpin/impl-unsafe-unpin.stderr @@ -0,0 +1,32 @@ +error[E0119]: conflicting implementations of trait `pin_project::UnsafeUnpin` for type `Foo<_, _>`: + --> $DIR/impl-unsafe-unpin.rs:3:1 + | +3 | #[pin_project(!Unpin)] //~ ERROR E0119 + | ^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Foo<_, _>` +... +10 | unsafe impl UnsafeUnpin for Foo where T: Unpin {} + | ---------------------------------------------------------- first implementation here + | + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0119]: conflicting implementations of trait `pin_project::UnsafeUnpin` for type `Bar<_, _>`: + --> $DIR/impl-unsafe-unpin.rs:12:1 + | +12 | #[pin_project(!Unpin)] //~ ERROR E0119 + | ^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Bar<_, _>` +... +19 | unsafe impl UnsafeUnpin for Bar {} + | ------------------------------------------- first implementation here + | + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0119]: conflicting implementations of trait `pin_project::UnsafeUnpin` for type `Baz<_, _>`: + --> $DIR/impl-unsafe-unpin.rs:21:1 + | +21 | #[pin_project(!Unpin)] //~ ERROR E0119 + | ^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Baz<_, _>` +... +28 | unsafe impl UnsafeUnpin for Baz {} + | --------------------------------------------------------- first implementation here + | + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/pin_project/add-pin-attr-to-struct.rs b/tests/ui/pin_project/add-pin-attr-to-struct.rs new file mode 100644 index 0000000..f5364fc --- /dev/null +++ b/tests/ui/pin_project/add-pin-attr-to-struct.rs @@ -0,0 +1,19 @@ +use auxiliary_macros::add_pin_attr; +use pin_project::pin_project; +use std::marker::PhantomPinned; + +#[pin_project] +#[add_pin_attr(struct)] //~ ERROR duplicate #[pin] attribute +struct Foo { + #[pin] + field: PhantomPinned, +} + +#[add_pin_attr(struct)] //~ ERROR #[pin] attribute may only be used on fields of structs or variants +#[pin_project] +struct Bar { + #[pin] + field: PhantomPinned, +} + +fn main() {} diff --git a/tests/ui/pin_project/add-pin-attr-to-struct.stderr b/tests/ui/pin_project/add-pin-attr-to-struct.stderr new file mode 100644 index 0000000..c2adaea --- /dev/null +++ b/tests/ui/pin_project/add-pin-attr-to-struct.stderr @@ -0,0 +1,15 @@ +error: duplicate #[pin] attribute + --> $DIR/add-pin-attr-to-struct.rs:6:1 + | +6 | #[add_pin_attr(struct)] //~ ERROR duplicate #[pin] attribute + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: #[pin] attribute may only be used on fields of structs or variants + --> $DIR/add-pin-attr-to-struct.rs:12:1 + | +12 | #[add_pin_attr(struct)] //~ ERROR #[pin] attribute may only be used on fields of structs or variants + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/pin_project/add-pinned-field.rs b/tests/ui/pin_project/add-pinned-field.rs new file mode 100644 index 0000000..76394cf --- /dev/null +++ b/tests/ui/pin_project/add-pinned-field.rs @@ -0,0 +1,23 @@ +use auxiliary_macros::add_pinned_field; +use pin_project::pin_project; + +fn is_unpin() {} + +#[pin_project] +#[add_pinned_field] +struct Foo { + #[pin] + field: u32, +} + +#[add_pinned_field] +#[pin_project] +struct Bar { + #[pin] + field: u32, +} + +fn main() { + is_unpin::(); //~ ERROR E0277 + is_unpin::(); //~ ERROR E0277 +} diff --git a/tests/ui/pin_project/add-pinned-field.stderr b/tests/ui/pin_project/add-pinned-field.stderr new file mode 100644 index 0000000..db07a74 --- /dev/null +++ b/tests/ui/pin_project/add-pinned-field.stderr @@ -0,0 +1,23 @@ +error[E0277]: `std::marker::PhantomPinned` cannot be unpinned + --> $DIR/add-pinned-field.rs:21:5 + | +4 | fn is_unpin() {} + | ----- required by this bound in `is_unpin` +... +21 | is_unpin::(); //~ ERROR E0277 + | ^^^^^^^^^^^^^^^ within `__SCOPE_Foo::__Foo<'_>`, the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned` + | + = note: required because it appears within the type `__SCOPE_Foo::__Foo<'_>` + = note: required because of the requirements on the impl of `std::marker::Unpin` for `Foo` + +error[E0277]: `std::marker::PhantomPinned` cannot be unpinned + --> $DIR/add-pinned-field.rs:22:5 + | +4 | fn is_unpin() {} + | ----- required by this bound in `is_unpin` +... +22 | is_unpin::(); //~ ERROR E0277 + | ^^^^^^^^^^^^^^^ within `__SCOPE_Bar::__Bar<'_>`, the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned` + | + = note: required because it appears within the type `__SCOPE_Bar::__Bar<'_>` + = note: required because of the requirements on the impl of `std::marker::Unpin` for `Bar` diff --git a/tests/ui/pin_project/conflict-drop.rs b/tests/ui/pin_project/conflict-drop.rs new file mode 100644 index 0000000..c965184 --- /dev/null +++ b/tests/ui/pin_project/conflict-drop.rs @@ -0,0 +1,31 @@ +use pin_project::{pin_project, pinned_drop}; +use std::pin::Pin; + +#[pin_project] //~ ERROR E0119 +struct Foo { + #[pin] + future: T, + field: U, +} + +impl Drop for Foo { + fn drop(&mut self) {} +} + +#[pin_project(PinnedDrop)] //~ ERROR E0119 +struct Bar { + #[pin] + future: T, + field: U, +} + +#[pinned_drop] +impl PinnedDrop for Bar { + fn drop(self: Pin<&mut Self>) {} +} + +impl Drop for Bar { + fn drop(&mut self) {} +} + +fn main() {} diff --git a/tests/ui/pin_project/conflict-drop.stderr b/tests/ui/pin_project/conflict-drop.stderr new file mode 100644 index 0000000..f97d060 --- /dev/null +++ b/tests/ui/pin_project/conflict-drop.stderr @@ -0,0 +1,19 @@ +error[E0119]: conflicting implementations of trait `__SCOPE_Foo::FooMustNotImplDrop` for type `Foo<_, _>`: + --> $DIR/conflict-drop.rs:4:1 + | +4 | #[pin_project] //~ ERROR E0119 + | ^^^^^^^^^^^^^^ + | | + | first implementation here + | conflicting implementation for `Foo<_, _>` + | + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0119]: conflicting implementations of trait `std::ops::Drop` for type `Bar<_, _>`: + --> $DIR/conflict-drop.rs:15:15 + | +15 | #[pin_project(PinnedDrop)] //~ ERROR E0119 + | ^^^^^^^^^^ conflicting implementation for `Bar<_, _>` +... +27 | impl Drop for Bar { + | ----------------------------- first implementation here diff --git a/tests/ui/pin_project/conflict-unpin.rs b/tests/ui/pin_project/conflict-unpin.rs new file mode 100644 index 0000000..0c48d27 --- /dev/null +++ b/tests/ui/pin_project/conflict-unpin.rs @@ -0,0 +1,37 @@ +use pin_project::pin_project; + +// The same implementation. + +#[pin_project] //~ ERROR E0119 +struct Foo { + #[pin] + future: T, + field: U, +} + +// conflicting implementations +impl Unpin for Foo where T: Unpin {} // Conditional Unpin impl + +// The implementation that under different conditions. + +#[pin_project] //~ ERROR E0119 +struct Bar { + #[pin] + future: T, + field: U, +} + +// conflicting implementations +impl Unpin for Bar {} // Non-conditional Unpin impl + +#[pin_project] //~ ERROR E0119 +struct Baz { + #[pin] + future: T, + field: U, +} + +// conflicting implementations +impl Unpin for Baz {} // Conditional Unpin impl + +fn main() {} diff --git a/tests/ui/pin_project/conflict-unpin.stderr b/tests/ui/pin_project/conflict-unpin.stderr new file mode 100644 index 0000000..0d6f439 --- /dev/null +++ b/tests/ui/pin_project/conflict-unpin.stderr @@ -0,0 +1,32 @@ +error[E0119]: conflicting implementations of trait `std::marker::Unpin` for type `Foo<_, _>`: + --> $DIR/conflict-unpin.rs:5:1 + | +5 | #[pin_project] //~ ERROR E0119 + | ^^^^^^^^^^^^^^ conflicting implementation for `Foo<_, _>` +... +13 | impl Unpin for Foo where T: Unpin {} // Conditional Unpin impl + | --------------------------------------------- first implementation here + | + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0119]: conflicting implementations of trait `std::marker::Unpin` for type `Bar<_, _>`: + --> $DIR/conflict-unpin.rs:17:1 + | +17 | #[pin_project] //~ ERROR E0119 + | ^^^^^^^^^^^^^^ conflicting implementation for `Bar<_, _>` +... +25 | impl Unpin for Bar {} // Non-conditional Unpin impl + | ------------------------------ first implementation here + | + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0119]: conflicting implementations of trait `std::marker::Unpin` for type `Baz<_, _>`: + --> $DIR/conflict-unpin.rs:27:1 + | +27 | #[pin_project] //~ ERROR E0119 + | ^^^^^^^^^^^^^^ conflicting implementation for `Baz<_, _>` +... +35 | impl Unpin for Baz {} // Conditional Unpin impl + | -------------------------------------------- first implementation here + | + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/pin_project/impl-unsafe-unpin.rs b/tests/ui/pin_project/impl-unsafe-unpin.rs new file mode 100644 index 0000000..94af322 --- /dev/null +++ b/tests/ui/pin_project/impl-unsafe-unpin.rs @@ -0,0 +1,30 @@ +use pin_project::{pin_project, UnsafeUnpin}; + +#[pin_project] //~ ERROR E0119 +struct Foo { + #[pin] + future: T, + field: U, +} + +unsafe impl UnsafeUnpin for Foo where T: Unpin {} + +#[pin_project] //~ ERROR E0119 +struct Bar { + #[pin] + future: T, + field: U, +} + +unsafe impl UnsafeUnpin for Bar {} + +#[pin_project] //~ ERROR E0119 +struct Baz { + #[pin] + future: T, + field: U, +} + +unsafe impl UnsafeUnpin for Baz {} + +fn main() {} diff --git a/tests/ui/pin_project/impl-unsafe-unpin.stderr b/tests/ui/pin_project/impl-unsafe-unpin.stderr new file mode 100644 index 0000000..78545c2 --- /dev/null +++ b/tests/ui/pin_project/impl-unsafe-unpin.stderr @@ -0,0 +1,32 @@ +error[E0119]: conflicting implementations of trait `pin_project::UnsafeUnpin` for type `Foo<_, _>`: + --> $DIR/impl-unsafe-unpin.rs:3:1 + | +3 | #[pin_project] //~ ERROR E0119 + | ^^^^^^^^^^^^^^ conflicting implementation for `Foo<_, _>` +... +10 | unsafe impl UnsafeUnpin for Foo where T: Unpin {} + | ---------------------------------------------------------- first implementation here + | + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0119]: conflicting implementations of trait `pin_project::UnsafeUnpin` for type `Bar<_, _>`: + --> $DIR/impl-unsafe-unpin.rs:12:1 + | +12 | #[pin_project] //~ ERROR E0119 + | ^^^^^^^^^^^^^^ conflicting implementation for `Bar<_, _>` +... +19 | unsafe impl UnsafeUnpin for Bar {} + | ------------------------------------------- first implementation here + | + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0119]: conflicting implementations of trait `pin_project::UnsafeUnpin` for type `Baz<_, _>`: + --> $DIR/impl-unsafe-unpin.rs:21:1 + | +21 | #[pin_project] //~ ERROR E0119 + | ^^^^^^^^^^^^^^ conflicting implementation for `Baz<_, _>` +... +28 | unsafe impl UnsafeUnpin for Baz {} + | --------------------------------------------------------- first implementation here + | + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/pin_project/invalid.rs b/tests/ui/pin_project/invalid.rs new file mode 100644 index 0000000..1d05608 --- /dev/null +++ b/tests/ui/pin_project/invalid.rs @@ -0,0 +1,199 @@ +mod pin_argument { + use pin_project::pin_project; + + #[pin_project] + struct Struct { + #[pin()] //~ ERROR unexpected token + field: (), + } + + #[pin_project] + struct TupleStruct(#[pin(foo)] ()); //~ ERROR unexpected token + + #[pin_project] + enum EnumTuple { + V(#[pin(foo)] ()), //~ ERROR unexpected token + } + + #[pin_project] + enum EnumStruct { + V { + #[pin(foo)] //~ ERROR unexpected token + field: (), + }, + } +} + +mod pin_attribute { + use pin_project::pin_project; + + #[pin_project] + struct DuplicateStruct { + #[pin] + #[pin] //~ ERROR duplicate #[pin] attribute + field: (), + } + + #[pin_project] + struct DuplicateTupleStruct( + #[pin] + #[pin] + (), + //~^^ ERROR duplicate #[pin] attribute + ); + + #[pin_project] + enum DuplicateEnumTuple { + V( + #[pin] + #[pin] + (), + //~^^ ERROR duplicate #[pin] attribute + ), + } + + #[pin_project] + enum DuplicateEnumStruct { + V { + #[pin] + #[pin] //~ ERROR duplicate #[pin] attribute + field: (), + }, + } +} + +mod pin_item { + use pin_project::pin_project; + + #[pin_project] + #[pin] //~ ERROR may only be used on fields of structs or variants + struct Struct { + #[pin] + field: (), + } + + #[pin_project] + enum Variant { + #[pin] //~ ERROR may only be used on fields of structs or variants + V(()), + } + + #[pin_project] + #[pin] //~ ERROR may only be used on fields of structs or variants + enum Enum { + V(()), + } +} + +mod pin_project_argument { + use pin_project::pin_project; + + #[pin_project(UnsafeUnpin,,)] //~ ERROR expected identifier + struct Unexpected1(#[pin] ()); + + #[pin_project(Foo)] //~ ERROR unexpected argument + struct Unexpected2(#[pin] ()); + + #[pin_project(,UnsafeUnpin)] //~ ERROR expected identifier + struct Unexpected3(#[pin] ()); + + #[pin_project()] // Ok + struct Unexpected4(#[pin] ()); + + #[pin_project(PinnedDrop, PinnedDrop)] //~ ERROR duplicate `PinnedDrop` argument + struct DuplicatePinnedDrop(#[pin] ()); + + #[pin_project(Replace, Replace)] //~ ERROR duplicate `Replace` argument + struct DuplicateReplace(#[pin] ()); + + #[pin_project(UnsafeUnpin, UnsafeUnpin)] //~ ERROR duplicate `UnsafeUnpin` argument + struct DuplicateUnsafeUnpin(#[pin] ()); + + #[pin_project(!Unpin, !Unpin)] //~ ERROR duplicate `!Unpin` argument + struct DuplicateNotUnpin(#[pin] ()); + + #[pin_project(PinnedDrop, UnsafeUnpin, UnsafeUnpin)] //~ ERROR duplicate `UnsafeUnpin` argument + struct Duplicate3(#[pin] ()); + + #[pin_project(PinnedDrop, UnsafeUnpin, PinnedDrop, UnsafeUnpin)] //~ ERROR duplicate `PinnedDrop` argument + struct Duplicate4(#[pin] ()); + + #[pin_project(PinnedDrop, Replace)] //~ ERROR arguments `PinnedDrop` and `Replace` are mutually exclusive + struct PinnedDropWithReplace1(#[pin] ()); + + #[pin_project(Replace, UnsafeUnpin, PinnedDrop)] //~ ERROR arguments `PinnedDrop` and `Replace` are mutually exclusive + struct PinnedDropWithReplace2(#[pin] ()); + + #[pin_project(UnsafeUnpin, !Unpin)] //~ ERROR arguments `UnsafeUnpin` and `!Unpin` are mutually exclusive + struct UnsafeUnpinWithNotUnpin1(#[pin] ()); + + #[pin_project(!Unpin, PinnedDrop, UnsafeUnpin)] //~ ERROR arguments `UnsafeUnpin` and `!Unpin` are mutually exclusive + struct UnsafeUnpinWithNotUnpin2(#[pin] ()); + + #[pin_project(!)] //~ ERROR unexpected end of input, expected `Unpin` + struct NotUnpin1(#[pin] ()); + + #[pin_project(Unpin)] //~ ERROR unexpected argument + struct NotUnpin2(#[pin] ()); +} + +mod pin_project_attribute { + use pin_project::pin_project; + + #[pin_project] + #[pin_project] //~ ERROR duplicate #[pin_project] attribute + struct Duplicate(#[pin] ()); +} + +mod pin_project_item { + use pin_project::pin_project; + + #[pin_project] + struct Struct {} //~ ERROR may not be used on structs with zero fields + + #[pin_project] + struct TupleStruct(); //~ ERROR may not be used on structs with zero fields + + #[pin_project] + struct UnitStruct; //~ ERROR may not be used on structs with zero fields + + #[pin_project] + enum EnumEmpty {} //~ ERROR may not be used on enums without variants + + #[pin_project] + enum EnumDiscriminant { + V = 2, //~ ERROR may not be used on enums with discriminants + } + + #[pin_project] + enum EnumZeroFields { + Unit, //~ ERROR may not be used on enums with zero fields + Tuple(), + Struct {}, + } + + #[pin_project] + union Union { + //~^ ERROR may only be used on structs or enums + f: (), + } +} + +// #[repr(packed)] is always detected first, even on unsupported structs. +mod pin_project_item_packed { + use pin_project::pin_project; + + #[pin_project] + #[repr(packed)] + struct Struct {} //~ ERROR may not be used on #[repr(packed)] types + + #[pin_project] + #[repr(packed)] + struct TupleStruct(); //~ ERROR may not be used on #[repr(packed)] types + + #[pin_project] + #[repr(packed)] + struct UnitStruct; //~ ERROR may not be used on #[repr(packed)] types +} + +fn main() {} diff --git a/tests/ui/pin_project/invalid.stderr b/tests/ui/pin_project/invalid.stderr new file mode 100644 index 0000000..95b57f0 --- /dev/null +++ b/tests/ui/pin_project/invalid.stderr @@ -0,0 +1,228 @@ +error: unexpected token: () + --> $DIR/invalid.rs:6:14 + | +6 | #[pin()] //~ ERROR unexpected token + | ^^ + +error: unexpected token: (foo) + --> $DIR/invalid.rs:11:29 + | +11 | struct TupleStruct(#[pin(foo)] ()); //~ ERROR unexpected token + | ^^^^^ + +error: unexpected token: (foo) + --> $DIR/invalid.rs:15:16 + | +15 | V(#[pin(foo)] ()), //~ ERROR unexpected token + | ^^^^^ + +error: unexpected token: (foo) + --> $DIR/invalid.rs:21:18 + | +21 | #[pin(foo)] //~ ERROR unexpected token + | ^^^^^ + +error: duplicate #[pin] attribute + --> $DIR/invalid.rs:33:9 + | +33 | #[pin] //~ ERROR duplicate #[pin] attribute + | ^^^^^^ + +error: duplicate #[pin] attribute + --> $DIR/invalid.rs:40:9 + | +40 | #[pin] + | ^^^^^^ + +error: duplicate #[pin] attribute + --> $DIR/invalid.rs:49:13 + | +49 | #[pin] + | ^^^^^^ + +error: duplicate #[pin] attribute + --> $DIR/invalid.rs:59:13 + | +59 | #[pin] //~ ERROR duplicate #[pin] attribute + | ^^^^^^ + +error: #[pin] attribute may only be used on fields of structs or variants + --> $DIR/invalid.rs:69:5 + | +69 | #[pin] //~ ERROR may only be used on fields of structs or variants + | ^^^^^^ + +error: #[pin] attribute may only be used on fields of structs or variants + --> $DIR/invalid.rs:77:9 + | +77 | #[pin] //~ ERROR may only be used on fields of structs or variants + | ^^^^^^ + +error: #[pin] attribute may only be used on fields of structs or variants + --> $DIR/invalid.rs:82:5 + | +82 | #[pin] //~ ERROR may only be used on fields of structs or variants + | ^^^^^^ + +error: expected identifier + --> $DIR/invalid.rs:91:31 + | +91 | #[pin_project(UnsafeUnpin,,)] //~ ERROR expected identifier + | ^ + +error: unexpected argument: Foo + --> $DIR/invalid.rs:94:19 + | +94 | #[pin_project(Foo)] //~ ERROR unexpected argument + | ^^^ + +error: expected identifier + --> $DIR/invalid.rs:97:19 + | +97 | #[pin_project(,UnsafeUnpin)] //~ ERROR expected identifier + | ^ + +error: duplicate `PinnedDrop` argument + --> $DIR/invalid.rs:103:31 + | +103 | #[pin_project(PinnedDrop, PinnedDrop)] //~ ERROR duplicate `PinnedDrop` argument + | ^^^^^^^^^^ + +error: duplicate `Replace` argument + --> $DIR/invalid.rs:106:28 + | +106 | #[pin_project(Replace, Replace)] //~ ERROR duplicate `Replace` argument + | ^^^^^^^ + +error: duplicate `UnsafeUnpin` argument + --> $DIR/invalid.rs:109:32 + | +109 | #[pin_project(UnsafeUnpin, UnsafeUnpin)] //~ ERROR duplicate `UnsafeUnpin` argument + | ^^^^^^^^^^^ + +error: duplicate `!Unpin` argument + --> $DIR/invalid.rs:112:27 + | +112 | #[pin_project(!Unpin, !Unpin)] //~ ERROR duplicate `!Unpin` argument + | ^^^^^^ + +error: duplicate `UnsafeUnpin` argument + --> $DIR/invalid.rs:115:44 + | +115 | #[pin_project(PinnedDrop, UnsafeUnpin, UnsafeUnpin)] //~ ERROR duplicate `UnsafeUnpin` argument + | ^^^^^^^^^^^ + +error: duplicate `PinnedDrop` argument + --> $DIR/invalid.rs:118:44 + | +118 | #[pin_project(PinnedDrop, UnsafeUnpin, PinnedDrop, UnsafeUnpin)] //~ ERROR duplicate `PinnedDrop` argument + | ^^^^^^^^^^ + +error: arguments `PinnedDrop` and `Replace` are mutually exclusive + --> $DIR/invalid.rs:121:19 + | +121 | #[pin_project(PinnedDrop, Replace)] //~ ERROR arguments `PinnedDrop` and `Replace` are mutually exclusive + | ^^^^^^^^^^ + +error: arguments `PinnedDrop` and `Replace` are mutually exclusive + --> $DIR/invalid.rs:124:41 + | +124 | #[pin_project(Replace, UnsafeUnpin, PinnedDrop)] //~ ERROR arguments `PinnedDrop` and `Replace` are mutually exclusive + | ^^^^^^^^^^ + +error: arguments `UnsafeUnpin` and `!Unpin` are mutually exclusive + --> $DIR/invalid.rs:127:19 + | +127 | #[pin_project(UnsafeUnpin, !Unpin)] //~ ERROR arguments `UnsafeUnpin` and `!Unpin` are mutually exclusive + | ^^^^^^^^^^^ + +error: arguments `UnsafeUnpin` and `!Unpin` are mutually exclusive + --> $DIR/invalid.rs:130:39 + | +130 | #[pin_project(!Unpin, PinnedDrop, UnsafeUnpin)] //~ ERROR arguments `UnsafeUnpin` and `!Unpin` are mutually exclusive + | ^^^^^^^^^^^ + +error: unexpected end of input, expected `Unpin` + --> $DIR/invalid.rs:133:5 + | +133 | #[pin_project(!)] //~ ERROR unexpected end of input, expected `Unpin` + | ^^^^^^^^^^^^^^^^^ + | + = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: unexpected argument: Unpin + --> $DIR/invalid.rs:136:19 + | +136 | #[pin_project(Unpin)] //~ ERROR unexpected argument + | ^^^^^ + +error: duplicate #[pin_project] attribute + --> $DIR/invalid.rs:144:5 + | +144 | #[pin_project] //~ ERROR duplicate #[pin_project] attribute + | ^^^^^^^^^^^^^^ + +error: #[pin_project] attribute may not be used on structs with zero fields + --> $DIR/invalid.rs:152:19 + | +152 | struct Struct {} //~ ERROR may not be used on structs with zero fields + | ^^ + +error: #[pin_project] attribute may not be used on structs with zero fields + --> $DIR/invalid.rs:155:23 + | +155 | struct TupleStruct(); //~ ERROR may not be used on structs with zero fields + | ^^ + +error: #[pin_project] attribute may not be used on structs with zero fields + --> $DIR/invalid.rs:158:12 + | +158 | struct UnitStruct; //~ ERROR may not be used on structs with zero fields + | ^^^^^^^^^^ + +error: #[pin_project] attribute may not be used on enums without variants + --> $DIR/invalid.rs:161:20 + | +161 | enum EnumEmpty {} //~ ERROR may not be used on enums without variants + | ^^ + +error: #[pin_project] attribute may not be used on enums with discriminants + --> $DIR/invalid.rs:165:13 + | +165 | V = 2, //~ ERROR may not be used on enums with discriminants + | ^ + +error: #[pin_project] attribute may not be used on enums with zero fields + --> $DIR/invalid.rs:170:9 + | +170 | / Unit, //~ ERROR may not be used on enums with zero fields +171 | | Tuple(), +172 | | Struct {}, + | |__________________^ + +error: #[pin_project] attribute may only be used on structs or enums + --> $DIR/invalid.rs:176:5 + | +176 | / union Union { +177 | | //~^ ERROR may only be used on structs or enums +178 | | f: (), +179 | | } + | |_____^ + +error: #[pin_project] attribute may not be used on #[repr(packed)] types + --> $DIR/invalid.rs:187:12 + | +187 | #[repr(packed)] + | ^^^^^^ + +error: #[pin_project] attribute may not be used on #[repr(packed)] types + --> $DIR/invalid.rs:191:12 + | +191 | #[repr(packed)] + | ^^^^^^ + +error: #[pin_project] attribute may not be used on #[repr(packed)] types + --> $DIR/invalid.rs:195:12 + | +195 | #[repr(packed)] + | ^^^^^^ diff --git a/tests/ui/pin_project/overlapping_unpin_struct.rs b/tests/ui/pin_project/overlapping_unpin_struct.rs new file mode 100644 index 0000000..00fef3c --- /dev/null +++ b/tests/ui/pin_project/overlapping_unpin_struct.rs @@ -0,0 +1,18 @@ +use pin_project::pin_project; +use std::marker::PhantomPinned; + +#[pin_project] +struct Foo { + #[pin] + inner: T, +} + +struct __Foo {} + +impl Unpin for __Foo {} + +fn is_unpin() {} + +fn main() { + is_unpin::>(); //~ ERROR E0277 +} diff --git a/tests/ui/pin_project/overlapping_unpin_struct.stderr b/tests/ui/pin_project/overlapping_unpin_struct.stderr new file mode 100644 index 0000000..d0fd4a9 --- /dev/null +++ b/tests/ui/pin_project/overlapping_unpin_struct.stderr @@ -0,0 +1,11 @@ +error[E0277]: `std::marker::PhantomPinned` cannot be unpinned + --> $DIR/overlapping_unpin_struct.rs:17:5 + | +14 | fn is_unpin() {} + | ----- required by this bound in `is_unpin` +... +17 | is_unpin::>(); //~ ERROR E0277 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ within `__SCOPE_Foo::__Foo<'_, std::marker::PhantomPinned>`, the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned` + | + = note: required because it appears within the type `__SCOPE_Foo::__Foo<'_, std::marker::PhantomPinned>` + = note: required because of the requirements on the impl of `std::marker::Unpin` for `Foo` diff --git a/tests/ui/pin_project/packed-enum.rs b/tests/ui/pin_project/packed-enum.rs new file mode 100644 index 0000000..9d4a4c3 --- /dev/null +++ b/tests/ui/pin_project/packed-enum.rs @@ -0,0 +1,20 @@ +use pin_project::pin_project; + +#[repr(packed)] //~ ERROR E0517 +enum E1 { + V(()), +} + +#[pin_project] +#[repr(packed)] //~ ERROR E0517 +enum E2 { + V(()), +} + +#[repr(packed)] //~ ERROR E0517 +#[pin_project] +enum E3 { + V(()), +} + +fn main() {} diff --git a/tests/ui/pin_project/packed-enum.stderr b/tests/ui/pin_project/packed-enum.stderr new file mode 100644 index 0000000..0a5d31b --- /dev/null +++ b/tests/ui/pin_project/packed-enum.stderr @@ -0,0 +1,30 @@ +error[E0517]: attribute should be applied to struct or union + --> $DIR/packed-enum.rs:3:8 + | +3 | #[repr(packed)] //~ ERROR E0517 + | ^^^^^^ +4 | / enum E1 { +5 | | V(()), +6 | | } + | |_- not a struct or union + +error[E0517]: attribute should be applied to struct or union + --> $DIR/packed-enum.rs:9:8 + | +9 | #[repr(packed)] //~ ERROR E0517 + | ^^^^^^ +10 | / enum E2 { +11 | | V(()), +12 | | } + | |_- not a struct or union + +error[E0517]: attribute should be applied to struct or union + --> $DIR/packed-enum.rs:14:8 + | +14 | #[repr(packed)] //~ ERROR E0517 + | ^^^^^^ +15 | #[pin_project] +16 | / enum E3 { +17 | | V(()), +18 | | } + | |_- not a struct or union diff --git a/tests/ui/pin_project/packed-name-value.rs b/tests/ui/pin_project/packed-name-value.rs new file mode 100644 index 0000000..ed819ca --- /dev/null +++ b/tests/ui/pin_project/packed-name-value.rs @@ -0,0 +1,20 @@ +use pin_project::pin_project; + +#[repr(packed = "")] //~ ERROR E0552 +struct S1 { + f: (), +} + +#[pin_project] +#[repr(packed = "")] //~ ERROR E0552 +struct S2 { + f: (), +} + +#[repr(packed = "")] //~ ERROR E0552 +#[pin_project] +struct S3 { + f: (), +} + +fn main() {} diff --git a/tests/ui/pin_project/packed-name-value.stderr b/tests/ui/pin_project/packed-name-value.stderr new file mode 100644 index 0000000..a3e2571 --- /dev/null +++ b/tests/ui/pin_project/packed-name-value.stderr @@ -0,0 +1,17 @@ +error[E0552]: unrecognized representation hint + --> $DIR/packed-name-value.rs:3:8 + | +3 | #[repr(packed = "")] //~ ERROR E0552 + | ^^^^^^^^^^^ + +error[E0552]: unrecognized representation hint + --> $DIR/packed-name-value.rs:9:8 + | +9 | #[repr(packed = "")] //~ ERROR E0552 + | ^^^^^^^^^^^ + +error[E0552]: unrecognized representation hint + --> $DIR/packed-name-value.rs:14:8 + | +14 | #[repr(packed = "")] //~ ERROR E0552 + | ^^^^^^^^^^^ diff --git a/tests/ui/pin_project/packed.rs b/tests/ui/pin_project/packed.rs new file mode 100644 index 0000000..86f3ecf --- /dev/null +++ b/tests/ui/pin_project/packed.rs @@ -0,0 +1,25 @@ +use pin_project::pin_project; + +#[pin_project] +#[repr(packed, C)] //~ ERROR may not be used on #[repr(packed)] types +struct A { + #[pin] + field: u8, +} + +// Test putting 'repr' before the 'pin_project' attribute +#[repr(packed, C)] //~ ERROR may not be used on #[repr(packed)] types +#[pin_project] +struct B { + #[pin] + field: u8, +} + +#[pin_project] +#[repr(packed(2))] //~ ERROR may not be used on #[repr(packed)] types +struct C { + #[pin] + field: u32, +} + +fn main() {} diff --git a/tests/ui/pin_project/packed.stderr b/tests/ui/pin_project/packed.stderr new file mode 100644 index 0000000..969faea --- /dev/null +++ b/tests/ui/pin_project/packed.stderr @@ -0,0 +1,17 @@ +error: #[pin_project] attribute may not be used on #[repr(packed)] types + --> $DIR/packed.rs:4:8 + | +4 | #[repr(packed, C)] //~ ERROR may not be used on #[repr(packed)] types + | ^^^^^^ + +error: #[pin_project] attribute may not be used on #[repr(packed)] types + --> $DIR/packed.rs:11:8 + | +11 | #[repr(packed, C)] //~ ERROR may not be used on #[repr(packed)] types + | ^^^^^^ + +error: #[pin_project] attribute may not be used on #[repr(packed)] types + --> $DIR/packed.rs:19:8 + | +19 | #[repr(packed(2))] //~ ERROR may not be used on #[repr(packed)] types + | ^^^^^^^^^ diff --git a/tests/ui/pin_project/packed_sneaky-1.rs b/tests/ui/pin_project/packed_sneaky-1.rs new file mode 100644 index 0000000..dcf5464 --- /dev/null +++ b/tests/ui/pin_project/packed_sneaky-1.rs @@ -0,0 +1,33 @@ +use auxiliary_macros::hidden_repr; +use pin_project::{pin_project, pinned_drop, UnsafeUnpin}; +use std::pin::Pin; + +#[pin_project] //~ ERROR may not be used on #[repr(packed)] types +#[hidden_repr(packed)] +struct A { + #[pin] + field: u32, +} + +#[pin_project(UnsafeUnpin)] //~ ERROR may not be used on #[repr(packed)] types +#[hidden_repr(packed)] +struct C { + #[pin] + field: u32, +} + +unsafe impl UnsafeUnpin for C {} + +#[pin_project(PinnedDrop)] //~ ERROR may not be used on #[repr(packed)] types +#[hidden_repr(packed)] +struct D { + #[pin] + field: u32, +} + +#[pinned_drop] +impl PinnedDrop for D { + fn drop(self: Pin<&mut Self>) {} +} + +fn main() {} diff --git a/tests/ui/pin_project/packed_sneaky-1.stderr b/tests/ui/pin_project/packed_sneaky-1.stderr new file mode 100644 index 0000000..06a4f62 --- /dev/null +++ b/tests/ui/pin_project/packed_sneaky-1.stderr @@ -0,0 +1,23 @@ +error: #[pin_project] attribute may not be used on #[repr(packed)] types + --> $DIR/packed_sneaky-1.rs:6:1 + | +6 | #[hidden_repr(packed)] + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: #[pin_project] attribute may not be used on #[repr(packed)] types + --> $DIR/packed_sneaky-1.rs:13:1 + | +13 | #[hidden_repr(packed)] + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: #[pin_project] attribute may not be used on #[repr(packed)] types + --> $DIR/packed_sneaky-1.rs:22:1 + | +22 | #[hidden_repr(packed)] + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/pin_project/packed_sneaky-2.rs b/tests/ui/pin_project/packed_sneaky-2.rs new file mode 100644 index 0000000..d162706 --- /dev/null +++ b/tests/ui/pin_project/packed_sneaky-2.rs @@ -0,0 +1,12 @@ +use auxiliary_macros::hidden_repr_macro; +use pin_project::pin_project; + +hidden_repr_macro! { //~ ERROR may not be used on #[repr(packed)] types + #[pin_project] + struct B { + #[pin] + field: u32, + } +} + +fn main() {} diff --git a/tests/ui/pin_project/packed_sneaky-2.stderr b/tests/ui/pin_project/packed_sneaky-2.stderr new file mode 100644 index 0000000..d653a4d --- /dev/null +++ b/tests/ui/pin_project/packed_sneaky-2.stderr @@ -0,0 +1,13 @@ +error: #[pin_project] attribute may not be used on #[repr(packed)] types + --> $DIR/packed_sneaky-2.rs:4:1 + | +4 | / hidden_repr_macro! { //~ ERROR may not be used on #[repr(packed)] types +5 | | #[pin_project] +6 | | struct B { +7 | | #[pin] +8 | | field: u32, +9 | | } +10 | | } + | |_^ + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/pin_project/private_in_public-enum.rs b/tests/ui/pin_project/private_in_public-enum.rs new file mode 100644 index 0000000..cbffa20 --- /dev/null +++ b/tests/ui/pin_project/private_in_public-enum.rs @@ -0,0 +1,23 @@ +// Even if allows private_in_public, these are errors. + +#![allow(private_in_public)] + +pub enum PublicEnum { + Variant(PrivateEnum), //~ ERROR E0446 +} + +enum PrivateEnum { + Variant(u8), +} + +mod foo { + pub(crate) enum CrateEnum { + Variant(PrivateEnum), //~ ERROR E0446 + } + + enum PrivateEnum { + Variant(u8), + } +} + +fn main() {} diff --git a/tests/ui/pin_project/private_in_public-enum.stderr b/tests/ui/pin_project/private_in_public-enum.stderr new file mode 100644 index 0000000..6f2988f --- /dev/null +++ b/tests/ui/pin_project/private_in_public-enum.stderr @@ -0,0 +1,17 @@ +error[E0446]: private type `PrivateEnum` in public interface + --> $DIR/private_in_public-enum.rs:6:13 + | +6 | Variant(PrivateEnum), //~ ERROR E0446 + | ^^^^^^^^^^^ can't leak private type +... +9 | enum PrivateEnum { + | - `PrivateEnum` declared as private + +error[E0446]: private type `foo::PrivateEnum` in public interface + --> $DIR/private_in_public-enum.rs:15:17 + | +15 | Variant(PrivateEnum), //~ ERROR E0446 + | ^^^^^^^^^^^ can't leak private type +... +18 | enum PrivateEnum { + | - `foo::PrivateEnum` declared as private diff --git a/tests/ui/pin_project/proper_unpin.rs b/tests/ui/pin_project/proper_unpin.rs new file mode 100644 index 0000000..e61789b --- /dev/null +++ b/tests/ui/pin_project/proper_unpin.rs @@ -0,0 +1,38 @@ +use pin_project::pin_project; +use std::marker::PhantomPinned; + +struct Inner { + val: T, +} + +#[pin_project] +struct Foo { + #[pin] + inner: Inner, + other: U, +} + +#[pin_project] +struct TrivialBounds { + #[pin] + field1: PhantomPinned, +} + +#[pin_project] +struct Bar<'a, T, U> { + #[pin] + inner: &'a mut Inner, + other: U, +} + +fn is_unpin() {} + +fn main() { + is_unpin::>(); //~ ERROR E0277 + is_unpin::>(); // Ok + is_unpin::>(); //~ ERROR E0277 + + is_unpin::(); //~ ERROR E0277 + + is_unpin::>(); // Ok +} diff --git a/tests/ui/pin_project/proper_unpin.stderr b/tests/ui/pin_project/proper_unpin.stderr new file mode 100644 index 0000000..9142887 --- /dev/null +++ b/tests/ui/pin_project/proper_unpin.stderr @@ -0,0 +1,37 @@ +error[E0277]: `std::marker::PhantomPinned` cannot be unpinned + --> $DIR/proper_unpin.rs:31:5 + | +28 | fn is_unpin() {} + | ----- required by this bound in `is_unpin` +... +31 | is_unpin::>(); //~ ERROR E0277 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ within `__SCOPE_Foo::__Foo<'_, std::marker::PhantomPinned, ()>`, the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned` + | + = note: required because it appears within the type `Inner` + = note: required because it appears within the type `__SCOPE_Foo::__Foo<'_, std::marker::PhantomPinned, ()>` + = note: required because of the requirements on the impl of `std::marker::Unpin` for `Foo` + +error[E0277]: `std::marker::PhantomPinned` cannot be unpinned + --> $DIR/proper_unpin.rs:33:5 + | +28 | fn is_unpin() {} + | ----- required by this bound in `is_unpin` +... +33 | is_unpin::>(); //~ ERROR E0277 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ within `__SCOPE_Foo::__Foo<'_, std::marker::PhantomPinned, std::marker::PhantomPinned>`, the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned` + | + = note: required because it appears within the type `Inner` + = note: required because it appears within the type `__SCOPE_Foo::__Foo<'_, std::marker::PhantomPinned, std::marker::PhantomPinned>` + = note: required because of the requirements on the impl of `std::marker::Unpin` for `Foo` + +error[E0277]: `std::marker::PhantomPinned` cannot be unpinned + --> $DIR/proper_unpin.rs:35:5 + | +28 | fn is_unpin() {} + | ----- required by this bound in `is_unpin` +... +35 | is_unpin::(); //~ ERROR E0277 + | ^^^^^^^^^^^^^^^^^^^^^^^^^ within `__SCOPE_TrivialBounds::__TrivialBounds<'_>`, the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned` + | + = note: required because it appears within the type `__SCOPE_TrivialBounds::__TrivialBounds<'_>` + = note: required because of the requirements on the impl of `std::marker::Unpin` for `TrivialBounds` diff --git a/tests/ui/pin_project/remove-attr-from-field.rs b/tests/ui/pin_project/remove-attr-from-field.rs new file mode 100644 index 0000000..eebd3cd --- /dev/null +++ b/tests/ui/pin_project/remove-attr-from-field.rs @@ -0,0 +1,32 @@ +use auxiliary_macros::remove_attr; +use pin_project::pin_project; +use std::{marker::PhantomPinned, pin::Pin}; + +fn is_unpin() {} + +#[pin_project] +#[remove_attr(field)] +struct Foo { + #[pin] + field: PhantomPinned, +} + +#[remove_attr(field)] +#[pin_project] +struct Bar { + #[pin] + field: PhantomPinned, +} + +fn main() { + is_unpin::(); + is_unpin::(); + + let mut x = Foo { field: PhantomPinned }; + let x = Pin::new(&mut x).project(); + let _: Pin<&mut PhantomPinned> = x.field; //~ ERROR E0308 + + let mut x = Bar { field: PhantomPinned }; + let x = Pin::new(&mut x).project(); + let _: Pin<&mut PhantomPinned> = x.field; //~ ERROR E0308 +} diff --git a/tests/ui/pin_project/remove-attr-from-field.stderr b/tests/ui/pin_project/remove-attr-from-field.stderr new file mode 100644 index 0000000..15195e7 --- /dev/null +++ b/tests/ui/pin_project/remove-attr-from-field.stderr @@ -0,0 +1,21 @@ +error[E0308]: mismatched types + --> $DIR/remove-attr-from-field.rs:27:38 + | +27 | let _: Pin<&mut PhantomPinned> = x.field; //~ ERROR E0308 + | ----------------------- ^^^^^^^ expected struct `std::pin::Pin`, found `&mut std::marker::PhantomPinned` + | | + | expected due to this + | + = note: expected struct `std::pin::Pin<&mut std::marker::PhantomPinned>` + found mutable reference `&mut std::marker::PhantomPinned` + +error[E0308]: mismatched types + --> $DIR/remove-attr-from-field.rs:31:38 + | +31 | let _: Pin<&mut PhantomPinned> = x.field; //~ ERROR E0308 + | ----------------------- ^^^^^^^ expected struct `std::pin::Pin`, found `&mut std::marker::PhantomPinned` + | | + | expected due to this + | + = note: expected struct `std::pin::Pin<&mut std::marker::PhantomPinned>` + found mutable reference `&mut std::marker::PhantomPinned` diff --git a/tests/ui/pin_project/remove-attr-from-struct.rs b/tests/ui/pin_project/remove-attr-from-struct.rs new file mode 100644 index 0000000..b395a42 --- /dev/null +++ b/tests/ui/pin_project/remove-attr-from-struct.rs @@ -0,0 +1,30 @@ +use auxiliary_macros::remove_attr; +use pin_project::pin_project; +use std::{marker::PhantomPinned, pin::Pin}; + +fn is_unpin() {} + +#[pin_project] +#[remove_attr(struct)] +struct Foo { + #[pin] //~ ERROR cannot find attribute `pin` in this scope + field: PhantomPinned, +} + +#[remove_attr(struct)] +#[pin_project] +struct Bar { + #[pin] //~ ERROR cannot find attribute `pin` in this scope + field: PhantomPinned, +} + +fn main() { + is_unpin::(); //~ ERROR E0277 + is_unpin::(); //~ ERROR E0277 + + let mut x = Foo { field: PhantomPinned }; + let _x = Pin::new(&mut x).project(); //~ ERROR E0277,E0599 + + let mut x = Bar { field: PhantomPinned }; + let _x = Pin::new(&mut x).project(); //~ ERROR E0277,E0599 +} diff --git a/tests/ui/pin_project/remove-attr-from-struct.stderr b/tests/ui/pin_project/remove-attr-from-struct.stderr new file mode 100644 index 0000000..3173248 --- /dev/null +++ b/tests/ui/pin_project/remove-attr-from-struct.stderr @@ -0,0 +1,63 @@ +error: cannot find attribute `pin` in this scope + --> $DIR/remove-attr-from-struct.rs:10:7 + | +10 | #[pin] //~ ERROR cannot find attribute `pin` in this scope + | ^^^ + +error: cannot find attribute `pin` in this scope + --> $DIR/remove-attr-from-struct.rs:17:7 + | +17 | #[pin] //~ ERROR cannot find attribute `pin` in this scope + | ^^^ + +error[E0277]: `std::marker::PhantomPinned` cannot be unpinned + --> $DIR/remove-attr-from-struct.rs:22:5 + | +5 | fn is_unpin() {} + | ----- required by this bound in `is_unpin` +... +22 | is_unpin::(); //~ ERROR E0277 + | ^^^^^^^^^^^^^^^ within `Foo`, the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned` + | + = note: required because it appears within the type `Foo` + +error[E0277]: `std::marker::PhantomPinned` cannot be unpinned + --> $DIR/remove-attr-from-struct.rs:23:5 + | +5 | fn is_unpin() {} + | ----- required by this bound in `is_unpin` +... +23 | is_unpin::(); //~ ERROR E0277 + | ^^^^^^^^^^^^^^^ within `Bar`, the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned` + | + = note: required because it appears within the type `Bar` + +error[E0277]: `std::marker::PhantomPinned` cannot be unpinned + --> $DIR/remove-attr-from-struct.rs:26:14 + | +26 | let _x = Pin::new(&mut x).project(); //~ ERROR E0277,E0599 + | ^^^^^^^^ within `Foo`, the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned` + | + = note: required because it appears within the type `Foo` + = note: required by `std::pin::Pin::

::new` + +error[E0599]: no method named `project` found for struct `std::pin::Pin<&mut Foo>` in the current scope + --> $DIR/remove-attr-from-struct.rs:26:31 + | +26 | let _x = Pin::new(&mut x).project(); //~ ERROR E0277,E0599 + | ^^^^^^^ method not found in `std::pin::Pin<&mut Foo>` + +error[E0277]: `std::marker::PhantomPinned` cannot be unpinned + --> $DIR/remove-attr-from-struct.rs:29:14 + | +29 | let _x = Pin::new(&mut x).project(); //~ ERROR E0277,E0599 + | ^^^^^^^^ within `Bar`, the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned` + | + = note: required because it appears within the type `Bar` + = note: required by `std::pin::Pin::

::new` + +error[E0599]: no method named `project` found for struct `std::pin::Pin<&mut Bar>` in the current scope + --> $DIR/remove-attr-from-struct.rs:29:31 + | +29 | let _x = Pin::new(&mut x).project(); //~ ERROR E0277,E0599 + | ^^^^^^^ method not found in `std::pin::Pin<&mut Bar>` diff --git a/tests/ui/pin_project/safe_packed_borrows.rs b/tests/ui/pin_project/safe_packed_borrows.rs new file mode 100644 index 0000000..c1a7d55 --- /dev/null +++ b/tests/ui/pin_project/safe_packed_borrows.rs @@ -0,0 +1,21 @@ +#![deny(safe_packed_borrows)] + +// Refs: https://github.com/rust-lang/rust/issues/46043 + +#[repr(packed)] +struct A { + field: u32, +} + +#[repr(packed(2))] +struct B { + field: u32, +} + +fn main() { + let a = A { field: 1 }; + &a.field; //~ ERROR borrow of packed field is unsafe and requires unsafe function or block + + let b = B { field: 1 }; + &b.field; //~ ERROR borrow of packed field is unsafe and requires unsafe function or block +} diff --git a/tests/ui/pin_project/safe_packed_borrows.stderr b/tests/ui/pin_project/safe_packed_borrows.stderr new file mode 100644 index 0000000..7b4cc08 --- /dev/null +++ b/tests/ui/pin_project/safe_packed_borrows.stderr @@ -0,0 +1,24 @@ +error: borrow of packed field is unsafe and requires unsafe function or block (error E0133) + --> $DIR/safe_packed_borrows.rs:17:5 + | +17 | &a.field; //~ ERROR borrow of packed field is unsafe and requires unsafe function or block + | ^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/safe_packed_borrows.rs:1:9 + | +1 | #![deny(safe_packed_borrows)] + | ^^^^^^^^^^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #46043 + = note: fields of packed structs might be misaligned: dereferencing a misaligned pointer or even just creating a misaligned reference is undefined behavior + +error: borrow of packed field is unsafe and requires unsafe function or block (error E0133) + --> $DIR/safe_packed_borrows.rs:20:5 + | +20 | &b.field; //~ ERROR borrow of packed field is unsafe and requires unsafe function or block + | ^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #46043 + = note: fields of packed structs might be misaligned: dereferencing a misaligned pointer or even just creating a misaligned reference is undefined behavior diff --git a/tests/ui/pin_project/unpin_sneaky.rs b/tests/ui/pin_project/unpin_sneaky.rs new file mode 100644 index 0000000..3ccb1a9 --- /dev/null +++ b/tests/ui/pin_project/unpin_sneaky.rs @@ -0,0 +1,11 @@ +use pin_project::pin_project; + +#[pin_project] +struct Foo { + #[pin] + inner: u8, +} + +impl Unpin for __Foo {} //~ ERROR E0412,E0321 + +fn main() {} diff --git a/tests/ui/pin_project/unpin_sneaky.stderr b/tests/ui/pin_project/unpin_sneaky.stderr new file mode 100644 index 0000000..0637a66 --- /dev/null +++ b/tests/ui/pin_project/unpin_sneaky.stderr @@ -0,0 +1,11 @@ +error[E0412]: cannot find type `__Foo` in this scope + --> $DIR/unpin_sneaky.rs:9:16 + | +9 | impl Unpin for __Foo {} //~ ERROR E0412,E0321 + | ^^^^^ not found in this scope + +error[E0321]: cross-crate traits with a default impl, like `std::marker::Unpin`, can only be implemented for a struct/enum type, not `[type error]` + --> $DIR/unpin_sneaky.rs:9:1 + | +9 | impl Unpin for __Foo {} //~ ERROR E0412,E0321 + | ^^^^^^^^^^^^^^^^^^^^ can't implement cross-crate trait with a default impl for non-struct/enum type diff --git a/tests/ui/pin_project/visibility.rs b/tests/ui/pin_project/visibility.rs new file mode 100644 index 0000000..4f0cb1b --- /dev/null +++ b/tests/ui/pin_project/visibility.rs @@ -0,0 +1,52 @@ +mod pub_ { + use pin_project::pin_project; + + #[pin_project] + pub struct Default(()); + + #[pin_project(Replace)] + pub struct Replace(()); +} +pub mod pub_use { + #[rustfmt::skip] + pub use crate::pub_::__DefaultProjection; //~ ERROR E0365 + #[rustfmt::skip] + pub use crate::pub_::__DefaultProjectionRef; //~ ERROR E0365 + #[rustfmt::skip] + pub use crate::pub_::__ReplaceProjection; //~ ERROR E0365 + #[rustfmt::skip] + pub use crate::pub_::__ReplaceProjectionOwned; //~ ERROR E0365 + #[rustfmt::skip] + pub use crate::pub_::__ReplaceProjectionRef; //~ ERROR E0365 + + // Confirm that the visibility of the original type is not changed. + pub use crate::pub_::{Default, Replace}; +} +pub mod pub_use2 { + // Ok + #[allow(unused_imports)] + pub(crate) use crate::pub_::{ + __DefaultProjection, __DefaultProjectionRef, __ReplaceProjection, __ReplaceProjectionOwned, + __ReplaceProjectionRef, + }; +} + +mod pub_crate { + use pin_project::pin_project; + + #[pin_project] + pub(crate) struct Default(()); + + #[pin_project(Replace)] + pub(crate) struct Replace(()); +} +pub mod pub_crate_use { + // Ok + #[allow(unused_imports)] + pub(crate) use crate::pub_crate::{ + __DefaultProjection, __DefaultProjectionRef, __ReplaceProjection, __ReplaceProjectionOwned, + __ReplaceProjectionRef, + }; +} + +fn main() {} diff --git a/tests/ui/pin_project/visibility.stderr b/tests/ui/pin_project/visibility.stderr new file mode 100644 index 0000000..1ea60d7 --- /dev/null +++ b/tests/ui/pin_project/visibility.stderr @@ -0,0 +1,39 @@ +error[E0365]: `__DefaultProjection` is private, and cannot be re-exported + --> $DIR/visibility.rs:12:13 + | +12 | pub use crate::pub_::__DefaultProjection; //~ ERROR E0365 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ re-export of private `__DefaultProjection` + | + = note: consider declaring type or module `__DefaultProjection` with `pub` + +error[E0365]: `__DefaultProjectionRef` is private, and cannot be re-exported + --> $DIR/visibility.rs:14:13 + | +14 | pub use crate::pub_::__DefaultProjectionRef; //~ ERROR E0365 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ re-export of private `__DefaultProjectionRef` + | + = note: consider declaring type or module `__DefaultProjectionRef` with `pub` + +error[E0365]: `__ReplaceProjection` is private, and cannot be re-exported + --> $DIR/visibility.rs:16:13 + | +16 | pub use crate::pub_::__ReplaceProjection; //~ ERROR E0365 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ re-export of private `__ReplaceProjection` + | + = note: consider declaring type or module `__ReplaceProjection` with `pub` + +error[E0365]: `__ReplaceProjectionOwned` is private, and cannot be re-exported + --> $DIR/visibility.rs:18:13 + | +18 | pub use crate::pub_::__ReplaceProjectionOwned; //~ ERROR E0365 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ re-export of private `__ReplaceProjectionOwned` + | + = note: consider declaring type or module `__ReplaceProjectionOwned` with `pub` + +error[E0365]: `__ReplaceProjectionRef` is private, and cannot be re-exported + --> $DIR/visibility.rs:20:13 + | +20 | pub use crate::pub_::__ReplaceProjectionRef; //~ ERROR E0365 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ re-export of private `__ReplaceProjectionRef` + | + = note: consider declaring type or module `__ReplaceProjectionRef` with `pub` diff --git a/tests/ui/pinned_drop/call-drop-inner.rs b/tests/ui/pinned_drop/call-drop-inner.rs new file mode 100644 index 0000000..c953acb --- /dev/null +++ b/tests/ui/pinned_drop/call-drop-inner.rs @@ -0,0 +1,16 @@ +use pin_project::{pin_project, pinned_drop}; +use std::pin::Pin; + +#[pin_project(PinnedDrop)] +struct Struct { + dropped: bool, +} + +#[pinned_drop] +impl PinnedDrop for Struct { + fn drop(mut self: Pin<&mut Self>) { + __drop_inner(__self); + } +} + +fn main() {} diff --git a/tests/ui/pinned_drop/call-drop-inner.stderr b/tests/ui/pinned_drop/call-drop-inner.stderr new file mode 100644 index 0000000..eb55ce7 --- /dev/null +++ b/tests/ui/pinned_drop/call-drop-inner.stderr @@ -0,0 +1,10 @@ +error[E0061]: this function takes 0 arguments but 1 argument was supplied + --> $DIR/call-drop-inner.rs:12:9 + | +9 | #[pinned_drop] + | -------------- defined here +... +12 | __drop_inner(__self); + | ^^^^^^^^^^^^ ------ supplied 1 argument + | | + | expected 0 arguments diff --git a/tests/ui/pinned_drop/conditional-drop-impl.rs b/tests/ui/pinned_drop/conditional-drop-impl.rs new file mode 100644 index 0000000..42d18b7 --- /dev/null +++ b/tests/ui/pinned_drop/conditional-drop-impl.rs @@ -0,0 +1,26 @@ +use pin_project::{pin_project, pinned_drop}; +use std::pin::Pin; + +// In `Drop` impl, the implementor must specify the same requirement as type definition. + +struct DropImpl { + field: T, +} + +impl Drop for DropImpl { + //~^ ERROR E0367 + fn drop(&mut self) {} +} + +#[pin_project(PinnedDrop)] //~ ERROR E0277 +struct PinnedDropImpl { + #[pin] + field: T, +} + +#[pinned_drop] +impl PinnedDrop for PinnedDropImpl { + fn drop(self: Pin<&mut Self>) {} +} + +fn main() {} diff --git a/tests/ui/pinned_drop/conditional-drop-impl.stderr b/tests/ui/pinned_drop/conditional-drop-impl.stderr new file mode 100644 index 0000000..ad8fb69 --- /dev/null +++ b/tests/ui/pinned_drop/conditional-drop-impl.stderr @@ -0,0 +1,26 @@ +error[E0367]: `Drop` impl requires `T: std::marker::Unpin` but the struct it is implemented for does not + --> $DIR/conditional-drop-impl.rs:10:9 + | +10 | impl Drop for DropImpl { + | ^^^^^ + | +note: the implementor must specify the same requirement + --> $DIR/conditional-drop-impl.rs:6:1 + | +6 | / struct DropImpl { +7 | | field: T, +8 | | } + | |_^ + +error[E0277]: `T` cannot be unpinned + --> $DIR/conditional-drop-impl.rs:15:15 + | +15 | #[pin_project(PinnedDrop)] //~ ERROR E0277 + | ^^^^^^^^^^ the trait `std::marker::Unpin` is not implemented for `T` + | + = note: required because of the requirements on the impl of `pin_project::__private::PinnedDrop` for `PinnedDropImpl` + = note: required by `pin_project::__private::PinnedDrop::drop` +help: consider restricting type parameter `T` + | +16 | struct PinnedDropImpl { + | ^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/pinned_drop/forget-pinned-drop-impl.rs b/tests/ui/pinned_drop/forget-pinned-drop-impl.rs new file mode 100644 index 0000000..6c9f718 --- /dev/null +++ b/tests/ui/pinned_drop/forget-pinned-drop-impl.rs @@ -0,0 +1,9 @@ +use pin_project::pin_project; + +#[pin_project(PinnedDrop)] //~ ERROR E0277 +struct Struct { + #[pin] + field: u8, +} + +fn main() {} diff --git a/tests/ui/pinned_drop/forget-pinned-drop-impl.stderr b/tests/ui/pinned_drop/forget-pinned-drop-impl.stderr new file mode 100644 index 0000000..67bdbe1 --- /dev/null +++ b/tests/ui/pinned_drop/forget-pinned-drop-impl.stderr @@ -0,0 +1,7 @@ +error[E0277]: the trait bound `Struct: pin_project::__private::PinnedDrop` is not satisfied + --> $DIR/forget-pinned-drop-impl.rs:3:15 + | +3 | #[pin_project(PinnedDrop)] //~ ERROR E0277 + | ^^^^^^^^^^ the trait `pin_project::__private::PinnedDrop` is not implemented for `Struct` + | + = note: required by `pin_project::__private::PinnedDrop::drop` diff --git a/tests/ui/pinned_drop/invalid-self.rs b/tests/ui/pinned_drop/invalid-self.rs new file mode 100644 index 0000000..73d3b43 --- /dev/null +++ b/tests/ui/pinned_drop/invalid-self.rs @@ -0,0 +1,14 @@ +// by-ref binding `ref (mut) self` and sub-patterns `@` are not allowed in receivers (rejected by rustc). + +use std::pin::Pin; + +struct Struct {} + +impl Struct { + fn take_ref_self(ref self: Pin<&mut Self>) {} //~ ERROR expected identifier, found keyword `self` + fn take_ref_mut_self(ref mut self: Pin<&mut Self>) {} //~ ERROR expected identifier, found keyword `self` + + fn self_subpat(self @ Struct {}: Self) {} //~ ERROR expected one of `)`, `,`, or `:`, found `@` +} + +fn main() {} diff --git a/tests/ui/pinned_drop/invalid-self.stderr b/tests/ui/pinned_drop/invalid-self.stderr new file mode 100644 index 0000000..a43e91d --- /dev/null +++ b/tests/ui/pinned_drop/invalid-self.stderr @@ -0,0 +1,25 @@ +error: expected identifier, found keyword `self` + --> $DIR/invalid-self.rs:8:26 + | +8 | fn take_ref_self(ref self: Pin<&mut Self>) {} //~ ERROR expected identifier, found keyword `self` + | ^^^^ expected identifier, found keyword + +error: expected identifier, found keyword `self` + --> $DIR/invalid-self.rs:9:34 + | +9 | fn take_ref_mut_self(ref mut self: Pin<&mut Self>) {} //~ ERROR expected identifier, found keyword `self` + | ^^^^ expected identifier, found keyword + +error: expected parameter name, found `@` + --> $DIR/invalid-self.rs:11:25 + | +11 | fn self_subpat(self @ Struct {}: Self) {} //~ ERROR expected one of `)`, `,`, or `:`, found `@` + | ^ expected parameter name + +error: expected one of `)`, `,`, or `:`, found `@` + --> $DIR/invalid-self.rs:11:25 + | +11 | fn self_subpat(self @ Struct {}: Self) {} //~ ERROR expected one of `)`, `,`, or `:`, found `@` + | -^ expected one of `)`, `,`, or `:` + | | + | help: missing `,` diff --git a/tests/ui/pinned_drop/invalid.rs b/tests/ui/pinned_drop/invalid.rs new file mode 100644 index 0000000..9ff5de2 --- /dev/null +++ b/tests/ui/pinned_drop/invalid.rs @@ -0,0 +1,207 @@ +mod argument { + use pin_project::{pin_project, pinned_drop}; + use std::pin::Pin; + + #[pin_project(PinnedDrop)] + struct UnexpectedArg1(()); + + #[pinned_drop(foo)] //~ ERROR unexpected token + impl PinnedDrop for UnexpectedArg1 { + fn drop(self: Pin<&mut Self>) {} + } + + #[pin_project(PinnedDrop)] + struct UnexpectedArg2(()); + + #[pinned_drop()] // Ok + impl PinnedDrop for UnexpectedArg2 { + fn drop(self: Pin<&mut Self>) {} + } +} + +mod attribute { + use pin_project::{pin_project, pinned_drop}; + + #[pin_project(PinnedDrop)] + struct Duplicate(()); + + #[pinned_drop] + #[pinned_drop] //~ ERROR duplicate #[pinned_drop] attribute + impl PinnedDrop for Duplicate { + fn drop(self: Pin<&mut Self>) {} + } +} + +mod item { + use pin_project::{pin_project, pinned_drop}; + + #[pin_project(PinnedDrop)] + struct TraitImpl(()); + + #[pinned_drop] + impl Drop for TraitImpl {} //~ ERROR may only be used on implementation for the `PinnedDrop` trait + + #[pin_project(PinnedDrop)] + struct InherentImpl(()); + + #[pinned_drop] + impl InherentImpl {} //~ ERROR may only be used on implementation for the `PinnedDrop` trait + + #[pinned_drop] + fn drop(_: Pin<&mut ()>) {} //~ ERROR expected `impl` +} + +mod unsafety { + use pin_project::{pin_project, pinned_drop}; + + #[pin_project(PinnedDrop)] + struct Impl(()); + + #[pinned_drop] + unsafe impl PinnedDrop for Impl { + //~^ ERROR implementing the trait `PinnedDrop` is not unsafe + fn drop(self: Pin<&mut Self>) {} + } + + #[pin_project(PinnedDrop)] + struct Method(()); + + #[pinned_drop] + impl PinnedDrop for Method { + unsafe fn drop(self: Pin<&mut Self>) {} //~ ERROR implementing the method `drop` is not unsafe + } +} + +mod assoc_item { + use pin_project::{pin_project, pinned_drop}; + + #[pin_project(PinnedDrop)] + struct Empty(()); + + #[pinned_drop] + impl PinnedDrop for Empty {} //~ ERROR not all trait items implemented, missing: `drop` + + #[pin_project(PinnedDrop)] + struct Const1(()); + + #[pinned_drop] + impl PinnedDrop for Const1 { + const A: u8 = 0; //~ ERROR const `A` is not a member of trait `PinnedDrop` + fn drop(self: Pin<&mut Self>) {} + } + + #[pin_project(PinnedDrop)] + struct Const2(()); + + #[pinned_drop] + impl PinnedDrop for Const2 { + fn drop(self: Pin<&mut Self>) {} + const A: u8 = 0; //~ ERROR const `A` is not a member of trait `PinnedDrop` + } + + #[pin_project(PinnedDrop)] + struct Type1(()); + + #[pinned_drop] + impl PinnedDrop for Type1 { + type A = u8; //~ ERROR type `A` is not a member of trait `PinnedDrop` + fn drop(self: Pin<&mut Self>) {} + } + + #[pin_project(PinnedDrop)] + struct Type2(()); + + #[pinned_drop] + impl PinnedDrop for Type2 { + fn drop(self: Pin<&mut Self>) {} + type A = u8; //~ ERROR type `A` is not a member of trait `PinnedDrop` + } + + #[pin_project(PinnedDrop)] + struct Duplicate(()); + + #[pinned_drop] + impl PinnedDrop for Duplicate { + fn drop(self: Pin<&mut Self>) {} + fn drop(self: Pin<&mut Self>) {} //~ ERROR duplicate definitions with name `drop` + } +} + +mod method { + use pin_project::{pin_project, pinned_drop}; + use std::pin::Pin; + + #[pin_project(PinnedDrop)] + struct RetUnit(()); + + #[pinned_drop] + impl PinnedDrop for RetUnit { + fn drop(self: Pin<&mut Self>) -> () {} // Ok + } + + #[pin_project(PinnedDrop)] + struct RetTy(()); + + #[pinned_drop] + impl PinnedDrop for RetTy { + fn drop(self: Pin<&mut Self>) -> Self {} //~ ERROR method `drop` must return the unit type + } + + #[pin_project(PinnedDrop)] + struct NoArg(()); + + #[pinned_drop] + impl PinnedDrop for NoArg { + fn drop() {} //~ ERROR method `drop` must take an argument `self: Pin<&mut Self>` + } + + #[pin_project(PinnedDrop)] + struct MultiArg(()); + + #[pinned_drop] + impl PinnedDrop for MultiArg { + fn drop(self: Pin<&mut Self>, _: ()) {} //~ ERROR method `drop` must take an argument `self: Pin<&mut Self>` + } + + #[pin_project(PinnedDrop)] + struct InvalidArg1(()); + + #[pinned_drop] + impl PinnedDrop for InvalidArg1 { + fn drop(&mut self) {} //~ ERROR method `drop` must take an argument `self: Pin<&mut Self>` + } + + #[pin_project(PinnedDrop)] + struct InvalidArg2(()); + + #[pinned_drop] + impl PinnedDrop for InvalidArg2 { + fn drop(_: Pin<&mut Self>) {} //~ ERROR method `drop` must take an argument `self: Pin<&mut Self>` + } + + #[pin_project(PinnedDrop)] + struct InvalidArg3(()); + + #[pinned_drop] + impl PinnedDrop for InvalidArg3 { + fn drop(self: Pin<&Self>) {} //~ ERROR method `drop` must take an argument `self: Pin<&mut Self>` + } + + #[pin_project(PinnedDrop)] + struct InvalidArg4(()); + + #[pinned_drop] + impl PinnedDrop for InvalidArg4 { + fn drop(self: Pin<&mut ()>) {} //~ ERROR method `drop` must take an argument `self: Pin<&mut Self>` + } + + #[pin_project(PinnedDrop)] + struct InvalidName(()); + + #[pinned_drop] + impl PinnedDrop for InvalidName { + fn pinned_drop(&mut self) {} //~ ERROR method `pinned_drop` is not a member of trait `PinnedDrop + } +} + +fn main() {} diff --git a/tests/ui/pinned_drop/invalid.stderr b/tests/ui/pinned_drop/invalid.stderr new file mode 100644 index 0000000..8046903 --- /dev/null +++ b/tests/ui/pinned_drop/invalid.stderr @@ -0,0 +1,125 @@ +error: unexpected token: foo + --> $DIR/invalid.rs:8:19 + | +8 | #[pinned_drop(foo)] //~ ERROR unexpected token + | ^^^ + +error: duplicate #[pinned_drop] attribute + --> $DIR/invalid.rs:29:5 + | +29 | #[pinned_drop] //~ ERROR duplicate #[pinned_drop] attribute + | ^^^^^^^^^^^^^^ + +error: #[pinned_drop] may only be used on implementation for the `PinnedDrop` trait + --> $DIR/invalid.rs:42:10 + | +42 | impl Drop for TraitImpl {} //~ ERROR may only be used on implementation for the `PinnedDrop` trait + | ^^^^ + +error: #[pinned_drop] may only be used on implementation for the `PinnedDrop` trait + --> $DIR/invalid.rs:48:10 + | +48 | impl InherentImpl {} //~ ERROR may only be used on implementation for the `PinnedDrop` trait + | ^^^^^^^^^^^^ + +error: expected `impl` + --> $DIR/invalid.rs:51:5 + | +51 | fn drop(_: Pin<&mut ()>) {} //~ ERROR expected `impl` + | ^^ + +error: implementing the trait `PinnedDrop` is not unsafe + --> $DIR/invalid.rs:61:5 + | +61 | unsafe impl PinnedDrop for Impl { + | ^^^^^^ + +error: implementing the method `drop` is not unsafe + --> $DIR/invalid.rs:71:9 + | +71 | unsafe fn drop(self: Pin<&mut Self>) {} //~ ERROR implementing the method `drop` is not unsafe + | ^^^^^^ + +error: not all trait items implemented, missing: `drop` + --> $DIR/invalid.rs:82:5 + | +82 | impl PinnedDrop for Empty {} //~ ERROR not all trait items implemented, missing: `drop` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: const `A` is not a member of trait `PinnedDrop` + --> $DIR/invalid.rs:89:9 + | +89 | const A: u8 = 0; //~ ERROR const `A` is not a member of trait `PinnedDrop` + | ^^^^^^^^^^^^^^^^ + +error: const `A` is not a member of trait `PinnedDrop` + --> $DIR/invalid.rs:99:9 + | +99 | const A: u8 = 0; //~ ERROR const `A` is not a member of trait `PinnedDrop` + | ^^^^^^^^^^^^^^^^ + +error: type `A` is not a member of trait `PinnedDrop` + --> $DIR/invalid.rs:107:9 + | +107 | type A = u8; //~ ERROR type `A` is not a member of trait `PinnedDrop` + | ^^^^^^^^^^^^ + +error: type `A` is not a member of trait `PinnedDrop` + --> $DIR/invalid.rs:117:9 + | +117 | type A = u8; //~ ERROR type `A` is not a member of trait `PinnedDrop` + | ^^^^^^^^^^^^ + +error: duplicate definitions with name `drop` + --> $DIR/invalid.rs:126:9 + | +126 | fn drop(self: Pin<&mut Self>) {} //~ ERROR duplicate definitions with name `drop` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: method `drop` must return the unit type + --> $DIR/invalid.rs:147:42 + | +147 | fn drop(self: Pin<&mut Self>) -> Self {} //~ ERROR method `drop` must return the unit type + | ^^^^ + +error: method `drop` must take an argument `self: Pin<&mut Self>` + --> $DIR/invalid.rs:155:16 + | +155 | fn drop() {} //~ ERROR method `drop` must take an argument `self: Pin<&mut Self>` + | ^^ + +error: method `drop` must take an argument `self: Pin<&mut Self>` + --> $DIR/invalid.rs:163:17 + | +163 | fn drop(self: Pin<&mut Self>, _: ()) {} //~ ERROR method `drop` must take an argument `self: Pin<&mut Self>` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: method `drop` must take an argument `self: Pin<&mut Self>` + --> $DIR/invalid.rs:171:17 + | +171 | fn drop(&mut self) {} //~ ERROR method `drop` must take an argument `self: Pin<&mut Self>` + | ^^^^^^^^^ + +error: method `drop` must take an argument `self: Pin<&mut Self>` + --> $DIR/invalid.rs:179:17 + | +179 | fn drop(_: Pin<&mut Self>) {} //~ ERROR method `drop` must take an argument `self: Pin<&mut Self>` + | ^^^^^^^^^^^^^^^^^ + +error: method `drop` must take an argument `self: Pin<&mut Self>` + --> $DIR/invalid.rs:187:17 + | +187 | fn drop(self: Pin<&Self>) {} //~ ERROR method `drop` must take an argument `self: Pin<&mut Self>` + | ^^^^^^^^^^^^^^^^ + +error: method `drop` must take an argument `self: Pin<&mut Self>` + --> $DIR/invalid.rs:195:17 + | +195 | fn drop(self: Pin<&mut ()>) {} //~ ERROR method `drop` must take an argument `self: Pin<&mut Self>` + | ^^^^^^^^^^^^^^^^^^ + +error: method `pinned_drop` is not a member of trait `PinnedDrop + --> $DIR/invalid.rs:203:12 + | +203 | fn pinned_drop(&mut self) {} //~ ERROR method `pinned_drop` is not a member of trait `PinnedDrop + | ^^^^^^^^^^^ diff --git a/tests/ui/pinned_drop/pinned-drop-no-attr-arg.rs b/tests/ui/pinned_drop/pinned-drop-no-attr-arg.rs new file mode 100644 index 0000000..1241b5b --- /dev/null +++ b/tests/ui/pinned_drop/pinned-drop-no-attr-arg.rs @@ -0,0 +1,15 @@ +use pin_project::{pin_project, pinned_drop}; +use std::pin::Pin; + +#[pin_project] +struct Foo { + #[pin] + field: u8, +} + +#[pinned_drop] +impl PinnedDrop for Foo { //~ ERROR E0119 + fn drop(self: Pin<&mut Self>) {} +} + +fn main() {} diff --git a/tests/ui/pinned_drop/pinned-drop-no-attr-arg.stderr b/tests/ui/pinned_drop/pinned-drop-no-attr-arg.stderr new file mode 100644 index 0000000..7353dc4 --- /dev/null +++ b/tests/ui/pinned_drop/pinned-drop-no-attr-arg.stderr @@ -0,0 +1,8 @@ +error[E0119]: conflicting implementations of trait `pin_project::__private::PinnedDrop` for type `Foo`: + --> $DIR/pinned-drop-no-attr-arg.rs:11:1 + | +4 | #[pin_project] + | -------------- first implementation here +... +11 | impl PinnedDrop for Foo { //~ ERROR E0119 + | ^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Foo` diff --git a/tests/ui/pinned_drop/self.rs b/tests/ui/pinned_drop/self.rs new file mode 100644 index 0000000..cd53b04 --- /dev/null +++ b/tests/ui/pinned_drop/self.rs @@ -0,0 +1,28 @@ +use pin_project::{pin_project, pinned_drop}; +use std::pin::Pin; + +fn self_in_macro_def() { + #[pin_project(PinnedDrop)] + pub struct Struct { + x: usize, + } + + #[pinned_drop] + impl PinnedDrop for Struct { + fn drop(self: Pin<&mut Self>) { + macro_rules! t { + () => {{ + let _ = self; //~ ERROR can't capture dynamic environment in a fn item + + fn f(self: ()) { + //~^ ERROR `self` parameter is only allowed in associated functions + let _ = self; + } + }}; + } + t!(); + } + } +} + +fn main() {} diff --git a/tests/ui/pinned_drop/self.stderr b/tests/ui/pinned_drop/self.stderr new file mode 100644 index 0000000..3ba333b --- /dev/null +++ b/tests/ui/pinned_drop/self.stderr @@ -0,0 +1,23 @@ +error: `self` parameter is only allowed in associated functions + --> $DIR/self.rs:17:26 + | +17 | fn f(self: ()) { + | ^^^^ not semantically valid as function parameter +... +23 | t!(); + | ----- in this macro invocation + | + = note: associated functions are those in `impl` or `trait` definitions + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0434]: can't capture dynamic environment in a fn item + --> $DIR/self.rs:15:29 + | +15 | let _ = self; //~ ERROR can't capture dynamic environment in a fn item + | ^^^^ +... +23 | t!(); + | ----- in this macro invocation + | + = help: use the `|| { ... }` closure form instead + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/pinned_drop/unsafe-call.rs b/tests/ui/pinned_drop/unsafe-call.rs new file mode 100644 index 0000000..2f400c1 --- /dev/null +++ b/tests/ui/pinned_drop/unsafe-call.rs @@ -0,0 +1,17 @@ +use pin_project::{pin_project, pinned_drop}; +use std::pin::Pin; + +#[pin_project(PinnedDrop)] +struct Struct { + #[pin] + field: u8, +} + +#[pinned_drop] +impl PinnedDrop for Struct { + fn drop(self: Pin<&mut Self>) { + self.project().field.get_unchecked_mut(); //~ ERROR call to unsafe function is unsafe and requires unsafe function or block [E0133] + } +} + +fn main() {} diff --git a/tests/ui/pinned_drop/unsafe-call.stderr b/tests/ui/pinned_drop/unsafe-call.stderr new file mode 100644 index 0000000..4e8e00b --- /dev/null +++ b/tests/ui/pinned_drop/unsafe-call.stderr @@ -0,0 +1,7 @@ +error[E0133]: call to unsafe function is unsafe and requires unsafe function or block + --> $DIR/unsafe-call.rs:13:9 + | +13 | self.project().field.get_unchecked_mut(); //~ ERROR call to unsafe function is unsafe and requires unsafe function or block [E0133] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function + | + = note: consult the function's documentation for information on how to avoid undefined behavior diff --git a/tests/ui/project/ambiguous-let.rs b/tests/ui/project/ambiguous-let.rs new file mode 100644 index 0000000..a706749 --- /dev/null +++ b/tests/ui/project/ambiguous-let.rs @@ -0,0 +1,24 @@ +use pin_project::{pin_project, project}; + +#[pin_project] +enum Enum { + A(#[pin] A), + B(B), +} + +struct Struct(T); + +#[project] +fn foo() { + let mut foo: Enum = Enum::A(true); + + #[project] + let Struct(x) = match Pin::new(&mut foo).project() { + //~^ ERROR Both initializer expression and pattern are replaceable, you need to split the initializer expression into separate let bindings to avoid ambiguity + Enum::A(_) => Struct(true), + Enum::B(_) => unreachable!(), + }; + assert!(x); +} + +fn main() {} diff --git a/tests/ui/project/ambiguous-let.stderr b/tests/ui/project/ambiguous-let.stderr new file mode 100644 index 0000000..e6552c8 --- /dev/null +++ b/tests/ui/project/ambiguous-let.stderr @@ -0,0 +1,5 @@ +error: Both initializer expression and pattern are replaceable, you need to split the initializer expression into separate let bindings to avoid ambiguity + --> $DIR/ambiguous-let.rs:16:9 + | +16 | let Struct(x) = match Pin::new(&mut foo).project() { + | ^^^^^^^^^ diff --git a/tests/ui/project/invalid.rs b/tests/ui/project/invalid.rs new file mode 100644 index 0000000..07e9970 --- /dev/null +++ b/tests/ui/project/invalid.rs @@ -0,0 +1,190 @@ +mod argument { + use pin_project::{pin_project, project}; + + #[pin_project] + struct A(#[pin] ()); + + #[project] + fn unexpected_local1() { + let mut x = A(()); + #[project()] //~ ERROR unexpected token + let A(_) = Pin::new(&mut x).project(); + } + + #[project] + fn unexpected_local1() { + let mut x = A(()); + #[project(foo)] //~ ERROR unexpected token + let A(_) = Pin::new(&mut x).project(); + } + + #[project] + fn unexpected_expr1() { + let mut x = A(()); + #[project()] //~ ERROR unexpected token + match Pin::new(&mut x).project() { + A(_) => {} + } + } + + #[project] + fn unexpected_expr1() { + let mut x = A(()); + #[project(foo)] //~ ERROR unexpected token + match Pin::new(&mut x).project() { + A(_) => {} + } + } + + #[project()] // Ok + fn unexpected_item1() {} + + #[project(foo)] //~ ERROR unexpected token + fn unexpected_item2() {} +} + +mod attribute { + use pin_project::{pin_project, project, project_ref, project_replace}; + + #[pin_project(Replace)] + struct A(#[pin] ()); + + #[project] + fn duplicate_stmt_project() { + let mut x = A(()); + #[project] + #[project] //~ ERROR duplicate #[project] attribute + let A(_) = Pin::new(&mut x).project(); + } + + #[project_ref] + fn duplicate_stmt_project_ref() { + let mut x = A(()); + #[project_ref] + #[project_ref] //~ ERROR duplicate #[project_ref] attribute + let A(_) = Pin::new(&mut x).project(); + } + + #[project_replace] + fn duplicate_stmt_project_replace() { + let mut x = A(()); + #[project_replace] + #[project_replace] //~ ERROR duplicate #[project_replace] attribute + let A(_) = Pin::new(&mut x).project(); + } + + #[project] + fn combine_stmt_project1() { + let mut x = A(()); + #[project] + #[project_ref] //~ ERROR are mutually exclusive + let A(_) = Pin::new(&mut x).project(); + } + + #[project] + fn combine_stmt_project2() { + let mut x = A(()); + #[project] + #[project_replace] //~ ERROR are mutually exclusive + let A(_) = Pin::new(&mut x).project(); + } + + #[project] + fn combine_stmt_project3() { + let mut x = A(()); + #[project_ref] + #[project_replace] //~ ERROR are mutually exclusive + let A(_) = Pin::new(&mut x).project(); + } + + #[project_ref] + fn combine_stmt_project_ref1() { + let mut x = A(()); + #[project] + #[project_ref] //~ ERROR are mutually exclusive + let A(_) = Pin::new(&mut x).project(); + } + + #[project_ref] + fn combine_stmt_project_ref2() { + let mut x = A(()); + #[project] + #[project_replace] //~ ERROR are mutually exclusive + let A(_) = Pin::new(&mut x).project(); + } + + #[project_ref] + fn combine_stmt_project_ref3() { + let mut x = A(()); + #[project_ref] + #[project_replace] //~ ERROR are mutually exclusive + let A(_) = Pin::new(&mut x).project(); + } + + #[project_replace] + fn combine_stmt_project_replace1() { + let mut x = A(()); + #[project] + #[project_ref] //~ ERROR are mutually exclusive + let A(_) = Pin::new(&mut x).project(); + } + + #[project_replace] + fn combine_stmt_project_replace2() { + let mut x = A(()); + #[project] + #[project_replace] //~ ERROR are mutually exclusive + let A(_) = Pin::new(&mut x).project(); + } + + #[project_replace] + fn combine_stmt_project_replace3() { + let mut x = A(()); + #[project_ref] + #[project_replace] //~ ERROR are mutually exclusive + let A(_) = Pin::new(&mut x).project(); + } + + #[project] + #[project] //~ ERROR duplicate #[project] attribute + fn duplicate_fn_project() {} + + #[project_ref] + #[project_ref] //~ ERROR duplicate #[project_ref] attribute + fn duplicate_fn_project_ref() {} + + #[project_replace] + #[project_replace] //~ ERROR duplicate #[project_replace] attribute + fn duplicate_fn_project_replace() {} + + #[project] + #[project] //~ ERROR duplicate #[project] attribute + impl A {} + + #[project_ref] + #[project_ref] //~ ERROR duplicate #[project_ref] attribute + impl A {} + + #[project_replace] + #[project_replace] //~ ERROR duplicate #[project_replace] attribute + impl A {} + + #[allow(unused_imports)] + mod use_ { + use pin_project::{project, project_ref, project_replace}; + + #[project] + #[project] //~ ERROR duplicate #[project] attribute + use super::A; + + #[project_ref] + #[project_ref] //~ ERROR duplicate #[project_ref] attribute + use super::A; + + #[project_replace] + #[project_replace] //~ ERROR duplicate #[project_replace] attribute + use super::A; + } +} + +fn main() {} diff --git a/tests/ui/project/invalid.stderr b/tests/ui/project/invalid.stderr new file mode 100644 index 0000000..287cac8 --- /dev/null +++ b/tests/ui/project/invalid.stderr @@ -0,0 +1,155 @@ +error: unexpected token: () + --> $DIR/invalid.rs:10:18 + | +10 | #[project()] //~ ERROR unexpected token + | ^^ + +error: unexpected token: (foo) + --> $DIR/invalid.rs:17:18 + | +17 | #[project(foo)] //~ ERROR unexpected token + | ^^^^^ + +error: unexpected token: () + --> $DIR/invalid.rs:24:18 + | +24 | #[project()] //~ ERROR unexpected token + | ^^ + +error: unexpected token: (foo) + --> $DIR/invalid.rs:33:18 + | +33 | #[project(foo)] //~ ERROR unexpected token + | ^^^^^ + +error: unexpected token: foo + --> $DIR/invalid.rs:42:15 + | +42 | #[project(foo)] //~ ERROR unexpected token + | ^^^ + +error: duplicate #[project] attribute + --> $DIR/invalid.rs:56:9 + | +56 | #[project] //~ ERROR duplicate #[project] attribute + | ^^^^^^^^^^ + +error: duplicate #[project_ref] attribute + --> $DIR/invalid.rs:64:9 + | +64 | #[project_ref] //~ ERROR duplicate #[project_ref] attribute + | ^^^^^^^^^^^^^^ + +error: duplicate #[project_replace] attribute + --> $DIR/invalid.rs:72:9 + | +72 | #[project_replace] //~ ERROR duplicate #[project_replace] attribute + | ^^^^^^^^^^^^^^^^^^ + +error: attributes `project_ref` and `project` are mutually exclusive + --> $DIR/invalid.rs:79:9 + | +79 | #[project] + | ^^^^^^^^^^ + +error: attributes `project` and `project_replace` are mutually exclusive + --> $DIR/invalid.rs:88:9 + | +88 | #[project_replace] //~ ERROR are mutually exclusive + | ^^^^^^^^^^^^^^^^^^ + +error: attributes `project_ref` and `project_replace` are mutually exclusive + --> $DIR/invalid.rs:96:9 + | +96 | #[project_replace] //~ ERROR are mutually exclusive + | ^^^^^^^^^^^^^^^^^^ + +error: attributes `project_ref` and `project` are mutually exclusive + --> $DIR/invalid.rs:103:9 + | +103 | #[project] + | ^^^^^^^^^^ + +error: attributes `project` and `project_replace` are mutually exclusive + --> $DIR/invalid.rs:112:9 + | +112 | #[project_replace] //~ ERROR are mutually exclusive + | ^^^^^^^^^^^^^^^^^^ + +error: attributes `project_ref` and `project_replace` are mutually exclusive + --> $DIR/invalid.rs:120:9 + | +120 | #[project_replace] //~ ERROR are mutually exclusive + | ^^^^^^^^^^^^^^^^^^ + +error: attributes `project_ref` and `project` are mutually exclusive + --> $DIR/invalid.rs:127:9 + | +127 | #[project] + | ^^^^^^^^^^ + +error: attributes `project` and `project_replace` are mutually exclusive + --> $DIR/invalid.rs:136:9 + | +136 | #[project_replace] //~ ERROR are mutually exclusive + | ^^^^^^^^^^^^^^^^^^ + +error: attributes `project_ref` and `project_replace` are mutually exclusive + --> $DIR/invalid.rs:144:9 + | +144 | #[project_replace] //~ ERROR are mutually exclusive + | ^^^^^^^^^^^^^^^^^^ + +error: duplicate #[project] attribute + --> $DIR/invalid.rs:149:5 + | +149 | #[project] //~ ERROR duplicate #[project] attribute + | ^^^^^^^^^^ + +error: duplicate #[project_ref] attribute + --> $DIR/invalid.rs:153:5 + | +153 | #[project_ref] //~ ERROR duplicate #[project_ref] attribute + | ^^^^^^^^^^^^^^ + +error: duplicate #[project_replace] attribute + --> $DIR/invalid.rs:157:5 + | +157 | #[project_replace] //~ ERROR duplicate #[project_replace] attribute + | ^^^^^^^^^^^^^^^^^^ + +error: duplicate #[project] attribute + --> $DIR/invalid.rs:161:5 + | +161 | #[project] //~ ERROR duplicate #[project] attribute + | ^^^^^^^^^^ + +error: duplicate #[project_ref] attribute + --> $DIR/invalid.rs:165:5 + | +165 | #[project_ref] //~ ERROR duplicate #[project_ref] attribute + | ^^^^^^^^^^^^^^ + +error: duplicate #[project_replace] attribute + --> $DIR/invalid.rs:169:5 + | +169 | #[project_replace] //~ ERROR duplicate #[project_replace] attribute + | ^^^^^^^^^^^^^^^^^^ + +error: duplicate #[project] attribute + --> $DIR/invalid.rs:177:9 + | +177 | #[project] //~ ERROR duplicate #[project] attribute + | ^^^^^^^^^^ + +error: duplicate #[project_ref] attribute + --> $DIR/invalid.rs:181:9 + | +181 | #[project_ref] //~ ERROR duplicate #[project_ref] attribute + | ^^^^^^^^^^^^^^ + +error: duplicate #[project_replace] attribute + --> $DIR/invalid.rs:185:9 + | +185 | #[project_replace] //~ ERROR duplicate #[project_replace] attribute + | ^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/project/type-mismatch.rs b/tests/ui/project/type-mismatch.rs new file mode 100644 index 0000000..41a70eb --- /dev/null +++ b/tests/ui/project/type-mismatch.rs @@ -0,0 +1,74 @@ +#![feature(proc_macro_hygiene, stmt_expr_attributes)] + +use pin_project::{pin_project, project}; +use std::pin::Pin; + +#[project] +fn type_mismatch() { + #[pin_project] + enum Enum { + Variant1(#[pin] A, B), + Variant2 { + #[pin] + field1: C, + field2: D, + }, + None, + } + + let mut foo = Enum::Variant1(1, 2); + let mut foo = Pin::new(&mut foo).project(); + + #[project] + match &mut foo { + Enum::Variant1(x, y) => { + let x: &mut Pin<&mut i32> = x; + assert_eq!(**x, 1); + + let y: &mut &mut i32 = y; + assert_eq!(**y, 2); + } + Enum::Variant2 { field1, field2 } => { + let _x: &mut Pin<&mut i32> = field1; + let _y: &mut &mut i32 = field2; + } + None => {} //~ ERROR mismatched types + } +} + +//~ ERROR mismatched types +// span is lost. +// Refs: https://github.com/rust-lang/rust/issues/43081 +fn type_mismatch_span_issue() { + #[pin_project] + enum Enum { + Variant1(#[pin] A, B), + Variant2 { + #[pin] + field1: C, + field2: D, + }, + None, + } + + let mut foo = Enum::Variant1(1, 2); + let mut foo = Pin::new(&mut foo).project(); + + #[project] + match &mut foo { + Enum::Variant1(x, y) => { + let x: &mut Pin<&mut i32> = x; + assert_eq!(**x, 1); + + let y: &mut &mut i32 = y; + assert_eq!(**y, 2); + } + Enum::Variant2 { field1, field2 } => { + let _x: &mut Pin<&mut i32> = field1; + let _y: &mut &mut i32 = field2; + } + None => {} + } +} + +fn main() {} diff --git a/tests/ui/project/type-mismatch.stderr b/tests/ui/project/type-mismatch.stderr new file mode 100644 index 0000000..b4c97d5 --- /dev/null +++ b/tests/ui/project/type-mismatch.stderr @@ -0,0 +1,16 @@ +error[E0308]: mismatched types + --> $DIR/type-mismatch.rs:35:9 + | +23 | match &mut foo { + | -------- this expression has type `&mut type_mismatch::__EnumProjection<'_, {integer}, {integer}, _, _>` +... +35 | None => {} //~ ERROR mismatched types + | ^^^^ expected enum `type_mismatch::__EnumProjection`, found enum `std::option::Option` + | + = note: expected enum `type_mismatch::__EnumProjection<'_, {integer}, {integer}, _, _>` + found enum `std::option::Option<_>` + +error[E0308]: mismatched types + | + = note: expected enum `type_mismatch_span_issue::__EnumProjection<'_, {integer}, {integer}, _, _>` + found enum `std::option::Option<_>` diff --git a/tests/ui/project/use-public.rs b/tests/ui/project/use-public.rs new file mode 100644 index 0000000..23c9b89 --- /dev/null +++ b/tests/ui/project/use-public.rs @@ -0,0 +1,15 @@ +use pin_project::pin_project; + +#[pin_project] +struct A { + field: u8, +} + +pub mod b { + use pin_project::project; + + #[project] + pub use crate::A; //~ ERROR E0365 +} + +fn main() {} diff --git a/tests/ui/project/use-public.stderr b/tests/ui/project/use-public.stderr new file mode 100644 index 0000000..7919d65 --- /dev/null +++ b/tests/ui/project/use-public.stderr @@ -0,0 +1,7 @@ +error[E0365]: `__AProjection` is private, and cannot be re-exported + --> $DIR/use-public.rs:12:13 + | +12 | pub use crate::A; //~ ERROR E0365 + | ^^^^^^^^ re-export of private `__AProjection` + | + = note: consider declaring type or module `__AProjection` with `pub` diff --git a/tests/ui/project/use.rs b/tests/ui/project/use.rs new file mode 100644 index 0000000..d4b02c1 --- /dev/null +++ b/tests/ui/project/use.rs @@ -0,0 +1,17 @@ +use pin_project::pin_project; + +#[pin_project] +struct A { + field: u8, +} + +mod b { + use pin_project::project; + + #[project] + use crate::A as B; //~ ERROR #[project] attribute may not be used on renamed imports + #[project] + use crate::*; //~ ERROR #[project] attribute may not be used on glob imports +} + +fn main() {} diff --git a/tests/ui/project/use.stderr b/tests/ui/project/use.stderr new file mode 100644 index 0000000..07d0241 --- /dev/null +++ b/tests/ui/project/use.stderr @@ -0,0 +1,11 @@ +error: #[project] attribute may not be used on renamed imports + --> $DIR/use.rs:12:16 + | +12 | use crate::A as B; //~ ERROR #[project] attribute may not be used on renamed imports + | ^^^^^^ + +error: #[project] attribute may not be used on glob imports + --> $DIR/use.rs:14:16 + | +14 | use crate::*; //~ ERROR #[project] attribute may not be used on glob imports + | ^ diff --git a/tests/ui/unsafe_unpin/conflict-unpin.rs b/tests/ui/unsafe_unpin/conflict-unpin.rs new file mode 100644 index 0000000..e0c8a7b --- /dev/null +++ b/tests/ui/unsafe_unpin/conflict-unpin.rs @@ -0,0 +1,30 @@ +use pin_project::pin_project; + +#[pin_project(UnsafeUnpin)] //~ ERROR E0119 +struct Foo { + #[pin] + future: T, + field: U, +} + +impl Unpin for Foo where T: Unpin {} + +#[pin_project(UnsafeUnpin)] //~ ERROR E0119 +struct Bar { + #[pin] + future: T, + field: U, +} + +impl Unpin for Bar {} + +#[pin_project(UnsafeUnpin)] //~ ERROR E0119 +struct Baz { + #[pin] + future: T, + field: U, +} + +impl Unpin for Baz {} + +fn main() {} diff --git a/tests/ui/unsafe_unpin/conflict-unpin.stderr b/tests/ui/unsafe_unpin/conflict-unpin.stderr new file mode 100644 index 0000000..62de016 --- /dev/null +++ b/tests/ui/unsafe_unpin/conflict-unpin.stderr @@ -0,0 +1,35 @@ +error[E0119]: conflicting implementations of trait `std::marker::Unpin` for type `Foo<_, _>`: + --> $DIR/conflict-unpin.rs:3:1 + | +3 | #[pin_project(UnsafeUnpin)] //~ ERROR E0119 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Foo<_, _>` +... +10 | impl Unpin for Foo where T: Unpin {} + | --------------------------------------------- first implementation here + | + = note: upstream crates may add a new impl of trait `pin_project::UnsafeUnpin` for type `pin_project::__private::Wrapper<'_, Foo<_, _>>` in future versions + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0119]: conflicting implementations of trait `std::marker::Unpin` for type `Bar<_, _>`: + --> $DIR/conflict-unpin.rs:12:1 + | +12 | #[pin_project(UnsafeUnpin)] //~ ERROR E0119 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Bar<_, _>` +... +19 | impl Unpin for Bar {} + | ------------------------------ first implementation here + | + = note: upstream crates may add a new impl of trait `pin_project::UnsafeUnpin` for type `pin_project::__private::Wrapper<'_, Bar<_, _>>` in future versions + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0119]: conflicting implementations of trait `std::marker::Unpin` for type `Baz<_, _>`: + --> $DIR/conflict-unpin.rs:21:1 + | +21 | #[pin_project(UnsafeUnpin)] //~ ERROR E0119 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Baz<_, _>` +... +28 | impl Unpin for Baz {} + | -------------------------------------------- first implementation here + | + = note: upstream crates may add a new impl of trait `pin_project::UnsafeUnpin` for type `pin_project::__private::Wrapper<'_, Baz<_, _>>` in future versions + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/unsafe_unpin/not-implement-unsafe-unpin.rs b/tests/ui/unsafe_unpin/not-implement-unsafe-unpin.rs new file mode 100644 index 0000000..429d60f --- /dev/null +++ b/tests/ui/unsafe_unpin/not-implement-unsafe-unpin.rs @@ -0,0 +1,14 @@ +use pin_project::pin_project; + +#[pin_project(UnsafeUnpin)] +struct Struct { + #[pin] + inner: T, + other: U, +} + +fn is_unpin() {} + +fn main() { + is_unpin::>(); //~ ERROR E0277 +} diff --git a/tests/ui/unsafe_unpin/not-implement-unsafe-unpin.stderr b/tests/ui/unsafe_unpin/not-implement-unsafe-unpin.stderr new file mode 100644 index 0000000..0baefe3 --- /dev/null +++ b/tests/ui/unsafe_unpin/not-implement-unsafe-unpin.stderr @@ -0,0 +1,11 @@ +error[E0277]: the trait bound `Struct<(), ()>: pin_project::UnsafeUnpin` is not satisfied + --> $DIR/not-implement-unsafe-unpin.rs:13:16 + | +10 | fn is_unpin() {} + | ----- required by this bound in `is_unpin` +... +13 | is_unpin::>(); //~ ERROR E0277 + | ^^^^^^^^^^^^^^ the trait `pin_project::UnsafeUnpin` is not implemented for `Struct<(), ()>` + | + = note: required because of the requirements on the impl of `pin_project::UnsafeUnpin` for `pin_project::__private::Wrapper<'_, Struct<(), ()>>` + = note: required because of the requirements on the impl of `std::marker::Unpin` for `Struct<(), ()>` diff --git a/tests/ui/unsafe_unpin/proper_unpin.rs b/tests/ui/unsafe_unpin/proper_unpin.rs new file mode 100644 index 0000000..6573aec --- /dev/null +++ b/tests/ui/unsafe_unpin/proper_unpin.rs @@ -0,0 +1,41 @@ +use pin_project::{pin_project, UnsafeUnpin}; +use std::marker::PhantomPinned; + +fn is_unpin() {} + +#[pin_project(UnsafeUnpin)] +struct Blah { + field1: U, + #[pin] + field2: T, +} + +unsafe impl UnsafeUnpin for Blah {} + +#[pin_project(UnsafeUnpin)] +struct TrivialBounds { + #[pin] + field1: PhantomPinned, +} + +#[pin_project(UnsafeUnpin)] +struct OverlappingLifetimeNames<'pin, T, U> { + #[pin] + field1: U, + #[pin] + field2: Option, + field3: &'pin (), +} + +unsafe impl UnsafeUnpin for OverlappingLifetimeNames<'_, T, U> {} + +fn main() { + is_unpin::>(); //~ ERROR E0277 + is_unpin::>(); // Ok + is_unpin::>(); //~ ERROR E0277 + + is_unpin::(); //~ ERROR E0277 + + is_unpin::>(); //~ ERROR E0277 + is_unpin::>(); //~ ERROR E0277 +} diff --git a/tests/ui/unsafe_unpin/proper_unpin.stderr b/tests/ui/unsafe_unpin/proper_unpin.stderr new file mode 100644 index 0000000..410dd0e --- /dev/null +++ b/tests/ui/unsafe_unpin/proper_unpin.stderr @@ -0,0 +1,63 @@ +error[E0277]: `std::marker::PhantomPinned` cannot be unpinned + --> $DIR/proper_unpin.rs:33:5 + | +4 | fn is_unpin() {} + | ----- required by this bound in `is_unpin` +... +33 | is_unpin::>(); //~ ERROR E0277 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned` + | + = note: required because of the requirements on the impl of `pin_project::UnsafeUnpin` for `Blah` + = note: required because of the requirements on the impl of `pin_project::UnsafeUnpin` for `pin_project::__private::Wrapper<'_, Blah>` + = note: required because of the requirements on the impl of `std::marker::Unpin` for `Blah` + +error[E0277]: `std::marker::PhantomPinned` cannot be unpinned + --> $DIR/proper_unpin.rs:35:5 + | +4 | fn is_unpin() {} + | ----- required by this bound in `is_unpin` +... +35 | is_unpin::>(); //~ ERROR E0277 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned` + | + = note: required because of the requirements on the impl of `pin_project::UnsafeUnpin` for `Blah` + = note: required because of the requirements on the impl of `pin_project::UnsafeUnpin` for `pin_project::__private::Wrapper<'_, Blah>` + = note: required because of the requirements on the impl of `std::marker::Unpin` for `Blah` + +error[E0277]: the trait bound `TrivialBounds: pin_project::UnsafeUnpin` is not satisfied + --> $DIR/proper_unpin.rs:37:16 + | +4 | fn is_unpin() {} + | ----- required by this bound in `is_unpin` +... +37 | is_unpin::(); //~ ERROR E0277 + | ^^^^^^^^^^^^^ the trait `pin_project::UnsafeUnpin` is not implemented for `TrivialBounds` + | + = note: required because of the requirements on the impl of `pin_project::UnsafeUnpin` for `pin_project::__private::Wrapper<'_, TrivialBounds>` + = note: required because of the requirements on the impl of `std::marker::Unpin` for `TrivialBounds` + +error[E0277]: `std::marker::PhantomPinned` cannot be unpinned + --> $DIR/proper_unpin.rs:39:5 + | +4 | fn is_unpin() {} + | ----- required by this bound in `is_unpin` +... +39 | is_unpin::>(); //~ ERROR E0277 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned` + | + = note: required because of the requirements on the impl of `pin_project::UnsafeUnpin` for `OverlappingLifetimeNames<'_, std::marker::PhantomPinned, ()>` + = note: required because of the requirements on the impl of `pin_project::UnsafeUnpin` for `pin_project::__private::Wrapper<'_, OverlappingLifetimeNames<'_, std::marker::PhantomPinned, ()>>` + = note: required because of the requirements on the impl of `std::marker::Unpin` for `OverlappingLifetimeNames<'_, std::marker::PhantomPinned, ()>` + +error[E0277]: `std::marker::PhantomPinned` cannot be unpinned + --> $DIR/proper_unpin.rs:40:5 + | +4 | fn is_unpin() {} + | ----- required by this bound in `is_unpin` +... +40 | is_unpin::>(); //~ ERROR E0277 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned` + | + = note: required because of the requirements on the impl of `pin_project::UnsafeUnpin` for `OverlappingLifetimeNames<'_, (), std::marker::PhantomPinned>` + = note: required because of the requirements on the impl of `pin_project::UnsafeUnpin` for `pin_project::__private::Wrapper<'_, OverlappingLifetimeNames<'_, (), std::marker::PhantomPinned>>` + = note: required because of the requirements on the impl of `std::marker::Unpin` for `OverlappingLifetimeNames<'_, (), std::marker::PhantomPinned>` diff --git a/tests/ui/unstable-features/README.md b/tests/ui/unstable-features/README.md new file mode 100644 index 0000000..b9215b6 --- /dev/null +++ b/tests/ui/unstable-features/README.md @@ -0,0 +1,5 @@ +# UI tests for unstable features + +These tests check how the guarantees and features provided by pin-project interact with unstable language features. + +The names of the files contained in this directory need to begin with the name of the feature. diff --git a/tests/ui/unstable-features/marker_trait_attr-feature-gate.rs b/tests/ui/unstable-features/marker_trait_attr-feature-gate.rs new file mode 100644 index 0000000..fa4b01e --- /dev/null +++ b/tests/ui/unstable-features/marker_trait_attr-feature-gate.rs @@ -0,0 +1,19 @@ +// NB: If you change this test, change 'marker_trait_attr.rs' at the same time. + +use pin_project::pin_project; +use std::marker::PhantomPinned; + +#[pin_project] //~ ERROR E0119 +struct Struct { + #[pin] + x: T, +} + +// unsound Unpin impl +impl Unpin for Struct {} + +fn is_unpin() {} + +fn main() { + is_unpin::>() +} diff --git a/tests/ui/unstable-features/marker_trait_attr-feature-gate.stderr b/tests/ui/unstable-features/marker_trait_attr-feature-gate.stderr new file mode 100644 index 0000000..bab534b --- /dev/null +++ b/tests/ui/unstable-features/marker_trait_attr-feature-gate.stderr @@ -0,0 +1,10 @@ +error[E0119]: conflicting implementations of trait `std::marker::Unpin` for type `Struct<_>`: + --> $DIR/marker_trait_attr-feature-gate.rs:6:1 + | +6 | #[pin_project] //~ ERROR E0119 + | ^^^^^^^^^^^^^^ conflicting implementation for `Struct<_>` +... +13 | impl Unpin for Struct {} + | --------------------------- first implementation here + | + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/unstable-features/marker_trait_attr.rs b/tests/ui/unstable-features/marker_trait_attr.rs new file mode 100644 index 0000000..0b8b30a --- /dev/null +++ b/tests/ui/unstable-features/marker_trait_attr.rs @@ -0,0 +1,25 @@ +// NB: If you change this test, change 'marker_trait_attr-feature-gate.rs' at the same time. + +// marker_trait_attr +// Tracking issue: https://github.com/rust-lang/rust/issues/29864 +#![feature(marker_trait_attr)] + +// See https://github.com/taiki-e/pin-project/issues/105#issuecomment-535355974 + +use pin_project::pin_project; +use std::marker::PhantomPinned; + +#[pin_project] //~ ERROR E0119 +struct Struct { + #[pin] + x: T, +} + +// unsound Unpin impl +impl Unpin for Struct {} + +fn is_unpin() {} + +fn main() { + is_unpin::>() +} diff --git a/tests/ui/unstable-features/marker_trait_attr.stderr b/tests/ui/unstable-features/marker_trait_attr.stderr new file mode 100644 index 0000000..9b3ec57 --- /dev/null +++ b/tests/ui/unstable-features/marker_trait_attr.stderr @@ -0,0 +1,10 @@ +error[E0119]: conflicting implementations of trait `std::marker::Unpin` for type `Struct<_>`: + --> $DIR/marker_trait_attr.rs:12:1 + | +12 | #[pin_project] //~ ERROR E0119 + | ^^^^^^^^^^^^^^ conflicting implementation for `Struct<_>` +... +19 | impl Unpin for Struct {} + | --------------------------- first implementation here + | + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/unstable-features/overlapping_marker_traits-feature-gate.rs b/tests/ui/unstable-features/overlapping_marker_traits-feature-gate.rs new file mode 100644 index 0000000..0bd4a32 --- /dev/null +++ b/tests/ui/unstable-features/overlapping_marker_traits-feature-gate.rs @@ -0,0 +1,19 @@ +// NB: If you change this test, change 'overlapping_marker_traits.rs' at the same time. + +use pin_project::pin_project; +use std::marker::PhantomPinned; + +#[pin_project] //~ ERROR E0119 +struct Struct { + #[pin] + x: T, +} + +// unsound Unpin impl +impl Unpin for Struct {} + +fn is_unpin() {} + +fn main() { + is_unpin::>() +} diff --git a/tests/ui/unstable-features/overlapping_marker_traits-feature-gate.stderr b/tests/ui/unstable-features/overlapping_marker_traits-feature-gate.stderr new file mode 100644 index 0000000..4a8e238 --- /dev/null +++ b/tests/ui/unstable-features/overlapping_marker_traits-feature-gate.stderr @@ -0,0 +1,10 @@ +error[E0119]: conflicting implementations of trait `std::marker::Unpin` for type `Struct<_>`: + --> $DIR/overlapping_marker_traits-feature-gate.rs:6:1 + | +6 | #[pin_project] //~ ERROR E0119 + | ^^^^^^^^^^^^^^ conflicting implementation for `Struct<_>` +... +13 | impl Unpin for Struct {} + | --------------------------- first implementation here + | + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/unstable-features/overlapping_marker_traits.rs b/tests/ui/unstable-features/overlapping_marker_traits.rs new file mode 100644 index 0000000..27d37a3 --- /dev/null +++ b/tests/ui/unstable-features/overlapping_marker_traits.rs @@ -0,0 +1,29 @@ +// NB: If you change this test, change 'overlapping_marker_traits-feature-gate.rs' at the same time. + +// This feature could break the guarantee for Unpin provided by pin-project, +// but was removed in https://github.com/rust-lang/rust/pull/68544 (nightly-2020-02-06). +// Refs: +// * https://github.com/rust-lang/rust/issues/29864#issuecomment-515780867. +// * https://github.com/taiki-e/pin-project/issues/105 + +// overlapping_marker_traits +// Tracking issue: https://github.com/rust-lang/rust/issues/29864 +#![feature(overlapping_marker_traits)] + +use pin_project::pin_project; +use std::marker::PhantomPinned; + +#[pin_project] +struct Struct { + #[pin] + x: T, +} + +// unsound Unpin impl +impl Unpin for Struct {} + +fn is_unpin() {} + +fn main() { + is_unpin::>() +} diff --git a/tests/ui/unstable-features/overlapping_marker_traits.stderr b/tests/ui/unstable-features/overlapping_marker_traits.stderr new file mode 100644 index 0000000..91aaf6c --- /dev/null +++ b/tests/ui/unstable-features/overlapping_marker_traits.stderr @@ -0,0 +1,18 @@ +error[E0557]: feature has been removed + --> $DIR/overlapping_marker_traits.rs:11:12 + | +11 | #![feature(overlapping_marker_traits)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ feature has been removed + | + = note: removed in favor of `#![feature(marker_trait_attr)]` + +error[E0119]: conflicting implementations of trait `std::marker::Unpin` for type `Struct<_>`: + --> $DIR/overlapping_marker_traits.rs:16:1 + | +16 | #[pin_project] + | ^^^^^^^^^^^^^^ conflicting implementation for `Struct<_>` +... +23 | impl Unpin for Struct {} + | --------------------------- first implementation here + | + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/unstable-features/run-pass/stmt_expr_attributes.rs b/tests/ui/unstable-features/run-pass/stmt_expr_attributes.rs new file mode 100644 index 0000000..8ad8e41 --- /dev/null +++ b/tests/ui/unstable-features/run-pass/stmt_expr_attributes.rs @@ -0,0 +1,62 @@ +// NB: If you change this test, change 'stmt_expr_attributes-feature-gate.rs' at the same time. + +// proc_macro_hygiene +// Tracking issue: https://github.com/rust-lang/rust/issues/54727 +#![feature(proc_macro_hygiene)] +// stmt_expr_attributes +// Tracking issue: https://github.com/rust-lang/rust/issues/15701 +#![feature(stmt_expr_attributes)] + +use pin_project::{pin_project, project}; +use std::pin::Pin; + +fn project_stmt_expr_nightly() { + #[pin_project] + enum Baz { + Variant1(#[pin] A, B), + Variant2 { + #[pin] + field1: C, + field2: D, + }, + None, + } + + let mut baz = Baz::Variant1(1, 2); + + let mut baz = Pin::new(&mut baz).project(); + + #[project] + match &mut baz { + Baz::Variant1(x, y) => { + let x: &mut Pin<&mut i32> = x; + assert_eq!(**x, 1); + + let y: &mut &mut i32 = y; + assert_eq!(**y, 2); + } + Baz::Variant2 { field1, field2 } => { + let _x: &mut Pin<&mut i32> = field1; + let _y: &mut &mut i32 = field2; + } + Baz::None => {} + } + + let () = #[project] + match &mut baz { + Baz::Variant1(x, y) => { + let x: &mut Pin<&mut i32> = x; + assert_eq!(**x, 1); + + let y: &mut &mut i32 = y; + assert_eq!(**y, 2); + } + Baz::Variant2 { field1, field2 } => { + let _x: &mut Pin<&mut i32> = field1; + let _y: &mut &mut i32 = field2; + } + Baz::None => {} + }; +} + +fn main() {} diff --git a/tests/ui/unstable-features/stmt_expr_attributes-feature-gate.rs b/tests/ui/unstable-features/stmt_expr_attributes-feature-gate.rs new file mode 100644 index 0000000..8226723 --- /dev/null +++ b/tests/ui/unstable-features/stmt_expr_attributes-feature-gate.rs @@ -0,0 +1,55 @@ +// NB: If you change this test, change 'stmt_expr_attributes.rs' at the same time. + +use pin_project::{pin_project, project}; +use std::pin::Pin; + +fn project_stmt_expr_nightly() { + #[pin_project] + enum Enum { + Variant1(#[pin] A, B), + Variant2 { + #[pin] + field1: C, + field2: D, + }, + None, + } + + let mut baz = Enum::Variant1(1, 2); + + let mut baz = Pin::new(&mut baz).project(); + + #[project] //~ ERROR E0658 + match &mut baz { + Enum::Variant1(x, y) => { + let x: &mut Pin<&mut i32> = x; + assert_eq!(**x, 1); + + let y: &mut &mut i32 = y; + assert_eq!(**y, 2); + } + Enum::Variant2 { field1, field2 } => { + let _x: &mut Pin<&mut i32> = field1; + let _y: &mut &mut i32 = field2; + } + Enum::None => {} + } + + let () = #[project] //~ ERROR E0658 + match &mut baz { + Enum::Variant1(x, y) => { + let x: &mut Pin<&mut i32> = x; + assert_eq!(**x, 1); + + let y: &mut &mut i32 = y; + assert_eq!(**y, 2); + } + Enum::Variant2 { field1, field2 } => { + let _x: &mut Pin<&mut i32> = field1; + let _y: &mut &mut i32 = field2; + } + Enum::None => {} + }; +} + +fn main() {} diff --git a/tests/ui/unstable-features/stmt_expr_attributes-feature-gate.stderr b/tests/ui/unstable-features/stmt_expr_attributes-feature-gate.stderr new file mode 100644 index 0000000..6510ec7 --- /dev/null +++ b/tests/ui/unstable-features/stmt_expr_attributes-feature-gate.stderr @@ -0,0 +1,35 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/stmt_expr_attributes-feature-gate.rs:22:5 + | +22 | #[project] //~ ERROR E0658 + | ^^^^^^^^^^ + | + = note: see issue #15701 for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error[E0658]: attributes on expressions are experimental + --> $DIR/stmt_expr_attributes-feature-gate.rs:38:14 + | +38 | let () = #[project] //~ ERROR E0658 + | ^^^^^^^^^^ + | + = note: see issue #15701 for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error[E0658]: custom attributes cannot be applied to expressions + --> $DIR/stmt_expr_attributes-feature-gate.rs:22:5 + | +22 | #[project] //~ ERROR E0658 + | ^^^^^^^^^^ + | + = note: see issue #54727 for more information + = help: add `#![feature(proc_macro_hygiene)]` to the crate attributes to enable + +error[E0658]: custom attributes cannot be applied to expressions + --> $DIR/stmt_expr_attributes-feature-gate.rs:38:14 + | +38 | let () = #[project] //~ ERROR E0658 + | ^^^^^^^^^^ + | + = note: see issue #54727 for more information + = help: add `#![feature(proc_macro_hygiene)]` to the crate attributes to enable diff --git a/tests/ui/unstable-features/trivial_bounds-bug.rs b/tests/ui/unstable-features/trivial_bounds-bug.rs new file mode 100644 index 0000000..2ec4960 --- /dev/null +++ b/tests/ui/unstable-features/trivial_bounds-bug.rs @@ -0,0 +1,33 @@ +// NB: If you change this test, change 'trivial_bounds-feature-gate.rs' at the same time. + +// trivial_bounds +// Tracking issue: https://github.com/rust-lang/rust/issues/48214 +#![feature(trivial_bounds)] + +mod phantom_pinned { + use std::marker::{PhantomData, PhantomPinned}; + + struct A(PhantomPinned); + + // bug of trivial_bounds? + impl Unpin for A where PhantomPinned: Unpin {} //~ ERROR E0277 + + struct Wrapper(T); + + impl Unpin for Wrapper where T: Unpin {} + + struct B(PhantomPinned); + + impl Unpin for B where Wrapper: Unpin {} // Ok + + struct WrapperWithLifetime<'a, T>(PhantomData<&'a ()>, T); + + impl Unpin for WrapperWithLifetime<'_, T> where T: Unpin {} + + struct C(PhantomPinned); + + // Ok + impl<'a> Unpin for C where WrapperWithLifetime<'a, PhantomPinned>: Unpin {} +} + +fn main() {} diff --git a/tests/ui/unstable-features/trivial_bounds-bug.stderr b/tests/ui/unstable-features/trivial_bounds-bug.stderr new file mode 100644 index 0000000..7ddf10c --- /dev/null +++ b/tests/ui/unstable-features/trivial_bounds-bug.stderr @@ -0,0 +1,10 @@ +error[E0277]: `std::marker::PhantomPinned` cannot be unpinned + --> $DIR/trivial_bounds-bug.rs:13:43 + | +13 | impl Unpin for A where PhantomPinned: Unpin {} //~ ERROR E0277 + | ^^^^^ the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned` + | + ::: /Users/taiki/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/src/rust/src/libcore/marker.rs:736:1 + | +736 | pub auto trait Unpin {} + | -------------------- required by this bound in `std::marker::Unpin` diff --git a/tests/ui/unstable-features/trivial_bounds-feature-gate.rs b/tests/ui/unstable-features/trivial_bounds-feature-gate.rs new file mode 100644 index 0000000..0453a3f --- /dev/null +++ b/tests/ui/unstable-features/trivial_bounds-feature-gate.rs @@ -0,0 +1,54 @@ +// NB: If you change this test, change 'trivial_bounds.rs' at the same time. + +mod phantom_pinned { + use std::marker::{PhantomData, PhantomPinned}; + + struct A(PhantomPinned); + + impl Unpin for A where PhantomPinned: Unpin {} //~ ERROR E0277 + + struct Wrapper(T); + + impl Unpin for Wrapper where T: Unpin {} + + struct B(PhantomPinned); + + impl Unpin for B where Wrapper: Unpin {} //~ ERROR E0277 + + struct WrapperWithLifetime<'a, T>(PhantomData<&'a ()>, T); + + impl Unpin for WrapperWithLifetime<'_, T> where T: Unpin {} + + struct C(PhantomPinned); + + impl<'a> Unpin for C where WrapperWithLifetime<'a, PhantomPinned>: Unpin {} + // Ok +} + +mod inner { + use std::marker::{PhantomData, PhantomPinned}; + + struct Inner(PhantomPinned); + + struct A(Inner); + + impl Unpin for A where Inner: Unpin {} //~ ERROR E0277 + + struct Wrapper(T); + + impl Unpin for Wrapper where T: Unpin {} + + struct B(Inner); + + impl Unpin for B where Wrapper: Unpin {} //~ ERROR E0277 + + struct WrapperWithLifetime<'a, T>(PhantomData<&'a ()>, T); + + impl Unpin for WrapperWithLifetime<'_, T> where T: Unpin {} + + struct C(Inner); + + impl<'a> Unpin for C where WrapperWithLifetime<'a, Inner>: Unpin {} // Ok +} + +fn main() {} diff --git a/tests/ui/unstable-features/trivial_bounds-feature-gate.stderr b/tests/ui/unstable-features/trivial_bounds-feature-gate.stderr new file mode 100644 index 0000000..31196a2 --- /dev/null +++ b/tests/ui/unstable-features/trivial_bounds-feature-gate.stderr @@ -0,0 +1,50 @@ +error[E0277]: `std::marker::PhantomPinned` cannot be unpinned + --> $DIR/trivial_bounds-feature-gate.rs:8:5 + | +8 | impl Unpin for A where PhantomPinned: Unpin {} //~ ERROR E0277 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned` + | + = help: see issue #48214 + = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable + +error[E0277]: `std::marker::PhantomPinned` cannot be unpinned + --> $DIR/trivial_bounds-feature-gate.rs:8:43 + | +8 | impl Unpin for A where PhantomPinned: Unpin {} //~ ERROR E0277 + | ^^^^^ the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned` + | + ::: /Users/taiki/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/src/rust/src/libcore/marker.rs:736:1 + | +736 | pub auto trait Unpin {} + | -------------------- required by this bound in `std::marker::Unpin` + +error[E0277]: `std::marker::PhantomPinned` cannot be unpinned + --> $DIR/trivial_bounds-feature-gate.rs:16:5 + | +16 | impl Unpin for B where Wrapper: Unpin {} //~ ERROR E0277 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned` + | + = note: required because of the requirements on the impl of `std::marker::Unpin` for `phantom_pinned::Wrapper` + = help: see issue #48214 + = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable + +error[E0277]: `std::marker::PhantomPinned` cannot be unpinned + --> $DIR/trivial_bounds-feature-gate.rs:35:5 + | +35 | impl Unpin for A where Inner: Unpin {} //~ ERROR E0277 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ within `inner::Inner`, the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned` + | + = note: required because it appears within the type `inner::Inner` + = help: see issue #48214 + = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable + +error[E0277]: `std::marker::PhantomPinned` cannot be unpinned + --> $DIR/trivial_bounds-feature-gate.rs:43:5 + | +43 | impl Unpin for B where Wrapper: Unpin {} //~ ERROR E0277 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ within `inner::Inner`, the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned` + | + = note: required because it appears within the type `inner::Inner` + = note: required because of the requirements on the impl of `std::marker::Unpin` for `inner::Wrapper` + = help: see issue #48214 + = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable diff --git a/tests/ui/unstable-features/trivial_bounds.rs b/tests/ui/unstable-features/trivial_bounds.rs new file mode 100644 index 0000000..680effe --- /dev/null +++ b/tests/ui/unstable-features/trivial_bounds.rs @@ -0,0 +1,34 @@ +// NB: If you change this test, change 'trivial_bounds-feature-gate.rs' at the same time. + +// trivial_bounds +// Tracking issue: https://github.com/rust-lang/rust/issues/48214 +#![feature(trivial_bounds)] +#![deny(trivial_bounds)] + +use std::marker::{PhantomData, PhantomPinned}; + +fn inner() { + struct Inner(PhantomPinned); + + struct A(Inner); + + impl Unpin for A where Inner: Unpin {} //~ ERROR std::marker::Unpin does not depend on any type or lifetime parameters + + struct Wrapper(T); + + impl Unpin for Wrapper where T: Unpin {} + + struct B(Inner); + + impl Unpin for B where Wrapper: Unpin {} //~ ERROR std::marker::Unpin does not depend on any type or lifetime parameters + + struct WrapperWithLifetime<'a, T>(PhantomData<&'a ()>, T); + + impl Unpin for WrapperWithLifetime<'_, T> where T: Unpin {} + + struct C(Inner); + + impl<'a> Unpin for C where WrapperWithLifetime<'a, Inner>: Unpin {} // Ok +} + +fn main() {} diff --git a/tests/ui/unstable-features/trivial_bounds.stderr b/tests/ui/unstable-features/trivial_bounds.stderr new file mode 100644 index 0000000..03d0161 --- /dev/null +++ b/tests/ui/unstable-features/trivial_bounds.stderr @@ -0,0 +1,17 @@ +error: Trait bound inner::Inner: std::marker::Unpin does not depend on any type or lifetime parameters + --> $DIR/trivial_bounds.rs:15:35 + | +15 | impl Unpin for A where Inner: Unpin {} //~ ERROR std::marker::Unpin does not depend on any type or lifetime parameters + | ^^^^^ + | +note: the lint level is defined here + --> $DIR/trivial_bounds.rs:6:9 + | +6 | #![deny(trivial_bounds)] + | ^^^^^^^^^^^^^^ + +error: Trait bound inner::Wrapper: std::marker::Unpin does not depend on any type or lifetime parameters + --> $DIR/trivial_bounds.rs:23:44 + | +23 | impl Unpin for B where Wrapper: Unpin {} //~ ERROR std::marker::Unpin does not depend on any type or lifetime parameters + | ^^^^^ diff --git a/tests/unsafe_unpin.rs b/tests/unsafe_unpin.rs new file mode 100644 index 0000000..5e0e7cf --- /dev/null +++ b/tests/unsafe_unpin.rs @@ -0,0 +1,53 @@ +#![warn(rust_2018_idioms, single_use_lifetimes)] +#![allow(dead_code)] + +use pin_project::{pin_project, UnsafeUnpin}; +use std::{marker::PhantomPinned, pin::Pin}; + +fn is_unpin() {} + +#[pin_project(UnsafeUnpin)] +pub struct Blah { + field1: U, + #[pin] + field2: T, +} + +unsafe impl UnsafeUnpin for Blah {} + +#[pin_project(UnsafeUnpin)] +pub struct OverlappingLifetimeNames<'pin, T, U> { + #[pin] + field1: T, + field2: U, + field3: &'pin (), +} + +unsafe impl UnsafeUnpin for OverlappingLifetimeNames<'_, T, U> {} + +#[test] +fn unsafe_unpin() { + is_unpin::>(); + is_unpin::>(); +} + +#[test] +fn trivial_bounds() { + #[pin_project(UnsafeUnpin)] + pub struct NotImplementUnsafUnpin { + #[pin] + field: PhantomPinned, + } +} + +#[test] +fn test() { + let mut x = OverlappingLifetimeNames { field1: 0, field2: 1, field3: &() }; + let x = Pin::new(&mut x); + let y = x.as_ref().project_ref(); + let _: Pin<&u8> = y.field1; + let _: &u8 = y.field2; + let y = x.project(); + let _: Pin<&mut u8> = y.field1; + let _: &mut u8 = y.field2; +} -- cgit v1.2.3