Squashed 'third_party/autocxx/' changes from c35090b75..fdfb26e26
fdfb26e26 Merge pull request #1315 from google/update-for-proc-macro-prob
ed6ab7560 cargo update
bfca1e046 Merge pull request #1310 from google/remove-errant-line
b06647b89 Remove commented Cargo line
dfb63e2fa Merge pull request #1309 from google/syn-newtype-wrapper-3
c3cdbcbe3 Newtype wrapper for more concise syn debug output.
2743df8f7 Merge pull request #1306 from google/more-cpp-version-notes-tests
1b68eb0b9 Further documentation on C++ versions.
907b2eff3 Merge pull request #1305 from google/cpp-version-notes
90a17d9c2 Documentation about C++ versions.
60230d257 Merge pull request #1299 from google/rev-0.26
10346e838 Revise to 0.26.
5492cb25f Merge pull request #1297 from google/dyn-trait-test
ad8fd562f Merge pull request #1298 from google/fix-moveit-unsoundness
3ff66d1c2 Use new trait in tests.
2f8fb49f9 Fix subclass Emplace trait name.
e420eb7b4 Merge commit '93ed457' into fix-moveit-unsoundness
5e863df88 Roll to moveit 0.6.
0386ad9c8 Testing and documenting dynamic dispatch for CppRef.
ad9377beb Merge pull request #1274 from google/test-for-1265
84281f824 Merge pull request #1296 from google/fix-mutable-refs-part-two
21d72ad05 Fix mutable ref returns, part two.
cfbe62b08 Merge pull request #1295 from google/fix-1294
a67fbe6a6 Fix reference wrappers + mutable refs problem.
366d1a32b Merge pull request #1293 from google/roll-bindgen-2
8cacf99c2 Ignore new test so it can be merged in.
b9a56c697 Absorb published autocxx-bindgen 0.65.1.
1bfbf9dcc Merge pull request #1292 from google/reference-wrapper-changes
7000c91e6 Upgrade to bindgen 0.65.1.
d35bdbd59 Fix doc problem.
43d94cf1f Remove unused features.
ebf858266 Improve reference wrapper example.
e0dfbf958 Allow unsized types in C++ references.
004ef5ab9 Add comments to C++ reference
7c8b8c0f6 Add comment on potential lurking bug.
902077adc Switch reference wrappers' Deref
6ee12660a Merge pull request #1286 from maurer/syn-2
2633a9aaf Update to syn-2
6cb6d5879 Merge pull request #1291 from badicsalex/main
718c50d30 Crude workaround for nested anonymous enums bug
b1ee9dae5 Add test for nested anonymous enums
cf123189e Merge pull request #1290 from google/adetaylor-patch-2
0c27dcb18 Update bug_report.md
73cf4798b Update issue templates
cce6f6c5d Merge pull request #1288 from google/clippy-1.70
b43a16e59 Clippy fix.
93ed45714 Simplify generics
7e6fc4982 Remove `moveit::EmplaceUnpinned`
2113daf76 Make `as_mov` take `ptr: impl AsMove<P>` and relax bound to `P: Deref`
2e442110d Patch moveit dependency
ad69a8417 Merge pull request #1284 from google/cargo-update-2
776bc8162 Merge pull request #1283 from google/subclass-diagnostics
70f9f0f7e cargo update to remove vulnerable version of time.
25c0ded71 Further book fixes.
f8749c71c Book fixes.
fee4442cf Fix diagnostics for in-line subclass macro.
ae4a4eeeb Merge pull request #1277 from gen-xu/main
02ba1a54e fix: feature flag for runtime
e397e2e71 Merge branch 'main' into test-for-1265
bec2301b5 Merge pull request #1264 from google/reject-unions
b0275ff0f Merge pull request #1275 from google/typo3
370e81d87 Fix typo.
f050f5ae7 Add test for unsoundness.
a44f39806 Format
deba33de8 Further tweaks
994a0b164 Merge pull request #1273 from google/rvalueparamhandler-comment
7b8b14507 More using nuance.
e9a62b429 Merge pull request #1272 from google/1270
98fcc88a8 Add comment explaining RValueParamHandler.
d93a1fd1c Attempts at fixing 1270.
6594410d4 Use a more sophisticated 'using' statement.
abb59541f Merge pull request #1263 from google/va-list
1f9a1d258 Attempt to fix namespaced anon struct case.
d4ad6fecc Add test for funsigned-char
f27bf7856 Add va_list test.
7f38ae7c9 Shorten excessively long name.
f4e57eaed Fix namespaced destructors.
ada12160e Merge pull request #1259 from google/fix-remove-dir-all
edabf36f1 Merge pull request #1258 from google/rev-0.25
8c7452b68 Roll tempfile.
22515641f Revise to 0.25.0.
9f5c550c5 Merge pull request #1257 from htynkn/patch-1
7c724ba32 Update examples.md
97c103aaa Update examples.md
a9edb13c5 fix 404 in examples document
c3771d02d Merge pull request #1253 from nak3/add-cxx.h
b429077f8 Merge pull request #1249 from google/reduction-tweaks
143b2944f Merge pull request #1254 from google/issue-1238
94f0cd51b Reject fns returning mut refs but no mut ref param
e7640d2dd Add failing test case for issue 1238.
0f1a28a28 Tweaks to reduction
252608fb4 Add an option to autocxx_gen to create cxx.h
79c72939e Merge pull request #1250 from google/tests-forcing-wrappers
2121c1a30 Disable the two tests which fail when wrappers are forced.
52028c9a2 Add to book about this.
4359edcf3 Clippy fix.
c34ae5e6d Add github workflow.
dca93fa74 Add to test suite
e20192af4 Add option to force wrapper generation.
5540b8618 Merge pull request #1206 from russelltg/construct_concrete_class
1783da369 Merge pull request #1248 from nak3/llvm-example
a1468090a Merge pull request #1247 from nak3/fix-log
9154d3187 Merge pull request #1245 from nak3/use-subclass
d24515a90 Enable llvm example for CI
36f7a3570 Print the number of files generated in the error of `--generate-exact`
faf3d8202 Do not use deprecated annotation is_subclass in examples
a303a9a72 fmt
e47ff2eff Merge pull request #1241 from google/reproduce-1237
8db3a4241 Fix problem with nested extern types.
20345ebbb Merge pull request #1242 from google/remove-no-mod-found-error
83cec3c50 Clippy fix.
d0e1b23e3 Merge pull request #1240 from google/fix-1229
88e702523 Support extern_rust_type without include_cpp!
6a429bd62 Add test for autodiscover without include_cpp
3bd7dba80 Fix use of extern_rust_type in Box<T> in wrappers.
68c236afb Rename a test to avoid ambiguity.
93e4c9629 Remove warning from test case.
68d2323c5 Fix for Box being passed without std::move.
ba81127ca Add reproduction test for #1237
ca436170e Experimental fix for #1229
59eaac153 Merge pull request #1224 from kitlith/no_std
05c0071a0 Move duplicate Pin definition into "non_cannonical_name"
14eeb91db Merge pull request #1232 from nak3/fix-comment
047943e22 Merge pull request #1234 from nak3/fix-help
31ccc891e Merge branch 'google:main' into test
cb16073c3 Add test for issue #1229
c7b6af021 Make help message of gen-rs-include accurate
9c359b4cc Fix comment about AUTOCXX_RS_JSON_ARCHIVE
8a5e11574 Merge remote-tracking branch 'origin/main' into construct_concrete_class
0b46f92ce go through APIs one final time to go through methods before classes
dcb4de8ba Merge pull request #1225 from EmbarkStudios/main
74a78bd16 FIXUP! cargo fmt
b579e7cde Merge pull request #1227 from google/linking-docs
29c267f4f Add some generic advice about linking.
6e1200d3a Merge pull request #1226 from google/remove-publish-delay
70b2d2b43 Cargo publish now waits automatically.
21a007020 Merge pull request #1223 from google/test-for-1214
a6ffcc103 Merge branch 'main' of github.com:google/autocxx into test-for-1214
788816f30 Format.
8935e64bd Revise to fixed autocxx-bindgen version.
e0da33972 Furthger enhance test.
88641ff57 Slightly bolstering an existing test.
2e4318803 Automatically create outdir if it doesn't exist
ad89df311 when adding a lifetime to pinned reference, allow from core as well.
6a09a1d1c Replace std with core (where possible) in integration tests.
abed8eabe s/::std/::core/, where possible
a3ab76cd7 Merge pull request #1222 from google/rev-0.24
21995a555 Add test for #1214.
1100f09f4 Format.
6c252fad2 More clippy changes.
6c3d25c09 Revise to 0.24.0
9e69fae2d Merge pull request #1193 from google/test-1192
4400acf2a Merge pull request #1221 from google/document-expectations
4d6f80303 Add comments towards #1192.
bde880b67 Merge pull request #1220 from google/repro-1197
aef43a1b8 Merge pull request #1217 from google/add_doc_cfg
c1102a73b Merge pull request #1218 from google/improve-diagnostic
0e8dd146b Merge pull request #1219 from google/no-rewrite
8cd277b4b Make cxxgen.h names predictable in autocxx-build.
e5898442c Fix doc build issue.
7433ac6a5 Improve diagnostics for wrong filename. Fixes #1200.
ae9181f43 Avoid unnecessary rebuilds.
b3748f736 Merge pull request #1216 from google/clippy-new
6a9d9227b Format.
c90cd896e address some comments
ef6dfd9ac Latest clippy fixes.
e28826e00 Clippy
103ce3f9e Merge pull request #1203 from aaronrsiphone/patch-1
2db6e7341 Merge pull request #1210 from nak3/remove-memory
eb486273a Merge pull request #1208 from nak3/fix-cpp_calling_rust
4d831b6a5 Merge pull request #1209 from nak3/fix-typo
316e63643 Merge pull request #1213 from nak3/bump-llvm
495e90916 Bump autocxx version in examples/llvm
49ba02805 Remove unnecessary memory include
fd728b20f Fix typo
acc14b9b6 Add test in github action
5def9b050 Bump miette to 5.x
8d4d785f4 clippy
e1d172f4a fmt
38f26efcc add missing trait
12bf667d1 listen to clippy
f6829c226 fix up overload resolution
d2aa27715 some more work
910e7b5f6 get started
b7a23c5d0 Merge pull request #1202 from google/reference-tweak
eb4b28d5b Update building.md
a476df3e5 Add a note about unsafety of reference wrappers.
34a143f12 Merge pull request #1199 from google/clippy-fixes
4b6020f7c Fix new cargo doc error.
c132a0180 Clippy fixes.
bd0efed70 Fix issue 1192.
81a3d9686 Log some more details of some APIs.
a7c4d35dd Fix the test
16c8a7059 Add test for issue 1192.
f6427a110 Merge pull request #1188 from google/rev-0.23.1
f5ea8a674 Revise to 0.23.1
1c11f4b78 Merge pull request #1187 from google/recursive-struct-fields
f68e08d21 Denote only dependencies of real field types.
a009d0f86 Add tests for circular dependencies.
034e10432 Merge pull request #1185 from google/rev-0.23
05a5c4243 Revise to 0.23.0.
0885061f4 Merge pull request #1180 from google/fix-815
1ee6da4c2 Merge branch 'main' of github.com:google/autocxx into fix-815
b00c5cd17 Merge pull request #1184 from google/nightly-clippies
74dadfb26 Further clippy fixes.
f81c4db3c Further clippy fixes.
d0c5616a2 Shrink error types to keep clippy happy.
5cae78b3d Merge branch 'main' of github.com:google/autocxx into fix-815
68d280b3c Merge pull request #1183 from google/merge-bindgen-upstream-0.62
8aed5e309 Update to released autocxx-bindgen.
59da591a0 Merge pull request #1179 from google/failing-1170-test
65dd0e218 Format.
775317a85 Cargo lock
f052bbdd7 Test against upstream bindgen 0.62.
a19307aa8 Depend on custom bindgen branch
162ec7df8 Remove bogus bits of test
29296ee9c Consider that enums are always movable/destroyable.
4eab29a29 Properly propagate deps for non-POD structs
04eab3251 Simpler test for #1170.
66ca18c78 Record defaulted attributes - no intentional changes yet
3ad39ce5c Failing test for derived class.
f3a803040 Test for explicitly defaulted constructors.
782d1365f Adding failing test for #1170.
6cca9b29c Merge pull request #1172 from google/reduce-with-rustc
9d021f66f Typo.
ee2bdf87f Merge pull request #1176 from google/rustversion-swap
3b28ff920 Fix nightly check error.
1cdbcc964 Clippy complaint.
bc5f4ff05 Compile generated rust code during reduction.
5ef057dce Merge pull request #1173 from google/up-of-vecs
9577311aa Update integration-tests/tests/cpprefs_test.rs
1cb8338a1 Eliminate further use of rustc_version.
e78b73b1c Remove dependency on rustc_version.
1b949ad9c Fix test.
20c8d6ba7 Merge pull request #1175 from google/1152-upmerge
8f2dbaef0 Fix compatibility with clap 3.2.
dda51d028 Merge branch 'main' of github.com:google/autocxx into 1152-upmerge
3086a9023 Merge pull request #1168 from google/extern_rust_fun_type_checking
14f224fde Merge pull request #1171 from google/reduce-quicker
fbeb0f3d6 Adding a test.
ea57ea7d7 Slight reduction speedup.
26f9cf4a2 extern_rust_function types: diagnostics
1196c39b1 Rename: ConvertError -> ConvertErrorFromCpp
759970ae6 Merge pull request #1164 from google/cppref-changes
cb9c680b2 Adding diagnostics for use on non-nightly.
93657b848 Add failing test for extern_rust_function
075bd8e79 Switch from receiver feature to arbitrary_self_types
8c576081d Fix tests for nightly.
51e4c271c Doc fixes.
1204dc7fa Rewrite reference_wrapper.rs to be structs.
c2846add0 Merge pull request #1163 from google/rev-cxx-1.0.72
4d939f0d5 Merge pull request #1162 from google/fix-1161
3d2767958 Revise cxx to 1.0.78.
cf96537ca Clippy fix.
458384470 Add Box test.
23cff78ca Fix GC problem with extern_rust_type.
b24563627 Merge pull request #1158 from google/issue-1097-partial-fix
1ce2ee335 Merge pull request #1160 from google/clippy-1.64-fix
3d376da55 Rust 1.64 clippy lint.
d01bcece0 Merge pull request #1157 from google/adetaylor-patch-1
bce713bfa Partial fix for issue 1097.
7460bfacb Remove extra logging
2d86cdb4a Merge pull request #1156 from google/fix-1143
e7ca09610 Clippy fix.
4fdb3ca22 Enable test.
40d70634e Fix #1143.
f785a168d Merge pull request #1150 from bsilver8192/extern_cpp_type-different-name
79b6b91df Update deps, format TOML
8d2e1dffb Merge pull request #1154 from google/rev-0.22.4
6555d8212 Revise to 0.22.4.
4b44f7f20 Merge pull request #1153 from google/autocxx-bindgen-0.60.3
05dcbf555 Update autocxx-bindgen and bump version to 0.22.4
2388f2d50 Support `extern_cpp_type!` with a different name
7cfa5ea58 Merge pull request #1149 from google/rev-autocxx-bindgen-0.60.1
dadb77ac5 Use official release version.
25cc290a9 Use specific branch and merge fixes
9d2487fab Revise to autocxx-bindgen 0.60.1.
8a9f5fd6d Merge pull request #1145 from boydjohnson/test-case
9d7d2cbdd Merge pull request #1147 from bsilver8192/multiple-json-archive-paths
948e49c4f Merge pull request #1148 from bsilver8192/1.63.0-warning-fixes
c3c020459 Support multiple AUTOCXX_RS_JSON_ARCHIVE entries
37353fabd Fix new clippy and doc warnings in 1.63.0
1e1d16d73 Add failing test case
d0bc76eb5 Merge pull request #1137 from bsilver8192/impl-abstract-type
ec7a4541a Generate `impl UniquePtr` for abstract types too
b830b9da6 Merge pull request #1139 from bsilver8192/bump-mdbook
da8dbd829 Bump mdbook to fix build on nightly
cdebaee64 Merge pull request #1136 from google/disable-windows-gnu-2
5fcc700c7 Disable windows-gnu tests.
0f8cf6309 Fix a comment.
b60c4d032 Merge pull request #1127 from dtolnay-contrib/prettyplease
8b766bd46 Pretty print using prettyplease instead of rustfmt
Change-Id: I0529fc0c35c16160944f5b3a5326c2d53fbe904b
git-subtree-dir: third_party/autocxx
git-subtree-split: fdfb26e26602e24466dfc506782567deb72c9a29
Signed-off-by: Austin Schuh <austin.linux@gmail.com>
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 0000000..59bdb4d
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,22 @@
+---
+name: Bug report
+about: Create a report to help us improve
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+**Describe the bug**
+A clear and concise description of what the bug is.
+
+**To Reproduce**
+Steps to reproduce the behavior with appropriate Rust and C++ code snippets. Ideally, please raise a new pull request with a failing test - instructions are [here](https://google.github.io/autocxx/contributing.html#reporting-bugs). If that isn't possible, a few other options are listed too.
+
+About half of the time people report bugs, they're not readily reproducible. That wastes time for everyone. That's why it's incredibly useful to have a test case which definitely fails. Thanks!
+
+**Expected behavior**
+A clear and concise description of what you expected to happen.
+
+**Additional context**
+Add any other context about the problem here.
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 367b729..563b8ff 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -32,9 +32,10 @@
- name: macOS
rust: nightly
os: macos
- - name: Windows (gnu)
- rust: nightly-x86_64-pc-windows-gnu
- os: windows
+ # Windows GNU disabled due to https://github.com/google/autocxx/issues/1134
+ # - name: Windows (gnu)
+ # rust: nightly-x86_64-pc-windows-gnu
+ # os: windows
- name: Windows (msvc)
rust: nightly-x86_64-pc-windows-msvc
os: windows
@@ -141,9 +142,16 @@
run: cargo build
- name: Build reference-wrappers example
working-directory: ./examples/reference-wrappers
+ if: matrix.rust == 'nightly'
run: cargo build
- # We do not build the LLVM example because even 'apt-get install llvm-13-dev'
- # does not work to install the LLVM 13 headers.
+ - name: Build cpp_calling_rust example
+ working-directory: ./examples/cpp_calling_rust
+ run: cargo build
+ - name: Build llvm example
+ working-directory: ./examples/llvm
+ # llvm example needs to install LLVM 13 headers via apt-get.
+ if: matrix.os == ''
+ run: sudo apt-get install llvm-13-dev && cargo build
sanitizer:
name: Address Sanitizer
@@ -169,6 +177,17 @@
RUST_BACKTRACE: "0"
run: cargo -Z build-std test --workspace --target x86_64-unknown-linux-gnu
+ force-wrapper-generation:
+ name: Test forcing wrapper generation
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ - uses: Swatinem/rust-cache@v1
+ - name: Tests generating all possible shims
+ env:
+ AUTOCXX_FORCE_WRAPPER_GENERATION: 1
+ run: cargo test --workspace
+
# Clippy check
clippy:
name: Clippy
diff --git a/Cargo.lock b/Cargo.lock
index 4f58182..a7872e3 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4,9 +4,9 @@
[[package]]
name = "addr2line"
-version = "0.17.0"
+version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b"
+checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3"
dependencies = [
"gimli",
]
@@ -18,48 +18,115 @@
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
-name = "aho-corasick"
-version = "0.7.18"
+name = "ahash"
+version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
+checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
+dependencies = [
+ "getrandom",
+ "once_cell",
+ "version_check",
+]
+
+[[package]]
+name = "aho-corasick"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41"
dependencies = [
"memchr",
]
[[package]]
-name = "ansi_term"
-version = "0.12.1"
+name = "android-tzdata"
+version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
+checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
+
+[[package]]
+name = "android_system_properties"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
dependencies = [
- "winapi",
+ "libc",
+]
+
+[[package]]
+name = "anstream"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163"
+dependencies = [
+ "anstyle",
+ "anstyle-parse",
+ "anstyle-query",
+ "anstyle-wincon",
+ "colorchoice",
+ "is-terminal",
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd"
+
+[[package]]
+name = "anstyle-parse"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333"
+dependencies = [
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle-query"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
+dependencies = [
+ "windows-sys",
+]
+
+[[package]]
+name = "anstyle-wincon"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188"
+dependencies = [
+ "anstyle",
+ "windows-sys",
]
[[package]]
name = "anyhow"
-version = "1.0.56"
+version = "1.0.71"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4361135be9122e0870de935d7c439aef945b9f9ddd4199a553b5270b49c82a27"
+checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8"
[[package]]
name = "aquamarine"
-version = "0.1.11"
+version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "96e14cb2a51c8b45d26a4219981985c7350fc05eacb7b5b2939bceb2ffefdf3e"
+checksum = "a941c39708478e8eea39243b5983f1c42d2717b3620ee91f4a52115fd02ac43f"
dependencies = [
"itertools 0.9.0",
"proc-macro-error",
"proc-macro2",
"quote",
- "syn",
+ "syn 1.0.109",
]
[[package]]
name = "assert_cmd"
-version = "1.0.8"
+version = "2.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c98233c6673d8601ab23e77eb38f999c51100d46c5703b17288c57fddf3a1ffe"
+checksum = "86d6b683edf8d1119fe420a94f8a7e389239666aa72e65495d91c00462510151"
dependencies = [
+ "anstyle",
"bstr",
"doc-comment",
"predicates",
@@ -74,7 +141,7 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
- "hermit-abi",
+ "hermit-abi 0.1.19",
"libc",
"winapi",
]
@@ -87,7 +154,7 @@
[[package]]
name = "autocxx"
-version = "0.22.3"
+version = "0.26.0"
dependencies = [
"aquamarine",
"autocxx-macro",
@@ -97,41 +164,41 @@
[[package]]
name = "autocxx-bindgen"
-version = "0.59.17"
+version = "0.65.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f9a9a26dd38d385d23b1bf61bd231b77f690c4368aef4c77cee1b7a6da2e2042"
+checksum = "6c9fb7b8dd83a582e12157367773d8d1195f2dea54d4250aaf3426abae3237aa"
dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
"cexpr",
"clang-sys",
- "clap 2.34.0",
- "env_logger 0.9.0",
- "itertools 0.10.3",
+ "itertools 0.10.5",
"lazy_static",
"lazycell",
"log",
"peeking_take_while",
+ "prettyplease",
"proc-macro2",
"quote",
"regex",
"rustc-hash",
"shlex",
+ "syn 2.0.23",
"which",
]
[[package]]
name = "autocxx-build"
-version = "0.22.3"
+version = "0.26.0"
dependencies = [
"autocxx-engine",
- "env_logger 0.9.0",
+ "env_logger 0.9.3",
"indexmap",
- "syn",
+ "syn 2.0.23",
]
[[package]]
name = "autocxx-demo"
-version = "0.22.3"
+version = "0.26.0"
dependencies = [
"autocxx",
"autocxx-build",
@@ -141,7 +208,7 @@
[[package]]
name = "autocxx-engine"
-version = "0.22.3"
+version = "0.26.0"
dependencies = [
"aquamarine",
"autocxx-bindgen",
@@ -150,16 +217,17 @@
"cxx-gen",
"indexmap",
"indoc",
- "itertools 0.10.3",
+ "itertools 0.10.5",
"log",
"miette",
"once_cell",
+ "prettyplease",
"proc-macro2",
"quote",
"regex",
+ "rustversion",
"serde_json",
- "strum_macros",
- "syn",
+ "syn 2.0.23",
"tempfile",
"thiserror",
"version_check",
@@ -167,17 +235,17 @@
[[package]]
name = "autocxx-gen"
-version = "0.22.3"
+version = "0.26.0"
dependencies = [
"assert_cmd",
"autocxx",
"autocxx-engine",
"autocxx-integration-tests",
- "clap 3.1.9",
+ "clap 3.2.25",
"cxx",
- "env_logger 0.9.0",
+ "env_logger 0.9.3",
"indexmap",
- "itertools 0.10.3",
+ "itertools 0.10.5",
"miette",
"pathdiff",
"proc-macro2",
@@ -186,22 +254,25 @@
[[package]]
name = "autocxx-integration-tests"
-version = "0.22.3"
+version = "0.26.0"
dependencies = [
"autocxx",
"autocxx-engine",
"cc",
"cxx",
- "env_logger 0.9.0",
+ "env_logger 0.9.3",
"indoc",
- "itertools 0.10.3",
+ "itertools 0.10.5",
"link-cplusplus",
"log",
+ "moveit",
"once_cell",
"proc-macro2",
"quote",
"rust_info",
- "syn",
+ "rustversion",
+ "static_assertions",
+ "syn 1.0.109",
"tempfile",
"test-log",
"trybuild",
@@ -209,76 +280,76 @@
[[package]]
name = "autocxx-macro"
-version = "0.22.3"
+version = "0.26.0"
dependencies = [
"autocxx-parser",
"proc-macro-error",
"proc-macro2",
"quote",
- "syn",
+ "syn 2.0.23",
]
[[package]]
name = "autocxx-mdbook-preprocessor"
-version = "0.22.3"
+version = "0.26.0"
dependencies = [
"anyhow",
"autocxx-integration-tests",
- "clap 3.1.9",
- "env_logger 0.9.0",
+ "clap 3.2.25",
+ "env_logger 0.9.3",
"gag",
- "itertools 0.10.3",
+ "itertools 0.10.5",
"mdbook",
"proc-macro2",
"rayon",
"regex",
"serde_json",
- "syn",
+ "syn 1.0.109",
]
[[package]]
name = "autocxx-parser"
-version = "0.22.3"
+version = "0.26.0"
dependencies = [
"indexmap",
- "itertools 0.10.3",
+ "itertools 0.10.5",
"log",
"once_cell",
"proc-macro2",
"quote",
"serde",
"serde_json",
- "syn",
+ "syn 2.0.23",
"thiserror",
]
[[package]]
name = "autocxx-reduce"
-version = "0.22.3"
+version = "0.26.0"
dependencies = [
"assert_cmd",
"autocxx-engine",
"autocxx-parser",
- "clap 3.1.9",
+ "clap 3.2.25",
"cxx-gen",
"indexmap",
"indoc",
- "itertools 0.10.3",
+ "itertools 0.10.5",
"proc-macro2",
"quote",
"regex",
"serde",
"serde_derive",
"serde_json",
- "syn",
+ "syn 2.0.23",
"tempfile",
]
[[package]]
name = "backtrace"
-version = "0.3.64"
+version = "0.3.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5e121dee8023ce33ab248d9ce1493df03c3b38a659b240096fcbd7048ff9c31f"
+checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12"
dependencies = [
"addr2line",
"cc",
@@ -290,60 +361,67 @@
]
[[package]]
+name = "backtrace-ext"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "537beee3be4a18fb023b570f80e3ae28003db9167a751266b259926e25539d50"
+dependencies = [
+ "backtrace",
+]
+
+[[package]]
+name = "basic-toml"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f838d03a705d72b12389b8930bd14cacf493be1380bfb15720d4d12db5ab03ac"
+dependencies = [
+ "serde",
+]
+
+[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
-name = "block-buffer"
-version = "0.7.3"
+name = "bitflags"
+version = "2.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b"
+checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42"
+
+[[package]]
+name = "block-buffer"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
dependencies = [
- "block-padding",
- "byte-tools",
- "byteorder",
"generic-array",
]
[[package]]
-name = "block-padding"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5"
-dependencies = [
- "byte-tools",
-]
-
-[[package]]
name = "bstr"
-version = "0.2.17"
+version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223"
+checksum = "a246e68bb43f6cd9db24bea052a53e40405417c5fb372e3d1a8a7f770a564ef5"
dependencies = [
- "lazy_static",
"memchr",
- "regex-automata",
+ "once_cell",
+ "regex-automata 0.1.10",
+ "serde",
]
[[package]]
-name = "byte-tools"
-version = "0.3.1"
+name = "bumpalo"
+version = "3.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
-
-[[package]]
-name = "byteorder"
-version = "1.4.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
+checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1"
[[package]]
name = "cc"
-version = "1.0.73"
+version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
+checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
[[package]]
name = "cexpr"
@@ -362,22 +440,21 @@
[[package]]
name = "chrono"
-version = "0.4.19"
+version = "0.4.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
+checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5"
dependencies = [
- "libc",
- "num-integer",
+ "android-tzdata",
+ "iana-time-zone",
"num-traits",
- "time",
"winapi",
]
[[package]]
name = "clang-sys"
-version = "1.3.1"
+version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4cc00842eed744b858222c4c9faf7243aafc6d33f92f96935263ef4d8a41ce21"
+checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f"
dependencies = [
"glob",
"libc",
@@ -386,54 +463,68 @@
[[package]]
name = "clap"
-version = "2.34.0"
+version = "3.2.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
+checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123"
dependencies = [
- "ansi_term",
"atty",
- "bitflags",
- "strsim 0.8.0",
- "textwrap 0.11.0",
- "unicode-width",
- "vec_map",
+ "bitflags 1.3.2",
+ "clap_lex 0.2.4",
+ "indexmap",
+ "once_cell",
+ "strsim",
+ "termcolor",
+ "textwrap 0.16.0",
]
[[package]]
name = "clap"
-version = "3.1.9"
+version = "4.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6aad2534fad53df1cc12519c5cda696dd3e20e6118a027e24054aea14a0bdcbe"
+checksum = "384e169cc618c613d5e3ca6404dda77a8685a63e08660dcc64abaf7da7cb0c7a"
dependencies = [
- "atty",
- "bitflags",
- "clap_lex",
- "indexmap",
- "lazy_static",
- "strsim 0.10.0",
- "termcolor",
- "textwrap 0.15.0",
+ "clap_builder",
+]
+
+[[package]]
+name = "clap_builder"
+version = "4.3.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef137bbe35aab78bdb468ccfba75a5f4d8321ae011d34063770780545176af2d"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "clap_lex 0.5.0",
+ "once_cell",
+ "strsim",
+ "terminal_size 0.2.6",
]
[[package]]
name = "clap_complete"
-version = "3.1.1"
+version = "4.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "df6f3613c0a3cddfd78b41b10203eb322cb29b600cbdf808a7d3db95691b8e25"
+checksum = "7f6b5c519bab3ea61843a7923d074b04245624bb84a64a8c150f5deb014e388b"
dependencies = [
- "clap 3.1.9",
+ "clap 4.3.10",
]
[[package]]
name = "clap_lex"
-version = "0.1.1"
+version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "189ddd3b5d32a70b35e7686054371742a937b0d99128e76dde6340210e966669"
+checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
dependencies = [
"os_str_bytes",
]
[[package]]
+name = "clap_lex"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b"
+
+[[package]]
name = "codespan-reporting"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -444,10 +535,31 @@
]
[[package]]
-name = "crossbeam-channel"
-version = "0.5.4"
+name = "colorchoice"
+version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5aaa7bd5fb665c6864b5f963dd9097905c54125909c7aa94c9e18507cdbe6c53"
+checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
+
+[[package]]
+name = "cpufeatures"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "crossbeam-channel"
+version = "0.5.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200"
dependencies = [
"cfg-if",
"crossbeam-utils",
@@ -455,9 +567,9 @@
[[package]]
name = "crossbeam-deque"
-version = "0.8.1"
+version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e"
+checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef"
dependencies = [
"cfg-if",
"crossbeam-epoch",
@@ -466,33 +578,41 @@
[[package]]
name = "crossbeam-epoch"
-version = "0.9.8"
+version = "0.9.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1145cf131a2c6ba0615079ab6a638f7e1973ac9c2634fcbeaaad6114246efe8c"
+checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7"
dependencies = [
"autocfg",
"cfg-if",
"crossbeam-utils",
- "lazy_static",
"memoffset",
"scopeguard",
]
[[package]]
name = "crossbeam-utils"
-version = "0.8.8"
+version = "0.8.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38"
+checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294"
dependencies = [
"cfg-if",
- "lazy_static",
+]
+
+[[package]]
+name = "crypto-common"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
+dependencies = [
+ "generic-array",
+ "typenum",
]
[[package]]
name = "cxx"
-version = "1.0.68"
+version = "1.0.97"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7e599641dff337570f6aa9c304ecca92341d30bf72e1c50287869ed6a36615a6"
+checksum = "e88abab2f5abbe4c56e8f1fb431b784d710b709888f35755a160e62e33fe38e8"
dependencies = [
"cc",
"cxxbridge-flags",
@@ -502,31 +622,31 @@
[[package]]
name = "cxx-gen"
-version = "0.7.68"
+version = "0.7.97"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e2c726d93799c3129c65224ab09eae1a31276bc593d4f7344be1c592c16a1ec"
+checksum = "83f6f8cddb97c1510ef1e7e849a40d60cd97377766187633ac6b9162dd862fd8"
dependencies = [
"codespan-reporting",
"proc-macro2",
"quote",
- "syn",
+ "syn 2.0.23",
]
[[package]]
name = "cxxbridge-flags"
-version = "1.0.68"
+version = "1.0.97"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3894ad0c6d517cb5a4ce8ec20b37cd0ea31b480fe582a104c5db67ae21270853"
+checksum = "8d3816ed957c008ccd4728485511e3d9aaf7db419aa321e3d2c5a2f3411e36c8"
[[package]]
name = "cxxbridge-macro"
-version = "1.0.68"
+version = "1.0.97"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "34fa7e395dc1c001083c7eed28c8f0f0b5a225610f3b6284675f444af6fab86b"
+checksum = "a26acccf6f445af85ea056362561a24ef56cdc15fcc685f03aec50b9c702cb6d"
dependencies = [
"proc-macro2",
"quote",
- "syn",
+ "syn 2.0.23",
]
[[package]]
@@ -537,11 +657,12 @@
[[package]]
name = "digest"
-version = "0.8.1"
+version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
+checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
- "generic-array",
+ "block-buffer",
+ "crypto-common",
]
[[package]]
@@ -552,18 +673,18 @@
[[package]]
name = "either"
-version = "1.6.1"
+version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
+checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
[[package]]
name = "env_logger"
-version = "0.7.1"
+version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36"
+checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7"
dependencies = [
"atty",
- "humantime 1.3.0",
+ "humantime",
"log",
"regex",
"termcolor",
@@ -571,28 +692,43 @@
[[package]]
name = "env_logger"
-version = "0.9.0"
+version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3"
+checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0"
dependencies = [
- "atty",
- "humantime 2.1.0",
+ "humantime",
+ "is-terminal",
"log",
"regex",
"termcolor",
]
[[package]]
-name = "fake-simd"
+name = "errno"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a"
+dependencies = [
+ "errno-dragonfly",
+ "libc",
+ "windows-sys",
+]
+
+[[package]]
+name = "errno-dragonfly"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
+checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
+dependencies = [
+ "cc",
+ "libc",
+]
[[package]]
name = "fastrand"
-version = "1.7.0"
+version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf"
+checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be"
dependencies = [
"instant",
]
@@ -620,50 +756,59 @@
[[package]]
name = "generic-array"
-version = "0.12.4"
+version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd"
+checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
+ "version_check",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi",
]
[[package]]
name = "gimli"
-version = "0.26.1"
+version = "0.27.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4"
+checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e"
[[package]]
name = "glob"
-version = "0.3.0"
+version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
+checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
[[package]]
name = "handlebars"
-version = "4.2.2"
+version = "4.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "99d6a30320f094710245150395bc763ad23128d6a1ebbad7594dc4164b62c56b"
+checksum = "83c3372087601b532857d332f5957cbae686da52bb7810bf038c3e3c3cc2fa0d"
dependencies = [
"log",
"pest",
"pest_derive",
- "quick-error 2.0.1",
"serde",
"serde_json",
+ "thiserror",
]
[[package]]
name = "hashbrown"
-version = "0.11.2"
+version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
-
-[[package]]
-name = "heck"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
+checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
+dependencies = [
+ "ahash",
+]
[[package]]
name = "hermit-abi"
@@ -675,13 +820,10 @@
]
[[package]]
-name = "humantime"
-version = "1.3.0"
+name = "hermit-abi"
+version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f"
-dependencies = [
- "quick-error 1.2.3",
-]
+checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b"
[[package]]
name = "humantime"
@@ -690,10 +832,33 @@
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]]
-name = "indexmap"
-version = "1.8.1"
+name = "iana-time-zone"
+version = "0.1.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee"
+checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613"
+dependencies = [
+ "android_system_properties",
+ "core-foundation-sys",
+ "iana-time-zone-haiku",
+ "js-sys",
+ "wasm-bindgen",
+ "windows",
+]
+
+[[package]]
+name = "iana-time-zone-haiku"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "indexmap"
+version = "1.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
dependencies = [
"autocfg",
"hashbrown",
@@ -702,12 +867,9 @@
[[package]]
name = "indoc"
-version = "1.0.4"
+version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e7906a9fababaeacb774f72410e497a1d18de916322e33797bb2cd29baa23c9e"
-dependencies = [
- "unindent",
-]
+checksum = "bfa799dd5ed20a7e349f3b4639aa80d74549c81716d9ec4f994c9b5815598306"
[[package]]
name = "instant"
@@ -719,6 +881,28 @@
]
[[package]]
+name = "io-lifetimes"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"
+dependencies = [
+ "hermit-abi 0.3.2",
+ "libc",
+ "windows-sys",
+]
+
+[[package]]
+name = "is-terminal"
+version = "0.4.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24fddda5af7e54bf7da53067d6e802dbcc381d0a8eef629df528e3ebf68755cb"
+dependencies = [
+ "hermit-abi 0.3.2",
+ "rustix 0.38.3",
+ "windows-sys",
+]
+
+[[package]]
name = "is_ci"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -735,18 +919,27 @@
[[package]]
name = "itertools"
-version = "0.10.3"
+version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3"
+checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
dependencies = [
"either",
]
[[package]]
name = "itoa"
-version = "1.0.1"
+version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
+checksum = "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a"
+
+[[package]]
+name = "js-sys"
+version = "0.3.64"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a"
+dependencies = [
+ "wasm-bindgen",
+]
[[package]]
name = "lazy_static"
@@ -762,15 +955,15 @@
[[package]]
name = "libc"
-version = "0.2.123"
+version = "0.2.147"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cb691a747a7ab48abc15c5b42066eaafde10dc427e3b6ee2a1cf43db04c763bd"
+checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
[[package]]
name = "libloading"
-version = "0.7.3"
+version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "efbc0f03f9a775e9f6aed295c6a1ba2253c5757a9e03d55c6caa46a681abcddd"
+checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f"
dependencies = [
"cfg-if",
"winapi",
@@ -778,48 +971,50 @@
[[package]]
name = "link-cplusplus"
-version = "1.0.6"
+version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8cae2cd7ba2f3f63938b9c724475dfb7b9861b545a90324476324ed21dbc8c8"
+checksum = "9d240c6f7e1ba3a28b0249f774e6a9dd0175054b52dfbb61b16eb8505c3785c9"
dependencies = [
"cc",
]
[[package]]
-name = "log"
-version = "0.4.16"
+name = "linux-raw-sys"
+version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8"
-dependencies = [
- "cfg-if",
-]
+checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"
[[package]]
-name = "maplit"
-version = "1.0.2"
+name = "linux-raw-sys"
+version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
+checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0"
+
+[[package]]
+name = "log"
+version = "0.4.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4"
[[package]]
name = "mdbook"
-version = "0.4.18"
+version = "0.4.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "74612ae81a3e5ee509854049dfa4c7975ae033c06f5fc4735c7dfbe60ee2a39d"
+checksum = "7b67ee4a744f36e6280792016c17e69921b51df357181d1eb17d620fcc3609f3"
dependencies = [
"anyhow",
"chrono",
- "clap 3.1.9",
+ "clap 4.3.10",
"clap_complete",
- "env_logger 0.7.1",
+ "env_logger 0.10.0",
"handlebars",
- "lazy_static",
"log",
"memchr",
+ "once_cell",
"opener",
"pulldown-cmark",
"regex",
"serde",
- "serde_derive",
"serde_json",
"shlex",
"tempfile",
@@ -829,48 +1024,49 @@
[[package]]
name = "memchr"
-version = "2.4.1"
+version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
+checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "memoffset"
-version = "0.6.5"
+version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
+checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
dependencies = [
"autocfg",
]
[[package]]
name = "miette"
-version = "4.4.0"
+version = "5.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0a097de91d72c13382f60213ed9f7f7a26afd8bee0ea320b47f886a9a67ca5a1"
+checksum = "a236ff270093b0b67451bc50a509bd1bad302cb1d3c7d37d5efe931238581fa9"
dependencies = [
- "atty",
"backtrace",
+ "backtrace-ext",
+ "is-terminal",
"miette-derive",
"once_cell",
"owo-colors",
"supports-color",
"supports-hyperlinks",
"supports-unicode",
- "terminal_size",
- "textwrap 0.15.0",
+ "terminal_size 0.1.17",
+ "textwrap 0.15.2",
"thiserror",
"unicode-width",
]
[[package]]
name = "miette-derive"
-version = "4.4.0"
+version = "5.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "45a95a48d0bc28f9af628286e8a4da09f96f34a97744a2e9a5a4db9814ad527d"
+checksum = "4901771e1d44ddb37964565c654a3223ba41a594d02b8da471cc4464912b5cfa"
dependencies = [
"proc-macro2",
"quote",
- "syn",
+ "syn 2.0.23",
]
[[package]]
@@ -881,88 +1077,71 @@
[[package]]
name = "miniz_oxide"
-version = "0.4.4"
+version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
+checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
dependencies = [
"adler",
- "autocfg",
]
[[package]]
name = "moveit"
-version = "0.5.0"
+version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "815d5988a1dd22f08bad572a83ee654563bb422ece5d5bce41f31ec49399dcb5"
+checksum = "87d7335204cb6ef7bd647fa6db0be3e4d7aa25b5823a7aa030027ddf512cefba"
dependencies = [
"cxx",
]
[[package]]
name = "nom"
-version = "7.1.1"
+version = "7.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36"
+checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
dependencies = [
"memchr",
"minimal-lexical",
]
[[package]]
-name = "num-integer"
-version = "0.1.44"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
-dependencies = [
- "autocfg",
- "num-traits",
-]
-
-[[package]]
name = "num-traits"
-version = "0.2.14"
+version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
+checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
dependencies = [
"autocfg",
]
[[package]]
name = "num_cpus"
-version = "1.13.1"
+version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1"
+checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
dependencies = [
- "hermit-abi",
+ "hermit-abi 0.3.2",
"libc",
]
[[package]]
name = "object"
-version = "0.27.1"
+version = "0.31.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "67ac1d3f9a1d3616fd9a60c8d74296f22406a238b6a72f5cc1e6f314df4ffbf9"
+checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1"
dependencies = [
"memchr",
]
[[package]]
name = "once_cell"
-version = "1.10.0"
+version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9"
-
-[[package]]
-name = "opaque-debug"
-version = "0.2.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
+checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
[[package]]
name = "opener"
-version = "0.5.0"
+version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ea3ebcd72a54701f56345f16785a6d3ac2df7e986d273eb4395c0b01db17952"
+checksum = "293c15678e37254c15bd2f092314abb4e51d7fdde05c2021279c12631b54f005"
dependencies = [
"bstr",
"winapi",
@@ -970,15 +1149,15 @@
[[package]]
name = "os_str_bytes"
-version = "6.0.0"
+version = "6.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64"
+checksum = "4d5d9eb14b174ee9aa2ef96dc2b94637a2d4b6e7cb873c7e171f0c20c6cf3eac"
[[package]]
name = "owo-colors"
-version = "3.3.0"
+version = "3.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5e72e30578e0d0993c8ae20823dd9cff2bc5517d2f586a8aef462a581e8a03eb"
+checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
[[package]]
name = "pathdiff"
@@ -994,18 +1173,19 @@
[[package]]
name = "pest"
-version = "2.1.3"
+version = "2.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53"
+checksum = "f73935e4d55e2abf7f130186537b19e7a4abc886a0252380b59248af473a3fc9"
dependencies = [
+ "thiserror",
"ucd-trie",
]
[[package]]
name = "pest_derive"
-version = "2.1.0"
+version = "2.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0"
+checksum = "aef623c9bbfa0eedf5a0efba11a5ee83209c326653ca31ff019bec3a95bfff2b"
dependencies = [
"pest",
"pest_generator",
@@ -1013,56 +1193,67 @@
[[package]]
name = "pest_generator"
-version = "2.1.3"
+version = "2.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55"
+checksum = "b3e8cba4ec22bada7fc55ffe51e2deb6a0e0db2d0b7ab0b103acc80d2510c190"
dependencies = [
"pest",
"pest_meta",
"proc-macro2",
"quote",
- "syn",
+ "syn 2.0.23",
]
[[package]]
name = "pest_meta"
-version = "2.1.3"
+version = "2.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d"
+checksum = "a01f71cb40bd8bb94232df14b946909e14660e33fc05db3e50ae2a82d7ea0ca0"
dependencies = [
- "maplit",
+ "once_cell",
"pest",
- "sha-1",
+ "sha2",
]
[[package]]
name = "predicates"
-version = "2.1.1"
+version = "3.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a5aab5be6e4732b473071984b3164dbbfb7a3674d30ea5ff44410b6bcd960c3c"
+checksum = "09963355b9f467184c04017ced4a2ba2d75cbcb4e7462690d388233253d4b1a9"
dependencies = [
+ "anstyle",
"difflib",
- "itertools 0.10.3",
+ "itertools 0.10.5",
"predicates-core",
]
[[package]]
name = "predicates-core"
-version = "1.0.3"
+version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "da1c2388b1513e1b605fcec39a95e0a9e8ef088f71443ef37099fa9ae6673fcb"
+checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174"
[[package]]
name = "predicates-tree"
-version = "1.0.5"
+version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4d86de6de25020a36c6d3643a86d9a6a9f552107c0559c60ea03551b5e16c032"
+checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf"
dependencies = [
"predicates-core",
"termtree",
]
[[package]]
+name = "prettyplease"
+version = "0.2.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92139198957b410250d43fad93e630d956499a625c527eda65175c8680f83387"
+dependencies = [
+ "proc-macro2",
+ "syn 2.0.23",
+]
+
+[[package]]
name = "proc-macro-error"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1071,7 +1262,7 @@
"proc-macro-error-attr",
"proc-macro2",
"quote",
- "syn",
+ "syn 1.0.109",
"version_check",
]
@@ -1088,62 +1279,48 @@
[[package]]
name = "proc-macro2"
-version = "1.0.37"
+version = "1.0.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1"
+checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb"
dependencies = [
- "unicode-xid",
+ "unicode-ident",
]
[[package]]
name = "pulldown-cmark"
-version = "0.9.1"
+version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "34f197a544b0c9ab3ae46c359a7ec9cbbb5c7bf97054266fecb7ead794a181d6"
+checksum = "77a1a2f1f0a7ecff9c31abbe177637be0e97a0aef46cf8738ece09327985d998"
dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
"memchr",
"unicase",
]
[[package]]
-name = "quick-error"
-version = "1.2.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
-
-[[package]]
-name = "quick-error"
-version = "2.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
-
-[[package]]
name = "quote"
-version = "1.0.18"
+version = "1.0.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
+checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rayon"
-version = "1.5.2"
+version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fd249e82c21598a9a426a4e00dd7adc1d640b22445ec8545feef801d1a74c221"
+checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b"
dependencies = [
- "autocfg",
- "crossbeam-deque",
"either",
"rayon-core",
]
[[package]]
name = "rayon-core"
-version = "1.9.2"
+version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9f51245e1e62e1f1629cbfec37b5793bbabcaeb90f30e94d2ba03564687353e4"
+checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d"
dependencies = [
"crossbeam-channel",
"crossbeam-deque",
@@ -1153,21 +1330,22 @@
[[package]]
name = "redox_syscall"
-version = "0.2.13"
+version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42"
+checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
]
[[package]]
name = "regex"
-version = "1.5.5"
+version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286"
+checksum = "89089e897c013b3deb627116ae56a6955a72b8bed395c9526af31c9fe528b484"
dependencies = [
"aho-corasick",
"memchr",
+ "regex-automata 0.3.0",
"regex-syntax",
]
@@ -1178,19 +1356,21 @@
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
[[package]]
-name = "regex-syntax"
-version = "0.6.25"
+name = "regex-automata"
+version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
+checksum = "fa250384981ea14565685dea16a9ccc4d1c541a13f82b9c168572264d1df8c56"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
[[package]]
-name = "remove_dir_all"
-version = "0.5.3"
+name = "regex-syntax"
+version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
-dependencies = [
- "winapi",
-]
+checksum = "2ab07dc67230e4a4718e70fd5c20055a4334b121f1f9db8fe63ef39ce9b8c846"
[[package]]
name = "rust_info"
@@ -1200,9 +1380,9 @@
[[package]]
name = "rustc-demangle"
-version = "0.1.21"
+version = "0.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
+checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
[[package]]
name = "rustc-hash"
@@ -1211,16 +1391,43 @@
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
-name = "rustversion"
-version = "1.0.6"
+name = "rustix"
+version = "0.37.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f2cc38e8fa666e2de3c4aba7edeb5ffc5246c1c2ed0e3d17e560aeeba736b23f"
+checksum = "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06"
+dependencies = [
+ "bitflags 1.3.2",
+ "errno",
+ "io-lifetimes",
+ "libc",
+ "linux-raw-sys 0.3.8",
+ "windows-sys",
+]
+
+[[package]]
+name = "rustix"
+version = "0.38.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac5ffa1efe7548069688cd7028f32591853cd7b5b756d41bcffd2353e4fc75b4"
+dependencies = [
+ "bitflags 2.3.3",
+ "errno",
+ "libc",
+ "linux-raw-sys 0.4.3",
+ "windows-sys",
+]
+
+[[package]]
+name = "rustversion"
+version = "1.0.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc31bd9b61a32c31f9650d18add92aa83a49ba979c143eefd27fe7177b05bd5f"
[[package]]
name = "ryu"
-version = "1.0.9"
+version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
+checksum = "fe232bdf6be8c8de797b22184ee71118d63780ea42ac85b61d1baa6d3b782ae9"
[[package]]
name = "scopeguard"
@@ -1230,29 +1437,29 @@
[[package]]
name = "serde"
-version = "1.0.136"
+version = "1.0.166"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789"
+checksum = "d01b7404f9d441d3ad40e6a636a7782c377d2abdbe4fa2440e2edcc2f4f10db8"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
-version = "1.0.136"
+version = "1.0.166"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9"
+checksum = "5dd83d6dde2b6b2d466e14d9d1acce8816dedee94f735eac6395808b3483c6d6"
dependencies = [
"proc-macro2",
"quote",
- "syn",
+ "syn 2.0.23",
]
[[package]]
name = "serde_json"
-version = "1.0.79"
+version = "1.0.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95"
+checksum = "0f1e14e89be7aa4c4b78bdbdc9eb5bf8517829a600ae8eaa39a6e1d960b5185c"
dependencies = [
"itoa",
"ryu",
@@ -1260,15 +1467,14 @@
]
[[package]]
-name = "sha-1"
-version = "0.8.2"
+name = "sha2"
+version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df"
+checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8"
dependencies = [
- "block-buffer",
+ "cfg-if",
+ "cpufeatures",
"digest",
- "fake-simd",
- "opaque-debug",
]
[[package]]
@@ -1284,10 +1490,10 @@
checksum = "f67ad224767faa3c7d8b6d91985b78e70a1324408abcb1cfcc2be4c06bc06043"
[[package]]
-name = "strsim"
-version = "0.8.0"
+name = "static_assertions"
+version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "strsim"
@@ -1296,76 +1502,74 @@
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
-name = "strum_macros"
-version = "0.24.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6878079b17446e4d3eba6192bb0a2950d5b14f0ed8424b852310e5a94345d0ef"
-dependencies = [
- "heck",
- "proc-macro2",
- "quote",
- "rustversion",
- "syn",
-]
-
-[[package]]
name = "supports-color"
-version = "1.3.0"
+version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4872ced36b91d47bae8a214a683fe54e7078875b399dfa251df346c9b547d1f9"
+checksum = "4950e7174bffabe99455511c39707310e7e9b440364a2fcb1cc21521be57b354"
dependencies = [
- "atty",
+ "is-terminal",
"is_ci",
]
[[package]]
name = "supports-hyperlinks"
-version = "1.2.0"
+version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "590b34f7c5f01ecc9d78dba4b3f445f31df750a67621cf31626f3b7441ce6406"
+checksum = "f84231692eb0d4d41e4cdd0cabfdd2e6cd9e255e65f80c9aa7c98dd502b4233d"
dependencies = [
- "atty",
+ "is-terminal",
]
[[package]]
name = "supports-unicode"
-version = "1.0.2"
+version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a8b945e45b417b125a8ec51f1b7df2f8df7920367700d1f98aedd21e5735f8b2"
+checksum = "4b6c2cb240ab5dd21ed4906895ee23fe5a48acdbd15a3ce388e7b62a9b66baf7"
dependencies = [
- "atty",
+ "is-terminal",
]
[[package]]
name = "syn"
-version = "1.0.91"
+version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b683b2b825c8eef438b77c36a06dc262294da3d5a5813fac20da149241dcd44d"
+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
- "unicode-xid",
+ "unicode-ident",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "59fb7d6d8281a51045d62b8eb3a7d1ce347b76f312af50cd3dc0af39c87c1737"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
]
[[package]]
name = "tempfile"
-version = "3.3.0"
+version = "3.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4"
+checksum = "31c0432476357e58790aaa47a8efb0c5138f137343f3b5f23bd36a27e3b0a6d6"
dependencies = [
+ "autocfg",
"cfg-if",
"fastrand",
- "libc",
"redox_syscall",
- "remove_dir_all",
- "winapi",
+ "rustix 0.37.23",
+ "windows-sys",
]
[[package]]
name = "termcolor"
-version = "1.1.3"
+version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
+checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6"
dependencies = [
"winapi-util",
]
@@ -1381,36 +1585,37 @@
]
[[package]]
-name = "termtree"
-version = "0.2.4"
+name = "terminal_size"
+version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "507e9898683b6c43a9aa55b64259b721b52ba226e0f3779137e50ad114a4c90b"
+checksum = "8e6bf6f19e9f8ed8d4048dc22981458ebcf406d67e94cd422e5ecd73d63b3237"
+dependencies = [
+ "rustix 0.37.23",
+ "windows-sys",
+]
+
+[[package]]
+name = "termtree"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76"
[[package]]
name = "test-log"
-version = "0.2.10"
+version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4235dbf7ea878b3ef12dea20a59c134b405a66aafc4fc2c7b9935916e289e735"
+checksum = "d9601d162c1d77e62c1ea0bc8116cd1caf143ce3af947536c3c9052a1677fe0c"
dependencies = [
"proc-macro2",
"quote",
- "syn",
+ "syn 1.0.109",
]
[[package]]
name = "textwrap"
-version = "0.11.0"
+version = "0.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
-dependencies = [
- "unicode-width",
-]
-
-[[package]]
-name = "textwrap"
-version = "0.15.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
+checksum = "b7b3e525a49ec206798b40326a44121291b530c963cfb01018f63e135bac543d"
dependencies = [
"smawk",
"unicode-linebreak",
@@ -1418,77 +1623,72 @@
]
[[package]]
-name = "thiserror"
-version = "1.0.30"
+name = "textwrap"
+version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
+checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d"
+
+[[package]]
+name = "thiserror"
+version = "1.0.41"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c16a64ba9387ef3fdae4f9c1a7f07a0997fce91985c0336f1ddc1822b3b37802"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
-version = "1.0.30"
+version = "1.0.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
+checksum = "d14928354b01c4d6a4f0e549069adef399a284e7995c7ccca94e8a07a5346c59"
dependencies = [
"proc-macro2",
"quote",
- "syn",
-]
-
-[[package]]
-name = "time"
-version = "0.1.44"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
-dependencies = [
- "libc",
- "wasi",
- "winapi",
+ "syn 2.0.23",
]
[[package]]
name = "toml"
-version = "0.5.9"
+version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7"
+checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
dependencies = [
"serde",
]
[[package]]
name = "topological-sort"
-version = "0.1.0"
+version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aa7c7f42dea4b1b99439786f5633aeb9c14c1b53f75e282803c2ec2ad545873c"
+checksum = "ea68304e134ecd095ac6c3574494fc62b909f416c4fca77e440530221e549d3d"
[[package]]
name = "trybuild"
-version = "1.0.59"
+version = "1.0.81"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "606ab3fe0065741fdbb51f64bcb6ba76f13fad49f1723030041826c631782764"
+checksum = "04366e99ff743345622cd00af2af01d711dc2d1ef59250d7347698d21b546729"
dependencies = [
+ "basic-toml",
"glob",
"once_cell",
"serde",
"serde_derive",
"serde_json",
"termcolor",
- "toml",
]
[[package]]
name = "typenum"
-version = "1.15.0"
+version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
+checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
[[package]]
name = "ucd-trie"
-version = "0.1.3"
+version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
+checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81"
[[package]]
name = "unicase"
@@ -1500,37 +1700,32 @@
]
[[package]]
-name = "unicode-linebreak"
-version = "0.1.2"
+name = "unicode-ident"
+version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3a52dcaab0c48d931f7cc8ef826fa51690a08e1ea55117ef26f89864f532383f"
+checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73"
+
+[[package]]
+name = "unicode-linebreak"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c5faade31a542b8b35855fff6e8def199853b2da8da256da52f52f1316ee3137"
dependencies = [
+ "hashbrown",
"regex",
]
[[package]]
name = "unicode-width"
-version = "0.1.9"
+version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
+checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
[[package]]
-name = "unicode-xid"
-version = "0.2.2"
+name = "utf8parse"
+version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
-
-[[package]]
-name = "unindent"
-version = "0.1.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "514672a55d7380da379785a4d70ca8386c8883ff7eaae877be4d2081cebe73d8"
-
-[[package]]
-name = "vec_map"
-version = "0.8.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
+checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
[[package]]
name = "version_check"
@@ -1549,19 +1744,73 @@
[[package]]
name = "wasi"
-version = "0.10.0+wasi-snapshot-preview1"
+version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.87"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.87"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd"
+dependencies = [
+ "bumpalo",
+ "log",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.23",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.87"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.87"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.23",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.87"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1"
[[package]]
name = "which"
-version = "4.2.5"
+version = "4.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c4fb54e6113b6a8772ee41c3404fb0301ac79604489467e0a9ce1f3e97c24ae"
+checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269"
dependencies = [
"either",
- "lazy_static",
"libc",
+ "once_cell",
]
[[package]]
@@ -1594,3 +1843,78 @@
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.48.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
diff --git a/Cargo.toml b/Cargo.toml
index 4a4a64a..b256777 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -8,7 +8,7 @@
[package]
name = "autocxx"
-version = "0.22.3"
+version = "0.26.0"
authors = ["Adrian Taylor <adetaylor@chromium.org>"]
license = "MIT OR Apache-2.0"
description = "Safe autogenerated interop between Rust and C++"
@@ -25,14 +25,14 @@
resolver = "2"
[dependencies]
-autocxx-macro = { path="macro", version="0.22.3" }
-cxx = "1.0.68" # ... also needed because expansion of type_id refers to ::cxx
+autocxx-macro = { path="macro", version="0.26.0" }
+cxx = "1.0.78" # ... also needed because expansion of type_id refers to ::cxx
aquamarine = "0.1" # docs
-moveit = { version = "0.5", features = [ "cxx" ] }
+moveit = { version = "0.6", features = [ "cxx" ] }
[workspace]
members = ["parser", "engine", "gen/cmd", "gen/build", "macro", "demo", "tools/reduce", "tools/mdbook-preprocessor", "integration-tests"]
-exclude = ["examples/s2", "examples/steam-mini", "examples/subclass", "examples/chromium-fake-render-frame-host", "examples/pod", "examples/non-trivial-type-on-stack", "examples/llvm", "examples/reference-wrappers", "tools/stress-test"]
+exclude = ["examples/s2", "examples/steam-mini", "examples/subclass", "examples/chromium-fake-render-frame-host", "examples/pod", "examples/non-trivial-type-on-stack", "examples/llvm", "examples/reference-wrappers", "examples/cpp_calling_rust", "tools/stress-test"]
#[patch.crates-io]
#cxx = { path="../cxx" }
diff --git a/book/src/building.md b/book/src/building.md
index 4d5d919..d4abd09 100644
--- a/book/src/building.md
+++ b/book/src/building.md
@@ -12,7 +12,7 @@
let mut b = autocxx_build::Builder::new("src/main.rs", &[&path])
.extra_clang_args(&["-std=c++17"])
.expect_build();
- b.flag_if_supported("-std=c++17")
+ b.flag_if_supported("-std=c++17") // use "-std:c++17" here if using msvc on windows
.compile("autocxx-demo"); // arbitrary library name, pick anything
println!("cargo:rerun-if-changed=src/main.rs");
// Add instructions to link to any C++ libraries you need.
@@ -58,3 +58,14 @@
```
This interop inevitably involves lots of fiddly small functions. It's likely to perform far better if you can achieve cross-language link-time-optimization (LTO). [This issue](https://github.com/dtolnay/cxx/issues/371) may give some useful hints - see also all the build-related help in [the cxx manual](https://cxx.rs/) which all applies here too.
+
+## C++ versions and other compiler command-line flags
+
+The code generated by cxx and autocxx requires C++ 14, so it's not possible to use an earlier version of C++ than that.
+
+To use a later version, you need to:
+
+* Build the generated code with a later C++ version, for example using the clang argument `-std=c++17`. If you're using autocxx's cargo support, then you would do this by calling methods on the returned `cc::Build` object, for instance [`flag_if_supported`](https://docs.rs/cc/latest/cc/struct.Build.html#method.flag_if_supported).
+* _Also_ give similar directives to the C++ parsing which happens _within_ autocxx (specifically, by autocxx's version of bindgen). To do that, use [`Builder::extra_clang_args`](https://docs.rs/autocxx-engine/latest/autocxx_engine/struct.Builder.html#method.extra_clang_args).
+
+The same applies with the command-line `autocxx_gen` support - you'll need to pass such extra compiler options to `autocxx_gen` and also use them when building the generated C++ code.
diff --git a/book/src/contributing.md b/book/src/contributing.md
index e95ca5a..d82e64e 100644
--- a/book/src/contributing.md
+++ b/book/src/contributing.md
@@ -71,7 +71,11 @@
This is especially valuable to see the `bindgen` output Rust code, and then the converted Rust code which we pass into cxx. Usually, most problems are due to some mis-conversion somewhere
in `engine/src/conversion`. See [here](https://docs.rs/autocxx-engine/latest/autocxx_engine/struct.IncludeCppEngine.html) for documentation and diagrams on how the engine works.
-You may also wish to set `AUTOCXX_ASAN=1` on Linux when running tests.
+You may also wish to set `AUTOCXX_ASAN=1` on Linux when running tests. To exercise all
+the code paths related to generating both C++ and Rust side shims, you can set
+`AUTOCXX_FORCE_WRAPPER_GENERATION=1`. The test suite doesn't do this by default because
+we also want to test the normal code paths. (In the future we might want to
+parameterize the test suite to do both.)
## Reporting bugs
@@ -99,6 +103,17 @@
the C++ bindings as distilled by `bindgen`, and then the version which
we've converted and moulded to be suitable for use by `cxx`.
+## Bugs related to linking problems
+
+Unfortunately, _linking_ C++ binaries is a complex area subject in itself, and
+we won't be able to debug your linking issues by means of an autocxx bug report.
+Assuming you're using autocxx's build.rs support, the actual C++ build and
+managed by the [`cc`](https://crates.io/crates/cc) crate. You can find
+many of its options on its [`Build` type](https://docs.rs/cc/latest/cc/struct.Build.html).
+If you need to bring in an external library, you may also need to emit certain
+[print statements from your `build.rs`](https://doc.rust-lang.org/cargo/reference/build-scripts.html#rustc-link-lib)
+to instruct cargo to link against that library.
+
## How to contribute to this manual
More examples in this manual are _very_ welcome!
diff --git a/book/src/examples.md b/book/src/examples.md
index d0246f5..e7f2afe 100644
--- a/book/src/examples.md
+++ b/book/src/examples.md
@@ -4,7 +4,7 @@
* [S2 example](https://github.com/google/autocxx/tree/main/examples/s2) - example using S2 geometry library
* [Steam example](https://github.com/google/autocxx/tree/main/examples/steam-mini) - example using (something like) the Steam client library
* [Subclass example](https://github.com/google/autocxx/tree/main/examples/subclass) - example using subclasses
-* [Integration tests](https://github.com/google/autocxx/blob/main/integration-tests/src/tests.rs)
+* [Integration tests](https://github.com/google/autocxx/blob/main/integration-tests/tests/integration_test.rs)
- hundreds of small snippets
Contributions of more examples to the `examples` directory are much appreciated!
diff --git a/book/src/rust_calls.md b/book/src/rust_calls.md
index ebd37c5..369c304 100644
--- a/book/src/rust_calls.md
+++ b/book/src/rust_calls.md
@@ -36,8 +36,7 @@
}
}
",
-"#include <memory>
-class GoatObserver {
+"class GoatObserver {
public:
virtual void goat_full() const = 0;
virtual ~GoatObserver() {}
@@ -62,7 +61,7 @@
use ffi::*;
-#[is_subclass(superclass("GoatObserver"))]
+#[subclass]
#[derive(Default)]
pub struct MyGoatObserver;
@@ -124,8 +123,6 @@
virtual ~Dinosaur() {}
};
-// Currently, autocxx requires at least one 'generate!' call.
-inline void do_a_thing() {};
",
{
use autocxx::prelude::*;
@@ -136,16 +133,15 @@
safety!(unsafe_ffi)
subclass!("Dinosaur", TRex)
subclass!("Dinosaur", Diplodocus)
- generate!("do_a_thing")
}
use ffi::*;
-#[is_subclass(superclass("Dinosaur"))]
+#[subclass]
#[derive(Default)]
pub struct TRex;
-#[is_subclass(superclass("Dinosaur"))]
+#[subclass]
#[derive(Default)]
pub struct Diplodocus;
diff --git a/book/src/tutorial.md b/book/src/tutorial.md
index 3e8e5b2..5582cd9 100644
--- a/book/src/tutorial.md
+++ b/book/src/tutorial.md
@@ -21,12 +21,12 @@
```toml
[dependencies]
-autocxx = "0.22.3"
+autocxx = "0.26.0"
cxx = "1.0"
[build-dependencies]
-autocxx-build = "0.22.3"
-miette = { version="4.3", features=["fancy"] } # optional but gives nicer error messages!
+autocxx-build = "0.26.0"
+miette = { version = "5", features = ["fancy"] } # optional but gives nicer error messages!
```
Now, add a `build.rs` next to your `Cargo.toml` (this is a standard `cargo` [build script](https://doc.rust-lang.org/cargo/reference/build-scripts.html)). This is where you need your include path:
diff --git a/build.rs b/build.rs
new file mode 100644
index 0000000..b2de2dc
--- /dev/null
+++ b/build.rs
@@ -0,0 +1,27 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// It would be nice to use the rustversion crate here instead,
+// but that doesn't work with inner attributes.
+fn main() {
+ if let Some(ver) = rustc_version() {
+ if ver.contains("nightly") {
+ println!("cargo:rustc-cfg=nightly")
+ }
+ }
+}
+
+fn rustc_version() -> Option<String> {
+ let rustc = std::env::var_os("RUSTC")?;
+ let output = std::process::Command::new(rustc)
+ .arg("--version")
+ .output()
+ .ok()?;
+ let version = String::from_utf8(output.stdout).ok()?;
+ Some(version)
+}
diff --git a/demo/Cargo.toml b/demo/Cargo.toml
index e81cb80..7064bea 100644
--- a/demo/Cargo.toml
+++ b/demo/Cargo.toml
@@ -8,14 +8,14 @@
[package]
name = "autocxx-demo"
-version = "0.22.3"
+version = "0.26.0"
authors = ["Adrian Taylor <adetaylor@chromium.org>"]
edition = "2021"
[dependencies]
-cxx = "1.0.68"
-autocxx = { path = "..", version="0.22.3" }
+cxx = "1.0.78"
+autocxx = { path = "..", version = "0.26.0" }
[build-dependencies]
-autocxx-build = { path = "../gen/build", version="0.22.3" }
-miette = { version="4.3", features=["fancy"]}
+autocxx-build = { path = "../gen/build", version = "0.26.0" }
+miette = { version = "5", features = ["fancy"] }
diff --git a/demo/build.rs b/demo/build.rs
index 9a16a34..69c632c 100644
--- a/demo/build.rs
+++ b/demo/build.rs
@@ -8,7 +8,7 @@
fn main() -> miette::Result<()> {
let path = std::path::PathBuf::from("src");
- let mut b = autocxx_build::Builder::new("src/main.rs", &[&path]).build()?;
+ let mut b = autocxx_build::Builder::new("src/main.rs", [&path]).build()?;
b.flag_if_supported("-std=c++14").compile("autocxx-demo");
println!("cargo:rerun-if-changed=src/main.rs");
diff --git a/engine/Cargo.toml b/engine/Cargo.toml
index 1400847..9736db9 100644
--- a/engine/Cargo.toml
+++ b/engine/Cargo.toml
@@ -8,7 +8,7 @@
[package]
name = "autocxx-engine"
-version = "0.22.3"
+version = "0.26.0"
authors = ["Adrian Taylor <adetaylor@chromium.org>"]
license = "MIT OR Apache-2.0"
description = "Safe autogenerated interop between Rust and C++"
@@ -18,42 +18,43 @@
categories = ["development-tools::ffi", "api-bindings"]
[features]
-default = [ "reproduction_case" ]
+default = ["reproduction_case", "runtime"]
build = ["cc"]
-nightly = [] # for doc generation purposes only; used by docs.rs
-reproduction_case = [ "serde_json", "autocxx-parser/reproduction_case" ]
-runtime = [ "autocxx-bindgen/runtime" ]
-static = [ "autocxx-bindgen/static" ]
+nightly = [] # for doc generation purposes only; used by docs.rs
+reproduction_case = ["serde_json", "autocxx-parser/reproduction_case"]
+runtime = ["autocxx-bindgen/runtime"]
+static = ["autocxx-bindgen/static"]
[dependencies]
log = "0.4"
proc-macro2 = "1.0.11"
quote = "1.0"
indoc = "1.0"
-autocxx-bindgen = "=0.59.17"
-#autocxx-bindgen = { git = "https://github.com/adetaylor/rust-bindgen", branch = "pollute-fewer-typedefs" }
+autocxx-bindgen = { version = "=0.65.1", default-features = false, features = ["logging", "which-rustfmt"] }
+#autocxx-bindgen = { git = "https://github.com/maurer/rust-bindgen", branch = "update-0.65.1", default-features = false, features = ["logging", "which-rustfmt"] }
itertools = "0.10.3"
cc = { version = "1.0", optional = true }
# Note: Keep the patch-level version of cxx-gen and cxx in sync.
# There can be interdependencies between the code generated by cxx-gen and
# what cxx expects to be there.
-cxx-gen = "0.7.68"
-autocxx-parser = { version = "=0.22.3", path="../parser" }
+cxx-gen = "0.7.78"
+autocxx-parser = { version = "=0.26.0", path = "../parser" }
version_check = "0.9"
-aquamarine = "0.1" # docs
-tempfile = "3.1"
+aquamarine = "0.1" # docs
+tempfile = "3.4"
once_cell = "1.7"
-strum_macros = "0.24"
serde_json = { version = "1.0", optional = true }
-miette = "4.3"
+miette = "5"
thiserror = "1"
regex = "1.5"
indexmap = "1.8"
+prettyplease = { version = "0.2.6", features = ["verbatim"] }
+rustversion = "1.0"
[dependencies.syn]
-version = "1.0.39"
-features = [ "full", "printing" ]
-#features = [ "full", "printing", "extra-traits" ]
+version = "2.0.1"
+features = ["full", "printing", "visit-mut"]
+#features = [ "full", "printing", "visit-mut", "extra-traits" ]
[package.metadata.docs.rs]
features = ["build", "nightly"]
diff --git a/engine/src/ast_discoverer.rs b/engine/src/ast_discoverer.rs
index 8840899..f555419 100644
--- a/engine/src/ast_discoverer.rs
+++ b/engine/src/ast_discoverer.rs
@@ -14,12 +14,13 @@
};
use itertools::Itertools;
use proc_macro2::Ident;
+use syn::visit_mut::{visit_type_mut, VisitMut};
use syn::{
- parse_quote, punctuated::Punctuated, Attribute, Binding, Expr, ExprAssign, ExprAssignOp,
- ExprAwait, ExprBinary, ExprBox, ExprBreak, ExprCast, ExprField, ExprGroup, ExprLet, ExprParen,
- ExprReference, ExprTry, ExprType, ExprUnary, ImplItem, Item, ItemEnum, ItemStruct, Pat, PatBox,
- PatReference, PatSlice, PatTuple, Path, Receiver, ReturnType, Signature, Stmt, TraitItem, Type,
- TypeArray, TypeGroup, TypeParamBound, TypeParen, TypePtr, TypeReference, TypeSlice,
+ parse_quote, punctuated::Punctuated, AssocConst, AssocType, Attribute, Expr, ExprAssign,
+ ExprAwait, ExprBinary, ExprBlock, ExprBreak, ExprCast, ExprConst, ExprField, ExprGroup,
+ ExprLet, ExprParen, ExprReference, ExprTry, ExprUnary, ImplItem, Item, ItemEnum, ItemStruct,
+ LocalInit, Pat, PatReference, PatSlice, PatTuple, Path, ReturnType, Signature, Stmt, TraitItem,
+ Type, TypeArray, TypeGroup, TypeParamBound, TypeParen, TypePtr, TypeReference, TypeSlice,
};
use thiserror::Error;
@@ -34,8 +35,6 @@
pub enum DiscoveryErr {
#[error("#[extern_rust_function] was attached to a method in an impl block that was too complex for autocxx. autocxx supports only \"impl X {{...}}\" where X is a single identifier, not a path or more complex type.")]
FoundExternRustFunOnTypeWithoutClearReceiver,
- #[error("#[extern_rust_function] was attached to a method taking no parameters.")]
- NonReferenceReceiver,
#[error("#[extern_rust_function] was attached to a method taking a receiver by value.")]
NoParameterOnMethod,
#[error("#[extern_rust_function] was in an impl block nested wihtin another block. This is only supported in the outermost mod of a file, alongside the include_cpp!.")]
@@ -103,7 +102,7 @@
self.discoveries.extern_rust_funs.push(RustFun {
path: self.deeper_path(&fun.sig.ident),
sig: fun.sig.clone(),
- receiver: None,
+ has_receiver: false,
});
}
}
@@ -178,7 +177,7 @@
}
fn search_trait_item(&mut self, itm: &TraitItem) -> Result<(), DiscoveryErr> {
- if let TraitItem::Method(itm) = itm {
+ if let TraitItem::Fn(itm) = itm {
if let Some(block) = &itm.default {
self.search_stmts(block.stmts.iter())?
}
@@ -199,13 +198,14 @@
fn search_stmt(&mut self, stmt: &Stmt) -> Result<(), DiscoveryErr> {
match stmt {
Stmt::Local(lcl) => {
- if let Some((_, expr)) = &lcl.init {
+ if let Some(LocalInit { expr, .. }) = &lcl.init {
self.search_expr(expr)?
}
self.search_pat(&lcl.pat)
}
Stmt::Item(itm) => self.search_item(itm),
- Stmt::Expr(exp) | Stmt::Semi(exp, _) => self.search_expr(exp),
+ Stmt::Expr(exp, _) => self.search_expr(exp),
+ Stmt::Macro(_) => Ok(()),
}
}
@@ -214,10 +214,9 @@
Expr::Path(exp) => {
self.search_path(&exp.path)?;
}
- Expr::Macro(_) => {}
+ Expr::Macro(_) | Expr::Infer(_) => {}
Expr::Array(array) => self.search_exprs(array.elems.iter())?,
Expr::Assign(ExprAssign { left, right, .. })
- | Expr::AssignOp(ExprAssignOp { left, right, .. })
| Expr::Binary(ExprBinary { left, right, .. }) => {
self.search_expr(left)?;
self.search_expr(right)?;
@@ -226,9 +225,10 @@
Expr::Await(ExprAwait { base, .. }) | Expr::Field(ExprField { base, .. }) => {
self.search_expr(base)?
}
- Expr::Block(blck) => self.search_stmts(blck.block.stmts.iter())?,
- Expr::Box(ExprBox { expr, .. })
- | Expr::Break(ExprBreak {
+ Expr::Block(ExprBlock { block, .. }) | Expr::Const(ExprConst { block, .. }) => {
+ self.search_stmts(block.stmts.iter())?
+ }
+ Expr::Break(ExprBreak {
expr: Some(expr), ..
})
| Expr::Cast(ExprCast { expr, .. })
@@ -236,7 +236,6 @@
| Expr::Paren(ExprParen { expr, .. })
| Expr::Reference(ExprReference { expr, .. })
| Expr::Try(ExprTry { expr, .. })
- | Expr::Type(ExprType { expr, .. })
| Expr::Unary(ExprUnary { expr, .. }) => self.search_expr(expr)?,
Expr::Call(exc) => {
self.search_expr(&exc.func)?;
@@ -281,8 +280,8 @@
self.search_exprs(mtc.args.iter())?;
}
Expr::Range(exr) => {
- self.search_option_expr(&exr.from)?;
- self.search_option_expr(&exr.to)?;
+ self.search_option_expr(&exr.start)?;
+ self.search_option_expr(&exr.end)?;
}
Expr::Repeat(exr) => {
self.search_expr(&exr.expr)?;
@@ -334,7 +333,7 @@
impl_item: &ImplItem,
receiver: Option<&RustPath>,
) -> Result<(), DiscoveryErr> {
- if let ImplItem::Method(itm) = impl_item {
+ if let ImplItem::Fn(itm) = impl_item {
if Self::has_attr(&itm.attrs, EXTERN_RUST_FUN) {
if self.mod_path.is_some() {
return Err(DiscoveryErr::FoundExternRustFunWithinMod);
@@ -347,7 +346,7 @@
self.discoveries.extern_rust_funs.push(RustFun {
path: self.deeper_path(&itm.sig.ident),
sig,
- receiver: Some(receiver.get_final_ident().clone()),
+ has_receiver: true,
});
self.discoveries.extern_rust_types.push(receiver.clone())
} else {
@@ -363,10 +362,15 @@
fn search_pat(&mut self, pat: &Pat) -> Result<(), DiscoveryErr> {
match pat {
- Pat::Box(PatBox { pat, .. }) | Pat::Reference(PatReference { pat, .. }) => {
- self.search_pat(pat)
+ Pat::Const(const_) => {
+ for stmt in &const_.block.stmts {
+ self.search_stmt(stmt)?
+ }
+ Ok(())
}
+ Pat::Reference(PatReference { pat, .. }) => self.search_pat(pat),
Pat::Ident(_) | Pat::Lit(_) | Pat::Macro(_) | Pat::Range(_) | Pat::Rest(_) => Ok(()),
+ Pat::Paren(paren) => self.search_pat(&paren.pat),
Pat::Or(pator) => {
for case in &pator.cases {
self.search_pat(case)?;
@@ -389,7 +393,7 @@
}
Pat::TupleStruct(tps) => {
self.search_path(&tps.path)?;
- for f in &tps.pat.elems {
+ for f in &tps.elems {
self.search_pat(f)?;
}
Ok(())
@@ -398,6 +402,7 @@
self.search_pat(&pt.pat)?;
self.search_type(&pt.ty)
}
+ Pat::Verbatim(_) | Pat::Wild(_) => Ok(()),
_ => Ok(()),
}
}
@@ -438,7 +443,7 @@
fn search_type_param_bounds(
&mut self,
- bounds: &Punctuated<TypeParamBound, syn::token::Add>,
+ bounds: &Punctuated<TypeParamBound, syn::token::Plus>,
) -> Result<(), DiscoveryErr> {
for b in bounds {
if let syn::TypeParamBound::Trait(tpbt) = b {
@@ -467,13 +472,17 @@
match arg {
syn::GenericArgument::Lifetime(_) => {}
syn::GenericArgument::Type(ty)
- | syn::GenericArgument::Binding(Binding { ty, .. }) => {
+ | syn::GenericArgument::AssocType(AssocType { ty, .. }) => {
self.search_type(ty)?
}
syn::GenericArgument::Constraint(c) => {
self.search_type_param_bounds(&c.bounds)?
}
- syn::GenericArgument::Const(c) => self.search_expr(c)?,
+ syn::GenericArgument::Const(value)
+ | syn::GenericArgument::AssocConst(AssocConst { value, .. }) => {
+ self.search_expr(value)?
+ }
+ _ => {}
}
}
}
@@ -489,7 +498,7 @@
fn has_attr(attrs: &[Attribute], attr_name: &str) -> bool {
attrs.iter().any(|attr| {
- attr.path
+ attr.path()
.segments
.last()
.map(|seg| seg.ident == attr_name)
@@ -498,6 +507,24 @@
}
}
+struct SelfSubstituter<'a> {
+ self_ty: &'a Ident,
+}
+
+impl<'a> SelfSubstituter<'a> {
+ pub fn new(self_ty: &'a Ident) -> Self {
+ Self { self_ty }
+ }
+}
+
+impl<'a> VisitMut for SelfSubstituter<'a> {
+ fn visit_type_path_mut(&mut self, i: &mut syn::TypePath) {
+ if i.qself.is_none() && i.path.is_ident("Self") {
+ i.path = Path::from(self.self_ty.clone());
+ }
+ }
+}
+
/// Take a method signature that may be `fn a(&self)`
/// and turn it into `fn a(self: &A)` which is what we will
/// need to specify to cxx.
@@ -505,25 +532,19 @@
let mut sig = sig.clone();
match sig.inputs.iter_mut().next() {
Some(first_arg) => match first_arg {
- syn::FnArg::Receiver(Receiver {
- reference: Some(_),
- mutability: Some(_),
- ..
- }) => {
+ syn::FnArg::Receiver(rec_arg) => {
+ let mut substituted_type = rec_arg.ty.clone();
+ visit_type_mut(&mut SelfSubstituter::new(receiver), &mut substituted_type);
*first_arg = parse_quote! {
- self: &mut #receiver
+ qelf: #substituted_type
+ };
+ if let syn::FnArg::Typed(ref mut pat_type) = *first_arg {
+ if let syn::Pat::Ident(ref mut pat_ident) = *pat_type.pat {
+ assert_eq!(pat_ident.ident.to_string(), "qelf");
+ pat_ident.ident = Ident::new("self", pat_ident.ident.span());
+ }
}
}
- syn::FnArg::Receiver(Receiver {
- reference: Some(_),
- mutability: None,
- ..
- }) => {
- *first_arg = parse_quote! {
- self: &#receiver
- }
- }
- syn::FnArg::Receiver(..) => return Err(DiscoveryErr::NonReferenceReceiver),
syn::FnArg::Typed(_) => {}
},
None => return Err(DiscoveryErr::NoParameterOnMethod),
@@ -534,7 +555,7 @@
#[cfg(test)]
mod tests {
use quote::{quote, ToTokens};
- use syn::{parse_quote, ImplItemMethod};
+ use syn::{parse_quote, ImplItemFn};
use crate::{ast_discoverer::add_receiver, types::make_ident};
@@ -690,7 +711,7 @@
#[test]
fn test_add_receiver() {
- let meth: ImplItemMethod = parse_quote! {
+ let meth: ImplItemFn = parse_quote! {
fn a(&self) {}
};
let a = make_ident("A");
@@ -702,7 +723,7 @@
quote! { fn a(self: &A) }.to_string()
);
- let meth: ImplItemMethod = parse_quote! {
+ let meth: ImplItemFn = parse_quote! {
fn a(&mut self, b: u32) -> Foo {}
};
assert_eq!(
@@ -713,12 +734,7 @@
quote! { fn a(self: &mut A, b: u32) -> Foo }.to_string()
);
- let meth: ImplItemMethod = parse_quote! {
- fn a(self) {}
- };
- assert!(add_receiver(&meth.sig, &a).is_err());
-
- let meth: ImplItemMethod = parse_quote! {
+ let meth: ImplItemFn = parse_quote! {
fn a() {}
};
assert!(add_receiver(&meth.sig, &a).is_err());
diff --git a/engine/src/builder.rs b/engine/src/builder.rs
index d0b5199..064056e 100644
--- a/engine/src/builder.rs
+++ b/engine/src/builder.rs
@@ -10,8 +10,8 @@
use miette::Diagnostic;
use thiserror::Error;
-use crate::generate_rs_single;
-use crate::{strip_system_headers, CppCodegenOptions, ParseError, RebuildDependencyRecorder};
+use crate::{generate_rs_single, CodegenOptions};
+use crate::{get_cxx_header_bytes, CppCodegenOptions, ParseError, RebuildDependencyRecorder};
use std::ffi::OsStr;
use std::ffi::OsString;
use std::fs::File;
@@ -63,8 +63,16 @@
/// An object to allow building of bindings from a `build.rs` file.
///
-/// It would be unusual to use this directly - see the `autocxx_build` or
+/// It would be unusual to create this directly - see the `autocxx_build` or
/// `autocxx_gen` crates.
+///
+/// Once you've got one of these objects, you may set some configuration
+/// options but then you're likely to want to call the [`build`] method.
+///
+/// # Setting C++ version
+///
+/// Ensure you use [`extra_clang_args`] as well as giving an appropriate
+/// option to the [`cc::Build`] which you receive from the [`build`] function.
#[cfg_attr(feature = "nightly", doc(cfg(feature = "build")))]
pub struct Builder<'a, BuilderContext> {
rs_file: PathBuf,
@@ -73,7 +81,7 @@
dependency_recorder: Option<Box<dyn RebuildDependencyRecorder>>,
custom_gendir: Option<PathBuf>,
auto_allowlist: bool,
- cpp_codegen_options: CppCodegenOptions<'a>,
+ codegen_options: CodegenOptions<'a>,
// This member is to ensure that this type is parameterized
// by a BuilderContext. The goal is to balance three needs:
// (1) have most of the functionality over in autocxx_engine,
@@ -108,12 +116,14 @@
dependency_recorder: CTX::get_dependency_recorder(),
custom_gendir: None,
auto_allowlist: false,
- cpp_codegen_options: CppCodegenOptions::default(),
+ codegen_options: CodegenOptions::default(),
ctx: PhantomData,
}
}
- /// Specify extra arguments for clang.
+ /// Specify extra arguments for clang. These are used when parsing
+ /// C++ headers. For example, you might want to provide
+ /// `-std=c++17` to specify C++17.
pub fn extra_clang_args(mut self, extra_clang_args: &[&str]) -> Self {
self.extra_clang_args = extra_clang_args.iter().map(|s| s.to_string()).collect();
self
@@ -130,7 +140,7 @@
where
F: FnOnce(&mut CppCodegenOptions),
{
- modifier(&mut self.cpp_codegen_options);
+ modifier(&mut self.codegen_options.cpp_codegen_options);
self
}
@@ -152,20 +162,33 @@
self
}
+ #[doc(hidden)]
+ /// Whether to force autocxx always to generate extra Rust and C++
+ /// side shims. This is only used by the integration test suite to
+ /// exercise more code paths - don't use it!
+ pub fn force_wrapper_generation(mut self, do_it: bool) -> Self {
+ self.codegen_options.force_wrapper_gen = do_it;
+ self
+ }
+
/// Whether to suppress inclusion of system headers (`memory`, `string` etc.)
/// from generated C++ bindings code. This should not normally be used,
/// but can occasionally be useful if you're reducing a test case and you
/// have a preprocessed header file which already contains absolutely everything
/// that the bindings could ever need.
pub fn suppress_system_headers(mut self, do_it: bool) -> Self {
- self.cpp_codegen_options.suppress_system_headers = do_it;
+ self.codegen_options
+ .cpp_codegen_options
+ .suppress_system_headers = do_it;
self
}
/// An annotation optionally to include on each C++ function.
/// For example to export the symbol from a library.
pub fn cxx_impl_annotations(mut self, cxx_impl_annotations: Option<String>) -> Self {
- self.cpp_codegen_options.cxx_impl_annotations = cxx_impl_annotations;
+ self.codegen_options
+ .cpp_codegen_options
+ .cxx_impl_annotations = cxx_impl_annotations;
self
}
@@ -176,6 +199,17 @@
/// so if you use the `miette` crate and its `fancy` feature, then simply
/// return a `miette::Result` from your main function, you should get nicely
/// printed diagnostics.
+ ///
+ /// As this is a [`cc::Build`] there are lots of options you can apply to
+ /// the resulting options, but please bear in mind that these only apply
+ /// to the build process for the generated code - such options will not
+ /// influence autocxx's process for parsing header files.
+ ///
+ /// For example, if you wish to set the C++ version to C++17, you might
+ /// be tempted to use [`cc::Build::flag_if_supported`] to add the
+ /// `-std=c++17` flag. However, this won't affect the header parsing which
+ /// autocxx does internally (by means of bindgen) so you _additionally_
+ /// should call [`extra_clang_args`] with that same option.
pub fn build(self) -> Result<BuilderBuild, BuilderError> {
self.build_listing_files().map(|r| r.0)
}
@@ -208,7 +242,11 @@
write_to_file(
&incdir,
"cxx.h",
- &Self::get_cxx_header_bytes(self.cpp_codegen_options.suppress_system_headers),
+ &get_cxx_header_bytes(
+ self.codegen_options
+ .cpp_codegen_options
+ .suppress_system_headers,
+ ),
)?;
let autocxx_inc = build_autocxx_inc(self.autocxx_incs, &incdir);
@@ -221,7 +259,7 @@
autocxx_inc,
clang_args,
self.dependency_recorder,
- &self.cpp_codegen_options,
+ &self.codegen_options,
)
.map_err(BuilderError::ParseError)?;
let mut counter = 0;
@@ -235,10 +273,10 @@
builder.includes(parsed_file.include_dirs());
for include_cpp in parsed_file.get_cpp_buildables() {
let generated_code = include_cpp
- .generate_h_and_cxx(&self.cpp_codegen_options)
+ .generate_h_and_cxx(&self.codegen_options.cpp_codegen_options)
.map_err(BuilderError::InvalidCxx)?;
for filepair in generated_code.0 {
- let fname = format!("gen{}.cxx", counter);
+ let fname = format!("gen{counter}.cxx");
counter += 1;
if let Some(implementation) = &filepair.implementation {
let gen_cxx_path = write_to_file(&cxxdir, &fname, implementation)?;
@@ -260,10 +298,6 @@
Ok(BuilderSuccess(builder, generated_rs, generated_cpp))
}
}
-
- fn get_cxx_header_bytes(suppress_system_headers: bool) -> Vec<u8> {
- strip_system_headers(crate::HEADER.as_bytes().to_vec(), suppress_system_headers)
- }
}
fn ensure_created(dir: &Path) -> Result<(), BuilderError> {
@@ -285,6 +319,13 @@
fn write_to_file(dir: &Path, filename: &str, content: &[u8]) -> Result<PathBuf, BuilderError> {
let path = dir.join(filename);
+ if let Ok(existing_contents) = std::fs::read(&path) {
+ // Avoid altering timestamps on disk if the file already exists,
+ // to stop downstream build steps recurring.
+ if existing_contents == content {
+ return Ok(path);
+ }
+ }
try_write_to_file(&path, content).map_err(|e| BuilderError::FileWriteFail(e, path.clone()))?;
Ok(path)
}
diff --git a/engine/src/conversion/analysis/abstract_types.rs b/engine/src/conversion/analysis/abstract_types.rs
index dac9684..884cbd0 100644
--- a/engine/src/conversion/analysis/abstract_types.rs
+++ b/engine/src/conversion/analysis/abstract_types.rs
@@ -6,6 +6,9 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
+use indexmap::map::IndexMap as HashMap;
+use syn::{punctuated::Punctuated, token::Comma, FnArg};
+
use super::{
fun::{
FnAnalysis, FnKind, FnPhase, FnPrePhase2, MethodKind, PodAndConstructorAnalysis,
@@ -13,106 +16,164 @@
},
pod::PodAnalysis,
};
-use crate::conversion::{api::Api, apivec::ApiVec};
use crate::conversion::{
- api::TypeKind,
+ analysis::{depth_first::fields_and_bases_first, fun::ReceiverMutability},
+ api::{ApiName, TypeKind},
error_reporter::{convert_apis, convert_item_apis},
- ConvertError,
+ ConvertErrorFromCpp,
+};
+use crate::{
+ conversion::{api::Api, apivec::ApiVec},
+ types::QualifiedName,
};
use indexmap::set::IndexSet as HashSet;
+#[derive(Hash, PartialEq, Eq, Clone, Debug)]
+struct Signature {
+ name: String,
+ args: Vec<syn::Type>,
+ constness: ReceiverMutability,
+}
+
+impl Signature {
+ fn new(
+ name: &ApiName,
+ params: &Punctuated<FnArg, Comma>,
+ constness: ReceiverMutability,
+ ) -> Self {
+ Signature {
+ name: name.cpp_name(),
+ args: params
+ .iter()
+ .skip(1) // skip `this` implicit argument
+ .filter_map(|p| {
+ if let FnArg::Typed(t) = p {
+ Some((*t.ty).clone())
+ } else {
+ None
+ }
+ })
+ .collect(),
+ constness,
+ }
+ }
+}
+
/// Spot types with pure virtual functions and mark them abstract.
-pub(crate) fn mark_types_abstract(mut apis: ApiVec<FnPrePhase2>) -> ApiVec<FnPrePhase2> {
- let mut abstract_types: HashSet<_> = apis
- .iter()
- .filter_map(|api| match &api {
- Api::Function {
- analysis:
- FnAnalysis {
- kind:
- FnKind::Method {
- impl_for: self_ty_name,
- method_kind: MethodKind::PureVirtual(_),
- ..
- },
- ..
- },
- ..
- } => Some(self_ty_name.clone()),
- _ => None,
+pub(crate) fn mark_types_abstract(apis: ApiVec<FnPrePhase2>) -> ApiVec<FnPrePhase2> {
+ #[derive(Default, Debug, Clone)]
+ struct ClassAbstractState {
+ undefined: HashSet<Signature>,
+ defined: HashSet<Signature>,
+ }
+ let mut class_states: HashMap<QualifiedName, ClassAbstractState> = HashMap::new();
+ let mut abstract_classes = HashSet::new();
+
+ for api in apis.iter() {
+ if let Api::Function {
+ name,
+ analysis:
+ FnAnalysis {
+ kind:
+ FnKind::Method {
+ impl_for: self_ty_name,
+ method_kind,
+ ..
+ },
+ params,
+ ..
+ },
+ ..
+ } = api
+ {
+ match method_kind {
+ MethodKind::PureVirtual(constness) => {
+ class_states
+ .entry(self_ty_name.clone())
+ .or_default()
+ .undefined
+ .insert(Signature::new(name, params, *constness));
+ }
+ MethodKind::Virtual(constness) => {
+ class_states
+ .entry(self_ty_name.clone())
+ .or_default()
+ .defined
+ .insert(Signature::new(name, params, *constness));
+ }
+ _ => {}
+ }
+ }
+ }
+
+ for api in fields_and_bases_first(apis.iter()) {
+ if let Api::Struct {
+ analysis:
+ PodAndConstructorAnalysis {
+ pod:
+ PodAnalysis {
+ bases,
+ kind: TypeKind::Pod | TypeKind::NonPod,
+ ..
+ },
+ ..
+ },
+ name,
+ ..
+ } = api
+ {
+ // resolve virtuals for a class: start with new pure virtuals in this class
+ let mut self_cs = class_states.get(&name.name).cloned().unwrap_or_default();
+
+ // then add pure virtuals of bases
+ for base in bases.iter() {
+ if let Some(base_cs) = class_states.get(base) {
+ self_cs.undefined.extend(base_cs.undefined.iter().cloned());
+ }
+ }
+
+ // then remove virtuals defined in this class
+ self_cs
+ .undefined
+ .retain(|und| !self_cs.defined.contains(und));
+
+ // if there are undefined functions, mark as virtual
+ if !self_cs.undefined.is_empty() {
+ abstract_classes.insert(name.name.clone());
+ }
+
+ // store it back so child classes can read it properly
+ *class_states.entry(name.name.clone()).or_default() = self_cs;
+ }
+ }
+
+ // mark abstract types as abstract
+ let mut apis: ApiVec<_> = apis
+ .into_iter()
+ .map(|mut api| {
+ if let Api::Struct { name, analysis, .. } = &mut api {
+ if abstract_classes.contains(&name.name) {
+ analysis.pod.kind = TypeKind::Abstract;
+ }
+ }
+ api
})
.collect();
- // Spot any derived classes (recursively). Also, any types which have a base
- // class that's not on the allowlist are presumed to be abstract, because we
- // have no way of knowing (as they're not on the allowlist, there will be
- // no methods associated so we won't be able to spot pure virtual methods).
- let mut iterate = true;
- while iterate {
- iterate = false;
- apis = apis
- .into_iter()
- .map(|api| {
- match api {
- Api::Struct {
- analysis:
- PodAndConstructorAnalysis {
- pod:
- PodAnalysis {
- bases,
- kind: TypeKind::Pod | TypeKind::NonPod,
- castable_bases,
- field_deps,
- field_info,
- is_generic,
- in_anonymous_namespace,
- },
- constructors,
- },
- name,
- details,
- } if abstract_types.contains(&name.name)
- || !abstract_types.is_disjoint(&bases) =>
- {
- abstract_types.insert(name.name.clone());
- // Recurse in case there are further dependent types
- iterate = true;
- Api::Struct {
- analysis: PodAndConstructorAnalysis {
- pod: PodAnalysis {
- bases,
- kind: TypeKind::Abstract,
- castable_bases,
- field_deps,
- field_info,
- is_generic,
- in_anonymous_namespace,
- },
- constructors,
- },
- name,
- details,
- }
- }
- _ => api,
- }
- })
- .collect()
- }
-
// We also need to remove any constructors belonging to these
// abstract types.
apis.retain(|api| {
!matches!(&api,
- Api::Function {
- analysis:
- FnAnalysis {
- kind: FnKind::Method{impl_for: self_ty, method_kind: MethodKind::Constructor{..}, ..}
- | FnKind::TraitMethod{ kind: TraitMethodKind::CopyConstructor | TraitMethodKind::MoveConstructor, impl_for: self_ty, ..},
+ Api::Function {
+ analysis:
+ FnAnalysis {
+ kind: FnKind::Method{impl_for: self_ty, method_kind: MethodKind::Constructor{..}, ..}
+ | FnKind::TraitMethod{ kind: TraitMethodKind::CopyConstructor | TraitMethodKind::MoveConstructor, impl_for: self_ty, ..},
+ ..
+ },
..
- },
- ..
- } if abstract_types.contains(self_ty))
+ } if abstract_classes.contains(self_ty)
+ )
});
// Finally, if there are any types which are nested inside other types,
@@ -143,10 +204,11 @@
.map(|n| n.contains("::"))
.unwrap_or_default() =>
{
- Err(ConvertError::AbstractNestedType)
+ Err(ConvertErrorFromCpp::AbstractNestedType)
}
_ => Ok(Box::new(std::iter::once(api))),
});
+
results
}
diff --git a/engine/src/conversion/analysis/allocators.rs b/engine/src/conversion/analysis/allocators.rs
index 3695de0..da25a77 100644
--- a/engine/src/conversion/analysis/allocators.rs
+++ b/engine/src/conversion/analysis/allocators.rs
@@ -12,9 +12,13 @@
use crate::{
conversion::{
- api::{Api, ApiName, CppVisibility, FuncToConvert, Provenance, References, TraitSynthesis},
+ api::{
+ Api, ApiName, CppVisibility, DeletedOrDefaulted, FuncToConvert, Provenance, References,
+ TraitSynthesis,
+ },
apivec::ApiVec,
},
+ minisyn::minisynize_punctuated,
types::{make_ident, QualifiedName},
};
@@ -73,8 +77,8 @@
fun: Box::new(FuncToConvert {
ident,
doc_attrs: Vec::new(),
- inputs,
- output,
+ inputs: minisynize_punctuated(inputs),
+ output: output.into(),
vis: parse_quote! { pub },
virtualness: crate::conversion::api::Virtualness::None,
cpp_vis: CppVisibility::Public,
@@ -86,7 +90,7 @@
synthesized_this_type: None,
synthetic_cpp: Some((cpp_function_body, CppFunctionKind::Function)),
add_to_trait: Some(synthesis),
- is_deleted: false,
+ is_deleted: DeletedOrDefaulted::Neither,
provenance: Provenance::SynthesizedOther,
variadic: false,
}),
diff --git a/engine/src/conversion/analysis/casts.rs b/engine/src/conversion/analysis/casts.rs
index 5f493f9..2426a50 100644
--- a/engine/src/conversion/analysis/casts.rs
+++ b/engine/src/conversion/analysis/casts.rs
@@ -6,13 +6,17 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
+use crate::minisyn::FnArg;
use itertools::Itertools;
use quote::quote;
-use syn::{parse_quote, FnArg};
+use syn::parse_quote;
use crate::{
conversion::{
- api::{Api, ApiName, CastMutability, Provenance, References, TraitSynthesis},
+ api::{
+ Api, ApiName, CastMutability, DeletedOrDefaulted, Provenance, References,
+ TraitSynthesis,
+ },
apivec::ApiVec,
},
types::{make_ident, QualifiedName},
@@ -112,7 +116,7 @@
mutable,
}),
synthetic_cpp: Some((CppFunctionBody::Cast, CppFunctionKind::Function)),
- is_deleted: false,
+ is_deleted: DeletedOrDefaulted::Neither,
provenance: Provenance::SynthesizedOther,
variadic: false,
}),
diff --git a/engine/src/conversion/analysis/ctypes.rs b/engine/src/conversion/analysis/ctypes.rs
index b2ce840..bc6f096 100644
--- a/engine/src/conversion/analysis/ctypes.rs
+++ b/engine/src/conversion/analysis/ctypes.rs
@@ -8,7 +8,7 @@
use indexmap::map::IndexMap as HashMap;
-use syn::Ident;
+use crate::minisyn::Ident;
use crate::conversion::api::ApiName;
use crate::conversion::apivec::ApiVec;
diff --git a/engine/src/conversion/analysis/deps.rs b/engine/src/conversion/analysis/deps.rs
index 7aca96c..a301090 100644
--- a/engine/src/conversion/analysis/deps.rs
+++ b/engine/src/conversion/analysis/deps.rs
@@ -6,8 +6,6 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use itertools::Itertools;
-
use crate::{
conversion::api::{Api, TypeKind},
types::QualifiedName,
@@ -22,10 +20,6 @@
pub(crate) trait HasDependencies {
fn name(&self) -> &QualifiedName;
fn deps(&self) -> Box<dyn Iterator<Item = &QualifiedName> + '_>;
-
- fn format_deps(&self) -> String {
- self.deps().join(",")
- }
}
impl HasDependencies for Api<FnPrePhase1> {
@@ -37,13 +31,9 @@
..
} => Box::new(old_tyname.iter().chain(deps.iter())),
Api::Struct {
- analysis:
- PodAnalysis {
- kind: TypeKind::Pod,
- bases,
- field_deps,
- ..
- },
+ analysis: PodAnalysis {
+ bases, field_deps, ..
+ },
..
} => Box::new(field_deps.iter().chain(bases.iter())),
Api::Function { analysis, .. } => Box::new(analysis.deps.iter()),
@@ -52,7 +42,7 @@
superclass,
} => Box::new(std::iter::once(superclass)),
Api::RustSubclassFn { details, .. } => Box::new(details.dependencies.iter()),
- Api::RustFn { receiver, .. } => Box::new(receiver.iter()),
+ Api::RustFn { deps, .. } => Box::new(deps.iter()),
_ => Box::new(std::iter::empty()),
}
}
@@ -105,7 +95,7 @@
superclass,
} => Box::new(std::iter::once(superclass)),
Api::RustSubclassFn { details, .. } => Box::new(details.dependencies.iter()),
- Api::RustFn { receiver, .. } => Box::new(receiver.iter()),
+ Api::RustFn { deps, .. } => Box::new(deps.iter()),
_ => Box::new(std::iter::empty()),
}
}
diff --git a/engine/src/conversion/analysis/depth_first.rs b/engine/src/conversion/analysis/depth_first.rs
index 02459e1..baadbd7 100644
--- a/engine/src/conversion/analysis/depth_first.rs
+++ b/engine/src/conversion/analysis/depth_first.rs
@@ -14,10 +14,20 @@
use crate::types::QualifiedName;
-use super::deps::HasDependencies;
+/// A little like `HasDependencies` but accounts for only direct fiele
+/// and bases.
+pub(crate) trait HasFieldsAndBases {
+ fn name(&self) -> &QualifiedName;
+ /// Return field and base class dependencies of this item.
+ /// This should only include those items where a definition is required,
+ /// not merely a declaration. So if the field type is
+ /// `std::unique_ptr<A>`, this should only return `std::unique_ptr`.
+ fn field_and_base_deps(&self) -> Box<dyn Iterator<Item = &QualifiedName> + '_>;
+}
-/// Return APIs in a depth-first order, i.e. those with no dependencies first.
-pub(super) fn depth_first<'a, T: HasDependencies + Debug + 'a>(
+/// Iterate through APIs in an order such that later APIs have no fields or bases
+/// other than those whose types have already been processed.
+pub(super) fn fields_and_bases_first<'a, T: HasFieldsAndBases + Debug + 'a>(
inputs: impl Iterator<Item = &'a T> + 'a,
) -> impl Iterator<Item = &'a T> {
let queue: VecDeque<_> = inputs.collect();
@@ -25,18 +35,21 @@
DepthFirstIter { queue, yet_to_do }
}
-struct DepthFirstIter<'a, T: HasDependencies + Debug> {
+struct DepthFirstIter<'a, T: HasFieldsAndBases + Debug> {
queue: VecDeque<&'a T>,
yet_to_do: HashSet<&'a QualifiedName>,
}
-impl<'a, T: HasDependencies + Debug> Iterator for DepthFirstIter<'a, T> {
+impl<'a, T: HasFieldsAndBases + Debug> Iterator for DepthFirstIter<'a, T> {
type Item = &'a T;
fn next(&mut self) -> Option<Self::Item> {
let first_candidate = self.queue.get(0).map(|api| api.name());
while let Some(candidate) = self.queue.pop_front() {
- if !candidate.deps().any(|d| self.yet_to_do.contains(&d)) {
+ if !candidate
+ .field_and_base_deps()
+ .any(|d| self.yet_to_do.contains(&d))
+ {
self.yet_to_do.remove(candidate.name());
return Some(candidate);
}
@@ -46,7 +59,11 @@
"Failed to find a candidate; there must be a circular dependency. Queue is {}",
self.queue
.iter()
- .map(|item| format!("{}: {}", item.name(), item.deps().join(",")))
+ .map(|item| format!(
+ "{}: {}",
+ item.name(),
+ item.field_and_base_deps().join(",")
+ ))
.join("\n")
);
}
@@ -59,17 +76,17 @@
mod test {
use crate::types::QualifiedName;
- use super::{depth_first, HasDependencies};
+ use super::{fields_and_bases_first, HasFieldsAndBases};
#[derive(Debug)]
struct Thing(QualifiedName, Vec<QualifiedName>);
- impl HasDependencies for Thing {
+ impl HasFieldsAndBases for Thing {
fn name(&self) -> &QualifiedName {
&self.0
}
- fn deps(&self) -> Box<dyn Iterator<Item = &QualifiedName> + '_> {
+ fn field_and_base_deps(&self) -> Box<dyn Iterator<Item = &QualifiedName> + '_> {
Box::new(self.1.iter())
}
}
@@ -89,7 +106,7 @@
vec![QualifiedName::new_from_cpp_name("a")],
);
let api_list = vec![a, b, c];
- let mut it = depth_first(api_list.iter());
+ let mut it = fields_and_bases_first(api_list.iter());
assert_eq!(it.next().unwrap().0, QualifiedName::new_from_cpp_name("a"));
assert_eq!(it.next().unwrap().0, QualifiedName::new_from_cpp_name("c"));
assert_eq!(it.next().unwrap().0, QualifiedName::new_from_cpp_name("b"));
diff --git a/engine/src/conversion/analysis/fun/bridge_name_tracker.rs b/engine/src/conversion/analysis/fun/bridge_name_tracker.rs
index 7e81c59..c98beaf 100644
--- a/engine/src/conversion/analysis/fun/bridge_name_tracker.rs
+++ b/engine/src/conversion/analysis/fun/bridge_name_tracker.rs
@@ -104,7 +104,7 @@
*count += 1;
prefix
} else {
- let r = format!("{}_autocxx{}", prefix, count);
+ let r = format!("{prefix}_autocxx{count}");
*count += 1;
r
}
diff --git a/engine/src/conversion/analysis/fun/function_wrapper.rs b/engine/src/conversion/analysis/fun/function_wrapper.rs
index ab3b7d9..0495533 100644
--- a/engine/src/conversion/analysis/fun/function_wrapper.rs
+++ b/engine/src/conversion/analysis/fun/function_wrapper.rs
@@ -6,12 +6,13 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
+use crate::minisyn::Ident;
use crate::{
- conversion::api::SubclassName,
+ conversion::{api::SubclassName, type_helpers::extract_pinned_mutable_reference_type},
types::{Namespace, QualifiedName},
};
use quote::ToTokens;
-use syn::{parse_quote, Ident, Type, TypeReference};
+use syn::{parse_quote, Type, TypeReference};
#[derive(Clone, Debug)]
pub(crate) enum CppConversionType {
@@ -85,9 +86,9 @@
/// variant params. That would remove the possibility of various runtime
/// panics by enforcing (for example) that conversion from a pointer always
/// has a Type::Ptr.
-#[derive(Clone)]
+#[derive(Clone, Debug)]
pub(crate) struct TypeConversionPolicy {
- unwrapped_type: Type,
+ unwrapped_type: crate::minisyn::Type,
pub(crate) cpp_conversion: CppConversionType,
pub(crate) rust_conversion: RustConversionType,
}
@@ -103,7 +104,7 @@
rust_conversion: RustConversionType,
) -> Self {
Self {
- unwrapped_type: ty,
+ unwrapped_type: ty.into(),
cpp_conversion,
rust_conversion,
}
@@ -118,7 +119,14 @@
Type::Reference(TypeReference {
elem, mutability, ..
}) => (*elem, mutability.is_some()),
- _ => panic!("Not a ptr: {}", ty.to_token_stream()),
+ Type::Path(ref tp) => {
+ if let Some(unwrapped_type) = extract_pinned_mutable_reference_type(tp) {
+ (unwrapped_type.clone(), true)
+ } else {
+ panic!("Path was not a mutable reference: {}", ty.to_token_stream())
+ }
+ }
+ _ => panic!("Not a reference: {}", ty.to_token_stream()),
};
TypeConversionPolicy {
unwrapped_type: if is_mut {
@@ -133,7 +141,7 @@
pub(crate) fn new_to_unique_ptr(ty: Type) -> Self {
TypeConversionPolicy {
- unwrapped_type: ty,
+ unwrapped_type: ty.into(),
cpp_conversion: CppConversionType::FromValueToUniquePtr,
rust_conversion: RustConversionType::None,
}
@@ -141,7 +149,7 @@
pub(crate) fn new_for_placement_return(ty: Type) -> Self {
TypeConversionPolicy {
- unwrapped_type: ty,
+ unwrapped_type: ty.into(),
cpp_conversion: CppConversionType::FromReturnValueToPlacementPtr,
// Rust conversion is marked as none here, since this policy
// will be applied to the return value, and the Rust-side
@@ -157,7 +165,7 @@
pub(crate) fn unconverted_rust_type(&self) -> Type {
match self.cpp_conversion {
CppConversionType::FromValueToUniquePtr => self.make_unique_ptr_type(),
- _ => self.unwrapped_type.clone(),
+ _ => self.unwrapped_type.clone().into(),
}
}
@@ -170,7 +178,7 @@
*mut #innerty
}
}
- _ => self.unwrapped_type.clone(),
+ _ => self.unwrapped_type.clone().into(),
}
}
@@ -222,7 +230,7 @@
}
}
-#[derive(Clone)]
+#[derive(Clone, Debug)]
pub(crate) enum CppFunctionBody {
FunctionCall(Namespace, Ident),
StaticMethodCall(Namespace, Ident, Ident),
@@ -234,7 +242,7 @@
FreeUninitialized(QualifiedName),
}
-#[derive(Clone)]
+#[derive(Clone, Debug)]
pub(crate) enum CppFunctionKind {
Function,
Method,
@@ -243,10 +251,10 @@
SynthesizedConstructor,
}
-#[derive(Clone)]
+#[derive(Clone, Debug)]
pub(crate) struct CppFunction {
pub(crate) payload: CppFunctionBody,
- pub(crate) wrapper_function_name: Ident,
+ pub(crate) wrapper_function_name: crate::minisyn::Ident,
pub(crate) original_cpp_name: String,
pub(crate) return_conversion: Option<TypeConversionPolicy>,
pub(crate) argument_conversion: Vec<TypeConversionPolicy>,
diff --git a/engine/src/conversion/analysis/fun/implicit_constructors.rs b/engine/src/conversion/analysis/fun/implicit_constructors.rs
index 469ed89..a1ebf27 100644
--- a/engine/src/conversion/analysis/fun/implicit_constructors.rs
+++ b/engine/src/conversion/analysis/fun/implicit_constructors.rs
@@ -11,13 +11,16 @@
use syn::{Type, TypeArray};
+use crate::conversion::api::DeletedOrDefaulted;
use crate::{
conversion::{
- analysis::{depth_first::depth_first, pod::PodAnalysis, type_converter::TypeKind},
+ analysis::{
+ depth_first::fields_and_bases_first, pod::PodAnalysis, type_converter::TypeKind,
+ },
api::{Api, ApiName, CppVisibility, FuncToConvert, SpecialMemberKind},
apivec::ApiVec,
convert_error::ConvertErrorWithContext,
- ConvertError,
+ ConvertErrorFromCpp,
},
known_types::{known_types, KnownTypeConstructorDetails},
types::QualifiedName,
@@ -177,6 +180,13 @@
apis: &ApiVec<FnPrePhase1>,
) -> HashMap<QualifiedName, ItemsFound> {
let (explicits, unknown_types) = find_explicit_items(apis);
+ let enums: HashSet<QualifiedName> = apis
+ .iter()
+ .filter_map(|api| match api {
+ Api::Enum { name, .. } => Some(name.name.clone()),
+ _ => None,
+ })
+ .collect();
// These contain all the classes we've seen so far with the relevant properties on their
// constructors of each kind. We iterate via [`depth_first`], so analyzing later classes
@@ -189,7 +199,7 @@
// These analyses include all bases and members of each class.
let mut all_items_found: HashMap<QualifiedName, ItemsFound> = HashMap::new();
- for api in depth_first(apis.iter()) {
+ for api in fields_and_bases_first(apis.iter()) {
if let Api::Struct {
name,
analysis:
@@ -211,7 +221,17 @@
})
};
let get_items_found = |qn: &QualifiedName| -> Option<ItemsFound> {
- if let Some(constructor_details) = known_types().get_constructor_details(qn) {
+ if enums.contains(qn) {
+ Some(ItemsFound {
+ default_constructor: SpecialMemberFound::NotPresent,
+ destructor: SpecialMemberFound::Implicit,
+ const_copy_constructor: SpecialMemberFound::Implicit,
+ non_const_copy_constructor: SpecialMemberFound::NotPresent,
+ move_constructor: SpecialMemberFound::Implicit,
+ name: Some(name.clone()),
+ })
+ } else if let Some(constructor_details) = known_types().get_constructor_details(qn)
+ {
Some(known_type_items_found(constructor_details))
} else {
all_items_found.get(qn).cloned()
@@ -539,8 +559,7 @@
all_items_found
.insert(name.name.clone(), items_found)
.is_none(),
- "Duplicate struct: {:?}",
- name
+ "Duplicate struct: {name:?}"
);
}
}
@@ -556,7 +575,7 @@
.entry(ExplicitType { ty, kind })
{
Entry::Vacant(entry) => {
- entry.insert(if fun.is_deleted {
+ entry.insert(if matches!(fun.is_deleted, DeletedOrDefaulted::Deleted) {
ExplicitFound::Deleted
} else {
ExplicitFound::UserDefined(fun.cpp_vis)
@@ -575,7 +594,8 @@
kind: FnKind::Method { impl_for, .. },
param_details,
ignore_reason:
- Ok(()) | Err(ConvertErrorWithContext(ConvertError::AssignmentOperator, _)),
+ Ok(())
+ | Err(ConvertErrorWithContext(ConvertErrorFromCpp::AssignmentOperator, _)),
..
},
fun,
diff --git a/engine/src/conversion/analysis/fun/mod.rs b/engine/src/conversion/analysis/fun/mod.rs
index 7194746..415e40a 100644
--- a/engine/src/conversion/analysis/fun/mod.rs
+++ b/engine/src/conversion/analysis/fun/mod.rs
@@ -19,9 +19,9 @@
type_converter::{self, add_analysis, TypeConversionContext, TypeConverter},
},
api::{
- ApiName, CastMutability, CppVisibility, FuncToConvert, NullPhase, Provenance,
- References, SpecialMemberKind, SubclassName, TraitImplSignature, TraitSynthesis,
- UnsafetyNeeded, Virtualness,
+ ApiName, CastMutability, CppVisibility, DeletedOrDefaulted, FuncToConvert, NullPhase,
+ Provenance, References, SpecialMemberKind, SubclassName, TraitImplSignature,
+ TraitSynthesis, UnsafetyNeeded, Virtualness,
},
apivec::ApiVec,
convert_error::ErrorContext,
@@ -29,6 +29,7 @@
error_reporter::{convert_apis, report_any_error},
},
known_types::known_types,
+ minisyn::minisynize_punctuated,
types::validate_ident_ok_for_rust,
};
use indexmap::map::IndexMap as HashMap;
@@ -40,14 +41,14 @@
use proc_macro2::Span;
use quote::quote;
use syn::{
- parse_quote, punctuated::Punctuated, token::Comma, FnArg, Ident, Pat, ReturnType, Type,
- TypePath, TypePtr, TypeReference, Visibility,
+ parse_quote, punctuated::Punctuated, token::Comma, FnArg, Ident, Pat, PatType, ReturnType,
+ Type, TypePath, TypePtr, TypeReference, Visibility,
};
use crate::{
conversion::{
api::{AnalysisPhase, Api, TypeKind},
- ConvertError,
+ ConvertErrorFromCpp,
},
types::{make_ident, validate_ident_ok_for_cxx, Namespace, QualifiedName},
};
@@ -64,13 +65,14 @@
};
use super::{
+ depth_first::HasFieldsAndBases,
doc_label::make_doc_attrs,
pod::{PodAnalysis, PodPhase},
tdef::TypedefAnalysis,
type_converter::{Annotated, PointerTreatment},
};
-#[derive(Clone, Debug)]
+#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
pub(crate) enum ReceiverMutability {
Const,
Mutable,
@@ -85,7 +87,7 @@
PureVirtual(ReceiverMutability),
}
-#[derive(Clone)]
+#[derive(Clone, Debug)]
pub(crate) enum TraitMethodKind {
CopyConstructor,
MoveConstructor,
@@ -95,11 +97,11 @@
Dealloc,
}
-#[derive(Clone)]
+#[derive(Clone, Debug)]
pub(crate) struct TraitMethodDetails {
pub(crate) trt: TraitImplSignature,
pub(crate) avoid_self: bool,
- pub(crate) method_name: Ident,
+ pub(crate) method_name: crate::minisyn::Ident,
/// For traits, where we're trying to implement a specific existing
/// interface, we may need to reorder the parameters to fit that
/// interface.
@@ -109,7 +111,7 @@
pub(crate) trait_call_is_unsafe: bool,
}
-#[derive(Clone)]
+#[derive(Clone, Debug)]
pub(crate) enum FnKind {
Function,
Method {
@@ -129,31 +131,31 @@
/// Strategy for ensuring that the final, callable, Rust name
/// is what the user originally expected.
-#[derive(Clone)]
+#[derive(Clone, Debug)]
pub(crate) enum RustRenameStrategy {
/// cxx::bridge name matches user expectations
None,
/// Even the #[rust_name] attribute would cause conflicts, and we need
/// to use a 'use XYZ as ABC'
- RenameInOutputMod(Ident),
+ RenameInOutputMod(crate::minisyn::Ident),
/// This function requires us to generate a Rust function to do
/// parameter conversion.
RenameUsingWrapperFunction,
}
-#[derive(Clone)]
+#[derive(Clone, Debug)]
pub(crate) struct FnAnalysis {
/// Each entry in the cxx::bridge needs to have a unique name, even if
/// (from the perspective of Rust and C++) things are in different
/// namespaces/mods.
- pub(crate) cxxbridge_name: Ident,
+ pub(crate) cxxbridge_name: crate::minisyn::Ident,
/// ... so record also the name under which we wish to expose it in Rust.
pub(crate) rust_name: String,
pub(crate) rust_rename_strategy: RustRenameStrategy,
pub(crate) params: Punctuated<FnArg, Comma>,
pub(crate) kind: FnKind,
- pub(crate) ret_type: ReturnType,
+ pub(crate) ret_type: crate::minisyn::ReturnType,
pub(crate) param_details: Vec<ArgumentAnalysis>,
pub(crate) ret_conversion: Option<TypeConversionPolicy>,
pub(crate) requires_unsafe: UnsafetyNeeded,
@@ -172,12 +174,13 @@
pub(crate) rust_wrapper_needed: bool,
}
-#[derive(Clone)]
+#[derive(Clone, Debug)]
pub(crate) struct ArgumentAnalysis {
pub(crate) conversion: TypeConversionPolicy,
- pub(crate) name: Pat,
+ pub(crate) name: crate::minisyn::Pat,
pub(crate) self_type: Option<(QualifiedName, ReceiverMutability)>,
pub(crate) has_lifetime: bool,
+ pub(crate) is_mutable_reference: bool,
pub(crate) deps: HashSet<QualifiedName>,
pub(crate) requires_unsafe: UnsafetyNeeded,
pub(crate) is_placement_return_destination: bool,
@@ -187,6 +190,7 @@
rt: ReturnType,
conversion: Option<TypeConversionPolicy>,
was_reference: bool,
+ was_mutable_reference: bool,
deps: HashSet<QualifiedName>,
placement_param_needed: Option<(FnArg, ArgumentAnalysis)>,
}
@@ -197,12 +201,14 @@
rt: parse_quote! {},
conversion: None,
was_reference: false,
+ was_mutable_reference: false,
deps: Default::default(),
placement_param_needed: None,
}
}
}
+#[derive(std::fmt::Debug)]
pub(crate) struct PodAndConstructorAnalysis {
pub(crate) pod: PodAnalysis,
pub(crate) constructors: PublicConstructors,
@@ -210,6 +216,7 @@
/// An analysis phase where we've analyzed each function, but
/// haven't yet determined which constructors/etc. belong to each type.
+#[derive(std::fmt::Debug)]
pub(crate) struct FnPrePhase1;
impl AnalysisPhase for FnPrePhase1 {
@@ -220,6 +227,7 @@
/// An analysis phase where we've analyzed each function, and identified
/// what implicit constructors/destructors are present in each type.
+#[derive(std::fmt::Debug)]
pub(crate) struct FnPrePhase2;
impl AnalysisPhase for FnPrePhase2 {
@@ -228,6 +236,7 @@
type FunAnalysis = FnAnalysis;
}
+#[derive(Debug)]
pub(crate) struct PodAndDepAnalysis {
pub(crate) pod: PodAnalysis,
pub(crate) constructor_and_allocator_deps: Vec<QualifiedName>,
@@ -236,6 +245,7 @@
/// Analysis phase after we've finished analyzing functions and determined
/// which constructors etc. belong to them.
+#[derive(std::fmt::Debug)]
pub(crate) struct FnPhase;
/// Indicates which kinds of public constructors are known to exist for a type.
@@ -283,6 +293,7 @@
generic_types: HashSet<QualifiedName>,
types_in_anonymous_namespace: HashSet<QualifiedName>,
existing_superclass_trait_api_names: HashSet<QualifiedName>,
+ force_wrapper_generation: bool,
}
impl<'a> FnAnalyzer<'a> {
@@ -290,6 +301,7 @@
apis: ApiVec<PodPhase>,
unsafe_policy: &'a UnsafePolicy,
config: &'a IncludeCppConfig,
+ force_wrapper_generation: bool,
) -> ApiVec<FnPrePhase2> {
let mut me = Self {
unsafe_policy,
@@ -305,6 +317,7 @@
generic_types: Self::build_generic_type_set(&apis),
existing_superclass_trait_api_names: HashSet::new(),
types_in_anonymous_namespace: Self::build_types_in_anonymous_namespace(&apis),
+ force_wrapper_generation,
};
let mut results = ApiVec::new();
convert_apis(
@@ -427,7 +440,7 @@
ty: Box<Type>,
ns: &Namespace,
pointer_treatment: PointerTreatment,
- ) -> Result<Annotated<Box<Type>>, ConvertError> {
+ ) -> Result<Annotated<Box<Type>>, ConvertErrorFromCpp> {
let ctx = TypeConversionContext::OuterType { pointer_treatment };
let mut annotated = self.type_converter.convert_boxed_type(ty, ns, &ctx)?;
self.extra_apis.append(&mut annotated.extra_apis);
@@ -513,7 +526,7 @@
**fun,
FuncToConvert {
special_member: Some(SpecialMemberKind::Destructor),
- is_deleted: false,
+ is_deleted: DeletedOrDefaulted::Neither | DeletedOrDefaulted::Defaulted,
cpp_vis: CppVisibility::Public,
..
}
@@ -741,6 +754,10 @@
sophistication,
false,
)
+ .map_err(|err| ConvertErrorFromCpp::Argument {
+ arg: describe_arg(i),
+ err: Box::new(err),
+ })
})
.partition(Result::is_ok);
let (mut params, mut param_details): (Punctuated<_, Comma>, Vec<_>) =
@@ -775,7 +792,7 @@
if initial_rust_name.ends_with('_') {
initial_rust_name // case 2
} else if validate_ident_ok_for_rust(cpp_name).is_err() {
- format!("{}_", cpp_name) // case 5
+ format!("{cpp_name}_") // case 5
} else {
cpp_name.to_string() // cases 3, 4, 6
}
@@ -829,7 +846,7 @@
let is_move =
matches!(fun.special_member, Some(SpecialMemberKind::MoveConstructor));
if let Some(constructor_suffix) = rust_name.strip_prefix(nested_type_ident) {
- rust_name = format!("new{}", constructor_suffix);
+ rust_name = format!("new{constructor_suffix}");
}
rust_name = predetermined_rust_name
.unwrap_or_else(|| self.get_overload_name(ns, type_ident, rust_name));
@@ -868,7 +885,7 @@
impl_for: self_ty,
details: Box::new(TraitMethodDetails {
trt: TraitImplSignature {
- ty,
+ ty: ty.into(),
trait_signature: parse_quote! {
autocxx::moveit::new:: #trait_id
},
@@ -904,7 +921,7 @@
impl_for: self_ty,
details: Box::new(TraitMethodDetails {
trt: TraitImplSignature {
- ty,
+ ty: ty.into(),
trait_signature: parse_quote! {
Drop
},
@@ -934,7 +951,7 @@
// fn make_unique(...args) -> UniquePtr<Type>
// If there are multiple constructors, bindgen generates
// new, new1, new2 etc. and we'll keep those suffixes.
- rust_name = format!("new{}", constructor_suffix);
+ rust_name = format!("new{constructor_suffix}");
MethodKind::Constructor {
is_default: matches!(
fun.special_member,
@@ -1033,10 +1050,10 @@
..
} => {
if param_details.len() < 2 {
- set_ignore_reason(ConvertError::ConstructorWithOnlyOneParam);
+ set_ignore_reason(ConvertErrorFromCpp::ConstructorWithOnlyOneParam);
}
if param_details.len() > 2 {
- set_ignore_reason(ConvertError::ConstructorWithMultipleParams);
+ set_ignore_reason(ConvertErrorFromCpp::ConstructorWithMultipleParams);
}
self.reanalyze_parameter(
0,
@@ -1058,10 +1075,10 @@
..
} => {
if param_details.len() < 2 {
- set_ignore_reason(ConvertError::ConstructorWithOnlyOneParam);
+ set_ignore_reason(ConvertErrorFromCpp::ConstructorWithOnlyOneParam);
}
if param_details.len() > 2 {
- set_ignore_reason(ConvertError::ConstructorWithMultipleParams);
+ set_ignore_reason(ConvertErrorFromCpp::ConstructorWithMultipleParams);
}
self.reanalyze_parameter(
0,
@@ -1099,14 +1116,14 @@
// or note whether the type is abstract.
let externally_callable = match fun.cpp_vis {
CppVisibility::Private => {
- set_ignore_reason(ConvertError::PrivateMethod);
+ set_ignore_reason(ConvertErrorFromCpp::PrivateMethod);
false
}
CppVisibility::Protected => false,
CppVisibility::Public => true,
};
if fun.variadic {
- set_ignore_reason(ConvertError::Variadic);
+ set_ignore_reason(ConvertErrorFromCpp::Variadic);
}
if let Some(problem) = bads.into_iter().next() {
match problem {
@@ -1116,7 +1133,7 @@
} else if fun.unused_template_param {
// This indicates that bindgen essentially flaked out because templates
// were too complex.
- set_ignore_reason(ConvertError::UnusedTemplateParam)
+ set_ignore_reason(ConvertErrorFromCpp::UnusedTemplateParam)
} else if matches!(
fun.special_member,
Some(SpecialMemberKind::AssignmentOperator)
@@ -1124,11 +1141,11 @@
// Be careful with the order of this if-else tree. Anything above here means we won't
// treat it as an assignment operator, but anything below we still consider when
// deciding which other C++ special member functions are implicitly defined.
- set_ignore_reason(ConvertError::AssignmentOperator)
+ set_ignore_reason(ConvertErrorFromCpp::AssignmentOperator)
} else if fun.references.rvalue_ref_return {
- set_ignore_reason(ConvertError::RValueReturn)
- } else if fun.is_deleted {
- set_ignore_reason(ConvertError::Deleted)
+ set_ignore_reason(ConvertErrorFromCpp::RValueReturn)
+ } else if matches!(fun.is_deleted, DeletedOrDefaulted::Deleted) {
+ set_ignore_reason(ConvertErrorFromCpp::Deleted)
} else {
match kind {
FnKind::Method {
@@ -1140,21 +1157,21 @@
| MethodKind::Virtual(..),
..
} if !known_types().is_cxx_acceptable_receiver(impl_for) => {
- set_ignore_reason(ConvertError::UnsupportedReceiver);
+ set_ignore_reason(ConvertErrorFromCpp::UnsupportedReceiver);
}
FnKind::Method { ref impl_for, .. } if !self.is_on_allowlist(impl_for) => {
// Bindgen will output methods for types which have been encountered
// virally as arguments on other allowlisted types. But we don't want
// to generate methods unless the user has specifically asked us to.
// It may, for instance, be a private type.
- set_ignore_reason(ConvertError::MethodOfNonAllowlistedType);
+ set_ignore_reason(ConvertErrorFromCpp::MethodOfNonAllowlistedType);
}
FnKind::Method { ref impl_for, .. } | FnKind::TraitMethod { ref impl_for, .. } => {
if self.is_generic_type(impl_for) {
- set_ignore_reason(ConvertError::MethodOfGenericType);
+ set_ignore_reason(ConvertErrorFromCpp::MethodOfGenericType);
}
if self.types_in_anonymous_namespace.contains(impl_for) {
- set_ignore_reason(ConvertError::MethodInAnonymousNamespace);
+ set_ignore_reason(ConvertErrorFromCpp::MethodInAnonymousNamespace);
}
}
_ => {}
@@ -1199,12 +1216,46 @@
let requires_unsafe = self.should_be_unsafe(¶m_details, &kind);
- let num_input_references = param_details.iter().filter(|pd| pd.has_lifetime).count();
- if num_input_references != 1 && return_analysis.was_reference {
+ // The following sections reject some types of function because of the arrangement
+ // of Rust references. We could lift these restrictions when/if we switch to using
+ // CppRef to represent C++ references.
+ if return_analysis.was_reference {
// cxx only allows functions to return a reference if they take exactly
- // one reference as a parameter. Let's see...
- set_ignore_reason(ConvertError::NotOneInputReference(rust_name.clone()));
+ // one reference as a parameter. Let's see.
+ let num_input_references = param_details.iter().filter(|pd| pd.has_lifetime).count();
+ if num_input_references == 0 {
+ set_ignore_reason(ConvertErrorFromCpp::NoInputReference(rust_name.clone()));
+ }
+ if num_input_references > 1 {
+ set_ignore_reason(ConvertErrorFromCpp::MultipleInputReferences(
+ rust_name.clone(),
+ ));
+ }
}
+ if return_analysis.was_mutable_reference {
+ // This one's a bit more subtle. We can't have:
+ // fn foo(thing: &Thing) -> &mut OtherThing
+ // because Rust doesn't allow it.
+ // We could probably allow:
+ // fn foo(thing: &mut Thing, thing2: &mut OtherThing) -> &mut OtherThing
+ // but probably cxx doesn't allow that. (I haven't checked). Even if it did,
+ // there's ambiguity here so won't allow it.
+ let num_input_mutable_references = param_details
+ .iter()
+ .filter(|pd| pd.has_lifetime && pd.is_mutable_reference)
+ .count();
+ if num_input_mutable_references == 0 {
+ set_ignore_reason(ConvertErrorFromCpp::NoMutableInputReference(
+ rust_name.clone(),
+ ));
+ }
+ if num_input_mutable_references > 1 {
+ set_ignore_reason(ConvertErrorFromCpp::MultipleMutableInputReferences(
+ rust_name.clone(),
+ ));
+ }
+ }
+
let mut ret_type = return_analysis.rt;
let ret_type_conversion = return_analysis.conversion;
@@ -1248,19 +1299,23 @@
_ if ret_type_conversion_needed => true,
_ if cpp_name_incompatible_with_cxx => true,
_ if fun.synthetic_cpp.is_some() => true,
+ _ if self.force_wrapper_generation => true,
_ => false,
};
let cpp_wrapper = if wrapper_function_needed {
// Generate a new layer of C++ code to wrap/unwrap parameters
// and return values into/out of std::unique_ptrs.
- let cpp_construction_ident = make_ident(&effective_cpp_name);
+ let cpp_construction_ident = make_ident(effective_cpp_name);
let joiner = if cxxbridge_name.to_string().ends_with('_') {
""
} else {
"_"
};
- cxxbridge_name = make_ident(&format!("{}{}autocxx_wrapper", cxxbridge_name, joiner));
+ cxxbridge_name = make_ident(
+ self.config
+ .uniquify_name_per_mod(&format!("{cxxbridge_name}{joiner}autocxx_wrapper")),
+ );
let (payload, cpp_function_kind) = match fun.synthetic_cpp.as_ref().cloned() {
Some((payload, cpp_function_kind)) => (payload, cpp_function_kind),
None => match kind {
@@ -1321,10 +1376,10 @@
params.clear();
for pd in ¶m_details {
let type_name = pd.conversion.converted_rust_type();
- let arg_name = if pd.self_type.is_some() {
+ let arg_name: syn::Pat = if pd.self_type.is_some() {
parse_quote!(autocxx_gen_this)
} else {
- pd.name.clone()
+ pd.name.clone().into()
};
params.push(parse_quote!(
#arg_name: #type_name
@@ -1358,12 +1413,15 @@
_ if any_param_needs_rust_conversion || return_needs_rust_conversion => true,
FnKind::TraitMethod { .. } => true,
FnKind::Method { .. } => cxxbridge_name != rust_name,
+ _ if self.force_wrapper_generation => true,
_ => false,
};
// Naming, part two.
// Work out our final naming strategy.
- validate_ident_ok_for_cxx(&cxxbridge_name.to_string()).unwrap_or_else(set_ignore_reason);
+ validate_ident_ok_for_cxx(&cxxbridge_name.to_string())
+ .map_err(ConvertErrorFromCpp::InvalidIdent)
+ .unwrap_or_else(set_ignore_reason);
let rust_name_ident = make_ident(&rust_name);
let rust_rename_strategy = match kind {
_ if rust_wrapper_needed => RustRenameStrategy::RenameUsingWrapperFunction,
@@ -1380,10 +1438,10 @@
params,
ret_conversion: ret_type_conversion,
kind,
- ret_type,
+ ret_type: ret_type.into(),
param_details,
requires_unsafe,
- vis,
+ vis: vis.into(),
cpp_wrapper,
deps,
ignore_reason,
@@ -1422,7 +1480,7 @@
sophistication: TypeConversionSophistication,
construct_into_self: bool,
is_move_constructor: bool,
- ) -> Result<(), ConvertError> {
+ ) -> Result<(), ConvertErrorFromCpp> {
self.convert_fn_arg(
fun.inputs.iter().nth(param_idx).unwrap(),
ns,
@@ -1484,7 +1542,7 @@
AsRef < #to_type >
},
parse_quote! {
- &'a mut ::std::pin::Pin < &'a mut #from_type_path >
+ &'a mut ::core::pin::Pin < &'a mut #from_type_path >
},
"as_ref",
),
@@ -1493,7 +1551,7 @@
autocxx::PinMut < #to_type >
},
parse_quote! {
- ::std::pin::Pin < &'a mut #from_type_path >
+ ::core::pin::Pin < &'a mut #from_type_path >
},
"pin_mut",
),
@@ -1505,7 +1563,7 @@
impl_for: from_type.clone(),
details: Box::new(TraitMethodDetails {
trt: TraitImplSignature {
- ty,
+ ty: ty.into(),
trait_signature,
unsafety: None,
},
@@ -1549,7 +1607,7 @@
impl_for: ty.clone(),
details: Box::new(TraitMethodDetails {
trt: TraitImplSignature {
- ty: Type::Path(typ),
+ ty: Type::Path(typ).into(),
trait_signature: parse_quote! { autocxx::moveit::MakeCppStorage },
unsafety: Some(parse_quote! { unsafe }),
},
@@ -1590,7 +1648,7 @@
force_rust_conversion: Option<RustConversionType>,
sophistication: TypeConversionSophistication,
construct_into_self: bool,
- ) -> Result<(FnArg, ArgumentAnalysis), ConvertError> {
+ ) -> Result<(FnArg, ArgumentAnalysis), ConvertErrorFromCpp> {
Ok(match arg {
FnArg::Typed(pt) => {
let mut pt = pt.clone();
@@ -1627,12 +1685,11 @@
};
Ok((this_type, receiver_mutability))
}
- _ => Err(ConvertError::UnexpectedThisType(QualifiedName::new(
- ns,
- make_ident(fn_name),
- ))),
+ _ => Err(ConvertErrorFromCpp::UnexpectedThisType(
+ QualifiedName::new(ns, make_ident(fn_name)),
+ )),
},
- _ => Err(ConvertError::UnexpectedThisType(QualifiedName::new(
+ _ => Err(ConvertErrorFromCpp::UnexpectedThisType(QualifiedName::new(
ns,
make_ident(fn_name),
))),
@@ -1646,8 +1703,9 @@
syn::Pat::Ident(pp)
}
syn::Pat::Ident(pp) => {
- validate_ident_ok_for_cxx(&pp.ident.to_string())?;
- pointer_treatment = references.param_treatment(&pp.ident);
+ validate_ident_ok_for_cxx(&pp.ident.to_string())
+ .map_err(ConvertErrorFromCpp::InvalidIdent)?;
+ pointer_treatment = references.param_treatment(&pp.ident.clone().into());
syn::Pat::Ident(pp)
}
_ => old_pat,
@@ -1684,13 +1742,17 @@
FnArg::Typed(pt),
ArgumentAnalysis {
self_type,
- name: new_pat,
+ name: new_pat.into(),
conversion,
has_lifetime: matches!(
annotated_type.kind,
type_converter::TypeKind::Reference
| type_converter::TypeKind::MutableReference
),
+ is_mutable_reference: matches!(
+ annotated_type.kind,
+ type_converter::TypeKind::MutableReference
+ ),
deps: annotated_type.types_encountered,
requires_unsafe,
is_placement_return_destination,
@@ -1859,7 +1921,7 @@
ns: &Namespace,
references: &References,
sophistication: TypeConversionSophistication,
- ) -> Result<ReturnTypeAnalysis, ConvertError> {
+ ) -> Result<ReturnTypeAnalysis, ConvertErrorFromCpp> {
Ok(match rt {
ReturnType::Default => ReturnTypeAnalysis::default(),
ReturnType::Type(rarrow, boxed_type) => {
@@ -1902,9 +1964,9 @@
conversion: Some(TypeConversionPolicy::new_for_placement_return(
ty.clone(),
)),
- was_reference: false,
deps: annotated_type.types_encountered,
placement_param_needed: Some((fnarg, analysis)),
+ ..Default::default()
}
} else {
// There are some types which we can't currently represent within a moveit::new::New.
@@ -1917,14 +1979,18 @@
ReturnTypeAnalysis {
rt: ReturnType::Type(*rarrow, boxed_type),
conversion,
- was_reference: false,
deps: annotated_type.types_encountered,
- placement_param_needed: None,
+ ..Default::default()
}
}
}
_ => {
- let was_reference = references.ref_return;
+ let was_mutable_reference = matches!(
+ annotated_type.kind,
+ type_converter::TypeKind::MutableReference
+ );
+ let was_reference = was_mutable_reference
+ || matches!(annotated_type.kind, type_converter::TypeKind::Reference);
let conversion = Some(
if was_reference
&& matches!(
@@ -1941,6 +2007,7 @@
rt: ReturnType::Type(*rarrow, boxed_type),
conversion,
was_reference,
+ was_mutable_reference,
deps: annotated_type.types_encountered,
placement_param_needed: None,
}
@@ -2091,9 +2158,12 @@
Box::new(FuncToConvert {
self_ty: Some(self_ty.clone()),
ident,
- doc_attrs: make_doc_attrs(format!("Synthesized {}.", special_member)),
- inputs,
- output: ReturnType::Default,
+ doc_attrs: make_doc_attrs(format!("Synthesized {special_member}."))
+ .into_iter()
+ .map(Into::into)
+ .collect(),
+ inputs: minisynize_punctuated(inputs),
+ output: ReturnType::Default.into(),
vis: parse_quote! { pub },
virtualness: Virtualness::None,
cpp_vis: CppVisibility::Public,
@@ -2102,7 +2172,7 @@
references,
original_name: None,
synthesized_this_type: None,
- is_deleted: false,
+ is_deleted: DeletedOrDefaulted::Neither,
add_to_trait: None,
synthetic_cpp: None,
provenance: Provenance::SynthesizedOther,
@@ -2192,7 +2262,7 @@
)
}
- pub(crate) fn cxxbridge_name(&self) -> Option<Ident> {
+ pub(crate) fn cxxbridge_name(&self) -> Option<crate::minisyn::Ident> {
match self {
Api::Function { ref analysis, .. } => Some(analysis.cxxbridge_name.clone()),
Api::StringConstructor { .. }
@@ -2224,3 +2294,60 @@
_ => panic!("did not find angle bracketed args"),
}
}
+
+impl HasFieldsAndBases for Api<FnPrePhase1> {
+ fn name(&self) -> &QualifiedName {
+ self.name()
+ }
+
+ fn field_and_base_deps(&self) -> Box<dyn Iterator<Item = &QualifiedName> + '_> {
+ match self {
+ Api::Struct {
+ analysis:
+ PodAnalysis {
+ field_definition_deps,
+ bases,
+ ..
+ },
+ ..
+ } => Box::new(field_definition_deps.iter().chain(bases.iter())),
+ _ => Box::new(std::iter::empty()),
+ }
+ }
+}
+
+impl HasFieldsAndBases for Api<FnPrePhase2> {
+ fn name(&self) -> &QualifiedName {
+ self.name()
+ }
+
+ fn field_and_base_deps(&self) -> Box<dyn Iterator<Item = &QualifiedName> + '_> {
+ match self {
+ Api::Struct {
+ analysis:
+ PodAndConstructorAnalysis {
+ pod:
+ PodAnalysis {
+ field_definition_deps,
+ bases,
+ ..
+ },
+ ..
+ },
+ ..
+ } => Box::new(field_definition_deps.iter().chain(bases.iter())),
+ _ => Box::new(std::iter::empty()),
+ }
+ }
+}
+
+/// Stringify a function argument for diagnostics
+fn describe_arg(arg: &FnArg) -> String {
+ match arg {
+ FnArg::Receiver(_) => "the function receiver (this/self paramter)".into(),
+ FnArg::Typed(PatType { pat, .. }) => match pat.as_ref() {
+ Pat::Ident(pti) => pti.ident.to_string(),
+ _ => "another argument we don't know how to describe".into(),
+ },
+ }
+}
diff --git a/engine/src/conversion/analysis/fun/overload_tracker.rs b/engine/src/conversion/analysis/fun/overload_tracker.rs
index 6fc532c..20165f5 100644
--- a/engine/src/conversion/analysis/fun/overload_tracker.rs
+++ b/engine/src/conversion/analysis/fun/overload_tracker.rs
@@ -49,7 +49,7 @@
if this_offset == 0 {
cpp_method_name
} else {
- format!("{}{}", cpp_method_name, this_offset)
+ format!("{cpp_method_name}{this_offset}")
}
}
}
diff --git a/engine/src/conversion/analysis/fun/subclass.rs b/engine/src/conversion/analysis/fun/subclass.rs
index 6383d2c..8698539 100644
--- a/engine/src/conversion/analysis/fun/subclass.rs
+++ b/engine/src/conversion/analysis/fun/subclass.rs
@@ -6,6 +6,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
+use std::ops::DerefMut;
+
use indexmap::map::IndexMap as HashMap;
use syn::{parse_quote, FnArg, PatType, Type, TypePtr};
@@ -17,6 +19,7 @@
SubclassName, SuperclassMethod, UnsafetyNeeded, Virtualness,
};
use crate::conversion::apivec::ApiVec;
+use crate::minisyn::minisynize_punctuated;
use crate::{
conversion::{
analysis::fun::function_wrapper::{
@@ -85,6 +88,7 @@
.param_details
.iter()
.map(|pd| pd.name.clone())
+ .map(Into::into)
.collect();
let requires_unsafe = if matches!(unsafe_policy, UnsafePolicy::AllFunctionsUnsafe) {
UnsafetyNeeded::Always
@@ -95,10 +99,10 @@
name,
details: SuperclassMethod {
name: make_ident(&analysis.rust_name),
- params: analysis.params.clone(),
+ params: minisynize_punctuated(analysis.params.clone()),
ret_type: analysis.ret_type.clone(),
param_names,
- receiver_mutability: receiver_mutability.clone(),
+ receiver_mutability: *receiver_mutability,
requires_unsafe,
is_pure_virtual,
receiver,
@@ -122,10 +126,10 @@
sub.0.name.get_final_item(),
name.name.get_final_item()
));
- let params = std::iter::once(parse_quote! {
+ let params = std::iter::once(crate::minisyn::FnArg(parse_quote! {
me: & #holder_name
- })
- .chain(analysis.params.iter().skip(1).cloned())
+ }))
+ .chain(analysis.params.iter().skip(1).cloned().map(Into::into))
.collect();
let kind = if matches!(receiver_mutability, ReceiverMutability::Mutable) {
CppFunctionKind::Method
@@ -161,7 +165,7 @@
qualification: Some(cpp),
},
superclass: superclass.clone(),
- receiver_mutability: receiver_mutability.clone(),
+ receiver_mutability: *receiver_mutability,
dependencies,
requires_unsafe,
is_pure_virtual: matches!(
@@ -213,7 +217,9 @@
let subclass_constructor_name =
make_ident(format!("{}_{}", cpp.get_final_item(), cpp.get_final_item()));
let mut existing_params = fun.inputs.clone();
- if let Some(FnArg::Typed(PatType { ty, .. })) = existing_params.first_mut() {
+ if let Some(FnArg::Typed(PatType { ty, .. })) =
+ existing_params.first_mut().map(DerefMut::deref_mut)
+ {
if let Type::Ptr(TypePtr { elem, .. }) = &mut **ty {
*elem = Box::new(Type::Path(sub.cpp().to_type_path()));
} else {
@@ -229,7 +235,7 @@
};
let inputs = self_param
.into_iter()
- .chain(std::iter::once(boxed_holder_param))
+ .chain(std::iter::once(boxed_holder_param.into()))
.chain(existing_params)
.collect();
let maybe_wrap = Box::new(FuncToConvert {
diff --git a/engine/src/conversion/analysis/name_check.rs b/engine/src/conversion/analysis/name_check.rs
index 7547c7c..38b8db4 100644
--- a/engine/src/conversion/analysis/name_check.rs
+++ b/engine/src/conversion/analysis/name_check.rs
@@ -8,14 +8,14 @@
use indexmap::map::IndexMap as HashMap;
-use syn::Ident;
+use crate::minisyn::Ident;
use crate::{
conversion::{
api::{Api, SubclassName},
apivec::ApiVec,
error_reporter::convert_item_apis,
- ConvertError,
+ ConvertErrorFromCpp,
},
types::{validate_ident_ok_for_cxx, QualifiedName},
};
@@ -92,7 +92,7 @@
if let Some(name) = my_name {
let symbols_for_this_name = names_found.entry(name).or_default();
if symbols_for_this_name.len() > 1usize {
- Err(ConvertError::DuplicateCxxBridgeName(
+ Err(ConvertErrorFromCpp::DuplicateCxxBridgeName(
symbols_for_this_name.clone(),
))
} else {
@@ -107,9 +107,9 @@
fn validate_all_segments_ok_for_cxx(
items: impl Iterator<Item = String>,
-) -> Result<(), ConvertError> {
+) -> Result<(), ConvertErrorFromCpp> {
for seg in items {
- validate_ident_ok_for_cxx(&seg)?;
+ validate_ident_ok_for_cxx(&seg).map_err(ConvertErrorFromCpp::InvalidIdent)?;
}
Ok(())
}
diff --git a/engine/src/conversion/analysis/pod/byvalue_checker.rs b/engine/src/conversion/analysis/pod/byvalue_checker.rs
index de72eec..6e2e9b9 100644
--- a/engine/src/conversion/analysis/pod/byvalue_checker.rs
+++ b/engine/src/conversion/analysis/pod/byvalue_checker.rs
@@ -7,7 +7,7 @@
// except according to those terms.
use crate::conversion::apivec::ApiVec;
-use crate::{conversion::ConvertError, known_types::known_types};
+use crate::{conversion::ConvertErrorFromCpp, known_types::known_types};
use crate::{
conversion::{
analysis::tdef::TypedefPhase,
@@ -48,6 +48,9 @@
/// std::string contains a self-referential pointer.
/// It is possible that this is duplicative of the information stored
/// elsewhere in the `Api` list and could possibly be removed or simplified.
+/// In general this is one of the oldest parts of autocxx and
+/// the code here could quite possibly be simplified by reusing code
+/// elsewhere.
pub struct ByValueChecker {
// Mapping from type name to whether it is safe to be POD
results: HashMap<QualifiedName, StructDetails>,
@@ -60,7 +63,7 @@
let safety = if by_value_safe {
PodState::IsPod
} else {
- PodState::UnsafeToBePod(format!("type {} is not safe for POD", tn))
+ PodState::UnsafeToBePod(format!("type {tn} is not safe for POD"))
};
results.insert(tn.clone(), StructDetails::new(safety));
}
@@ -72,7 +75,7 @@
pub(crate) fn new_from_apis(
apis: &ApiVec<TypedefPhase>,
config: &IncludeCppConfig,
- ) -> Result<ByValueChecker, ConvertError> {
+ ) -> Result<ByValueChecker, ConvertErrorFromCpp> {
let mut byvalue_checker = ByValueChecker::new();
for blocklisted in config.get_blocklist() {
let tn = QualifiedName::new_from_cpp_name(blocklisted);
@@ -81,6 +84,11 @@
.results
.insert(tn, StructDetails::new(safety));
}
+ // As we do this analysis, we need to be aware that structs
+ // may depend on other types. Ideally we'd use the depth first iterator
+ // but that's awkward given that our ApiPhase does not yet have a fixed
+ // list of field/base types. Instead, we'll iterate first over non-struct
+ // types and then over structs.
for api in apis.iter() {
match api {
Api::Typedef { analysis, .. } => {
@@ -94,7 +102,7 @@
_ => None,
},
TypedefKind::Use(_, ref ty) => match **ty {
- Type::Path(ref typ) => {
+ crate::minisyn::Type(Type::Path(ref typ)) => {
let target_tn = QualifiedName::from_type_path(typ);
known_types().consider_substitution(&target_tn)
}
@@ -113,15 +121,7 @@
None => byvalue_checker.ingest_nonpod_type(name.clone()),
}
}
- Api::Struct { details, .. } => {
- byvalue_checker.ingest_struct(&details.item, api.name().get_namespace())
- }
- Api::Enum { .. } => {
- byvalue_checker
- .results
- .insert(api.name().clone(), StructDetails::new(PodState::IsPod));
- }
- Api::ExternCppType { pod: true, .. } => {
+ Api::Enum { .. } | Api::ExternCppType { pod: true, .. } => {
byvalue_checker
.results
.insert(api.name().clone(), StructDetails::new(PodState::IsPod));
@@ -129,6 +129,11 @@
_ => {}
}
}
+ for api in apis.iter() {
+ if let Api::Struct { details, .. } = api {
+ byvalue_checker.ingest_struct(&details.item, api.name().get_namespace())
+ }
+ }
let pod_requests = config
.get_pod_requests()
.iter()
@@ -136,27 +141,32 @@
.collect();
byvalue_checker
.satisfy_requests(pod_requests)
- .map_err(ConvertError::UnsafePodType)?;
+ .map_err(ConvertErrorFromCpp::UnsafePodType)?;
Ok(byvalue_checker)
}
fn ingest_struct(&mut self, def: &ItemStruct, ns: &Namespace) {
// For this struct, work out whether it _could_ be safe as a POD.
- let tyname = QualifiedName::new(ns, def.ident.clone());
+ let tyname = QualifiedName::new(ns, def.ident.clone().into());
let mut field_safety_problem = PodState::SafeToBePod;
let fieldlist = Self::get_field_types(def);
for ty_id in &fieldlist {
match self.results.get(ty_id) {
+ None if ty_id.get_final_item() == "__BindgenUnionField" => {
+ field_safety_problem = PodState::UnsafeToBePod(format!(
+ "Type {tyname} could not be POD because it is a union"
+ ));
+ break;
+ }
None => {
field_safety_problem = PodState::UnsafeToBePod(format!(
- "Type {} could not be POD because its dependent type {} isn't known",
- tyname, ty_id
+ "Type {tyname} could not be POD because its dependent type {ty_id} isn't known"
));
break;
}
Some(deets) => {
if let PodState::UnsafeToBePod(reason) = &deets.state {
- let new_reason = format!("Type {} could not be POD because its dependent type {} isn't safe to be POD. Because: {}", tyname, ty_id, reason);
+ let new_reason = format!("Type {tyname} could not be POD because its dependent type {ty_id} isn't safe to be POD. Because: {reason}");
field_safety_problem = PodState::UnsafeToBePod(new_reason);
break;
}
@@ -164,10 +174,8 @@
}
}
if Self::has_vtable(def) {
- let reason = format!(
- "Type {} could not be POD because it has virtual functions.",
- tyname
- );
+ let reason =
+ format!("Type {tyname} could not be POD because it has virtual functions.");
field_safety_problem = PodState::UnsafeToBePod(reason);
}
let mut my_details = StructDetails::new(field_safety_problem);
@@ -176,7 +184,7 @@
}
fn ingest_nonpod_type(&mut self, tyname: QualifiedName) {
- let new_reason = format!("Type {} is a typedef to a complex type", tyname);
+ let new_reason = format!("Type {tyname} is a typedef to a complex type");
self.results.insert(
tyname,
StructDetails::new(PodState::UnsafeToBePod(new_reason)),
@@ -191,8 +199,7 @@
match deets {
None => {
return Err(format!(
- "Unable to make {} POD because we never saw a struct definition",
- ty_id
+ "Unable to make {ty_id} POD because we never saw a struct definition"
))
}
Some(deets) => match &deets.state {
@@ -236,6 +243,10 @@
)
}
+ /// This is a miniature version of the analysis in `super::get_struct_field_types`.
+ /// It would be nice to unify them. However, this version only cares about spotting
+ /// fields which may be non-POD, so can largely concern itself with just `Type::Path`
+ /// fields.
fn get_field_types(def: &ItemStruct) -> Vec<QualifiedName> {
let mut results = Vec::new();
for f in &def.fields {
@@ -261,10 +272,11 @@
#[cfg(test)]
mod tests {
use super::ByValueChecker;
+ use crate::minisyn::ItemStruct;
use crate::types::{Namespace, QualifiedName};
- use syn::{parse_quote, Ident, ItemStruct};
+ use syn::parse_quote;
- fn ty_from_ident(id: &Ident) -> QualifiedName {
+ fn ty_from_ident(id: &syn::Ident) -> QualifiedName {
QualifiedName::new_from_cpp_name(&id.to_string())
}
diff --git a/engine/src/conversion/analysis/pod/mod.rs b/engine/src/conversion/analysis/pod/mod.rs
index eeb5051..2fafc95 100644
--- a/engine/src/conversion/analysis/pod/mod.rs
+++ b/engine/src/conversion/analysis/pod/mod.rs
@@ -13,7 +13,7 @@
use autocxx_parser::IncludeCppConfig;
use byvalue_checker::ByValueChecker;
-use syn::{ItemEnum, ItemStruct, Type, Visibility};
+use syn::{ItemStruct, Type, Visibility};
use crate::{
conversion::{
@@ -23,18 +23,21 @@
convert_error::{ConvertErrorWithContext, ErrorContext},
error_reporter::convert_apis,
parse::BindgenSemanticAttributes,
- ConvertError,
+ ConvertErrorFromCpp,
},
types::{Namespace, QualifiedName},
};
use super::tdef::{TypedefAnalysis, TypedefPhase};
+#[derive(std::fmt::Debug)]
+
pub(crate) struct FieldInfo {
pub(crate) ty: Type,
pub(crate) type_kind: type_converter::TypeKind,
}
+#[derive(std::fmt::Debug)]
pub(crate) struct PodAnalysis {
pub(crate) kind: TypeKind,
pub(crate) bases: HashSet<QualifiedName>,
@@ -43,12 +46,18 @@
/// because otherwise we don't know whether they're
/// abstract or not.
pub(crate) castable_bases: HashSet<QualifiedName>,
+ /// All field types. e.g. for std::unique_ptr<A>, this would include
+ /// both std::unique_ptr and A
pub(crate) field_deps: HashSet<QualifiedName>,
+ /// Types within fields where we need a definition, e.g. for
+ /// std::unique_ptr<A> it would just be std::unique_ptr.
+ pub(crate) field_definition_deps: HashSet<QualifiedName>,
pub(crate) field_info: Vec<FieldInfo>,
pub(crate) is_generic: bool,
pub(crate) in_anonymous_namespace: bool,
}
+#[derive(std::fmt::Debug)]
pub(crate) struct PodPhase;
impl AnalysisPhase for PodPhase {
@@ -65,7 +74,7 @@
pub(crate) fn analyze_pod_apis(
apis: ApiVec<TypedefPhase>,
config: &IncludeCppConfig,
-) -> Result<ApiVec<PodPhase>, ConvertError> {
+) -> Result<ApiVec<PodPhase>, ConvertErrorFromCpp> {
// This next line will return an error if any of the 'generate_pod'
// directives from the user can't be met because, for instance,
// a type contains a std::string or some other type which can't be
@@ -118,7 +127,7 @@
fn analyze_enum(
name: ApiName,
- mut item: ItemEnum,
+ mut item: crate::minisyn::ItemEnum,
) -> Result<Box<dyn Iterator<Item = Api<PodPhase>>>, ConvertErrorWithContext> {
let metadata = BindgenSemanticAttributes::new_retaining_others(&mut item.attrs);
metadata.check_for_fatal_attrs(&name.name.get_final_ident())?;
@@ -138,12 +147,14 @@
metadata.check_for_fatal_attrs(&id)?;
let bases = get_bases(&details.item);
let mut field_deps = HashSet::new();
+ let mut field_definition_deps = HashSet::new();
let mut field_info = Vec::new();
let field_conversion_errors = get_struct_field_types(
type_converter,
name.name.get_namespace(),
&details.item,
&mut field_deps,
+ &mut field_definition_deps,
&mut field_info,
extra_apis,
);
@@ -152,7 +163,7 @@
// Let's not allow anything to be POD if it's got rvalue reference fields.
if details.has_rvalue_reference_fields {
return Err(ConvertErrorWithContext(
- ConvertError::RValueReferenceField,
+ ConvertErrorFromCpp::RValueReferenceField,
Some(ErrorContext::new_for_item(id)),
));
}
@@ -186,6 +197,7 @@
bases: bases.into_keys().collect(),
castable_bases,
field_deps,
+ field_definition_deps,
field_info,
is_generic,
in_anonymous_namespace,
@@ -198,9 +210,10 @@
ns: &Namespace,
s: &ItemStruct,
field_deps: &mut HashSet<QualifiedName>,
+ field_definition_deps: &mut HashSet<QualifiedName>,
field_info: &mut Vec<FieldInfo>,
extra_apis: &mut ApiVec<NullPhase>,
-) -> Vec<ConvertError> {
+) -> Vec<ConvertErrorFromCpp> {
let mut convert_errors = Vec::new();
let struct_type_params = s
.generics
@@ -225,6 +238,14 @@
.unwrap_or(false)
{
field_deps.extend(r.types_encountered);
+ if let Type::Path(typ) = &r.ty {
+ // Later analyses need to know about the field
+ // types where we need full definitions, as opposed
+ // to just declarations. That means just the outermost
+ // type path.
+ // TODO: consider arrays.
+ field_definition_deps.insert(QualifiedName::from_type_path(typ));
+ }
field_info.push(FieldInfo {
ty: r.ty,
type_kind: r.kind,
diff --git a/engine/src/conversion/analysis/remove_ignored.rs b/engine/src/conversion/analysis/remove_ignored.rs
index bd11b13..4ed4b9f 100644
--- a/engine/src/conversion/analysis/remove_ignored.rs
+++ b/engine/src/conversion/analysis/remove_ignored.rs
@@ -11,7 +11,7 @@
use super::deps::HasDependencies;
use super::fun::{FnAnalysis, FnKind, FnPhase};
use crate::conversion::apivec::ApiVec;
-use crate::conversion::{convert_error::ErrorContext, ConvertError};
+use crate::conversion::{convert_error::ErrorContext, ConvertErrorFromCpp};
use crate::{conversion::api::Api, known_types};
/// Remove any APIs which depend on other items which have been ignored.
@@ -44,7 +44,10 @@
if !ignored_dependents.is_empty() {
iterate_again = true;
ignored_items.insert(api.name().clone());
- create_ignore_item(api, ConvertError::IgnoredDependent(ignored_dependents))
+ create_ignore_item(
+ api,
+ ConvertErrorFromCpp::IgnoredDependent(ignored_dependents),
+ )
} else {
let mut missing_deps = api.deps().filter(|dep| {
!valid_types.contains(*dep) && !known_types().is_known_type(dep)
@@ -52,7 +55,10 @@
let first = missing_deps.next();
std::mem::drop(missing_deps);
if let Some(missing_dep) = first.cloned() {
- create_ignore_item(api, ConvertError::UnknownDependentType(missing_dep))
+ create_ignore_item(
+ api,
+ ConvertErrorFromCpp::UnknownDependentType(missing_dep),
+ )
} else {
api
}
@@ -63,7 +69,7 @@
apis
}
-fn create_ignore_item(api: Api<FnPhase>, err: ConvertError) -> Api<FnPhase> {
+fn create_ignore_item(api: Api<FnPhase>, err: ConvertErrorFromCpp) -> Api<FnPhase> {
let id = api.name().get_final_ident();
log::info!("Marking as ignored: {} because {}", id.to_string(), err);
Api::IgnoredItem {
diff --git a/engine/src/conversion/analysis/replace_hopeless_typedef_targets.rs b/engine/src/conversion/analysis/replace_hopeless_typedef_targets.rs
index 8d5d033..918fb8f 100644
--- a/engine/src/conversion/analysis/replace_hopeless_typedef_targets.rs
+++ b/engine/src/conversion/analysis/replace_hopeless_typedef_targets.rs
@@ -15,7 +15,7 @@
api::Api,
apivec::ApiVec,
convert_error::{ConvertErrorWithContext, ErrorContext},
- ConvertError,
+ ConvertErrorFromCpp,
},
types::QualifiedName,
};
@@ -71,7 +71,7 @@
{
Api::IgnoredItem {
name: api.name_info().clone(),
- err: ConvertError::NestedOpaqueTypedef,
+ err: ConvertErrorFromCpp::NestedOpaqueTypedef,
ctx: Some(ErrorContext::new_for_item(name_id)),
}
} else {
diff --git a/engine/src/conversion/analysis/tdef.rs b/engine/src/conversion/analysis/tdef.rs
index 5a635f8..1c414e8 100644
--- a/engine/src/conversion/analysis/tdef.rs
+++ b/engine/src/conversion/analysis/tdef.rs
@@ -19,11 +19,12 @@
convert_error::{ConvertErrorWithContext, ErrorContext},
error_reporter::convert_apis,
parse::BindgenSemanticAttributes,
- ConvertError,
+ ConvertErrorFromCpp,
},
types::QualifiedName,
};
+#[derive(std::fmt::Debug)]
pub(crate) struct TypedefAnalysis {
pub(crate) kind: TypedefKind,
pub(crate) deps: HashSet<QualifiedName>,
@@ -31,6 +32,7 @@
/// Analysis phase where typedef analysis has been performed but no other
/// analyses just yet.
+#[derive(std::fmt::Debug)]
pub(crate) struct TypedefPhase;
impl AnalysisPhase for TypedefPhase {
@@ -57,7 +59,7 @@
Ok(Box::new(std::iter::once(match item {
TypedefKind::Type(ity) => get_replacement_typedef(
name,
- ity,
+ ity.into(),
old_tyname,
&mut type_converter,
&mut extra_apis,
@@ -87,7 +89,7 @@
) -> Result<Api<TypedefPhase>, ConvertErrorWithContext> {
if !ity.generics.params.is_empty() {
return Err(ConvertErrorWithContext(
- ConvertError::TypedefTakesGenericParameters,
+ ConvertErrorFromCpp::TypedefTakesGenericParameters,
Some(ErrorContext::new_for_item(name.name.get_final_ident())),
));
}
@@ -108,7 +110,7 @@
ty: syn::Type::Path(ref typ),
..
}) if QualifiedName::from_type_path(typ) == name.name => Err(ConvertErrorWithContext(
- ConvertError::InfinitelyRecursiveTypedef(name.name.clone()),
+ ConvertErrorFromCpp::InfinitelyRecursiveTypedef(name.name.clone()),
Some(ErrorContext::new_for_item(name.name.get_final_ident())),
)),
Ok(mut final_type) => {
@@ -116,10 +118,10 @@
extra_apis.append(&mut final_type.extra_apis);
Ok(Api::Typedef {
name,
- item: TypedefKind::Type(ity),
+ item: TypedefKind::Type(ity.into()),
old_tyname,
analysis: TypedefAnalysis {
- kind: TypedefKind::Type(converted_type),
+ kind: TypedefKind::Type(converted_type.into()),
deps: final_type.types_encountered,
},
})
diff --git a/engine/src/conversion/analysis/type_converter.rs b/engine/src/conversion/analysis/type_converter.rs
index 7e2d2bd..63f7ae9 100644
--- a/engine/src/conversion/analysis/type_converter.rs
+++ b/engine/src/conversion/analysis/type_converter.rs
@@ -10,8 +10,8 @@
conversion::{
api::{AnalysisPhase, Api, ApiName, NullPhase, TypedefKind, UnanalyzedApi},
apivec::ApiVec,
- codegen_cpp::type_to_cpp::type_to_cpp,
- ConvertError,
+ codegen_cpp::type_to_cpp::CppNameMap,
+ ConvertErrorFromCpp,
},
known_types::{known_types, CxxGenericType},
types::{make_ident, Namespace, QualifiedName},
@@ -34,7 +34,7 @@
pub(crate) enum TypeKind {
Regular,
Pointer,
- SubclassHolder(Ident),
+ SubclassHolder(crate::minisyn::Ident),
Reference,
RValueReference,
MutableReference,
@@ -109,14 +109,9 @@
matches!(self, Self::WithinReference)
}
fn allowed_generic_type(&self, ident: &Ident) -> bool {
- match self {
+ !matches!(self,
Self::WithinStructField { struct_type_params }
- if struct_type_params.contains(ident) =>
- {
- false
- }
- _ => true,
- }
+ if struct_type_params.contains(ident))
}
}
@@ -135,6 +130,7 @@
forward_declarations: HashSet<QualifiedName>,
ignored_types: HashSet<QualifiedName>,
config: &'a IncludeCppConfig,
+ original_name_map: CppNameMap,
}
impl<'a> TypeConverter<'a> {
@@ -149,6 +145,7 @@
forward_declarations: Self::find_incomplete_types(apis),
ignored_types: Self::find_ignored_types(apis),
config,
+ original_name_map: CppNameMap::new_from_apis(apis),
}
}
@@ -157,7 +154,7 @@
ty: Box<Type>,
ns: &Namespace,
ctx: &TypeConversionContext,
- ) -> Result<Annotated<Box<Type>>, ConvertError> {
+ ) -> Result<Annotated<Box<Type>>, ConvertErrorFromCpp> {
Ok(self.convert_type(*ty, ns, ctx)?.map(Box::new))
}
@@ -166,7 +163,7 @@
ty: Type,
ns: &Namespace,
ctx: &TypeConversionContext,
- ) -> Result<Annotated<Type>, ConvertError> {
+ ) -> Result<Annotated<Type>, ConvertErrorFromCpp> {
let result = match ty {
Type::Path(p) => {
let newp = self.convert_type_path(p, ns, ctx)?;
@@ -175,7 +172,7 @@
if !ctx.allow_instantiation_of_forward_declaration()
&& self.forward_declarations.contains(&qn)
{
- return Err(ConvertError::TypeContainingForwardDeclaration(qn));
+ return Err(ConvertErrorFromCpp::TypeContainingForwardDeclaration(qn));
}
// Special handling because rust_Str (as emitted by bindgen)
// doesn't simply get renamed to a different type _identifier_.
@@ -220,7 +217,11 @@
)
}
Type::Ptr(ptr) => self.convert_ptr(ptr, ns, ctx.pointer_treatment())?,
- _ => return Err(ConvertError::UnknownType(ty.to_token_stream().to_string())),
+ _ => {
+ return Err(ConvertErrorFromCpp::UnknownType(
+ ty.to_token_stream().to_string(),
+ ))
+ }
};
Ok(result)
}
@@ -230,7 +231,7 @@
mut typ: TypePath,
ns: &Namespace,
ctx: &TypeConversionContext,
- ) -> Result<Annotated<Type>, ConvertError> {
+ ) -> Result<Annotated<Type>, ConvertErrorFromCpp> {
// First, qualify any unqualified paths.
if typ.path.segments.iter().next().unwrap().ident != "root" {
let ty = QualifiedName::from_type_path(&typ);
@@ -241,7 +242,7 @@
if !known_types().is_known_type(&ty) {
let num_segments = typ.path.segments.len();
if num_segments > 1 {
- return Err(ConvertError::UnsupportedBuiltInType(ty));
+ return Err(ConvertErrorFromCpp::UnsupportedBuiltInType(ty));
}
if !self.types_found.contains(&ty) {
typ.path.segments = std::iter::once(&"root".to_string())
@@ -257,9 +258,11 @@
}
let original_tn = QualifiedName::from_type_path(&typ);
- original_tn.validate_ok_for_cxx()?;
+ original_tn
+ .validate_ok_for_cxx()
+ .map_err(ConvertErrorFromCpp::InvalidIdent)?;
if self.config.is_on_blocklist(&original_tn.to_cpp_name()) {
- return Err(ConvertError::Blocked(original_tn));
+ return Err(ConvertErrorFromCpp::Blocked(original_tn));
}
let mut deps = HashSet::new();
@@ -331,7 +334,9 @@
)?;
deps.extend(innerty.types_encountered.drain(..));
} else {
- return Err(ConvertError::TemplatedTypeContainingNonPathArg(tn.clone()));
+ return Err(ConvertErrorFromCpp::TemplatedTypeContainingNonPathArg(
+ tn.clone(),
+ ));
}
} else {
// Oh poop. It's a generic type which cxx won't be able to handle.
@@ -347,7 +352,9 @@
if typ.path.segments.len() == 1
&& !ctx.allowed_generic_type(&seg.ident)
{
- return Err(ConvertError::ReferringToGenericTypeParam);
+ return Err(
+ ConvertErrorFromCpp::ReferringToGenericTypeParam,
+ );
}
}
}
@@ -361,7 +368,7 @@
// this a bit.
let qn = QualifiedName::from_type_path(&typ); // ignores generic params
if self.ignored_types.contains(&qn) {
- return Err(ConvertError::ConcreteVersionOfIgnoredTemplate);
+ return Err(ConvertErrorFromCpp::ConcreteVersionOfIgnoredTemplate);
}
let (new_tn, api) = self.get_templated_typename(&Type::Path(typ))?;
extra_apis.extend(api.into_iter());
@@ -385,7 +392,7 @@
pun: Punctuated<GenericArgument, P>,
ns: &Namespace,
ctx: &TypeConversionContext,
- ) -> Result<Annotated<Punctuated<GenericArgument, P>>, ConvertError>
+ ) -> Result<Annotated<Punctuated<GenericArgument, P>>, ConvertErrorFromCpp>
where
P: Default,
{
@@ -411,7 +418,10 @@
))
}
- fn resolve_typedef<'b>(&'b self, tn: &QualifiedName) -> Result<Option<&'b Type>, ConvertError> {
+ fn resolve_typedef<'b>(
+ &'b self,
+ tn: &QualifiedName,
+ ) -> Result<Option<&'b Type>, ConvertErrorFromCpp> {
let mut encountered = HashSet::new();
let mut tn = tn.clone();
let mut previous_typ = None;
@@ -422,7 +432,7 @@
previous_typ = r;
let new_tn = QualifiedName::from_type_path(typ);
if encountered.contains(&new_tn) {
- return Err(ConvertError::InfinitelyRecursiveTypedef(tn.clone()));
+ return Err(ConvertErrorFromCpp::InfinitelyRecursiveTypedef(tn.clone()));
}
if typ
.path
@@ -430,7 +440,7 @@
.iter()
.any(|seg| seg.ident.to_string().starts_with("_bindgen_mod"))
{
- return Err(ConvertError::TypedefToTypeInAnonymousNamespace);
+ return Err(ConvertErrorFromCpp::TypedefToTypeInAnonymousNamespace);
}
encountered.insert(new_tn.clone());
tn = new_tn;
@@ -446,10 +456,10 @@
mut ptr: TypePtr,
ns: &Namespace,
pointer_treatment: PointerTreatment,
- ) -> Result<Annotated<Type>, ConvertError> {
+ ) -> Result<Annotated<Type>, ConvertErrorFromCpp> {
match pointer_treatment {
PointerTreatment::Pointer => {
- crate::known_types::ensure_pointee_is_valid(&ptr)?;
+ Self::ensure_pointee_is_valid(&ptr)?;
let innerty =
self.convert_boxed_type(ptr.elem, ns, &TypeConversionContext::WithinReference)?;
ptr.elem = innerty.ty;
@@ -470,7 +480,7 @@
// be a plain value. We should detect and abort.
let mut outer = elem.map(|elem| match mutability {
Some(_) => Type::Path(parse_quote! {
- ::std::pin::Pin < & #mutability #elem >
+ ::core::pin::Pin < & #mutability #elem >
}),
None => Type::Reference(parse_quote! {
& #elem
@@ -484,7 +494,7 @@
Ok(outer)
}
PointerTreatment::RValueReference => {
- crate::known_types::ensure_pointee_is_valid(&ptr)?;
+ Self::ensure_pointee_is_valid(&ptr)?;
let innerty =
self.convert_boxed_type(ptr.elem, ns, &TypeConversionContext::WithinReference)?;
ptr.elem = innerty.ty;
@@ -498,15 +508,26 @@
}
}
+ fn ensure_pointee_is_valid(ptr: &TypePtr) -> Result<(), ConvertErrorFromCpp> {
+ match *ptr.elem {
+ Type::Path(..) => Ok(()),
+ Type::Array(..) => Err(ConvertErrorFromCpp::InvalidArrayPointee),
+ Type::Ptr(..) => Err(ConvertErrorFromCpp::InvalidPointerPointee),
+ _ => Err(ConvertErrorFromCpp::InvalidPointee(
+ ptr.elem.to_token_stream().to_string(),
+ )),
+ }
+ }
+
fn get_templated_typename(
&mut self,
rs_definition: &Type,
- ) -> Result<(QualifiedName, Option<UnanalyzedApi>), ConvertError> {
+ ) -> Result<(QualifiedName, Option<UnanalyzedApi>), ConvertErrorFromCpp> {
let count = self.concrete_templates.len();
// We just use this as a hash key, essentially.
// TODO: Once we've completed the TypeConverter refactoring (see #220),
// pass in an actual original_name_map here.
- let cpp_definition = type_to_cpp(rs_definition, &HashMap::new())?;
+ let cpp_definition = self.original_name_map.type_to_cpp(rs_definition)?;
let e = self.concrete_templates.get(&cpp_definition);
match e {
Some(tn) => Ok((tn.clone(), None)),
@@ -530,12 +551,12 @@
.find(|s| s == &synthetic_ident)
{
None => synthetic_ident,
- Some(_) => format!("AutocxxConcrete{}", count),
+ Some(_) => format!("AutocxxConcrete{count}"),
};
let api = UnanalyzedApi::ConcreteType {
- name: ApiName::new_in_root_namespace(make_ident(&synthetic_ident)),
+ name: ApiName::new_in_root_namespace(make_ident(synthetic_ident)),
cpp_definition: cpp_definition.clone(),
- rs_definition: Some(Box::new(rs_definition.clone())),
+ rs_definition: Some(Box::new(rs_definition.clone().into())),
};
self.concrete_templates
.insert(cpp_definition, api.name().clone());
@@ -550,21 +571,25 @@
desc: &QualifiedName,
generic_behavior: CxxGenericType,
forward_declarations_ok: bool,
- ) -> Result<TypeKind, ConvertError> {
+ ) -> Result<TypeKind, ConvertErrorFromCpp> {
for inner in path_args {
match inner {
GenericArgument::Type(Type::Path(typ)) => {
let inner_qn = QualifiedName::from_type_path(typ);
if !forward_declarations_ok && self.forward_declarations.contains(&inner_qn) {
- return Err(ConvertError::TypeContainingForwardDeclaration(inner_qn));
+ return Err(ConvertErrorFromCpp::TypeContainingForwardDeclaration(
+ inner_qn,
+ ));
}
match generic_behavior {
CxxGenericType::Rust => {
if !inner_qn.get_namespace().is_empty() {
- return Err(ConvertError::RustTypeWithAPath(inner_qn));
+ return Err(ConvertErrorFromCpp::RustTypeWithAPath(inner_qn));
}
if !self.config.is_rust_type(&inner_qn.get_final_ident()) {
- return Err(ConvertError::BoxContainingNonRustType(inner_qn));
+ return Err(ConvertErrorFromCpp::BoxContainingNonRustType(
+ inner_qn,
+ ));
}
if self
.config
@@ -577,12 +602,12 @@
}
CxxGenericType::CppPtr => {
if !known_types().permissible_within_unique_ptr(&inner_qn) {
- return Err(ConvertError::InvalidTypeForCppPtr(inner_qn));
+ return Err(ConvertErrorFromCpp::InvalidTypeForCppPtr(inner_qn));
}
}
CxxGenericType::CppVector => {
if !known_types().permissible_within_vector(&inner_qn) {
- return Err(ConvertError::InvalidTypeForCppVector(inner_qn));
+ return Err(ConvertErrorFromCpp::InvalidTypeForCppVector(inner_qn));
}
if matches!(
typ.path.segments.last().map(|ps| &ps.arguments),
@@ -591,14 +616,14 @@
| PathArguments::AngleBracketed(_)
)
) {
- return Err(ConvertError::GenericsWithinVector);
+ return Err(ConvertErrorFromCpp::GenericsWithinVector);
}
}
_ => {}
}
}
_ => {
- return Err(ConvertError::TemplatedTypeContainingNonPathArg(
+ return Err(ConvertErrorFromCpp::TemplatedTypeContainingNonPathArg(
desc.clone(),
))
}
diff --git a/engine/src/conversion/api.rs b/engine/src/conversion/api.rs
index c5a1b60..d5da41c 100644
--- a/engine/src/conversion/api.rs
+++ b/engine/src/conversion/api.rs
@@ -9,17 +9,20 @@
use indexmap::set::IndexSet as HashSet;
use std::fmt::Display;
-use crate::types::{make_ident, Namespace, QualifiedName};
-use autocxx_parser::{ExternCppType, RustFun, RustPath};
-use itertools::Itertools;
-use quote::ToTokens;
use syn::{
parse::Parse,
punctuated::Punctuated,
token::{Comma, Unsafe},
+};
+
+use crate::minisyn::{
Attribute, FnArg, Ident, ItemConst, ItemEnum, ItemStruct, ItemType, ItemUse, LitBool, LitInt,
Pat, ReturnType, Type, Visibility,
};
+use crate::types::{make_ident, Namespace, QualifiedName};
+use autocxx_parser::{ExternCppType, RustFun, RustPath};
+use itertools::Itertools;
+use quote::ToTokens;
use super::{
analysis::{
@@ -30,10 +33,10 @@
PointerTreatment,
},
convert_error::{ConvertErrorWithContext, ErrorContext},
- ConvertError,
+ ConvertErrorFromCpp,
};
-#[derive(Copy, Clone, Eq, PartialEq)]
+#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub(crate) enum TypeKind {
Pod, // trivial. Can be moved and copied in Rust.
NonPod, // has destructor or non-trivial move constructors. Can only hold by UniquePtr
@@ -53,6 +56,7 @@
}
/// Details about a C++ struct.
+#[derive(Debug)]
pub(crate) struct StructDetails {
pub(crate) item: ItemStruct,
pub(crate) layout: Option<Layout>,
@@ -60,7 +64,7 @@
}
/// Layout of a type, equivalent to the same type in ir/layout.rs in bindgen
-#[derive(Clone)]
+#[derive(Clone, Debug)]
pub(crate) struct Layout {
/// The size (in bytes) of this layout.
pub(crate) size: usize,
@@ -85,14 +89,14 @@
}
}
-#[derive(Clone)]
+#[derive(Clone, Debug)]
pub(crate) enum Virtualness {
None,
Virtual,
PureVirtual,
}
-#[derive(Clone, Copy)]
+#[derive(Clone, Copy, Debug)]
pub(crate) enum CastMutability {
ConstToConst,
MutToConst,
@@ -101,7 +105,7 @@
/// Indicates that this function (which is synthetic) should
/// be a trait implementation rather than a method or free function.
-#[derive(Clone)]
+#[derive(Clone, Debug)]
pub(crate) enum TraitSynthesis {
Cast {
to_type: QualifiedName,
@@ -113,7 +117,7 @@
/// Details of a subclass constructor.
/// TODO: zap this; replace with an extra API.
-#[derive(Clone)]
+#[derive(Clone, Debug)]
pub(crate) struct SubclassConstructorDetails {
pub(crate) subclass: SubclassName,
pub(crate) is_trivial: bool,
@@ -124,7 +128,7 @@
/// Contributions to traits representing C++ superclasses that
/// we may implement as Rust subclasses.
-#[derive(Clone)]
+#[derive(Clone, Debug)]
pub(crate) struct SuperclassMethod {
pub(crate) name: Ident,
pub(crate) receiver: QualifiedName,
@@ -139,7 +143,7 @@
/// Information about references (as opposed to pointers) to be found
/// within the function signature. This is derived from bindgen annotations
/// which is why it's not within `FuncToConvert::inputs`
-#[derive(Default, Clone)]
+#[derive(Default, Clone, Debug)]
pub(crate) struct References {
pub(crate) rvalue_ref_params: HashSet<Ident>,
pub(crate) ref_params: HashSet<Ident>,
@@ -175,7 +179,7 @@
}
}
-#[derive(Clone)]
+#[derive(Clone, Debug)]
pub(crate) struct TraitImplSignature {
pub(crate) ty: Type,
pub(crate) trait_signature: Type,
@@ -239,13 +243,21 @@
}
}
-#[derive(Clone)]
+#[derive(Clone, Debug)]
pub(crate) enum Provenance {
Bindgen,
SynthesizedOther,
SynthesizedSubclassConstructor(Box<SubclassConstructorDetails>),
}
+/// Whether a function has =delete or =default
+#[derive(Clone, Copy, Debug)]
+pub(crate) enum DeletedOrDefaulted {
+ Neither,
+ Deleted,
+ Defaulted,
+}
+
/// A C++ function for which we need to generate bindings, but haven't
/// yet analyzed in depth. This is little more than a `ForeignItemFn`
/// broken down into its constituent parts, plus some metadata from the
@@ -256,7 +268,7 @@
/// during normal bindgen parsing. If that happens, they'll create one
/// of these structures, and typically fill in some of the
/// `synthesized_*` members which are not filled in from bindgen.
-#[derive(Clone)]
+#[derive(Clone, Debug)]
pub(crate) struct FuncToConvert {
pub(crate) provenance: Provenance,
pub(crate) ident: Ident,
@@ -283,18 +295,20 @@
/// If Some, this function didn't really exist in the original
/// C++ and instead we're synthesizing it.
pub(crate) synthetic_cpp: Option<(CppFunctionBody, CppFunctionKind)>,
- pub(crate) is_deleted: bool,
+ /// =delete
+ pub(crate) is_deleted: DeletedOrDefaulted,
}
/// Layers of analysis which may be applied to decorate each API.
/// See description of the purpose of this trait within `Api`.
-pub(crate) trait AnalysisPhase {
- type TypedefAnalysis;
- type StructAnalysis;
- type FunAnalysis;
+pub(crate) trait AnalysisPhase: std::fmt::Debug {
+ type TypedefAnalysis: std::fmt::Debug;
+ type StructAnalysis: std::fmt::Debug;
+ type FunAnalysis: std::fmt::Debug;
}
/// No analysis has been applied to this API.
+#[derive(std::fmt::Debug)]
pub(crate) struct NullPhase;
impl AnalysisPhase for NullPhase {
@@ -303,7 +317,7 @@
type FunAnalysis = ();
}
-#[derive(Clone)]
+#[derive(Clone, Debug)]
pub(crate) enum TypedefKind {
Use(ItemUse, Box<Type>),
Type(ItemType),
@@ -365,7 +379,7 @@
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.name)?;
if let Some(cpp_name) = &self.cpp_name {
- write!(f, " (cpp={})", cpp_name)?;
+ write!(f, " (cpp={cpp_name})")?;
}
Ok(())
}
@@ -418,7 +432,7 @@
}
// TODO this and the following should probably include both class name and method name
pub(crate) fn get_super_fn_name(superclass_namespace: &Namespace, id: &str) -> QualifiedName {
- let id = make_ident(format!("{}_super", id));
+ let id = make_ident(format!("{id}_super"));
QualifiedName::new(superclass_namespace, id)
}
pub(crate) fn get_methods_trait_name(superclass_name: &QualifiedName) -> QualifiedName {
@@ -434,7 +448,7 @@
}
}
-#[derive(strum_macros::Display)]
+#[derive(std::fmt::Debug)]
/// Different types of API we might encounter.
///
/// This type is parameterized over an `ApiAnalysis`. This is any additional
@@ -445,11 +459,9 @@
/// because sometimes we pass on the `bindgen` output directly in the
/// Rust codegen output.
///
-/// This derives from [strum_macros::Display] because we want to be
-/// able to debug-print the enum discriminant without worrying about
-/// the fact that their payloads may not be `Debug` or `Display`.
-/// (Specifically, allowing `syn` Types to be `Debug` requires
-/// enabling syn's `extra-traits` feature which increases compile time.)
+/// Any `syn` types represented in this `Api` type, or any of the types from
+/// which it is composed, should be wrapped in `crate::minisyn` equivalents
+/// to avoid excessively verbose `Debug` logging.
pub(crate) enum Api<T: AnalysisPhase> {
/// A forward declaration, which we mustn't store in a UniquePtr.
ForwardDeclaration {
@@ -522,7 +534,7 @@
/// dependent items.
IgnoredItem {
name: ApiName,
- err: ConvertError,
+ err: ConvertErrorFromCpp,
ctx: Option<ErrorContext>,
},
/// A Rust type which is not a C++ type.
@@ -531,7 +543,7 @@
RustFn {
name: ApiName,
details: RustFun,
- receiver: Option<QualifiedName>,
+ deps: Vec<QualifiedName>,
},
/// Some function for the extern "Rust" block.
RustSubclassFn {
@@ -559,6 +571,7 @@
},
}
+#[derive(Debug)]
pub(crate) struct RustSubclassFnDetails {
pub(crate) params: Punctuated<FnArg, Comma>,
pub(crate) ret: ReturnType,
@@ -646,12 +659,6 @@
}
}
-impl<T: AnalysisPhase> std::fmt::Debug for Api<T> {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- write!(f, "{:?} (kind={})", self.name_info(), self)
- }
-}
-
pub(crate) type UnanalyzedApi = Api<NullPhase>;
impl<T: AnalysisPhase> Api<T> {
diff --git a/engine/src/conversion/apivec.rs b/engine/src/conversion/apivec.rs
index 1e440cc..24e70ed 100644
--- a/engine/src/conversion/apivec.rs
+++ b/engine/src/conversion/apivec.rs
@@ -15,7 +15,7 @@
use indexmap::set::IndexSet as HashSet;
use crate::{
- conversion::{api::ApiName, convert_error::ErrorContext, ConvertError},
+ conversion::{api::ApiName, convert_error::ErrorContext, ConvertErrorFromCpp},
types::QualifiedName,
};
@@ -64,7 +64,7 @@
self.retain(|api| api.name() != name);
self.push(Api::IgnoredItem {
name: ApiName::new_from_qualified_name(name.clone()),
- err: ConvertError::DuplicateItemsFoundInParsing,
+ err: ConvertErrorFromCpp::DuplicateItemsFoundInParsing,
ctx: Some(ErrorContext::new_for_item(name.get_final_ident())),
})
}
diff --git a/engine/src/conversion/codegen_cpp/function_wrapper_cpp.rs b/engine/src/conversion/codegen_cpp/function_wrapper_cpp.rs
index 5367626..661a5b3 100644
--- a/engine/src/conversion/codegen_cpp/function_wrapper_cpp.rs
+++ b/engine/src/conversion/codegen_cpp/function_wrapper_cpp.rs
@@ -11,16 +11,16 @@
use crate::conversion::{
analysis::fun::function_wrapper::{CppConversionType, TypeConversionPolicy},
api::Pointerness,
- ConvertError,
+ ConvertErrorFromCpp,
};
-use super::type_to_cpp::{type_to_cpp, CppNameMap};
+use super::type_to_cpp::CppNameMap;
impl TypeConversionPolicy {
pub(super) fn unconverted_type(
&self,
cpp_name_map: &CppNameMap,
- ) -> Result<String, ConvertError> {
+ ) -> Result<String, ConvertErrorFromCpp> {
match self.cpp_conversion {
CppConversionType::FromUniquePtrToValue => self.unique_ptr_wrapped_type(cpp_name_map),
CppConversionType::FromPtrToValue => {
@@ -30,7 +30,10 @@
}
}
- pub(super) fn converted_type(&self, cpp_name_map: &CppNameMap) -> Result<String, ConvertError> {
+ pub(super) fn converted_type(
+ &self,
+ cpp_name_map: &CppNameMap,
+ ) -> Result<String, ConvertErrorFromCpp> {
match self.cpp_conversion {
CppConversionType::FromValueToUniquePtr => self.unique_ptr_wrapped_type(cpp_name_map),
CppConversionType::FromReferenceToPointer => {
@@ -46,15 +49,18 @@
Ok(format!(
"{}{}*",
const_string,
- type_to_cpp(ty, cpp_name_map)?
+ cpp_name_map.type_to_cpp(ty)?
))
}
_ => self.unwrapped_type_as_string(cpp_name_map),
}
}
- fn unwrapped_type_as_string(&self, cpp_name_map: &CppNameMap) -> Result<String, ConvertError> {
- type_to_cpp(self.cxxbridge_type(), cpp_name_map)
+ fn unwrapped_type_as_string(
+ &self,
+ cpp_name_map: &CppNameMap,
+ ) -> Result<String, ConvertErrorFromCpp> {
+ cpp_name_map.type_to_cpp(self.cxxbridge_type())
}
pub(crate) fn is_a_pointer(&self) -> Pointerness {
@@ -71,7 +77,7 @@
fn unique_ptr_wrapped_type(
&self,
original_name_map: &CppNameMap,
- ) -> Result<String, ConvertError> {
+ ) -> Result<String, ConvertErrorFromCpp> {
Ok(format!(
"std::unique_ptr<{}>",
self.unwrapped_type_as_string(original_name_map)?
@@ -83,17 +89,17 @@
var_name: &str,
cpp_name_map: &CppNameMap,
is_return: bool,
- ) -> Result<Option<String>, ConvertError> {
+ ) -> Result<Option<String>, ConvertErrorFromCpp> {
// If is_return we want to avoid unnecessary std::moves because they
// make RVO less effective
Ok(match self.cpp_conversion {
CppConversionType::None | CppConversionType::FromReturnValueToPlacementPtr => {
Some(var_name.to_string())
}
- CppConversionType::FromPointerToReference { .. } => Some(format!("(*{})", var_name)),
- CppConversionType::Move => Some(format!("std::move({})", var_name)),
+ CppConversionType::FromPointerToReference { .. } => Some(format!("(*{var_name})")),
+ CppConversionType::Move => Some(format!("std::move({var_name})")),
CppConversionType::FromUniquePtrToValue | CppConversionType::FromPtrToMove => {
- Some(format!("std::move(*{})", var_name))
+ Some(format!("std::move(*{var_name})"))
}
CppConversionType::FromValueToUniquePtr => Some(format!(
"std::make_unique<{}>({})",
@@ -101,15 +107,15 @@
var_name
)),
CppConversionType::FromPtrToValue => {
- let dereference = format!("*{}", var_name);
+ let dereference = format!("*{var_name}");
Some(if is_return {
dereference
} else {
- format!("std::move({})", dereference)
+ format!("std::move({dereference})")
})
}
CppConversionType::IgnoredPlacementPtrParameter => None,
- CppConversionType::FromReferenceToPointer { .. } => Some(format!("&{}", var_name)),
+ CppConversionType::FromReferenceToPointer { .. } => Some(format!("&{var_name}")),
})
}
}
diff --git a/engine/src/conversion/codegen_cpp/mod.rs b/engine/src/conversion/codegen_cpp/mod.rs
index 0e6dcce..bb341a8 100644
--- a/engine/src/conversion/codegen_cpp/mod.rs
+++ b/engine/src/conversion/codegen_cpp/mod.rs
@@ -20,11 +20,7 @@
use indexmap::set::IndexSet as HashSet;
use itertools::Itertools;
use std::borrow::Cow;
-use type_to_cpp::{original_name_map_from_apis, type_to_cpp, CppNameMap};
-
-use self::type_to_cpp::{
- final_ident_using_original_name_map, namespaced_name_using_original_name_map,
-};
+use type_to_cpp::CppNameMap;
use super::{
analysis::{
@@ -36,7 +32,7 @@
},
api::{Api, Provenance, SubclassName, TypeKind},
apivec::ApiVec,
- ConvertError,
+ ConvertErrorFromCpp,
};
#[derive(Ord, PartialOrd, Eq, PartialEq, Clone, Hash)]
@@ -55,17 +51,17 @@
) -> String {
let blank = "".to_string();
match self {
- Self::System(name) => format!("#include <{}>", name),
+ Self::System(name) => format!("#include <{name}>"),
Self::CxxH => {
let prefix = cpp_codegen_options.path_to_cxx_h.as_ref().unwrap_or(&blank);
- format!("#include \"{}cxx.h\"", prefix)
+ format!("#include \"{prefix}cxx.h\"")
}
Self::CxxgenH => {
let prefix = cpp_codegen_options
.path_to_cxxgen_h
.as_ref()
.unwrap_or(&blank);
- format!("#include \"{}{}\"", prefix, cxxgen_header_name)
+ format!("#include \"{prefix}{cxxgen_header_name}\"")
}
Header::NewDeletePrelude => new_and_delete_prelude::NEW_AND_DELETE_PRELUDE.to_string(),
}
@@ -120,11 +116,11 @@
config: &'a IncludeCppConfig,
cpp_codegen_options: &CppCodegenOptions,
cxxgen_header_name: &str,
- ) -> Result<Option<CppFilePair>, ConvertError> {
+ ) -> Result<Option<CppFilePair>, ConvertErrorFromCpp> {
let mut gen = CppCodeGenerator {
additional_functions: Vec::new(),
inclusions,
- original_name_map: original_name_map_from_apis(apis),
+ original_name_map: CppNameMap::new_from_apis(apis),
config,
cpp_codegen_options,
cxxgen_header_name,
@@ -139,7 +135,7 @@
fn add_needs<'b>(
&mut self,
apis: impl Iterator<Item = &'a Api<FnPhase>>,
- ) -> Result<(), ConvertError> {
+ ) -> Result<(), ConvertErrorFromCpp> {
let mut constructors_by_subclass: HashMap<SubclassName, Vec<&CppFunction>> = HashMap::new();
let mut methods_by_subclass: HashMap<SubclassName, Vec<SubclassFunction>> = HashMap::new();
let mut deferred_apis = Vec::new();
@@ -172,7 +168,7 @@
} => {
let effective_cpp_definition = match rs_definition {
Some(rs_definition) => {
- Cow::Owned(type_to_cpp(rs_definition, &self.original_name_map)?)
+ Cow::Owned(self.original_name_map.type_to_cpp(rs_definition)?)
}
None => Cow::Borrowed(cpp_definition),
};
@@ -248,10 +244,8 @@
.any(|x| x.definition.is_some())
{
let definitions = self.concat_additional_items(|x| x.definition.as_ref());
- let definitions = format!(
- "#include \"{}\"\n{}\n{}",
- header_name, cpp_headers, definitions
- );
+ let definitions =
+ format!("#include \"{header_name}\"\n{cpp_headers}\n{definitions}");
log::info!("Additional C++ defs:\n{}", definitions);
Some(definitions.into_bytes())
} else {
@@ -302,7 +296,7 @@
// can result in destructors for nested types being called multiple times
// if we represent them as trivial types. So generate an extra
// assertion to make sure.
- let declaration = Some(format!("static_assert(::rust::IsRelocatable<{}>::value, \"type {} should be trivially move constructible and trivially destructible to be used with generate_pod! in autocxx\");", name, name));
+ let declaration = Some(format!("static_assert(::rust::IsRelocatable<{name}>::value, \"type {name} should be trivially move constructible and trivially destructible to be used with generate_pod! in autocxx\");"));
self.additional_functions.push(ExtraCpp {
declaration,
headers: vec![Header::CxxH],
@@ -312,7 +306,7 @@
fn generate_string_constructor(&mut self) {
let makestring_name = self.config.get_makestring_name();
- let declaration = Some(format!("inline std::unique_ptr<std::string> {}(::rust::Str str) {{ return std::make_unique<std::string>(std::string(str)); }}", makestring_name));
+ let declaration = Some(format!("inline std::unique_ptr<std::string> {makestring_name}(::rust::Str str) {{ return std::make_unique<std::string>(std::string(str)); }}"));
self.additional_functions.push(ExtraCpp {
declaration,
headers: vec![
@@ -324,7 +318,7 @@
})
}
- fn generate_cpp_function(&mut self, details: &CppFunction) -> Result<(), ConvertError> {
+ fn generate_cpp_function(&mut self, details: &CppFunction) -> Result<(), ConvertErrorFromCpp> {
self.additional_functions
.push(self.generate_cpp_function_inner(
details,
@@ -343,7 +337,7 @@
conversion_direction: ConversionDirection,
requires_rust_declarations: bool,
force_name: Option<&str>,
- ) -> Result<ExtraCpp, ConvertError> {
+ ) -> Result<ExtraCpp, ConvertErrorFromCpp> {
// Even if the original function call is in a namespace,
// we generate this wrapper in the global namespace.
// We could easily do this the other way round, and when
@@ -373,7 +367,7 @@
// may be able to remove this.
"autocxx_gen_this".to_string()
} else {
- format!("arg{}", counter)
+ format!("arg{counter}")
}
};
// If this returns a non-POD value, we may instead wish to emplace
@@ -425,16 +419,13 @@
CppFunctionKind::ConstMethod => " const",
_ => "",
};
- let declaration = format!("{} {}({}){}", ret_type, name, args, constness);
+ let declaration = format!("{ret_type} {name}({args}){constness}");
let qualification = if let Some(qualification) = &details.qualification {
format!("{}::", qualification.to_cpp_name())
} else {
"".to_string()
};
- let qualified_declaration = format!(
- "{} {}{}({}){}",
- ret_type, qualification, name, args, constness
- );
+ let qualified_declaration = format!("{ret_type} {qualification}{name}({args}){constness}");
// Whether there's a placement param in which to put the return value
let placement_param = details
.argument_conversion
@@ -491,13 +482,38 @@
)
}
CppFunctionBody::Destructor(ns, id) => {
- let ty_id = QualifiedName::new(ns, id.clone());
- let ty_id = final_ident_using_original_name_map(&ty_id, &self.original_name_map);
- (format!("{}->~{}()", arg_list, ty_id), "".to_string(), false)
+ let full_name = QualifiedName::new(ns, id.clone());
+ let ty_id = self.original_name_map.get_final_item(&full_name);
+ let is_a_nested_struct = self.original_name_map.get(&full_name).is_some();
+ // This is all super duper fiddly.
+ // All we want to do is call a destructor. Constraints:
+ // * an unnamed struct, e.g. typedef struct { .. } A, does not
+ // have any way of fully qualifying its destructor name.
+ // We have to use a 'using' statement.
+ // * we don't get enough information from bindgen to distinguish
+ // typedef struct { .. } A // unnamed struct
+ // from
+ // struct A { .. } // named struct
+ // * we can only do 'using A::B::C' if 'B' is a namespace,
+ // as opposed to a type with an inner type.
+ // * we can always do 'using C = A::B::C' but then SOME C++
+ // compilers complain that it's unused, iff it's a named struct.
+ let destructor_call = format!("{arg_list}->{ty_id}::~{ty_id}()");
+ let destructor_call = if ns.is_empty() {
+ destructor_call
+ } else {
+ let path = self.original_name_map.map(&full_name);
+ if is_a_nested_struct {
+ format!("{{ using {ty_id} = {path}; {destructor_call}; {ty_id}* pointless; (void)pointless; }}")
+ } else {
+ format!("{{ using {path}; {destructor_call}; }}")
+ }
+ };
+ (destructor_call, "".to_string(), false)
}
CppFunctionBody::FunctionCall(ns, id) => match receiver {
Some(receiver) => (
- format!("{}.{}({})", receiver, id, arg_list),
+ format!("{receiver}.{id}({arg_list})"),
"".to_string(),
false,
),
@@ -508,7 +524,7 @@
.chain(std::iter::once(id.to_string()))
.join("::");
(
- format!("{}({})", underlying_function_call, arg_list),
+ format!("{underlying_function_call}({arg_list})"),
"".to_string(),
false,
)
@@ -521,7 +537,7 @@
.chain([ty_id.to_string(), fn_id.to_string()].iter().cloned())
.join("::");
(
- format!("{}({})", underlying_function_call, arg_list),
+ format!("{underlying_function_call}({arg_list})"),
"".to_string(),
false,
)
@@ -530,7 +546,7 @@
CppFunctionBody::AllocUninitialized(ty) => {
let namespaced_ty = self.namespaced_name(ty);
(
- format!("new_appropriately<{}>();", namespaced_ty,),
+ format!("new_appropriately<{namespaced_ty}>();",),
"".to_string(),
true,
)
@@ -559,39 +575,35 @@
underlying_function_call = match placement_param {
Some(placement_param) => {
- let tyname = type_to_cpp(ret.cxxbridge_type(), &self.original_name_map)?;
- format!("new({}) {}({})", placement_param, tyname, call_itself)
+ let tyname = self.original_name_map.type_to_cpp(ret.cxxbridge_type())?;
+ format!("new({placement_param}) {tyname}({call_itself})")
}
- None => format!("return {}", call_itself),
+ None => format!("return {call_itself}"),
};
};
if !underlying_function_call.is_empty() {
- underlying_function_call = format!("{};", underlying_function_call);
+ underlying_function_call = format!("{underlying_function_call};");
}
let field_assignments =
if let CppFunctionBody::ConstructSuperclass(superclass_name) = &details.payload {
let superclass_assignments = if field_assignments.is_empty() {
"".to_string()
} else {
- format!("{}({}), ", superclass_name, field_assignments)
+ format!("{superclass_name}({field_assignments}), ")
};
- format!(": {}obs(std::move(arg0))", superclass_assignments)
+ format!(": {superclass_assignments}obs(std::move(arg0))")
} else {
"".into()
};
- let definition_after_sig =
- format!("{} {{ {} }}", field_assignments, underlying_function_call,);
+ let definition_after_sig = format!("{field_assignments} {{ {underlying_function_call} }}",);
let (declaration, definition) = if requires_rust_declarations {
(
- Some(format!("{};", declaration)),
- Some(format!(
- "{} {}",
- qualified_declaration, definition_after_sig
- )),
+ Some(format!("{declaration};")),
+ Some(format!("{qualified_declaration} {definition_after_sig}")),
)
} else {
(
- Some(format!("inline {} {}", declaration, definition_after_sig)),
+ Some(format!("inline {declaration} {definition_after_sig}")),
None,
)
};
@@ -609,7 +621,7 @@
}
fn namespaced_name(&self, name: &QualifiedName) -> String {
- namespaced_name_using_original_name_map(name, &self.original_name_map)
+ self.original_name_map.map(name)
}
fn generate_ctype_typedef(&mut self, tn: &QualifiedName) {
@@ -620,7 +632,7 @@
fn generate_typedef(&mut self, tn: &QualifiedName, definition: &str) {
let our_name = tn.get_final_item();
self.additional_functions.push(ExtraCpp {
- type_definition: Some(format!("typedef {} {};", definition, our_name)),
+ type_definition: Some(format!("typedef {definition} {our_name};")),
..Default::default()
})
}
@@ -631,10 +643,10 @@
subclass: &SubclassName,
constructors: Vec<&CppFunction>,
methods: Vec<SubclassFunction>,
- ) -> Result<(), ConvertError> {
+ ) -> Result<(), ConvertErrorFromCpp> {
let holder = subclass.holder();
self.additional_functions.push(ExtraCpp {
- type_definition: Some(format!("struct {};", holder)),
+ type_definition: Some(format!("struct {holder};")),
..Default::default()
});
let mut method_decls = Vec::new();
@@ -677,12 +689,10 @@
// In future, for each superclass..
let super_name = superclass.get_final_item();
method_decls.push(format!(
- "const {}& As_{}() const {{ return *this; }}",
- super_name, super_name,
+ "const {super_name}& As_{super_name}() const {{ return *this; }}",
));
method_decls.push(format!(
- "{}& As_{}_mut() {{ return *this; }}",
- super_name, super_name
+ "{super_name}& As_{super_name}_mut() {{ return *this; }}"
));
self.additional_functions.push(ExtraCpp {
declaration: Some(format!(
diff --git a/engine/src/conversion/codegen_cpp/type_to_cpp.rs b/engine/src/conversion/codegen_cpp/type_to_cpp.rs
index 7febb7a..b2da44b 100644
--- a/engine/src/conversion/codegen_cpp/type_to_cpp.rs
+++ b/engine/src/conversion/codegen_cpp/type_to_cpp.rs
@@ -7,7 +7,7 @@
// except according to those terms.
use crate::{
- conversion::{apivec::ApiVec, AnalysisPhase, ConvertError},
+ conversion::{apivec::ApiVec, AnalysisPhase, ConvertErrorFromCpp},
types::QualifiedName,
};
use indexmap::map::IndexMap as HashMap;
@@ -19,116 +19,129 @@
/// Map from QualifiedName to original C++ name. Original C++ name does not
/// include the namespace; this can be assumed to be the same as the namespace
/// in the QualifiedName.
-pub(crate) type CppNameMap = HashMap<QualifiedName, String>;
+/// The "original C++ name" is mostly relevant in the case of nested types,
+/// where the typename might be A::B within a namespace C::D.
+pub(crate) struct CppNameMap(HashMap<QualifiedName, String>);
-pub(crate) fn original_name_map_from_apis<T: AnalysisPhase>(apis: &ApiVec<T>) -> CppNameMap {
- apis.iter()
- .filter_map(|api| {
- api.cpp_name()
- .as_ref()
- .map(|cpp_name| (api.name().clone(), cpp_name.clone()))
- })
- .collect()
-}
-
-pub(crate) fn namespaced_name_using_original_name_map(
- qual_name: &QualifiedName,
- original_name_map: &CppNameMap,
-) -> String {
- if let Some(cpp_name) = original_name_map.get(qual_name) {
- qual_name
- .get_namespace()
- .iter()
- .chain(once(cpp_name))
- .join("::")
- } else {
- qual_name.to_cpp_name()
- }
-}
-
-pub(crate) fn final_ident_using_original_name_map(
- qual_name: &QualifiedName,
- original_name_map: &CppNameMap,
-) -> String {
- match original_name_map.get(qual_name) {
- Some(original_name) => {
- // If we have an original name, this may be a nested struct
- // (e.g. A::B). The final ident here is just 'B' so...
- original_name
- .rsplit_once("::")
- .map_or(original_name.clone(), |(_, original_name)| {
- original_name.to_string()
+impl CppNameMap {
+ /// Look through the APIs we've found to assemble the original name
+ /// map.
+ pub(crate) fn new_from_apis<T: AnalysisPhase>(apis: &ApiVec<T>) -> Self {
+ Self(
+ apis.iter()
+ .filter_map(|api| {
+ api.cpp_name()
+ .as_ref()
+ .map(|cpp_name| (api.name().clone(), cpp_name.clone()))
})
- }
- None => qual_name.get_final_cpp_item(),
+ .collect(),
+ )
}
-}
-pub(crate) fn type_to_cpp(ty: &Type, cpp_name_map: &CppNameMap) -> Result<String, ConvertError> {
- match ty {
- Type::Path(typ) => {
- // If this is a std::unique_ptr we do need to pass
- // its argument through.
- let qual_name = QualifiedName::from_type_path(typ);
- let root = namespaced_name_using_original_name_map(&qual_name, cpp_name_map);
- if root == "Pin" {
- // Strip all Pins from type names when describing them in C++.
- let inner_type = &typ.path.segments.last().unwrap().arguments;
- if let syn::PathArguments::AngleBracketed(ab) = inner_type {
- let inner_type = ab.args.iter().next().unwrap();
- if let syn::GenericArgument::Type(gat) = inner_type {
- return type_to_cpp(gat, cpp_name_map);
- }
- }
- panic!("Pin<...> didn't contain the inner types we expected");
- }
- let suffix = match &typ.path.segments.last().unwrap().arguments {
- syn::PathArguments::AngleBracketed(ab) => {
- let results: Result<Vec<_>, _> = ab
- .args
- .iter()
- .map(|x| match x {
- syn::GenericArgument::Type(gat) => type_to_cpp(gat, cpp_name_map),
- _ => Ok("".to_string()),
- })
- .collect();
- Some(results?.join(", "))
- }
- syn::PathArguments::None | syn::PathArguments::Parenthesized(_) => None,
- };
- match suffix {
- None => Ok(root),
- Some(suffix) => Ok(format!("{}<{}>", root, suffix)),
- }
+ /// Imagine a nested struct in namespace::outer::inner
+ /// This function converts from the bindgen name, namespace::outer_inner,
+ /// to namespace::outer::inner.
+ pub(crate) fn map(&self, qual_name: &QualifiedName) -> String {
+ if let Some(cpp_name) = self.0.get(qual_name) {
+ qual_name
+ .get_namespace()
+ .iter()
+ .chain(once(cpp_name))
+ .join("::")
+ } else {
+ qual_name.to_cpp_name()
}
- Type::Reference(typr) => match &*typr.elem {
- Type::Path(typ) if typ.path.is_ident("str") => Ok("rust::Str".into()),
- _ => Ok(format!(
- "{}{}&",
- get_mut_string(&typr.mutability),
- type_to_cpp(typr.elem.as_ref(), cpp_name_map)?
+ }
+
+ /// Get a stringified version of the last ident in the name.
+ /// e.g. for namespace::outer_inner this will return inner.
+ /// This is useful for doing things such as calling constructors
+ /// such as inner() or destructors such as ~inner()
+ pub(crate) fn get_final_item<'b>(&'b self, qual_name: &'b QualifiedName) -> &'b str {
+ match self.get(qual_name) {
+ Some(n) => match n.rsplit_once("::") {
+ Some((_, suffix)) => suffix,
+ None => qual_name.get_final_item(),
+ },
+ None => qual_name.get_final_item(),
+ }
+ }
+
+ /// Convert a type to its C++ spelling.
+ pub(crate) fn type_to_cpp(&self, ty: &Type) -> Result<String, ConvertErrorFromCpp> {
+ match ty {
+ Type::Path(typ) => {
+ // If this is a std::unique_ptr we do need to pass
+ // its argument through.
+ let qual_name = QualifiedName::from_type_path(typ);
+ let root = self.map(&qual_name);
+ if root == "Pin" {
+ // Strip all Pins from type names when describing them in C++.
+ let inner_type = &typ.path.segments.last().unwrap().arguments;
+ if let syn::PathArguments::AngleBracketed(ab) = inner_type {
+ let inner_type = ab.args.iter().next().unwrap();
+ if let syn::GenericArgument::Type(gat) = inner_type {
+ return self.type_to_cpp(gat);
+ }
+ }
+ panic!("Pin<...> didn't contain the inner types we expected");
+ }
+ let suffix = match &typ.path.segments.last().unwrap().arguments {
+ syn::PathArguments::AngleBracketed(ab) => {
+ let results: Result<Vec<_>, _> = ab
+ .args
+ .iter()
+ .map(|x| match x {
+ syn::GenericArgument::Type(gat) => self.type_to_cpp(gat),
+ _ => Ok("".to_string()),
+ })
+ .collect();
+ Some(results?.join(", "))
+ }
+ syn::PathArguments::None | syn::PathArguments::Parenthesized(_) => None,
+ };
+ match suffix {
+ None => Ok(root),
+ Some(suffix) => Ok(format!("{root}<{suffix}>")),
+ }
+ }
+ Type::Reference(typr) => match &*typr.elem {
+ Type::Path(typ) if typ.path.is_ident("str") => Ok("rust::Str".into()),
+ _ => Ok(format!(
+ "{}{}&",
+ get_mut_string(&typr.mutability),
+ self.type_to_cpp(typr.elem.as_ref())?
+ )),
+ },
+ Type::Ptr(typp) => Ok(format!(
+ "{}{}*",
+ get_mut_string(&typp.mutability),
+ self.type_to_cpp(typp.elem.as_ref())?
)),
- },
- Type::Ptr(typp) => Ok(format!(
- "{}{}*",
- get_mut_string(&typp.mutability),
- type_to_cpp(typp.elem.as_ref(), cpp_name_map)?
- )),
- Type::Array(_)
- | Type::BareFn(_)
- | Type::Group(_)
- | Type::ImplTrait(_)
- | Type::Infer(_)
- | Type::Macro(_)
- | Type::Never(_)
- | Type::Paren(_)
- | Type::Slice(_)
- | Type::TraitObject(_)
- | Type::Tuple(_)
- | Type::Verbatim(_) => Err(ConvertError::UnsupportedType(
- ty.to_token_stream().to_string(),
- )),
- _ => Err(ConvertError::UnknownType(ty.to_token_stream().to_string())),
+ Type::Array(_)
+ | Type::BareFn(_)
+ | Type::Group(_)
+ | Type::ImplTrait(_)
+ | Type::Infer(_)
+ | Type::Macro(_)
+ | Type::Never(_)
+ | Type::Paren(_)
+ | Type::Slice(_)
+ | Type::TraitObject(_)
+ | Type::Tuple(_)
+ | Type::Verbatim(_) => Err(ConvertErrorFromCpp::UnsupportedType(
+ ty.to_token_stream().to_string(),
+ )),
+ _ => Err(ConvertErrorFromCpp::UnknownType(
+ ty.to_token_stream().to_string(),
+ )),
+ }
+ }
+
+ /// Check an individual item in the name map. Returns a thing if
+ /// it's an inner type, otherwise returns none.
+ pub(crate) fn get(&self, name: &QualifiedName) -> Option<&String> {
+ self.0.get(name)
}
}
diff --git a/engine/src/conversion/codegen_rs/fun_codegen.rs b/engine/src/conversion/codegen_rs/fun_codegen.rs
index db222a6..1b68e8c 100644
--- a/engine/src/conversion/codegen_rs/fun_codegen.rs
+++ b/engine/src/conversion/codegen_rs/fun_codegen.rs
@@ -34,6 +34,7 @@
},
api::{Pointerness, UnsafetyNeeded},
},
+ minisyn::minisynize_vec,
types::{Namespace, QualifiedName},
};
use crate::{
@@ -104,7 +105,7 @@
let params = analysis.params;
let vis = analysis.vis;
let kind = analysis.kind;
- let doc_attrs = fun.doc_attrs;
+ let doc_attrs = minisynize_vec(fun.doc_attrs);
let mut cpp_name_attr = Vec::new();
let mut impl_entry = None;
@@ -132,7 +133,6 @@
params,
Cow::Borrowed(&ret_type),
non_pod_types,
- true,
);
if analysis.rust_wrapper_needed {
@@ -170,10 +170,10 @@
FnKind::Method { .. } | FnKind::TraitMethod { .. } => None,
FnKind::Function => match analysis.rust_rename_strategy {
_ if analysis.rust_wrapper_needed => {
- Some(Use::SpecificNameFromBindgen(make_ident(rust_name)))
+ Some(Use::SpecificNameFromBindgen(make_ident(rust_name).into()))
}
RustRenameStrategy::RenameInOutputMod(ref alias) => {
- Some(Use::UsedFromCxxBridgeWithAlias(alias.clone()))
+ Some(Use::UsedFromCxxBridgeWithAlias(alias.clone().into()))
}
_ => Some(Use::UsedFromCxxBridge),
},
@@ -259,14 +259,14 @@
let mut ptr_arg_name = None;
let mut ret_type: Cow<'a, _> = ret_type
.map(Cow::Owned)
- .unwrap_or(Cow::Borrowed(self.ret_type));
+ .unwrap_or_else(|| Cow::Borrowed(self.ret_type));
let mut any_conversion_requires_unsafe = false;
let mut variable_counter = 0usize;
for pd in self.param_details {
- let wrapper_arg_name = if pd.self_type.is_some() && !avoid_self {
+ let wrapper_arg_name: syn::Pat = if pd.self_type.is_some() && !avoid_self {
parse_quote!(self)
} else {
- pd.name.clone()
+ pd.name.clone().into()
};
let rust_for_param = pd
.conversion
@@ -308,7 +308,6 @@
wrapper_params,
ret_type,
self.non_pod_types,
- false,
);
let cxxbridge_name = self.cxxbridge_name;
@@ -322,6 +321,17 @@
|| self.always_unsafe_due_to_trait_definition;
let (call_body, ret_type) = match self.ret_conversion {
Some(ret_conversion) if ret_conversion.rust_work_needed() => {
+ // There's a potential lurking bug below. If the return type conversion requires
+ // unsafe, then we'll end up doing something like
+ // unsafe { do_return_conversion( unsafe { call_body() })}
+ // and the generated code will get warnings about nested unsafe blocks.
+ // That's because we convert the call body to tokens in the following
+ // line without considering the fact it's embedded in another expression.
+ // At the moment this is OK because no return type conversions require
+ // unsafe, but if this happens in future, we should do:
+ // let temp_ret_val = unsafe { call_body() };
+ // do_return_conversion(temp_ret_val)
+ // by returning a vector of MaybeUnsafes within call_body.
let expr = maybe_unsafes_to_tokens(vec![call_body], context_is_unsafe);
let conv =
ret_conversion.rust_conversion(parse_quote! { #expr }, &mut variable_counter);
@@ -391,33 +401,26 @@
.map(|pd| pd.conversion.is_a_pointer())
.unwrap_or(Pointerness::Not);
let ty = impl_block_type_name.get_final_ident();
- let ty = if self.reference_wrappers {
- match receiver_pointerness {
- Pointerness::MutPtr => ImplBlockKey {
- ty: parse_quote! {
- CppMutRef< 'a, #ty>
- },
- lifetime: Some(parse_quote! { 'a }),
+ let ty = match receiver_pointerness {
+ Pointerness::MutPtr if self.reference_wrappers => ImplBlockKey {
+ ty: parse_quote! {
+ #ty
},
- Pointerness::ConstPtr => ImplBlockKey {
- ty: parse_quote! {
- CppRef< 'a, #ty>
- },
- lifetime: Some(parse_quote! { 'a }),
+ lifetime: Some(parse_quote! { 'a }),
+ },
+ Pointerness::ConstPtr if self.reference_wrappers => ImplBlockKey {
+ ty: parse_quote! {
+ #ty
},
- Pointerness::Not => ImplBlockKey {
- ty: parse_quote! { # ty },
- lifetime: None,
- },
- }
- } else {
- ImplBlockKey {
+ lifetime: Some(parse_quote! { 'a }),
+ },
+ _ => ImplBlockKey {
ty: parse_quote! { # ty },
lifetime: None,
- }
+ },
};
Box::new(ImplBlockDetails {
- item: ImplItem::Method(parse_quote! {
+ item: ImplItem::Fn(parse_quote! {
#(#doc_attrs)*
pub #unsafety fn #rust_name #lifetime_tokens ( #wrapper_params ) #ret_type {
#call_body
@@ -453,7 +456,7 @@
let ret_type: ReturnType = parse_quote! { -> impl autocxx::moveit::new::New<Output=Self> };
let (lifetime_tokens, wrapper_params, ret_type, call_body) =
self.common_parts(true, &None, Some(ret_type));
- let rust_name = make_ident(&self.rust_name);
+ let rust_name = make_ident(self.rust_name);
let doc_attrs = self.doc_attrs;
let unsafety = self.unsafety.wrapper_token();
let ty = impl_block_type_name.get_final_ident();
@@ -465,7 +468,7 @@
}
};
Box::new(ImplBlockDetails {
- item: ImplItem::Method(parse_quote! { #stuff }),
+ item: ImplItem::Fn(parse_quote! { #stuff }),
ty: ImplBlockKey { ty, lifetime: None },
})
}
diff --git a/engine/src/conversion/codegen_rs/function_wrapper_rs.rs b/engine/src/conversion/codegen_rs/function_wrapper_rs.rs
index 708d41c..4afe3c7 100644
--- a/engine/src/conversion/codegen_rs/function_wrapper_rs.rs
+++ b/engine/src/conversion/codegen_rs/function_wrapper_rs.rs
@@ -63,11 +63,11 @@
}
RustConversionType::FromPinMaybeUninitToPtr => {
let ty = match self.cxxbridge_type() {
- Type::Ptr(TypePtr { elem, .. }) => &*elem,
+ Type::Ptr(TypePtr { elem, .. }) => elem,
_ => panic!("Not a ptr"),
};
let ty = parse_quote! {
- ::std::pin::Pin<&mut ::std::mem::MaybeUninit< #ty >>
+ ::core::pin::Pin<&mut ::core::mem::MaybeUninit< #ty >>
};
RustParamConversion::Param {
ty,
@@ -80,17 +80,17 @@
}
RustConversionType::FromPinMoveRefToPtr => {
let ty = match self.cxxbridge_type() {
- Type::Ptr(TypePtr { elem, .. }) => &*elem,
+ Type::Ptr(TypePtr { elem, .. }) => elem,
_ => panic!("Not a ptr"),
};
let ty = parse_quote! {
- ::std::pin::Pin<autocxx::moveit::MoveRef< '_, #ty >>
+ ::core::pin::Pin<autocxx::moveit::MoveRef< '_, #ty >>
};
RustParamConversion::Param {
ty,
local_variables: Vec::new(),
conversion: quote! {
- { let r: &mut _ = ::std::pin::Pin::into_inner_unchecked(#var.as_mut());
+ { let r: &mut _ = ::core::pin::Pin::into_inner_unchecked(#var.as_mut());
r
}
},
@@ -99,7 +99,7 @@
}
RustConversionType::FromTypeToPtr => {
let ty = match self.cxxbridge_type() {
- Type::Ptr(TypePtr { elem, .. }) => &*elem,
+ Type::Ptr(TypePtr { elem, .. }) => elem,
_ => panic!("Not a ptr"),
};
let ty = parse_quote! { &mut #ty };
@@ -124,7 +124,7 @@
let param_trait = make_ident(param_trait);
let var_counter = *counter;
*counter += 1;
- let space_var_name = format!("space{}", var_counter);
+ let space_var_name = format!("space{var_counter}");
let space_var_name = make_ident(space_var_name);
let ty = self.cxxbridge_type();
let ty = parse_quote! { impl autocxx::#param_trait<#ty> };
@@ -138,10 +138,10 @@
),
MaybeUnsafeStmt::binary(
quote! { let mut #space_var_name =
- unsafe { ::std::pin::Pin::new_unchecked(&mut #space_var_name) };
+ unsafe { ::core::pin::Pin::new_unchecked(&mut #space_var_name) };
},
quote! { let mut #space_var_name =
- ::std::pin::Pin::new_unchecked(&mut #space_var_name);
+ ::core::pin::Pin::new_unchecked(&mut #space_var_name);
},
),
MaybeUnsafeStmt::needs_unsafe(
@@ -172,16 +172,16 @@
_ => panic!("Not a pointer"),
};
let (ty, wrapper_name) = if is_mut {
- (parse_quote! { CppMutRef<'a, #ty> }, "CppMutRef")
+ (parse_quote! { autocxx::CppMutRef<'a, #ty> }, "CppMutRef")
} else {
- (parse_quote! { CppRef<'a, #ty> }, "CppRef")
+ (parse_quote! { autocxx::CppRef<'a, #ty> }, "CppRef")
};
let wrapper_name = make_ident(wrapper_name);
RustParamConversion::Param {
ty,
local_variables: Vec::new(),
conversion: quote! {
- #wrapper_name (#var, std::marker::PhantomData)
+ autocxx::#wrapper_name::from_ptr (#var)
},
conversion_requires_unsafe: false,
}
@@ -194,15 +194,21 @@
_ => panic!("Not a pointer"),
};
let ty = if is_mut {
- parse_quote! { &mut CppMutRef<'a, #ty> }
+ parse_quote! { &mut autocxx::CppMutRef<'a, #ty> }
} else {
- parse_quote! { &CppRef<'a, #ty> }
+ parse_quote! { &autocxx::CppRef<'a, #ty> }
};
RustParamConversion::Param {
ty,
local_variables: Vec::new(),
- conversion: quote! {
- #var .0
+ conversion: if is_mut {
+ quote! {
+ #var .as_mut_ptr()
+ }
+ } else {
+ quote! {
+ #var .as_ptr()
+ }
},
conversion_requires_unsafe: false,
}
diff --git a/engine/src/conversion/codegen_rs/lifetime.rs b/engine/src/conversion/codegen_rs/lifetime.rs
index ea2c782..5560158 100644
--- a/engine/src/conversion/codegen_rs/lifetime.rs
+++ b/engine/src/conversion/codegen_rs/lifetime.rs
@@ -36,7 +36,6 @@
mut params: Punctuated<FnArg, Comma>,
ret_type: Cow<'r, ReturnType>,
non_pod_types: &HashSet<QualifiedName>,
- assert_all_parameters_are_references: bool,
) -> (
Option<TokenStream>,
Punctuated<FnArg, Comma>,
@@ -92,25 +91,16 @@
match new_return_type {
None => (None, params, ret_type),
Some(new_return_type) => {
- for mut param in params.iter_mut() {
- match &mut param {
- FnArg::Typed(PatType { ty, .. }) => match ty.as_mut() {
- Type::Path(TypePath {
- path: Path { segments, .. },
- ..
- }) => add_lifetime_to_pinned_reference(segments).unwrap_or_else(|e| {
- if assert_all_parameters_are_references {
- panic!("Expected a pinned reference: {:?}", e)
- }
- }),
- Type::Reference(tyr) => add_lifetime_to_reference(tyr),
- Type::ImplTrait(tyit) => add_lifetime_to_impl_trait(tyit),
- _ if assert_all_parameters_are_references => {
- panic!("Expected Pin<&mut T> or &T")
- }
- _ => {}
- },
- _ if assert_all_parameters_are_references => panic!("Unexpected fnarg"),
+ for FnArg::Typed(PatType { ty, .. }) | FnArg::Receiver(syn::Receiver { ty, .. }) in
+ params.iter_mut()
+ {
+ match ty.as_mut() {
+ Type::Path(TypePath {
+ path: Path { segments, .. },
+ ..
+ }) => add_lifetime_to_pinned_reference(segments).unwrap_or(()),
+ Type::Reference(tyr) => add_lifetime_to_reference(tyr),
+ Type::ImplTrait(tyit) => add_lifetime_to_impl_trait(tyit),
_ => {}
}
}
@@ -168,16 +158,19 @@
}
fn add_lifetime_to_pinned_reference(
- segments: &mut Punctuated<PathSegment, syn::token::Colon2>,
+ segments: &mut Punctuated<PathSegment, syn::token::PathSep>,
) -> Result<(), AddLifetimeError> {
- static EXPECTED_SEGMENTS: &[(&str, bool)] = &[
- ("std", false),
- ("pin", false),
- ("Pin", true), // true = act on the arguments of this segment
+ static EXPECTED_SEGMENTS: &[(&[&str], bool)] = &[
+ (&["std", "core"], false),
+ (&["pin"], false),
+ (&["Pin"], true), // true = act on the arguments of this segment
];
for (seg, (expected_name, act)) in segments.iter_mut().zip(EXPECTED_SEGMENTS.iter()) {
- if seg.ident != expected_name {
+ if !expected_name
+ .iter()
+ .any(|expected_name| seg.ident == expected_name)
+ {
return Err(AddLifetimeError::WasNotPin);
}
if *act {
diff --git a/engine/src/conversion/codegen_rs/mod.rs b/engine/src/conversion/codegen_rs/mod.rs
index 5dd4cbb..abab612 100644
--- a/engine/src/conversion/codegen_rs/mod.rs
+++ b/engine/src/conversion/codegen_rs/mod.rs
@@ -28,13 +28,11 @@
};
use crate::{
- conversion::{
- codegen_rs::{
- non_pod_struct::{make_non_pod, new_non_pod_struct},
- unqualify::{unqualify_params, unqualify_ret_type},
- },
- doc_attr::get_doc_attrs,
+ conversion::codegen_rs::{
+ non_pod_struct::{make_non_pod, new_non_pod_struct},
+ unqualify::{unqualify_params, unqualify_ret_type},
},
+ minisyn::minisynize_punctuated,
types::{make_ident, Namespace, QualifiedName},
};
use impl_item_creator::create_impl_items;
@@ -51,15 +49,14 @@
},
api::{AnalysisPhase, Api, SubclassName, TypeKind, TypedefKind},
convert_error::ErrorContextType,
+ doc_attr::get_doc_attrs,
};
use super::{
api::{Layout, Provenance, RustSubclassFnDetails, SuperclassMethod, TraitImplSignature},
apivec::ApiVec,
- codegen_cpp::type_to_cpp::{
- namespaced_name_using_original_name_map, original_name_map_from_apis, CppNameMap,
- },
+ codegen_cpp::type_to_cpp::CppNameMap,
};
-use super::{convert_error::ErrorContext, ConvertError};
+use super::{convert_error::ErrorContext, ConvertErrorFromCpp};
use quote::quote;
#[derive(Clone, Hash, PartialEq, Eq)]
@@ -137,91 +134,6 @@
.to_vec()
}
-fn get_cppref_items() -> Vec<Item> {
- [
- Item::Struct(parse_quote! {
- #[repr(transparent)]
- pub struct CppRef<'a, T>(pub *const T, pub ::std::marker::PhantomData<&'a T>);
- }),
- Item::Impl(parse_quote! {
- impl<'a, T> autocxx::CppRef<'a, T> for CppRef<'a, T> {
- fn as_ptr(&self) -> *const T {
- self.0
- }
- }
- }),
- Item::Struct(parse_quote! {
- #[repr(transparent)]
- pub struct CppMutRef<'a, T>(pub *mut T, pub ::std::marker::PhantomData<&'a T>);
- }),
- Item::Impl(parse_quote! {
- impl<'a, T> autocxx::CppRef<'a, T> for CppMutRef<'a, T> {
- fn as_ptr(&self) -> *const T {
- self.0
- }
- }
- }),
- Item::Impl(parse_quote! {
- impl<'a, T> autocxx::CppMutRef<'a, T> for CppMutRef<'a, T> {
- fn as_mut_ptr(&self) -> *mut T {
- self.0
- }
- }
- }),
- Item::Impl(parse_quote! {
- impl<'a, T: ::cxx::private::UniquePtrTarget> CppMutRef<'a, T> {
- /// Create a const C++ reference from this mutable C++ reference.
- pub fn as_cpp_ref(&self) -> CppRef<'a, T> {
- use autocxx::CppRef;
- CppRef(self.as_ptr(), ::std::marker::PhantomData)
- }
- }
- }),
- Item::Struct(parse_quote! {
- /// "Pins" a `UniquePtr` to an object, so that C++-compatible references can be created.
- /// See [`::autocxx::CppPin`]
- #[repr(transparent)]
- pub struct CppUniquePtrPin<T: ::cxx::private::UniquePtrTarget>(::cxx::UniquePtr<T>);
- }),
- Item::Impl(parse_quote! {
- impl<'a, T: 'a + ::cxx::private::UniquePtrTarget> autocxx::CppPin<'a, T> for CppUniquePtrPin<T>
- {
- type CppRef = CppRef<'a, T>;
- type CppMutRef = CppMutRef<'a, T>;
- fn as_ptr(&self) -> *const T {
- // TODO add as_ptr to cxx to avoid the ephemeral reference
- self.0.as_ref().unwrap() as *const T
- }
- fn as_mut_ptr(&mut self) -> *mut T {
- unsafe { ::std::pin::Pin::into_inner_unchecked(self.0.as_mut().unwrap()) as *mut T }
- }
- fn as_cpp_ref(&self) -> Self::CppRef {
- CppRef(self.as_ptr(), ::std::marker::PhantomData)
- }
- fn as_cpp_mut_ref(&mut self) -> Self::CppMutRef {
- CppMutRef(self.as_mut_ptr(), ::std::marker::PhantomData)
- }
- }
- }),
- Item::Impl(parse_quote! {
- impl<T: ::cxx::private::UniquePtrTarget> CppUniquePtrPin<T> {
- pub fn new(item: ::cxx::UniquePtr<T>) -> Self {
- Self(item)
- }
- }
- }),
- Item::Fn(parse_quote! {
- /// Pin this item so that we can create C++ references to it.
- /// This makes it impossible to hold Rust references because Rust
- /// references are fundamentally incompatible with C++ references.
- pub fn cpp_pin_uniqueptr<T: ::cxx::private::UniquePtrTarget> (item: ::cxx::UniquePtr<T>) -> CppUniquePtrPin<T> {
- CppUniquePtrPin::new(item)
- }
- })
- ]
- .to_vec()
-}
-
/// Type which handles generation of Rust code.
/// In practice, much of the "generation" involves connecting together
/// existing lumps of code within the Api structures.
@@ -248,7 +160,7 @@
unsafe_policy,
include_list,
bindgen_mod,
- original_name_map: original_name_map_from_apis(&all_apis),
+ original_name_map: CppNameMap::new_from_apis(&all_apis),
config,
header_name,
};
@@ -314,9 +226,6 @@
let mut extern_rust_mod_items = extern_rust_mod_items.into_iter().flatten().collect();
// And a list of global items to include at the top level.
let mut all_items: Vec<Item> = all_items.into_iter().flatten().collect();
- if self.config.unsafe_policy.requires_cpprefs() {
- all_items.append(&mut get_cppref_items())
- }
// And finally any C++ we need to generate. And by "we" I mean autocxx not cxx.
let has_additional_cpp_needs = additional_cpp_needs.into_iter().any(std::convert::identity);
extern_c_mod_items.extend(self.build_include_foreign_items(has_additional_cpp_needs));
@@ -430,7 +339,7 @@
Use::UsedFromCxxBridge => Self::generate_cxx_use_stmt(name, None),
Use::UsedFromBindgen => Self::generate_bindgen_use_stmt(name),
Use::SpecificNameFromBindgen(id) => {
- let name = QualifiedName::new(name.get_namespace(), id.clone());
+ let name = QualifiedName::new(name.get_namespace(), id.clone().into());
Self::generate_bindgen_use_stmt(&name)
}
Use::Custom(item) => *item.clone(),
@@ -459,9 +368,6 @@
if !self.config.exclude_utilities() {
imports_from_super.push("ToCppString");
}
- if self.config.unsafe_policy.requires_cpprefs() {
- imports_from_super.extend(["CppRef", "CppMutRef"]);
- }
let imports_from_super = imports_from_super.into_iter().map(make_ident);
let super_duper = std::iter::repeat(make_ident("super")); // I'll get my coat
let supers = super_duper.clone().take(ns.depth() + 2);
@@ -576,9 +482,9 @@
fn #make_string_name(str_: &str) -> UniquePtr<CxxString>;
))],
global_items: get_string_items(),
- materializations: vec![Use::UsedFromCxxBridgeWithAlias(make_ident(
- "make_string",
- ))],
+ materializations: vec![Use::UsedFromCxxBridgeWithAlias(
+ make_ident("make_string").into(),
+ )],
..Default::default()
}
}
@@ -591,14 +497,14 @@
self.config,
),
Api::Const { const_item, .. } => RsCodegenResult {
- bindgen_mod_items: vec![Item::Const(const_item)],
+ bindgen_mod_items: vec![Item::Const(const_item.into())],
materializations: vec![Use::UsedFromBindgen],
..Default::default()
},
Api::Typedef { analysis, .. } => RsCodegenResult {
bindgen_mod_items: vec![match analysis.kind {
- TypedefKind::Type(type_item) => Item::Type(type_item),
- TypedefKind::Use(use_item, _) => Item::Use(use_item),
+ TypedefKind::Type(type_item) => Item::Type(type_item.into()),
+ TypedefKind::Use(use_item, _) => Item::Use(use_item.into()),
}],
materializations: vec![Use::UsedFromBindgen],
..Default::default()
@@ -624,7 +530,7 @@
kind,
constructors.move_constructor,
constructors.destructor,
- || Some((Item::Struct(details.item), doc_attrs)),
+ || Some((Item::Struct(details.item.into()), doc_attrs)),
associated_methods,
layout,
is_generic,
@@ -638,15 +544,13 @@
TypeKind::Pod,
true,
true,
- || Some((Item::Enum(item), doc_attrs)),
+ || Some((Item::Enum(item.into()), doc_attrs)),
associated_methods,
None,
false,
)
}
- Api::ForwardDeclaration { .. }
- | Api::ConcreteType { .. }
- | Api::OpaqueTypedef { .. } => self.generate_type(
+ Api::ConcreteType { .. } => self.generate_type(
&name,
id,
TypeKind::Abstract,
@@ -657,56 +561,68 @@
None,
false,
),
+ Api::ForwardDeclaration { .. } | Api::OpaqueTypedef { .. } => self.generate_type(
+ &name,
+ id,
+ TypeKind::Abstract,
+ false, // these types can't be kept in a Vector
+ false, // these types can't be put in a smart pointer
+ || None,
+ associated_methods,
+ None,
+ false,
+ ),
Api::CType { .. } => RsCodegenResult {
extern_c_mod_items: vec![ForeignItem::Verbatim(quote! {
type #id = autocxx::#id;
})],
..Default::default()
},
- Api::RustType { path, .. } => RsCodegenResult {
- global_items: vec![parse_quote! {
- use super::#path;
- }],
- extern_rust_mod_items: vec![parse_quote! {
- type #id;
- }],
- ..Default::default()
- },
+ Api::RustType { path, .. } => {
+ let id = path.get_final_ident();
+ RsCodegenResult {
+ global_items: vec![parse_quote! {
+ use super::#path;
+ }],
+ extern_rust_mod_items: vec![parse_quote! {
+ type #id;
+ }],
+ bindgen_mod_items: vec![parse_quote! {
+ #[allow(unused_imports)]
+ use super::super::#id;
+ }],
+ ..Default::default()
+ }
+ }
Api::RustFn {
details:
RustFun {
path,
- sig,
- receiver: None,
+ mut sig,
+ has_receiver,
..
},
..
- } => RsCodegenResult {
- global_items: vec![parse_quote! {
- use super::#path;
- }],
- extern_rust_mod_items: vec![parse_quote! {
- #sig;
- }],
- ..Default::default()
- },
- Api::RustFn {
- details:
- RustFun {
- sig,
- receiver: Some(_),
- ..
+ } => {
+ sig.inputs = unqualify_params(sig.inputs);
+ sig.output = unqualify_ret_type(sig.output);
+ RsCodegenResult {
+ global_items: if !has_receiver {
+ vec![parse_quote! {
+ use super::#path;
+ }]
+ } else {
+ Vec::new()
},
- ..
- } => RsCodegenResult {
- extern_rust_mod_items: vec![parse_quote! {
- #sig;
- }],
- ..Default::default()
- },
+ extern_rust_mod_items: vec![parse_quote! {
+ #sig;
+ }],
+ ..Default::default()
+ }
+ }
Api::RustSubclassFn {
details, subclass, ..
- } => Self::generate_subclass_fn(id, *details, subclass),
+ } => Self::generate_subclass_fn(id.into(), *details, subclass),
Api::Subclass {
name, superclass, ..
} => {
@@ -812,7 +728,7 @@
bindgen_mod_items.push(parse_quote! {
impl autocxx::subclass::CppPeerConstructor<#cpp_id> for super::super::super::#id {
fn make_peer(&mut self, peer_holder: autocxx::subclass::CppSubclassRustPeerHolder<Self>) -> cxx::UniquePtr<#cpp_path> {
- use autocxx::moveit::EmplaceUnpinned;
+ use autocxx::moveit::Emplace;
cxx::UniquePtr::emplace(#cpp_id :: new(peer_holder))
}
}
@@ -820,15 +736,15 @@
};
// Once for each superclass, in future...
- let as_id = make_ident(format!("As_{}", super_name));
+ let as_id = make_ident(format!("As_{super_name}"));
extern_c_mod_items.push(parse_quote! {
fn #as_id(self: &#cpp_id) -> &#super_cxxxbridge_id;
});
- let as_mut_id = make_ident(format!("As_{}_mut", super_name));
+ let as_mut_id = make_ident(format!("As_{super_name}_mut"));
extern_c_mod_items.push(parse_quote! {
fn #as_mut_id(self: Pin<&mut #cpp_id>) -> Pin<&mut #super_cxxxbridge_id>;
});
- let as_unique_ptr_id = make_ident(format!("{}_As_{}_UniquePtr", cpp_id, super_name));
+ let as_unique_ptr_id = make_ident(format!("{cpp_id}_As_{super_name}_UniquePtr"));
extern_c_mod_items.push(parse_quote! {
fn #as_unique_ptr_id(u: UniquePtr<#cpp_id>) -> UniquePtr<#super_cxxxbridge_id>;
});
@@ -843,13 +759,13 @@
// TODO it would be nice to impl AsMut here but pin prevents us
bindgen_mod_items.push(parse_quote! {
impl super::super::super::#id {
- pub fn pin_mut(&mut self) -> ::std::pin::Pin<&mut cxxbridge::#super_cxxxbridge_id> {
+ pub fn pin_mut(&mut self) -> ::core::pin::Pin<&mut cxxbridge::#super_cxxxbridge_id> {
use autocxx::subclass::CppSubclass;
self.peer_mut().#as_mut_id()
}
}
});
- let rs_as_unique_ptr_id = make_ident(format!("as_{}_unique_ptr", super_name));
+ let rs_as_unique_ptr_id = make_ident(format!("as_{super_name}_unique_ptr"));
bindgen_mod_items.push(parse_quote! {
impl super::super::super::#id {
pub fn #rs_as_unique_ptr_id(u: cxx::UniquePtr<#cpp_id>) -> cxx::UniquePtr<cxxbridge::#super_cxxxbridge_id> {
@@ -896,8 +812,8 @@
let ret = details.ret;
let unsafe_token = details.requires_unsafe.wrapper_token();
let global_def = quote! { #unsafe_token fn #api_name(#params) #ret };
- let params = unqualify_params(params);
- let ret = unqualify_ret_type(ret);
+ let params = unqualify_params(minisynize_punctuated(params));
+ let ret = unqualify_ret_type(ret.into());
let method_name = details.method_name;
let cxxbridge_decl: ForeignItemFn =
parse_quote! { #unsafe_token fn #api_name(#params) #ret; };
@@ -930,7 +846,7 @@
.as_ref()
.#borrow()
.expect(#reentrancy_panic_msg);
- let r = ::std::ops::#deref_ty::#deref_call(& #mut_token b);
+ let r = ::core::ops::#deref_ty::#deref_call(& #mut_token b);
#methods_trait :: #method_name
(r,
#args)
@@ -955,7 +871,7 @@
fn generate_type<F>(
&self,
name: &QualifiedName,
- id: Ident,
+ id: crate::minisyn::Ident,
type_kind: TypeKind,
movable: bool,
destroyable: bool,
@@ -1004,7 +920,7 @@
make_non_pod(s, layout);
} else {
// enum
- item = Item::Struct(new_non_pod_struct(id.clone()));
+ item = Item::Struct(new_non_pod_struct(id.clone().into()));
}
}
bindgen_mod_items.push(item);
@@ -1042,6 +958,7 @@
extern_c_mod_items: vec![
self.generate_cxxbridge_type(name, false, doc_attrs)
],
+ bridge_items: create_impl_items(&id, movable, destroyable, self.config),
bindgen_mod_items,
materializations,
..Default::default()
@@ -1065,8 +982,9 @@
let super_id =
SubclassName::get_super_fn_name(&Namespace::new(), &id.to_string())
.get_final_ident();
+ let params = minisynize_punctuated(method.params.clone());
let param_names: Punctuated<Expr, Comma> =
- Self::args_from_sig(&method.params).collect();
+ Self::args_from_sig(¶ms).collect();
let mut params = method.params.clone();
*(params.iter_mut().next().unwrap()) = match method.receiver_mutability {
ReceiverMutability::Const => parse_quote!(&self),
@@ -1110,7 +1028,7 @@
#(#mains)*
}
});
- materializations.push(Use::SpecificNameFromBindgen(supers_name));
+ materializations.push(Use::SpecificNameFromBindgen(supers_name.into()));
} else {
bindgen_mod_items.push(parse_quote! {
#[allow(non_snake_case)]
@@ -1119,7 +1037,7 @@
}
});
}
- materializations.push(Use::SpecificNameFromBindgen(methods_name));
+ materializations.push(Use::SpecificNameFromBindgen(methods_name.into()));
}
}
@@ -1132,13 +1050,16 @@
let id = name.type_path_from_root();
let super_duper = std::iter::repeat(make_ident("super"));
let supers = super_duper.take(ns_depth + 2);
+ let name_final = name.get_final_ident();
let use_statement = parse_quote! {
pub use #(#supers)::* :: #id;
};
RsCodegenResult {
bindgen_mod_items: vec![use_statement],
extern_c_mod_items: vec![self.generate_cxxbridge_type(name, true, Vec::new())],
- materializations: vec![Use::Custom(Box::new(parse_quote! { pub use #rust_path; }))],
+ materializations: vec![Use::Custom(Box::new(
+ parse_quote! { pub use #rust_path as #name_final; },
+ ))],
..Default::default()
}
}
@@ -1146,8 +1067,8 @@
/// Generates something in the output mod that will carry a docstring
/// explaining why a given type or function couldn't have bindings
/// generated.
- fn generate_error_entry(err: ConvertError, ctx: ErrorContext) -> RsCodegenResult {
- let err = format!("autocxx bindings couldn't be generated: {}", err);
+ fn generate_error_entry(err: ConvertErrorFromCpp, ctx: ErrorContext) -> RsCodegenResult {
+ let err = format!("autocxx bindings couldn't be generated: {err}");
let (impl_entry, bindgen_mod_item, materialization) = match ctx.into_type() {
ErrorContextType::Item(id) => (
// Populate within bindgen mod because impl blocks may attach.
@@ -1156,7 +1077,7 @@
#[doc = #err]
pub struct #id;
}),
- Some(Use::SpecificNameFromBindgen(id)),
+ Some(Use::SpecificNameFromBindgen(id.into())),
),
ErrorContextType::SanitizedItem(id) => (
// Guaranteed to be no impl blocks - populate directly in output mod.
@@ -1214,7 +1135,7 @@
}
fn generate_extern_type_impl(&self, type_kind: TypeKind, tyname: &QualifiedName) -> Vec<Item> {
- let tynamestring = namespaced_name_using_original_name_map(tyname, &self.original_name_map);
+ let tynamestring = self.original_name_map.map(tyname);
let fulltypath = tyname.get_bindgen_path_idents();
let kind_item = match type_kind {
TypeKind::Pod => "Trivial",
@@ -1288,7 +1209,7 @@
ForeignItem::Verbatim(for_extern_c_ts)
}
- fn find_output_mod_root(ns: &Namespace) -> impl Iterator<Item = Ident> {
+ fn find_output_mod_root(ns: &Namespace) -> impl Iterator<Item = crate::minisyn::Ident> {
std::iter::repeat(make_ident("super")).take(ns.depth())
}
}
diff --git a/engine/src/conversion/codegen_rs/non_pod_struct.rs b/engine/src/conversion/codegen_rs/non_pod_struct.rs
index e4bec2f..d9a75c8 100644
--- a/engine/src/conversion/codegen_rs/non_pod_struct.rs
+++ b/engine/src/conversion/codegen_rs/non_pod_struct.rs
@@ -55,7 +55,7 @@
let doc_attr = s
.attrs
.iter()
- .filter(|a| a.path.get_ident().iter().any(|p| *p == "doc"))
+ .filter(|a| a.path().get_ident().iter().any(|p| *p == "doc"))
.cloned();
let repr_attr = if let Some(layout) = &layout {
let align = make_lit_int(layout.align);
@@ -85,9 +85,9 @@
.filter_map(|(counter, gp)| match gp {
GenericParam::Type(gpt) => {
let id = &gpt.ident;
- let field_name = make_ident(&format!("_phantom_{}", counter));
+ let field_name = make_ident(format!("_phantom_{counter}"));
let toks = quote! {
- #field_name: ::std::marker::PhantomData<::std::cell::UnsafeCell< #id >>
+ #field_name: ::core::marker::PhantomData<::core::cell::UnsafeCell< #id >>
};
Some(Field::parse_named.parse2(toks).unwrap())
}
diff --git a/engine/src/conversion/conversion_tests.rs b/engine/src/conversion/conversion_tests.rs
index b0474d6..e74888e 100644
--- a/engine/src/conversion/conversion_tests.rs
+++ b/engine/src/conversion/conversion_tests.rs
@@ -11,7 +11,7 @@
use syn::parse_quote;
use syn::ItemMod;
-use crate::CppCodegenOptions;
+use crate::CodegenOptions;
use super::BridgeConverter;
@@ -33,7 +33,8 @@
input,
UnsafePolicy::AllFunctionsSafe,
inclusions,
- &CppCodegenOptions::default(),
+ &CodegenOptions::default(),
+ "",
)
.unwrap();
}
diff --git a/engine/src/conversion/convert_error.rs b/engine/src/conversion/convert_error.rs
index ba8344d..855b235 100644
--- a/engine/src/conversion/convert_error.rs
+++ b/engine/src/conversion/convert_error.rs
@@ -8,19 +8,35 @@
use indexmap::set::IndexSet as HashSet;
+use crate::minisyn::Ident;
use itertools::Itertools;
-use syn::Ident;
+use miette::{Diagnostic, SourceSpan};
+use proc_macro2::Span;
use thiserror::Error;
use crate::{
- known_types,
- types::{make_ident, Namespace, QualifiedName},
+ known_types, proc_macro_span_to_miette_span,
+ types::{make_ident, InvalidIdentError, Namespace, QualifiedName},
};
-#[derive(Debug, Clone, Error)]
+/// Errors which can occur during conversion
+#[derive(Debug, Clone, Error, Diagnostic)]
pub enum ConvertError {
#[error("The initial run of 'bindgen' did not generate any content. This might be because none of the requested items for generation could be converted.")]
NoContent,
+ #[error(transparent)]
+ Cpp(ConvertErrorFromCpp),
+ #[error(transparent)]
+ #[diagnostic(transparent)]
+ Rust(LocatedConvertErrorFromRust),
+}
+
+/// Errors that can occur during conversion which are detected from some C++
+/// source code. Currently, we do not gain span information from bindgen
+/// so these errors are presented without useful source code snippets.
+/// We hope to change this in future.
+#[derive(Debug, Clone, Error)]
+pub enum ConvertErrorFromCpp {
#[error("An item was requested using 'generate_pod' which was not safe to hold by value in Rust. {0}")]
UnsafePodType(String),
#[error("Bindgen generated some unexpected code in a foreign mod section. You may have specified something in a 'generate' directive which is not currently compatible with autocxx.")]
@@ -39,8 +55,14 @@
ConflictingTemplatedArgsWithTypedef(QualifiedName),
#[error("Function {0} has a parameter or return type which is either on the blocklist or a forward declaration")]
UnacceptableParam(String),
- #[error("Function {0} has a return reference parameter, but 0 or >1 input reference parameters, so the lifetime of the output reference cannot be deduced.")]
- NotOneInputReference(String),
+ #[error("Function {0} has a reference return value, but no reference parameters, so the lifetime of the output reference cannot be deduced.")]
+ NoInputReference(String),
+ #[error("Function {0} has a reference return value, but >1 input reference parameters, so the lifetime of the output reference cannot be deduced.")]
+ MultipleInputReferences(String),
+ #[error("Function {0} has a mutable reference return value, but no mutable reference parameters, so the lifetime of the output reference cannot be deduced.")]
+ NoMutableInputReference(String),
+ #[error("Function {0} has a mutable reference return value, but >1 input mutable reference parameters, so the lifetime of the output reference cannot be deduced.")]
+ MultipleMutableInputReferences(String),
#[error("Encountered type not yet supported by autocxx: {0}")]
UnsupportedType(String),
#[error("Encountered type not yet known by autocxx: {0}")]
@@ -53,8 +75,12 @@
UnexpectedUseStatement(Option<String>),
#[error("Type {} was parameterized over something complex which we don't yet support", .0.to_cpp_name())]
TemplatedTypeContainingNonPathArg(QualifiedName),
- #[error("Pointer pointed to something unsupported")]
- InvalidPointee,
+ #[error("Pointer pointed to an array, which is not yet supported")]
+ InvalidArrayPointee,
+ #[error("Pointer pointed to another pointer, which is not yet supported")]
+ InvalidPointerPointee,
+ #[error("Pointer pointed to something unsupported (autocxx only supports pointers to named types): {0}")]
+ InvalidPointee(String),
#[error("The 'generate' or 'generate_pod' directive for '{0}' did not result in any code being generated. Perhaps this was mis-spelled or you didn't qualify the name with any namespaces? Otherwise please report a bug.")]
DidNotGenerateAnything(String),
#[error("Found an attempt at using a forward declaration ({}) inside a templated cxx type such as UniquePtr or CxxVector. If the forward declaration is a typedef, perhaps autocxx wasn't sure whether or not it involved a forward declaration. If you're sure it didn't, then you may be able to solve this by using instantiable!.", .0.to_cpp_name())]
@@ -63,14 +89,12 @@
Blocked(QualifiedName),
#[error("This function or method uses a type where one of the template parameters was incomprehensible to bindgen/autocxx - probably because it uses template specialization.")]
UnusedTemplateParam,
- #[error("Names containing __ are reserved by C++ so not acceptable to cxx")]
- TooManyUnderscores,
#[error("This item relies on a type not known to autocxx ({})", .0.to_cpp_name())]
UnknownDependentType(QualifiedName),
#[error("This item depends on some other type(s) which autocxx could not generate, some of them are: {}", .0.iter().join(", "))]
IgnoredDependent(HashSet<QualifiedName>),
- #[error("The item name '{0}' is a reserved word in Rust.")]
- ReservedName(String),
+ #[error(transparent)]
+ InvalidIdent(InvalidIdentError),
#[error("This item name is used in multiple namespaces. At present, autocxx and cxx allow only one type of a given name. This limitation will be fixed in future. (Items found with this name: {})", .0.iter().join(", "))]
DuplicateCxxBridgeName(Vec<String>),
#[error("This is a method on a type which can't be used as the receiver in Rust (i.e. self/this). This is probably because some type involves template specialization.")]
@@ -123,29 +147,71 @@
MethodInAnonymousNamespace,
#[error("We're unable to make a concrete version of this template, because we found an error handling the template.")]
ConcreteVersionOfIgnoredTemplate,
- #[error("bindgen decided to call this type _bindgen_ty_N because it couldn't deduce the correct name for it. That means we can't generate C++ bindings to it.")]
- BindgenTy,
#[error("This is a typedef to a type in an anonymous namespace, not currently supported.")]
TypedefToTypeInAnonymousNamespace,
#[error("This type refers to a generic type parameter of an outer type, which is not yet supported.")]
ReferringToGenericTypeParam,
#[error("This forward declaration was nested within another struct/class. autocxx is unable to represent inner types if they are forward declarations.")]
ForwardDeclaredNestedType,
+ #[error("Problem handling function argument {arg}: {err}")]
+ Argument {
+ arg: String,
+ #[source]
+ err: Box<ConvertErrorFromCpp>,
+ },
+}
+
+/// Error types derived from Rust code. This is separate from [`ConvertError`] because these
+/// may have spans attached for better diagnostics.
+#[derive(Debug, Clone, Error)]
+pub enum ConvertErrorFromRust {
+ #[error("extern_rust_function only supports limited parameter and return types. This is not such a supported type")]
+ UnsupportedTypeForExternFun,
+ #[error("extern_rust_function requires a fully qualified receiver, that is: fn a(self: &SomeType) as opposed to fn a(&self)")]
+ ExternRustFunRequiresFullyQualifiedReceiver,
+ #[error("extern_rust_function cannot support &mut T references; instead use Pin<&mut T> (see cxx documentation for more details")]
+ PinnedReferencesRequiredForExternFun,
+ #[error("extern_rust_function cannot currently support qualified type paths (that is, foo::bar::Baz). All type paths must be within the current module, imported using 'use'. This restriction may be lifted in future.")]
+ NamespacesNotSupportedForExternFun,
+ #[error("extern_rust_function signatures must never reference Self: instead, spell out the type explicitly.")]
+ ExplicitSelf,
+}
+
+/// A [`ConvertErrorFromRust`] which also implements [`miette::Diagnostic`] so can be pretty-printed
+/// to show the affected span of code.
+#[derive(Error, Debug, Diagnostic, Clone)]
+#[error("{err}")]
+pub struct LocatedConvertErrorFromRust {
+ err: ConvertErrorFromRust,
+ #[source_code]
+ file: String,
+ #[label("error here")]
+ span: SourceSpan,
+}
+
+impl LocatedConvertErrorFromRust {
+ pub(crate) fn new(err: ConvertErrorFromRust, span: &Span, file: &str) -> Self {
+ Self {
+ err,
+ span: proc_macro_span_to_miette_span(span),
+ file: file.to_string(),
+ }
+ }
}
/// Ensures that error contexts are always created using the constructors in this
/// mod, therefore undergoing identifier sanitation.
-#[derive(Clone)]
+#[derive(Clone, Debug)]
struct PhantomSanitized;
/// The context of an error, e.g. whether it applies to a function or a method.
/// This is used to generate suitable rustdoc in the output codegen so that
/// the errors can be revealed in rust-analyzer-based IDEs, etc.
-#[derive(Clone)]
-pub(crate) struct ErrorContext(ErrorContextType, PhantomSanitized);
+#[derive(Clone, Debug)]
+pub(crate) struct ErrorContext(Box<ErrorContextType>, PhantomSanitized);
/// All idents in this structure are guaranteed to be something we can safely codegen for.
-#[derive(Clone)]
+#[derive(Clone, Debug)]
pub(crate) enum ErrorContextType {
Item(Ident),
SanitizedItem(Ident),
@@ -155,8 +221,11 @@
impl ErrorContext {
pub(crate) fn new_for_item(id: Ident) -> Self {
match Self::sanitize_error_ident(&id) {
- None => Self(ErrorContextType::Item(id), PhantomSanitized),
- Some(sanitized) => Self(ErrorContextType::SanitizedItem(sanitized), PhantomSanitized),
+ None => Self(Box::new(ErrorContextType::Item(id)), PhantomSanitized),
+ Some(sanitized) => Self(
+ Box::new(ErrorContextType::SanitizedItem(sanitized)),
+ PhantomSanitized,
+ ),
}
}
@@ -166,14 +235,16 @@
// an impl block.
match Self::sanitize_error_ident(&self_ty) {
None => Self(
- ErrorContextType::Method {
+ Box::new(ErrorContextType::Method {
self_ty,
method: Self::sanitize_error_ident(&method).unwrap_or(method),
- },
+ }),
PhantomSanitized,
),
Some(_) => Self(
- ErrorContextType::SanitizedItem(make_ident(format!("{}_{}", self_ty, method))),
+ Box::new(ErrorContextType::SanitizedItem(make_ident(format!(
+ "{self_ty}_{method}"
+ )))),
PhantomSanitized,
),
}
@@ -195,21 +266,24 @@
}
pub(crate) fn into_type(self) -> ErrorContextType {
- self.0
+ *self.0
}
}
impl std::fmt::Display for ErrorContext {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- match &self.0 {
- ErrorContextType::Item(id) | ErrorContextType::SanitizedItem(id) => write!(f, "{}", id),
- ErrorContextType::Method { self_ty, method } => write!(f, "{}::{}", self_ty, method),
+ match &*self.0 {
+ ErrorContextType::Item(id) | ErrorContextType::SanitizedItem(id) => write!(f, "{id}"),
+ ErrorContextType::Method { self_ty, method } => write!(f, "{self_ty}::{method}"),
}
}
}
#[derive(Clone)]
-pub(crate) struct ConvertErrorWithContext(pub(crate) ConvertError, pub(crate) Option<ErrorContext>);
+pub(crate) struct ConvertErrorWithContext(
+ pub(crate) ConvertErrorFromCpp,
+ pub(crate) Option<ErrorContext>,
+);
impl std::fmt::Debug for ConvertErrorWithContext {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
diff --git a/engine/src/conversion/doc_attr.rs b/engine/src/conversion/doc_attr.rs
index 8fe9d0b..7a44f9b 100644
--- a/engine/src/conversion/doc_attr.rs
+++ b/engine/src/conversion/doc_attr.rs
@@ -12,7 +12,7 @@
pub(super) fn get_doc_attrs(attrs: &[Attribute]) -> Vec<Attribute> {
attrs
.iter()
- .filter(|a| a.path.get_ident().iter().any(|p| *p == "doc"))
+ .filter(|a| a.path().get_ident().iter().any(|p| *p == "doc"))
.cloned()
.collect()
}
diff --git a/engine/src/conversion/error_reporter.rs b/engine/src/conversion/error_reporter.rs
index 7942a32..adea153 100644
--- a/engine/src/conversion/error_reporter.rs
+++ b/engine/src/conversion/error_reporter.rs
@@ -6,13 +6,13 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use syn::ItemEnum;
+use crate::minisyn::ItemEnum;
use super::{
api::{AnalysisPhase, Api, ApiName, FuncToConvert, StructDetails, TypedefKind},
apivec::ApiVec,
convert_error::{ConvertErrorWithContext, ErrorContext},
- ConvertError,
+ ConvertErrorFromCpp,
};
use crate::{
conversion::convert_error::ErrorContextType,
@@ -33,7 +33,7 @@
match fun() {
Ok(result) => Some(result),
Err(ConvertErrorWithContext(err, None)) => {
- eprintln!("Ignored item: {}", err);
+ eprintln!("Ignored item: {err}");
None
}
Err(ConvertErrorWithContext(err, Some(ctx))) => {
@@ -127,11 +127,11 @@
Api::RustFn {
name,
details,
- receiver,
+ deps,
} => Ok(Box::new(std::iter::once(Api::RustFn {
name,
details,
- receiver,
+ deps,
}))),
Api::RustSubclassFn {
name,
@@ -210,7 +210,7 @@
out_apis: &mut ApiVec<B>,
mut fun: F,
) where
- F: FnMut(Api<A>) -> Result<Box<dyn Iterator<Item = Api<B>>>, ConvertError>,
+ F: FnMut(Api<A>) -> Result<Box<dyn Iterator<Item = Api<B>>>, ConvertErrorFromCpp>,
A: AnalysisPhase,
B: AnalysisPhase,
{
diff --git a/engine/src/conversion/mod.rs b/engine/src/conversion/mod.rs
index aa639a2..61d7d6d 100644
--- a/engine/src/conversion/mod.rs
+++ b/engine/src/conversion/mod.rs
@@ -17,18 +17,18 @@
mod doc_attr;
mod error_reporter;
mod parse;
+mod type_helpers;
mod utilities;
use analysis::fun::FnAnalyzer;
use autocxx_parser::IncludeCppConfig;
pub(crate) use codegen_cpp::CppCodeGenerator;
pub(crate) use convert_error::ConvertError;
+use convert_error::ConvertErrorFromCpp;
use itertools::Itertools;
use syn::{Item, ItemMod};
-use crate::{
- conversion::analysis::deps::HasDependencies, CppCodegenOptions, CppFilePair, UnsafePolicy,
-};
+use crate::{CodegenOptions, CppFilePair, UnsafePolicy};
use self::{
analysis::{
@@ -37,7 +37,6 @@
casts::add_casts,
check_names,
constructor_deps::decorate_types_with_constructor_deps,
- fun::FnPhase,
gc::filter_apis_by_following_edges_from_allowlist,
pod::analyze_pod_apis,
remove_ignored::filter_apis_by_ignored_dependents,
@@ -87,23 +86,10 @@
fn dump_apis<T: AnalysisPhase>(label: &str, apis: &ApiVec<T>) {
if LOG_APIS {
log::info!(
- "APIs after {}:\n{}",
+ "##### APIs after {}:\n{}",
label,
apis.iter()
- .map(|api| { format!(" {:?}", api) })
- .sorted()
- .join("\n")
- )
- }
- }
-
- fn dump_apis_with_deps(label: &str, apis: &ApiVec<FnPhase>) {
- if LOG_APIS {
- log::info!(
- "APIs after {}:\n{}",
- label,
- apis.iter()
- .map(|api| { format!(" {:?}, deps={}", api, api.format_deps()) })
+ .map(|api| { format!(" {api:?}") })
.sorted()
.join("\n")
)
@@ -121,7 +107,8 @@
mut bindgen_mod: ItemMod,
unsafe_policy: UnsafePolicy,
inclusions: String,
- cpp_codegen_options: &CppCodegenOptions,
+ codegen_options: &CodegenOptions,
+ source_file_contents: &str,
) -> Result<CodegenResults, ConvertError> {
match &mut bindgen_mod.content {
None => Err(ConvertError::NoContent),
@@ -129,7 +116,7 @@
// Parse the bindgen mod.
let items_to_process = items.drain(..).collect();
let parser = ParseBindgen::new(self.config);
- let apis = parser.parse_items(items_to_process)?;
+ let apis = parser.parse_items(items_to_process, source_file_contents)?;
Self::dump_apis("parsing", &apis);
// Inside parse_results, we now have a list of APIs.
// We now enter various analysis phases.
@@ -143,9 +130,9 @@
// Specifically, let's confirm that the items requested by the user to be
// POD really are POD, and duly mark any dependent types.
// This returns a new list of `Api`s, which will be parameterized with
- // the analysis results. It also returns an object which can be used
- // by subsequent phases to work out which objects are POD.
- let analyzed_apis = analyze_pod_apis(apis, self.config)?;
+ // the analysis results.
+ let analyzed_apis =
+ analyze_pod_apis(apis, self.config).map_err(ConvertError::Cpp)?;
Self::dump_apis("pod analysis", &analyzed_apis);
let analyzed_apis = replace_hopeless_typedef_targets(self.config, analyzed_apis);
let analyzed_apis = add_casts(analyzed_apis);
@@ -156,8 +143,12 @@
// part of `autocxx`. Again, this returns a new set of `Api`s, but
// parameterized by a richer set of metadata.
Self::dump_apis("adding casts", &analyzed_apis);
- let analyzed_apis =
- FnAnalyzer::analyze_functions(analyzed_apis, &unsafe_policy, self.config);
+ let analyzed_apis = FnAnalyzer::analyze_functions(
+ analyzed_apis,
+ &unsafe_policy,
+ self.config,
+ codegen_options.force_wrapper_gen,
+ );
// If any of those functions turned out to be pure virtual, don't attempt
// to generate UniquePtr implementations for the type, since it can't
// be instantiated.
@@ -167,9 +158,9 @@
// Annotate structs with a note of any copy/move constructors which
// we may want to retain to avoid garbage collecting them later.
let analyzed_apis = decorate_types_with_constructor_deps(analyzed_apis);
- Self::dump_apis_with_deps("adding constructor deps", &analyzed_apis);
+ Self::dump_apis("adding constructor deps", &analyzed_apis);
let analyzed_apis = discard_ignored_functions(analyzed_apis);
- Self::dump_apis_with_deps("ignoring ignorable fns", &analyzed_apis);
+ Self::dump_apis("ignoring ignorable fns", &analyzed_apis);
// Remove any APIs whose names are not compatible with cxx.
let analyzed_apis = check_names(analyzed_apis);
// During parsing or subsequent processing we might have encountered
@@ -177,24 +168,28 @@
// There might be other items depending on such things. Let's remove them
// too.
let analyzed_apis = filter_apis_by_ignored_dependents(analyzed_apis);
- Self::dump_apis_with_deps("removing ignored dependents", &analyzed_apis);
+ Self::dump_apis("removing ignored dependents", &analyzed_apis);
// We now garbage collect the ones we don't need...
let mut analyzed_apis =
filter_apis_by_following_edges_from_allowlist(analyzed_apis, self.config);
// Determine what variably-sized C types (e.g. int) we need to include
analysis::ctypes::append_ctype_information(&mut analyzed_apis);
- Self::dump_apis_with_deps("GC", &analyzed_apis);
+ Self::dump_apis("GC", &analyzed_apis);
// And finally pass them to the code gen phases, which outputs
// code suitable for cxx to consume.
- let cxxgen_header_name = cpp_codegen_options.cxxgen_header_namer.name_header();
+ let cxxgen_header_name = codegen_options
+ .cpp_codegen_options
+ .cxxgen_header_namer
+ .name_header();
let cpp = CppCodeGenerator::generate_cpp_code(
inclusions,
&analyzed_apis,
self.config,
- cpp_codegen_options,
+ &codegen_options.cpp_codegen_options,
&cxxgen_header_name,
- )?;
+ )
+ .map_err(ConvertError::Cpp)?;
let rs = RsCodeGenerator::generate_rs_code(
analyzed_apis,
&unsafe_policy,
diff --git a/engine/src/conversion/parse/bindgen_semantic_attributes.rs b/engine/src/conversion/parse/bindgen_semantic_attributes.rs
index a8de9ce..f2dd7d7 100644
--- a/engine/src/conversion/parse/bindgen_semantic_attributes.rs
+++ b/engine/src/conversion/parse/bindgen_semantic_attributes.rs
@@ -14,9 +14,9 @@
};
use crate::conversion::{
- api::{CppVisibility, Layout, References, SpecialMemberKind, Virtualness},
+ api::{CppVisibility, DeletedOrDefaulted, Layout, References, SpecialMemberKind, Virtualness},
convert_error::{ConvertErrorWithContext, ErrorContext},
- ConvertError,
+ ConvertErrorFromCpp,
};
/// The set of all annotations that autocxx_bindgen has added
@@ -31,7 +31,7 @@
// item can't be processed.
pub(crate) fn new_retaining_others(attrs: &mut Vec<Attribute>) -> Self {
let metadata = Self::new(attrs);
- attrs.retain(|a| a.path.segments.last().unwrap().ident != "cpp_semantics");
+ attrs.retain(|a| a.path().segments.last().unwrap().ident != "cpp_semantics");
metadata
}
@@ -40,7 +40,7 @@
attrs
.iter()
.filter_map(|attr| {
- if attr.path.segments.last().unwrap().ident == "cpp_semantics" {
+ if attr.path().segments.last().unwrap().ident == "cpp_semantics" {
let r: Result<BindgenSemanticAttribute, syn::Error> = attr.parse_args();
r.ok()
} else {
@@ -58,13 +58,13 @@
) -> Result<(), ConvertErrorWithContext> {
if self.has_attr("unused_template_param") {
Err(ConvertErrorWithContext(
- ConvertError::UnusedTemplateParam,
- Some(ErrorContext::new_for_item(id_for_context.clone())),
+ ConvertErrorFromCpp::UnusedTemplateParam,
+ Some(ErrorContext::new_for_item(id_for_context.clone().into())),
))
} else if self.get_cpp_visibility() != CppVisibility::Public {
Err(ConvertErrorWithContext(
- ConvertError::NonPublicNestedType,
- Some(ErrorContext::new_for_item(id_for_context.clone())),
+ ConvertErrorFromCpp::NonPublicNestedType,
+ Some(ErrorContext::new_for_item(id_for_context.clone().into())),
))
} else {
Ok(())
@@ -98,6 +98,16 @@
}
}
+ pub(super) fn get_deleted_or_defaulted(&self) -> DeletedOrDefaulted {
+ if self.has_attr("deleted") {
+ DeletedOrDefaulted::Deleted
+ } else if self.has_attr("defaulted") {
+ DeletedOrDefaulted::Defaulted
+ } else {
+ DeletedOrDefaulted::Neither
+ }
+ }
+
fn parse_if_present<T: Parse>(&self, annotation: &str) -> Option<T> {
self.0
.iter()
@@ -144,12 +154,12 @@
} else if a.is_ident("arg_type_reference") {
let r: Result<Ident, syn::Error> = a.parse_args();
if let Ok(ls) = r {
- results.ref_params.insert(ls);
+ results.ref_params.insert(ls.into());
}
} else if a.is_ident("arg_type_rvalue_reference") {
let r: Result<Ident, syn::Error> = a.parse_args();
if let Ok(ls) = r {
- results.rvalue_ref_params.insert(ls);
+ results.rvalue_ref_params.insert(ls.into());
}
}
}
diff --git a/engine/src/conversion/parse/extern_fun_signatures.rs b/engine/src/conversion/parse/extern_fun_signatures.rs
new file mode 100644
index 0000000..cb002b5
--- /dev/null
+++ b/engine/src/conversion/parse/extern_fun_signatures.rs
@@ -0,0 +1,253 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use indexmap::IndexSet as HashSet;
+
+use syn::{
+ spanned::Spanned, AngleBracketedGenericArguments, GenericArgument, PatType, PathArguments,
+ PathSegment, ReturnType, Signature, Type, TypePath, TypeReference,
+};
+
+use crate::{
+ conversion::convert_error::{ConvertErrorFromRust, LocatedConvertErrorFromRust},
+ types::QualifiedName,
+};
+
+pub(super) fn assemble_extern_fun_deps(
+ sig: &Signature,
+ file: &str,
+) -> Result<Vec<QualifiedName>, LocatedConvertErrorFromRust> {
+ let mut deps = HashSet::new();
+ // It's possible that this will need to be implemented using TypeConverter
+ // and the encountered_types field on its annotated results.
+ // But the design of that code is intended to convert from C++ types
+ // (via bindgen) to cxx types, and instead here we're starting with pure
+ // Rust types as written by a Rustacean human. It may therefore not
+ // be quite right to go via TypeConverter.
+ // Also, by doing it ourselves here, we're in a better place to emit
+ // meaningful errors about types which can't be supported within
+ // extern_rust_fun.
+ if let ReturnType::Type(_, ty) = &sig.output {
+ add_type_to_deps(ty, &mut deps, file)?;
+ }
+ for input in &sig.inputs {
+ match input {
+ syn::FnArg::Receiver(_) => {
+ return Err(LocatedConvertErrorFromRust::new(
+ ConvertErrorFromRust::ExternRustFunRequiresFullyQualifiedReceiver,
+ &input.span(),
+ file,
+ ))
+ }
+ syn::FnArg::Typed(PatType { ty, .. }) => add_type_to_deps(ty, &mut deps, file)?,
+ }
+ }
+ Ok(deps.into_iter().collect())
+}
+
+/// For all types within an extern_rust_function signature, add them to the deps
+/// hash, or raise an appropriate error.
+fn add_type_to_deps(
+ ty: &Type,
+ deps: &mut HashSet<QualifiedName>,
+ file: &str,
+) -> Result<(), LocatedConvertErrorFromRust> {
+ match ty {
+ Type::Reference(TypeReference {
+ mutability: Some(_),
+ ..
+ }) => {
+ return Err(LocatedConvertErrorFromRust::new(
+ ConvertErrorFromRust::PinnedReferencesRequiredForExternFun,
+ &ty.span(),
+ file,
+ ))
+ }
+ Type::Reference(TypeReference { elem, .. }) => match &**elem {
+ Type::Path(tp) => add_path_to_deps(tp, deps, file)?,
+ _ => {
+ return Err(LocatedConvertErrorFromRust::new(
+ ConvertErrorFromRust::UnsupportedTypeForExternFun,
+ &ty.span(),
+ file,
+ ))
+ }
+ },
+ Type::Path(tp) => {
+ if tp.path.segments.len() != 1 {
+ return Err(LocatedConvertErrorFromRust::new(
+ ConvertErrorFromRust::NamespacesNotSupportedForExternFun,
+ &tp.span(),
+ file,
+ ));
+ }
+ if let Some(PathSegment {
+ ident,
+ arguments:
+ PathArguments::AngleBracketed(AngleBracketedGenericArguments { args, .. }),
+ }) = tp.path.segments.last()
+ {
+ if ident == "Pin" {
+ if args.len() != 1 {
+ return Err(LocatedConvertErrorFromRust::new(
+ ConvertErrorFromRust::UnsupportedTypeForExternFun,
+ &tp.span(),
+ file,
+ ));
+ }
+
+ if let Some(GenericArgument::Type(Type::Reference(TypeReference {
+ mutability: Some(_),
+ elem,
+ ..
+ }))) = args.first()
+ {
+ if let Type::Path(tp) = &**elem {
+ add_path_to_deps(tp, deps, file)?
+ } else {
+ return Err(LocatedConvertErrorFromRust::new(
+ ConvertErrorFromRust::UnsupportedTypeForExternFun,
+ &elem.span(),
+ file,
+ ));
+ }
+ } else {
+ return Err(LocatedConvertErrorFromRust::new(
+ ConvertErrorFromRust::UnsupportedTypeForExternFun,
+ &ty.span(),
+ file,
+ ));
+ }
+ } else if ident == "Box" || ident == "Vec" {
+ if args.len() != 1 {
+ return Err(LocatedConvertErrorFromRust::new(
+ ConvertErrorFromRust::UnsupportedTypeForExternFun,
+ &tp.span(),
+ file,
+ ));
+ }
+ if let Some(GenericArgument::Type(Type::Path(tp))) = args.first() {
+ add_path_to_deps(tp, deps, file)?
+ } else {
+ return Err(LocatedConvertErrorFromRust::new(
+ ConvertErrorFromRust::UnsupportedTypeForExternFun,
+ &ty.span(),
+ file,
+ ));
+ }
+ } else {
+ return Err(LocatedConvertErrorFromRust::new(
+ ConvertErrorFromRust::UnsupportedTypeForExternFun,
+ &ident.span(),
+ file,
+ ));
+ }
+ } else {
+ add_path_to_deps(tp, deps, file)?
+ }
+ }
+ _ => {
+ return Err(LocatedConvertErrorFromRust::new(
+ ConvertErrorFromRust::UnsupportedTypeForExternFun,
+ &ty.span(),
+ file,
+ ))
+ }
+ };
+ Ok(())
+}
+
+fn add_path_to_deps(
+ type_path: &TypePath,
+ deps: &mut HashSet<QualifiedName>,
+ file: &str,
+) -> Result<(), LocatedConvertErrorFromRust> {
+ if let Some(PathSegment {
+ arguments: PathArguments::AngleBracketed(..) | PathArguments::Parenthesized(..),
+ ..
+ }) = type_path.path.segments.last()
+ {
+ return Err(LocatedConvertErrorFromRust::new(
+ ConvertErrorFromRust::UnsupportedTypeForExternFun,
+ &type_path.span(),
+ file,
+ ));
+ }
+ let qn = QualifiedName::from_type_path(type_path);
+ if !qn.get_namespace().is_empty() {
+ return Err(LocatedConvertErrorFromRust::new(
+ ConvertErrorFromRust::NamespacesNotSupportedForExternFun,
+ &type_path.span(),
+ file,
+ ));
+ }
+ if qn.get_final_item() == "Self" {
+ return Err(LocatedConvertErrorFromRust::new(
+ ConvertErrorFromRust::ExplicitSelf,
+ &type_path.span(),
+ file,
+ ));
+ }
+ deps.insert(qn);
+ Ok(())
+}
+
+#[cfg(test)]
+mod tests {
+ use syn::parse_quote;
+
+ use super::*;
+
+ fn run_test_expect_ok(sig: Signature, expected_deps: &[&str]) {
+ let expected_as_set: HashSet<QualifiedName> = expected_deps
+ .iter()
+ .cloned()
+ .map(QualifiedName::new_from_cpp_name)
+ .collect();
+ let result = assemble_extern_fun_deps(&sig, "").unwrap();
+ let actual_as_set: HashSet<QualifiedName> = result.into_iter().collect();
+ assert_eq!(expected_as_set, actual_as_set);
+ }
+
+ fn run_test_expect_fail(sig: Signature) {
+ assert!(assemble_extern_fun_deps(&sig, "").is_err())
+ }
+
+ #[test]
+ fn test_assemble_extern_fun_deps() {
+ run_test_expect_fail(parse_quote! { fn function(self: A::B)});
+ run_test_expect_fail(parse_quote! { fn function(self: Self)});
+ run_test_expect_fail(parse_quote! { fn function(self: Self)});
+ run_test_expect_fail(parse_quote! { fn function(self)});
+ run_test_expect_fail(parse_quote! { fn function(&self)});
+ run_test_expect_fail(parse_quote! { fn function(&mut self)});
+ run_test_expect_fail(parse_quote! { fn function(self: Pin<&mut Self>)});
+ run_test_expect_fail(parse_quote! { fn function(self: Pin<&mut A::B>)});
+ run_test_expect_fail(parse_quote! { fn function(a: Pin<A>)});
+ run_test_expect_fail(parse_quote! { fn function(a: Pin<A::B>)});
+ run_test_expect_fail(parse_quote! { fn function(a: A::B)});
+ run_test_expect_fail(parse_quote! { fn function(a: &mut A)});
+ run_test_expect_fail(parse_quote! { fn function() -> A::B});
+ run_test_expect_fail(parse_quote! { fn function() -> &A::B});
+ run_test_expect_fail(parse_quote! { fn function(a: ())});
+ run_test_expect_fail(parse_quote! { fn function(a: &[A])});
+ run_test_expect_fail(parse_quote! { fn function(a: Bob<A>)});
+ run_test_expect_fail(parse_quote! { fn function(a: Box<A, B>)});
+ run_test_expect_fail(parse_quote! { fn function(a: a::Pin<&mut A>)});
+ run_test_expect_fail(parse_quote! { fn function(a: Pin<&A>)});
+ run_test_expect_ok(parse_quote! { fn function(a: A, b: B)}, &["A", "B"]);
+ run_test_expect_ok(parse_quote! { fn function(a: Box<A>)}, &["A"]);
+ run_test_expect_ok(parse_quote! { fn function(a: Vec<A>)}, &["A"]);
+ run_test_expect_ok(parse_quote! { fn function(a: &A)}, &["A"]);
+ run_test_expect_ok(parse_quote! { fn function(a: Pin<&mut A>)}, &["A"]);
+ run_test_expect_ok(
+ parse_quote! { fn function(a: A, b: B) -> Box<C>},
+ &["A", "B", "C"],
+ );
+ }
+}
diff --git a/engine/src/conversion/parse/mod.rs b/engine/src/conversion/parse/mod.rs
index 3f42ce4..b4f2e07 100644
--- a/engine/src/conversion/parse/mod.rs
+++ b/engine/src/conversion/parse/mod.rs
@@ -7,6 +7,7 @@
// except according to those terms.
mod bindgen_semantic_attributes;
+mod extern_fun_signatures;
mod parse_bindgen;
mod parse_foreign_mod;
diff --git a/engine/src/conversion/parse/parse_bindgen.rs b/engine/src/conversion/parse/parse_bindgen.rs
index 2d4e3de..f499145 100644
--- a/engine/src/conversion/parse/parse_bindgen.rs
+++ b/engine/src/conversion/parse/parse_bindgen.rs
@@ -13,7 +13,8 @@
conversion::{
api::{Api, ApiName, NullPhase, StructDetails, SubclassName, TypedefKind, UnanalyzedApi},
apivec::ApiVec,
- ConvertError,
+ convert_error::LocatedConvertErrorFromRust,
+ ConvertError, ConvertErrorFromCpp,
},
types::Namespace,
types::QualifiedName,
@@ -41,7 +42,7 @@
}
fn api_name(ns: &Namespace, id: Ident, attrs: &BindgenSemanticAttributes) -> ApiName {
- ApiName::new_with_cpp_name(ns, id, attrs.get_original_name())
+ ApiName::new_with_cpp_name(ns, id.into(), attrs.get_original_name())
}
pub(crate) fn api_name_qualified(
@@ -51,8 +52,11 @@
) -> Result<ApiName, ConvertErrorWithContext> {
match validate_ident_ok_for_cxx(&id.to_string()) {
Err(e) => {
- let ctx = ErrorContext::new_for_item(id);
- Err(ConvertErrorWithContext(e, Some(ctx)))
+ let ctx = ErrorContext::new_for_item(id.into());
+ Err(ConvertErrorWithContext(
+ ConvertErrorFromCpp::InvalidIdent(e),
+ Some(ctx),
+ ))
}
Ok(..) => Ok(api_name(ns, id, attrs)),
}
@@ -71,43 +75,49 @@
pub(crate) fn parse_items(
mut self,
items: Vec<Item>,
+ source_file_contents: &str,
) -> Result<ApiVec<NullPhase>, ConvertError> {
- let items = Self::find_items_in_root(items)?;
+ let items = Self::find_items_in_root(items).map_err(ConvertError::Cpp)?;
if !self.config.exclude_utilities() {
generate_utilities(&mut self.apis, self.config);
}
- self.add_apis_from_config();
+ self.add_apis_from_config(source_file_contents)
+ .map_err(ConvertError::Rust)?;
let root_ns = Namespace::new();
self.parse_mod_items(items, root_ns);
- self.confirm_all_generate_directives_obeyed()?;
+ self.confirm_all_generate_directives_obeyed()
+ .map_err(ConvertError::Cpp)?;
self.replace_extern_cpp_types();
Ok(self.apis)
}
/// Some API items are not populated from bindgen output, but instead
/// directly from items in the config.
- fn add_apis_from_config(&mut self) {
+ fn add_apis_from_config(
+ &mut self,
+ source_file_contents: &str,
+ ) -> Result<(), LocatedConvertErrorFromRust> {
self.apis
.extend(self.config.subclasses.iter().map(|sc| Api::Subclass {
- name: SubclassName::new(sc.subclass.clone()),
+ name: SubclassName::new(sc.subclass.clone().into()),
superclass: QualifiedName::new_from_cpp_name(&sc.superclass),
}));
- self.apis
- .extend(self.config.extern_rust_funs.iter().map(|fun| {
- let id = fun.sig.ident.clone();
- Api::RustFn {
- name: ApiName::new_in_root_namespace(id),
- details: fun.clone(),
- receiver: fun.receiver.as_ref().map(|receiver_id| {
- QualifiedName::new(&Namespace::new(), receiver_id.clone())
- }),
- }
- }));
+ for fun in &self.config.extern_rust_funs {
+ let id = fun.sig.ident.clone();
+ self.apis.push(Api::RustFn {
+ name: ApiName::new_in_root_namespace(id.into()),
+ details: fun.clone(),
+ deps: super::extern_fun_signatures::assemble_extern_fun_deps(
+ &fun.sig,
+ source_file_contents,
+ )?,
+ })
+ }
let unique_rust_types: HashSet<&RustPath> = self.config.rust_types.iter().collect();
self.apis.extend(unique_rust_types.into_iter().map(|path| {
let id = path.get_final_ident();
Api::RustType {
- name: ApiName::new_in_root_namespace(id.clone()),
+ name: ApiName::new_in_root_namespace(id.clone().into()),
path: path.clone(),
}
}));
@@ -117,7 +127,7 @@
.0
.iter()
.map(|(cpp_definition, rust_id)| {
- let name = ApiName::new_in_root_namespace(rust_id.clone());
+ let name = ApiName::new_in_root_namespace(rust_id.clone().into());
Api::ConcreteType {
name,
cpp_definition: cpp_definition.clone(),
@@ -125,6 +135,7 @@
}
}),
);
+ Ok(())
}
/// We do this last, _after_ we've parsed all the APIs, because we might want to actually
@@ -154,7 +165,7 @@
self.apis.extend(replacements.into_iter().map(|(_, v)| v));
}
- fn find_items_in_root(items: Vec<Item>) -> Result<Vec<Item>, ConvertError> {
+ fn find_items_in_root(items: Vec<Item>) -> Result<Vec<Item>, ConvertErrorFromCpp> {
for item in items {
match item {
Item::Mod(root_mod) => {
@@ -166,7 +177,7 @@
return Ok(items);
}
}
- _ => return Err(ConvertError::UnexpectedOuterItem),
+ _ => return Err(ConvertErrorFromCpp::UnexpectedOuterItem),
}
}
Ok(Vec::new())
@@ -224,8 +235,8 @@
// forward declarations.
if err.is_none() && name.cpp_name().contains("::") {
err = Some(ConvertErrorWithContext(
- ConvertError::ForwardDeclaredNestedType,
- Some(ErrorContext::new_for_item(s.ident)),
+ ConvertErrorFromCpp::ForwardDeclaredNestedType,
+ Some(ErrorContext::new_for_item(s.ident.into())),
));
}
Some(UnanalyzedApi::ForwardDeclaration { name, err })
@@ -237,7 +248,7 @@
name,
details: Box::new(StructDetails {
layout: annotations.get_layout(),
- item: s,
+ item: s.into(),
has_rvalue_reference_fields,
}),
analysis: (),
@@ -254,7 +265,7 @@
let annotations = BindgenSemanticAttributes::new(&e.attrs);
let api = UnanalyzedApi::Enum {
name: api_name_qualified(ns, e.ident.clone(), &annotations)?,
- item: e,
+ item: e.into(),
};
if !self.config.is_on_blocklist(&api.name().to_cpp_name()) {
self.apis.push(api);
@@ -293,7 +304,7 @@
UseTree::Rename(urn) => {
let old_id = &urn.ident;
let new_id = &urn.rename;
- let new_tyname = QualifiedName::new(ns, new_id.clone());
+ let new_tyname = QualifiedName::new(ns, new_id.clone().into());
assert!(segs.remove(0) == "self", "Path didn't start with self");
assert!(
segs.remove(0) == "super",
@@ -309,8 +320,8 @@
let old_tyname = QualifiedName::from_type_path(&old_path);
if new_tyname == old_tyname {
return Err(ConvertErrorWithContext(
- ConvertError::InfinitelyRecursiveTypedef(new_tyname),
- Some(ErrorContext::new_for_item(new_id.clone())),
+ ConvertErrorFromCpp::InfinitelyRecursiveTypedef(new_tyname),
+ Some(ErrorContext::new_for_item(new_id.clone().into())),
));
}
let annotations = BindgenSemanticAttributes::new(&use_item.attrs);
@@ -320,7 +331,7 @@
parse_quote! {
pub use #old_path as #new_id;
},
- Box::new(Type::Path(old_path)),
+ Box::new(Type::Path(old_path).into()),
),
old_tyname: Some(old_tyname),
analysis: (),
@@ -329,7 +340,7 @@
}
_ => {
return Err(ConvertErrorWithContext(
- ConvertError::UnexpectedUseStatement(
+ ConvertErrorFromCpp::UnexpectedUseStatement(
segs.into_iter().last().map(|i| i.to_string()),
),
None,
@@ -341,10 +352,23 @@
}
Item::Const(const_item) => {
let annotations = BindgenSemanticAttributes::new(&const_item.attrs);
- self.apis.push(UnanalyzedApi::Const {
- name: api_name(ns, const_item.ident.clone(), &annotations),
- const_item,
- });
+ // Bindgen generates const expressions for nested unnamed enums,
+ // but autcxx will refuse to expand those enums, making these consts
+ // invalid.
+ let mut enum_type_name_valid = true;
+ if let Type::Path(p) = &*const_item.ty {
+ if let Some(p) = &p.path.segments.last() {
+ if validate_ident_ok_for_cxx(&p.ident.to_string()).is_err() {
+ enum_type_name_valid = false;
+ }
+ }
+ }
+ if enum_type_name_valid {
+ self.apis.push(UnanalyzedApi::Const {
+ name: api_name(ns, const_item.ident.clone(), &annotations),
+ const_item: const_item.into(),
+ });
+ }
Ok(())
}
Item::Type(ity) => {
@@ -353,14 +377,14 @@
// same name - see test_issue_264.
self.apis.push(UnanalyzedApi::Typedef {
name: api_name(ns, ity.ident.clone(), &annotations),
- item: TypedefKind::Type(ity),
+ item: TypedefKind::Type(ity.into()),
old_tyname: None,
analysis: (),
});
Ok(())
}
_ => Err(ConvertErrorWithContext(
- ConvertError::UnexpectedItemInMod,
+ ConvertErrorFromCpp::UnexpectedItemInMod,
None,
)),
}
@@ -380,7 +404,7 @@
.any(|id| id == desired_id)
}
- fn confirm_all_generate_directives_obeyed(&self) -> Result<(), ConvertError> {
+ fn confirm_all_generate_directives_obeyed(&self) -> Result<(), ConvertErrorFromCpp> {
let api_names: HashSet<_> = self
.apis
.iter()
@@ -388,7 +412,9 @@
.collect();
for generate_directive in self.config.must_generate_list() {
if !api_names.contains(&generate_directive) {
- return Err(ConvertError::DidNotGenerateAnything(generate_directive));
+ return Err(ConvertErrorFromCpp::DidNotGenerateAnything(
+ generate_directive,
+ ));
}
}
Ok(())
diff --git a/engine/src/conversion/parse/parse_foreign_mod.rs b/engine/src/conversion/parse/parse_foreign_mod.rs
index 08c1fd8..ea82ff8 100644
--- a/engine/src/conversion/parse/parse_foreign_mod.rs
+++ b/engine/src/conversion/parse/parse_foreign_mod.rs
@@ -15,8 +15,9 @@
convert_error::ConvertErrorWithContext,
convert_error::ErrorContext,
};
+use crate::minisyn::{minisynize_punctuated, minisynize_vec};
use crate::{
- conversion::ConvertError,
+ conversion::ConvertErrorFromCpp,
types::{Namespace, QualifiedName},
};
use std::collections::HashMap;
@@ -72,11 +73,11 @@
self.funcs_to_convert.push(FuncToConvert {
provenance: Provenance::Bindgen,
self_ty: None,
- ident: item.sig.ident,
- doc_attrs,
- inputs: item.sig.inputs,
- output: item.sig.output,
- vis: item.vis,
+ ident: item.sig.ident.into(),
+ doc_attrs: minisynize_vec(doc_attrs),
+ inputs: minisynize_punctuated(item.sig.inputs),
+ output: item.sig.output.into(),
+ vis: item.vis.into(),
virtualness: annotations.get_virtualness(),
cpp_vis: annotations.get_cpp_visibility(),
special_member: annotations.special_member_kind(),
@@ -86,18 +87,18 @@
original_name: annotations.get_original_name(),
synthesized_this_type: None,
add_to_trait: None,
- is_deleted: annotations.has_attr("deleted"),
+ is_deleted: annotations.get_deleted_or_defaulted(),
synthetic_cpp: None,
variadic: item.sig.variadic.is_some(),
});
Ok(())
}
ForeignItem::Static(item) => Err(ConvertErrorWithContext(
- ConvertError::StaticData(item.ident.to_string()),
- Some(ErrorContext::new_for_item(item.ident)),
+ ConvertErrorFromCpp::StaticData(item.ident.to_string()),
+ Some(ErrorContext::new_for_item(item.ident.into())),
)),
_ => Err(ConvertErrorWithContext(
- ConvertError::UnexpectedForeignItem,
+ ConvertErrorFromCpp::UnexpectedForeignItem,
None,
)),
}
@@ -111,14 +112,14 @@
_ => return,
};
for i in imp.items {
- if let ImplItem::Method(itm) = i {
+ if let ImplItem::Fn(itm) = i {
let effective_fun_name = match get_called_function(&itm.block) {
Some(id) => id.clone(),
None => itm.sig.ident,
};
self.method_receivers.insert(
effective_fun_name,
- QualifiedName::new(&self.ns, ty_id.clone()),
+ QualifiedName::new(&self.ns, ty_id.clone().into()),
);
}
}
@@ -151,7 +152,7 @@
/// name of the actual function call inside the block's body.
fn get_called_function(block: &Block) -> Option<&Ident> {
match block.stmts.first() {
- Some(Stmt::Expr(Expr::Call(ExprCall { func, .. }))) => match **func {
+ Some(Stmt::Expr(Expr::Call(ExprCall { func, .. }), _)) => match **func {
Expr::Path(ref exp) => exp.path.segments.first().map(|ps| &ps.ident),
_ => None,
},
diff --git a/engine/src/conversion/type_helpers.rs b/engine/src/conversion/type_helpers.rs
new file mode 100644
index 0000000..8eb3a3a
--- /dev/null
+++ b/engine/src/conversion/type_helpers.rs
@@ -0,0 +1,58 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use syn::{
+ AngleBracketedGenericArguments, GenericArgument, PathArguments, PathSegment, Type, TypePath,
+ TypeReference,
+};
+
+/// Looks in a `core::pin::Pin<&mut Something>` and returns the `Something`
+/// if it's found.
+/// This code could _almost_ be used from various other places around autocxx
+/// but they each have slightly different requirements. Over time we should
+/// try to migrate other instances to use this, though.
+pub(crate) fn extract_pinned_mutable_reference_type(tp: &TypePath) -> Option<&Type> {
+ if !is_pin(tp) {
+ return None;
+ }
+ if let Some(PathSegment {
+ arguments: PathArguments::AngleBracketed(AngleBracketedGenericArguments { args, .. }),
+ ..
+ }) = tp.path.segments.last()
+ {
+ if args.len() == 1 {
+ if let Some(GenericArgument::Type(Type::Reference(TypeReference {
+ mutability: Some(_),
+ elem,
+ ..
+ }))) = args.first()
+ {
+ return Some(elem);
+ }
+ }
+ }
+ None
+}
+
+/// Whether this type path is a `Pin`
+fn is_pin(tp: &TypePath) -> bool {
+ if tp.path.segments.len() != 3 {
+ return false;
+ }
+ static EXPECTED_SEGMENTS: &[&[&str]] = &[&["std", "core"], &["pin"], &["Pin"]];
+
+ for (seg, expected_name) in tp.path.segments.iter().zip(EXPECTED_SEGMENTS.iter()) {
+ if !expected_name
+ .iter()
+ .any(|expected_name| seg.ident == expected_name)
+ {
+ return false;
+ }
+ }
+ true
+}
diff --git a/engine/src/known_types.rs b/engine/src/known_types.rs
index 377101a..10199fb 100644
--- a/engine/src/known_types.rs
+++ b/engine/src/known_types.rs
@@ -6,14 +6,11 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use crate::{
- conversion::ConvertError,
- types::{make_ident, QualifiedName},
-};
+use crate::types::{make_ident, QualifiedName};
use indexmap::map::IndexMap as HashMap;
use indoc::indoc;
use once_cell::sync::OnceCell;
-use syn::{parse_quote, Type, TypePath, TypePtr};
+use syn::{parse_quote, TypePath};
//// The behavior of the type.
#[derive(Debug)]
@@ -103,12 +100,12 @@
let mut segs = self.rs_name.split("::").peekable();
if segs.peek().map(|seg| seg.is_empty()).unwrap_or_default() {
segs.next();
- let segs = segs.into_iter().map(make_ident);
+ let segs = segs.map(make_ident);
parse_quote! {
::#(#segs)::*
}
} else {
- let segs = segs.into_iter().map(make_ident);
+ let segs = segs.map(make_ident);
parse_quote! {
#(#segs)::*
}
@@ -143,7 +140,7 @@
}
/// The type of payload that a cxx generic can contain.
-#[derive(PartialEq, Clone, Copy)]
+#[derive(PartialEq, Eq, Clone, Copy)]
pub enum CxxGenericType {
/// Not a generic at all
Not,
@@ -247,7 +244,9 @@
.map(|td| {
matches!(
td.behavior,
- Behavior::CxxContainerPtr | Behavior::CxxContainerVector
+ Behavior::CxxContainerPtr
+ | Behavior::CxxContainerVector
+ | Behavior::RustContainerByValueSafe
)
})
.unwrap_or(false)
@@ -447,8 +446,8 @@
));
for (cpp_type, rust_type) in (4..7).map(|x| 2i32.pow(x)).flat_map(|x| {
vec![
- (format!("uint{}_t", x), format!("u{}", x)),
- (format!("int{}_t", x), format!("i{}", x)),
+ (format!("uint{x}_t"), format!("u{x}")),
+ (format!("int{x}_t"), format!("i{x}")),
]
}) {
db.insert(TypeDetails::new(
@@ -470,10 +469,10 @@
));
db.insert(TypeDetails::new(
- "std::pin::Pin",
+ "core::pin::Pin",
"Pin",
Behavior::RustByValue, // because this is actually Pin<&something>
- None,
+ Some("std::pin::Pin".to_string()),
true,
false,
));
@@ -481,18 +480,18 @@
let mut insert_ctype = |cname: &str| {
let concatenated_name = cname.replace(' ', "");
db.insert(TypeDetails::new(
- format!("autocxx::c_{}", concatenated_name),
+ format!("autocxx::c_{concatenated_name}"),
cname,
Behavior::CVariableLengthByValue,
- Some(format!("std::os::raw::c_{}", concatenated_name)),
+ Some(format!("std::os::raw::c_{concatenated_name}")),
true,
true,
));
db.insert(TypeDetails::new(
- format!("autocxx::c_u{}", concatenated_name),
- format!("unsigned {}", cname),
+ format!("autocxx::c_u{concatenated_name}"),
+ format!("unsigned {cname}"),
Behavior::CVariableLengthByValue,
- Some(format!("std::os::raw::c_u{}", concatenated_name)),
+ Some(format!("std::os::raw::c_u{concatenated_name}")),
true,
true,
));
@@ -553,10 +552,3 @@
));
db
}
-
-pub(crate) fn ensure_pointee_is_valid(ptr: &TypePtr) -> Result<(), ConvertError> {
- match *ptr.elem {
- Type::Path(..) => Ok(()),
- _ => Err(ConvertError::InvalidPointee),
- }
-}
diff --git a/engine/src/lib.rs b/engine/src/lib.rs
index 86a31ea..3988ab2 100644
--- a/engine/src/lib.rs
+++ b/engine/src/lib.rs
@@ -10,15 +10,14 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-// This feature=nightly could be set by build.rs, but since we only care
-// about it for docs, we ask docs.rs to set it in the Cargo.toml.
-#![cfg_attr(feature = "nightly", feature(doc_cfg))]
#![forbid(unsafe_code)]
+#![cfg_attr(feature = "nightly", feature(doc_cfg))]
mod ast_discoverer;
mod conversion;
mod cxxbridge;
mod known_types;
+mod minisyn;
mod output_generators;
mod parse_callbacks;
mod parse_file;
@@ -28,6 +27,7 @@
#[cfg(any(test, feature = "build"))]
mod builder;
+use autocxx_bindgen::BindgenError;
use autocxx_parser::{IncludeCppConfig, UnsafePolicy};
use conversion::BridgeConverter;
use miette::{SourceOffset, SourceSpan};
@@ -35,7 +35,9 @@
use parse_file::CppBuildable;
use proc_macro2::TokenStream as TokenStream2;
use regex::Regex;
+use std::cell::RefCell;
use std::path::PathBuf;
+use std::rc::Rc;
use std::{
fs::File,
io::prelude::*,
@@ -113,7 +115,7 @@
#[derive(Debug, Error, Diagnostic)]
pub enum Error {
#[error("Bindgen was unable to generate the initial .rs bindings for this file. This may indicate a parsing problem with the C++ headers.")]
- Bindgen(()),
+ Bindgen(BindgenError),
#[error(transparent)]
#[diagnostic(transparent)]
MacroParsing(LocatedSynError),
@@ -123,7 +125,10 @@
#[error("no C++ include directory was provided.")]
NoAutoCxxInc,
#[error(transparent)]
+ #[diagnostic(transparent)]
Conversion(conversion::ConvertError),
+ #[error("Using `unsafe_references_wrapped` requires the Rust nightly `arbitrary_self_types` feature")]
+ WrappedReferencesButNoArbitrarySelfTypes,
}
/// Result type.
@@ -142,6 +147,16 @@
Generated(Box<GenerationResults>),
}
+/// Code generation options.
+#[derive(Default)]
+pub struct CodegenOptions<'a> {
+ // An option used by the test suite to force a more convoluted
+ // route through our code, to uncover bugs.
+ pub force_wrapper_gen: bool,
+ /// Options about the C++ code generation.
+ pub cpp_codegen_options: CppCodegenOptions<'a>,
+}
+
const AUTOCXX_CLANG_ARGS: &[&str; 4] = &["-x", "c++", "-std=c++14", "-DBINDGEN"];
/// Implement to learn of header files which get included
@@ -254,6 +269,7 @@
pub struct IncludeCppEngine {
config: IncludeCppConfig,
state: State,
+ source_code: Option<Rc<String>>, // so we can create diagnostics
}
impl Parse for IncludeCppEngine {
@@ -264,14 +280,31 @@
} else {
State::NotGenerated
};
- Ok(Self { config, state })
+ Ok(Self {
+ config,
+ state,
+ source_code: None,
+ })
}
}
impl IncludeCppEngine {
- pub fn new_from_syn(mac: Macro, file_contents: &str) -> Result<Self> {
- mac.parse_body::<IncludeCppEngine>()
- .map_err(|e| Error::MacroParsing(LocatedSynError::new(e, file_contents)))
+ pub fn new_from_syn(mac: Macro, file_contents: Rc<String>) -> Result<Self> {
+ let mut this = mac
+ .parse_body::<IncludeCppEngine>()
+ .map_err(|e| Error::MacroParsing(LocatedSynError::new(e, &file_contents)))?;
+ this.source_code = Some(file_contents);
+ Ok(this)
+ }
+
+ /// Used if we find that we're asked to auto-discover extern_rust_type and similar
+ /// but didn't have any include_cpp macro at all.
+ pub fn new_for_autodiscover() -> Self {
+ Self {
+ config: IncludeCppConfig::default(),
+ state: State::NotGenerated,
+ source_code: None,
+ }
}
pub fn config_mut(&mut self) -> &mut IncludeCppConfig {
@@ -287,7 +320,7 @@
self.config
.inclusions
.iter()
- .map(|path| format!("#include \"{}\"\n", path)),
+ .map(|path| format!("#include \"{path}\"\n")),
"",
)
}
@@ -304,7 +337,11 @@
.default_enum_style(bindgen::EnumVariation::Rust {
non_exhaustive: false,
})
- .rustfmt_bindings(log::log_enabled!(log::Level::Info))
+ .formatter(if log::log_enabled!(log::Level::Info) {
+ bindgen::Formatter::Rustfmt
+ } else {
+ bindgen::Formatter::None
+ })
.size_t_is_usize(true)
.enable_cxx_namespaces()
.generate_inline_functions(true)
@@ -335,7 +372,7 @@
builder
.command_line_flags()
.into_iter()
- .map(|f| format!("\"{}\"", f))
+ .map(|f| format!("\"{f}\""))
.join(" ")
);
builder
@@ -370,7 +407,7 @@
let bindings = bindings.to_string();
// Manually add the mod ffi {} so that we can ask syn to parse
// into a single construct.
- let bindings = format!("mod bindgen {{ {} }}", bindings);
+ let bindings = format!("mod bindgen {{ {bindings} }}");
info!("Bindings: {}", bindings);
syn::parse_str::<ItemMod>(&bindings)
.map_err(|e| Error::BindingsParsing(LocatedSynError::new(e, &bindings)))
@@ -386,7 +423,7 @@
inc_dirs: Vec<PathBuf>,
extra_clang_args: &[&str],
dep_recorder: Option<Box<dyn RebuildDependencyRecorder>>,
- cpp_codegen_options: &CppCodegenOptions,
+ codegen_options: &CodegenOptions,
) -> Result<()> {
// If we are in parse only mode, do nothing. This is used for
// doc tests to ensure the parsing is valid, but we can't expect
@@ -397,6 +434,14 @@
State::Generated(_) => panic!("Only call generate once"),
}
+ if matches!(
+ self.config.unsafe_policy,
+ UnsafePolicy::ReferencesWrappedAllFunctionsSafe
+ ) && !rustversion::cfg!(nightly)
+ {
+ return Err(Error::WrappedReferencesButNoArbitrarySelfTypes);
+ }
+
let mod_name = self.config.get_mod_name();
let mut builder = self.make_bindgen_builder(&inc_dirs, extra_clang_args);
if let Some(dep_recorder) = dep_recorder {
@@ -411,6 +456,14 @@
let bindings = builder.generate().map_err(Error::Bindgen)?;
let bindings = self.parse_bindings(bindings)?;
+ // Source code contents just used for diagnostics - if we don't have it,
+ // use a blank string and miette will not attempt to annotate it nicely.
+ let source_file_contents = self
+ .source_code
+ .as_ref()
+ .cloned()
+ .unwrap_or_else(|| Rc::new("".to_string()));
+
let converter = BridgeConverter::new(&self.config.inclusions, &self.config);
let conversion = converter
@@ -418,7 +471,8 @@
bindings,
self.config.unsafe_policy.clone(),
header_contents,
- cpp_codegen_options,
+ codegen_options,
+ &source_file_contents,
)
.map_err(Error::Conversion)?;
let mut items = conversion.rs;
@@ -433,7 +487,7 @@
new_bindings.content.as_mut().unwrap().1.append(&mut items);
info!(
"New bindings:\n{}",
- rust_pretty_printer::pretty_print(&new_bindings.to_token_stream())
+ rust_pretty_printer::pretty_print(&new_bindings)
);
self.state = State::Generated(Box::new(GenerationResults {
item_mod: new_bindings,
@@ -484,7 +538,7 @@
"header": header,
"config": config
});
- let f = File::create(&output_path).unwrap();
+ let f = File::create(output_path).unwrap();
serde_json::to_writer(f, &json).unwrap();
}
}
@@ -505,12 +559,12 @@
// to refer to local headers on the reduction machine too.
let suffix = ALL_KNOWN_SYSTEM_HEADERS
.iter()
- .map(|hdr| format!("#include <{}>\n", hdr))
+ .map(|hdr| format!("#include <{hdr}>\n"))
.join("\n");
let input = format!("/*\nautocxx config:\n\n{:?}\n\nend autocxx config.\nautocxx preprocessed input:\n*/\n\n{}\n\n/* autocxx: extra headers added below for completeness. */\n\n{}\n{}\n",
self.config, header, suffix, cxx_gen::HEADER);
let mut tf = NamedTempFile::new().unwrap();
- write!(tf, "{}", input).unwrap();
+ write!(tf, "{input}").unwrap();
let tp = tf.into_temp_path();
preprocess(&tp, &PathBuf::from(output_path), inc_dirs, extra_clang_args).unwrap();
}
@@ -565,7 +619,11 @@
})
}
-pub(crate) fn strip_system_headers(input: Vec<u8>, suppress_system_headers: bool) -> Vec<u8> {
+pub fn get_cxx_header_bytes(suppress_system_headers: bool) -> Vec<u8> {
+ strip_system_headers(cxx_gen::HEADER.as_bytes().to_vec(), suppress_system_headers)
+}
+
+fn strip_system_headers(input: Vec<u8>, suppress_system_headers: bool) -> Vec<u8> {
if suppress_system_headers {
std::str::from_utf8(&input)
.unwrap()
@@ -660,7 +718,7 @@
impl Default for AutocxxgenHeaderNamer<'static> {
fn default() -> Self {
- Self(Box::new(|mod_name| format!("autocxxgen_{}.h", mod_name)))
+ Self(Box::new(|mod_name| format!("autocxxgen_{mod_name}.h")))
}
}
@@ -677,7 +735,27 @@
impl Default for CxxgenHeaderNamer<'static> {
fn default() -> Self {
- Self(Box::new(|| "cxxgen.h".into()))
+ // The default implementation here is to name these headers
+ // cxxgen.h, cxxgen1.h, cxxgen2.h etc.
+ // These names are not especially predictable by callers and this
+ // behavior is not tested anywhere - so this is considered semi-
+ // supported, at best. This only comes into play in the rare case
+ // that you're generating bindings to multiple include_cpp!
+ // or a mix of include_cpp! and #[cxx::bridge] bindings.
+ let header_counter = Rc::new(RefCell::new(0));
+ Self(Box::new(move || {
+ let header_counter = header_counter.clone();
+ let header_counter_cell = header_counter.as_ref();
+ let mut header_counter = header_counter_cell.borrow_mut();
+ if *header_counter == 0 {
+ *header_counter += 1;
+ "cxxgen.h".into()
+ } else {
+ let count = *header_counter;
+ *header_counter += 1;
+ format!("cxxgen{count}.h")
+ }
+ }))
}
}
@@ -694,10 +772,10 @@
/// You may wish to do this to make a hermetic test case with no
/// external dependencies.
pub suppress_system_headers: bool,
- /// Optionally, a prefix to go at `#include "<here>cxx.h". This is a header file from the `cxx`
+ /// Optionally, a prefix to go at `#include "*here*cxx.h". This is a header file from the `cxx`
/// crate.
pub path_to_cxx_h: Option<String>,
- /// Optionally, a prefix to go at `#include "<here>cxxgen.h". This is a header file which we
+ /// Optionally, a prefix to go at `#include "*here*cxxgen.h". This is a header file which we
/// generate.
pub path_to_cxxgen_h: Option<String>,
/// Optionally, a function called to determine the name that will be used
@@ -720,7 +798,7 @@
// miette.
struct Err;
let r: Result<(usize, usize), Err> = (|| {
- let span_desc = format!("{:?}", span);
+ let span_desc = format!("{span:?}");
let re = Regex::new(r"(\d+)..(\d+)").unwrap();
let captures = re.captures(&span_desc).ok_or(Err)?;
let start = captures.get(1).ok_or(Err)?;
diff --git a/engine/src/minisyn.rs b/engine/src/minisyn.rs
new file mode 100644
index 0000000..e675baa
--- /dev/null
+++ b/engine/src/minisyn.rs
@@ -0,0 +1,187 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Newtype wrappers for `syn` types implementing a different
+//! `Debug` implementation that results in more concise output.
+
+use std::fmt::Display;
+
+use proc_macro2::TokenStream;
+use quote::ToTokens;
+use syn::punctuated::{Pair, Punctuated};
+
+macro_rules! minisyn_no_parse {
+ ($syntype:ident) => {
+ /// Equivalent to the identically-named `syn` type except
+ /// that its `Debug` implementation is more concise.
+ #[derive(Clone, Hash, Eq, PartialEq)]
+ pub struct $syntype(pub ::syn::$syntype);
+ impl std::fmt::Debug for $syntype {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
+ write!(f, "{}", self.0.to_token_stream().to_string())
+ }
+ }
+ impl ToTokens for $syntype
+ where
+ ::syn::$syntype: ToTokens,
+ {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ self.0.to_tokens(tokens)
+ }
+
+ fn to_token_stream(&self) -> TokenStream {
+ self.0.to_token_stream()
+ }
+ fn into_token_stream(self) -> TokenStream
+ where
+ Self: Sized,
+ {
+ self.0.into_token_stream()
+ }
+ }
+ impl std::ops::Deref for $syntype {
+ type Target = ::syn::$syntype;
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+ }
+ impl std::ops::DerefMut for $syntype {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.0
+ }
+ }
+ impl std::convert::From<::syn::$syntype> for $syntype {
+ fn from(inner: ::syn::$syntype) -> Self {
+ Self(inner)
+ }
+ }
+ impl std::convert::From<$syntype> for syn::$syntype {
+ fn from(inner: $syntype) -> Self {
+ inner.0
+ }
+ }
+ };
+}
+
+macro_rules! minisyn {
+ ($syntype:ident) => {
+ minisyn_no_parse!($syntype);
+
+ impl syn::parse::Parse for $syntype {
+ fn parse(input: syn::parse::ParseStream<'_>) -> syn::parse::Result<Self> {
+ syn::parse::Parse::parse(input).map(Self)
+ }
+ }
+ };
+}
+
+minisyn!(ItemMod);
+minisyn_no_parse!(Attribute);
+minisyn_no_parse!(AssocConst);
+minisyn_no_parse!(AssocType);
+minisyn!(Expr);
+minisyn!(ExprAssign);
+minisyn!(ExprAwait);
+minisyn!(ExprBinary);
+minisyn!(ExprBlock);
+minisyn!(ExprBreak);
+minisyn!(ExprConst);
+minisyn!(ExprCast);
+minisyn!(ExprField);
+minisyn_no_parse!(ExprGroup);
+minisyn!(ExprLet);
+minisyn!(ExprParen);
+minisyn!(ExprReference);
+minisyn!(ExprTry);
+minisyn!(ExprUnary);
+minisyn_no_parse!(Field);
+minisyn_no_parse!(Fields);
+minisyn!(ForeignItem);
+minisyn!(FnArg);
+minisyn!(GenericArgument);
+minisyn!(GenericParam);
+minisyn!(Ident);
+minisyn!(ImplItem);
+minisyn!(Item);
+minisyn!(ItemConst);
+minisyn!(ItemEnum);
+minisyn!(ItemForeignMod);
+minisyn!(ItemStruct);
+minisyn!(ItemType);
+minisyn!(ItemUse);
+minisyn!(LitBool);
+minisyn!(LitInt);
+minisyn!(Macro);
+minisyn_no_parse!(Pat);
+minisyn_no_parse!(PatType);
+minisyn_no_parse!(PatReference);
+minisyn_no_parse!(PatSlice);
+minisyn_no_parse!(PatTuple);
+minisyn!(Path);
+minisyn_no_parse!(PathArguments);
+minisyn!(PathSegment);
+minisyn!(Receiver);
+minisyn!(ReturnType);
+minisyn!(Signature);
+minisyn!(Stmt);
+minisyn!(TraitItem);
+minisyn!(Type);
+minisyn!(TypeArray);
+minisyn!(TypeGroup);
+minisyn!(TypeParamBound);
+minisyn!(TypeParen);
+minisyn!(TypePath);
+minisyn!(TypePtr);
+minisyn!(TypeReference);
+minisyn!(TypeSlice);
+minisyn!(Visibility);
+
+/// Converts a `syn::Punctuated` from being full of `syn` types to being
+/// full of `minisyn` types or vice-versa.
+pub(crate) fn minisynize_punctuated<T1, T2, S>(input: Punctuated<T1, S>) -> Punctuated<T2, S>
+where
+ T1: Into<T2>,
+{
+ input
+ .into_pairs()
+ .map(|p| match p {
+ Pair::Punctuated(t, p) => Pair::Punctuated(t.into(), p),
+ Pair::End(t) => Pair::End(t.into()),
+ })
+ .collect()
+}
+
+/// Converts a `Vec` from being full of `syn` types to being
+/// full of `minisyn` types or vice-versa.
+pub(crate) fn minisynize_vec<T1, T2>(input: Vec<T1>) -> Vec<T2>
+where
+ T1: Into<T2>,
+{
+ input.into_iter().map(Into::into).collect()
+}
+
+impl Ident {
+ pub(crate) fn new(string: &str, span: proc_macro2::Span) -> Self {
+ Self(syn::Ident::new(string, span))
+ }
+}
+
+impl Display for Ident {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.write_str(&self.0.to_string())
+ }
+}
+
+impl<T> PartialEq<T> for Ident
+where
+ T: AsRef<str> + ?Sized,
+{
+ fn eq(&self, rhs: &T) -> bool {
+ self.0.eq(rhs)
+ }
+}
diff --git a/engine/src/output_generators.rs b/engine/src/output_generators.rs
index a862558..09c143a 100644
--- a/engine/src/output_generators.rs
+++ b/engine/src/output_generators.rs
@@ -19,7 +19,7 @@
/// Creates an on-disk archive (actually a JSON file) of the Rust side of the bindings
/// for multiple `include_cpp` macros. If you use this, you will want to tell
-/// `autocxx_macro` how to find this file using the `AUTOCXX_RS_ARCHIVE`
+/// `autocxx_macro` how to find this file using the `AUTOCXX_RS_JSON_ARCHIVE`
/// environment variable.
pub fn generate_rs_archive<'a>(rs_outputs: impl Iterator<Item = RsOutput<'a>>) -> String {
let mut multi_bindings = MultiBindings::default();
diff --git a/engine/src/parse_file.rs b/engine/src/parse_file.rs
index 3571d19..16ea625 100644
--- a/engine/src/parse_file.rs
+++ b/engine/src/parse_file.rs
@@ -12,14 +12,15 @@
cxxbridge::CxxBridge, Error as EngineError, GeneratedCpp, IncludeCppEngine,
RebuildDependencyRecorder,
};
-use crate::{CppCodegenOptions, LocatedSynError};
+use crate::{proc_macro_span_to_miette_span, CodegenOptions, CppCodegenOptions, LocatedSynError};
use autocxx_parser::directive_names::SUBCLASS;
use autocxx_parser::{AllowlistEntry, RustPath, Subclass, SubclassAttrs};
use indexmap::set::IndexSet as HashSet;
-use miette::Diagnostic;
+use miette::{Diagnostic, SourceSpan};
use quote::ToTokens;
use std::{io::Read, path::PathBuf};
use std::{panic::UnwindSafe, path::Path, rc::Rc};
+use syn::spanned::Spanned;
use syn::{token::Brace, Item, ItemMod};
use thiserror::Error;
@@ -27,7 +28,7 @@
/// and interpret include_cxx macros.
#[derive(Error, Diagnostic, Debug)]
pub enum ParseError {
- #[error("unable to open the source file: {0}")]
+ #[error("unable to open the source file containing your autocxx bindings. (This filename is usually specified within your build.rs file.): {0}")]
FileOpen(std::io::Error),
#[error("the .rs file couldn't be read: {0}")]
FileRead(std::io::Error),
@@ -39,6 +40,8 @@
#[error("the subclass attribute couldn't be parsed: {0}")]
#[diagnostic(transparent)]
SubclassSyntax(LocatedSynError),
+ #[error("the subclass attribute macro with a superclass attribute requires the Builder::auto_allowlist option to be specified (probably in your build script). This is not recommended - instead you can specify subclass! within your include_cpp!.")]
+ SubclassSuperclassWithoutAutoAllowlist(#[source_code] String, #[label("here")] SourceSpan),
/// The include CPP macro could not be expanded into
/// Rust bindings to C++, because of some problem during the conversion
/// process. This could be anything from a C++ parsing error to some
@@ -52,8 +55,6 @@
/// mod name.
#[error("there are two or more include_cpp! mods with the same mod name")]
ConflictingModNames,
- #[error("dynamic discovery was enabled but no mod was found")]
- ZeroModsForDynamicDiscovery,
#[error("dynamic discovery was enabled but multiple mods were found")]
MultipleModsForDynamicDiscovery,
#[error("a problem occurred while discovering C++ APIs used within the Rust: {0}")]
@@ -87,12 +88,13 @@
extra_superclasses: Vec<Subclass>,
discoveries: Discoveries,
}
+ let file_contents = Rc::new(file_contents.to_string());
impl State {
fn parse_item(
&mut self,
item: Item,
mod_path: Option<RustPath>,
- file_contents: &str,
+ file_contents: Rc<String>,
) -> Result<(), ParseError> {
let result = match item {
Item::Macro(mac)
@@ -110,10 +112,9 @@
)
}
Item::Mod(itm)
- if itm
- .attrs
- .iter()
- .any(|attr| attr.path.to_token_stream().to_string() == "cxx :: bridge") =>
+ if itm.attrs.iter().any(|attr| {
+ attr.path().to_token_stream().to_string() == "cxx :: bridge"
+ }) =>
{
Segment::Cxx(CxxBridge::from(itm))
}
@@ -128,7 +129,11 @@
Some(mod_path) => mod_path.append(itm.ident.clone()),
};
for item in items {
- mod_state.parse_item(item, Some(mod_path.clone()), file_contents)?
+ mod_state.parse_item(
+ item,
+ Some(mod_path.clone()),
+ file_contents.clone(),
+ )?
}
self.extra_superclasses.extend(mod_state.extra_superclasses);
self.discoveries.extend(mod_state.discoveries);
@@ -146,26 +151,34 @@
Segment::Other(Item::Mod(itm))
}
}
- Item::Struct(ref its) if self.auto_allowlist => {
+ Item::Struct(ref its) => {
let attrs = &its.attrs;
let is_superclass_attr = attrs.iter().find(|attr| {
- attr.path
+ attr.path()
.segments
.last()
.map(|seg| seg.ident == "is_subclass" || seg.ident == SUBCLASS)
.unwrap_or(false)
});
if let Some(is_superclass_attr) = is_superclass_attr {
- if !is_superclass_attr.tokens.is_empty() {
+ if is_superclass_attr.meta.require_path_only().is_err() {
let subclass = its.ident.clone();
let args: SubclassAttrs =
is_superclass_attr.parse_args().map_err(|e| {
ParseError::SubclassSyntax(LocatedSynError::new(
e,
- file_contents,
+ &file_contents,
))
})?;
if let Some(superclass) = args.superclass {
+ if !self.auto_allowlist {
+ return Err(
+ ParseError::SubclassSuperclassWithoutAutoAllowlist(
+ file_contents.to_string(),
+ proc_macro_span_to_miette_span(&its.span()),
+ ),
+ );
+ }
self.extra_superclasses.push(Subclass {
superclass,
subclass,
@@ -194,7 +207,7 @@
..Default::default()
};
for item in source.items {
- state.parse_item(item, None, file_contents)?
+ state.parse_item(item, None, file_contents.clone())?
}
let State {
auto_allowlist,
@@ -210,13 +223,18 @@
// We do not want to enter this 'if' block unless the above conditions are true,
// since we may emit errors.
if must_handle_discovered_things {
+ // If we have to handle discovered things but there was no include_cpp! macro,
+ // fake one.
+ if !results.iter().any(|seg| matches!(seg, Segment::Autocxx(_))) {
+ results.push(Segment::Autocxx(IncludeCppEngine::new_for_autodiscover()));
+ }
let mut autocxx_seg_iterator = results.iter_mut().filter_map(|seg| match seg {
Segment::Autocxx(engine) => Some(engine),
_ => None,
});
let our_seg = autocxx_seg_iterator.next();
match our_seg {
- None => return Err(ParseError::ZeroModsForDynamicDiscovery),
+ None => panic!("We should have just added a fake mod but apparently didn't"),
Some(engine) => {
engine
.config_mut()
@@ -364,7 +382,7 @@
autocxx_inc: Vec<PathBuf>,
extra_clang_args: &[&str],
dep_recorder: Option<Box<dyn RebuildDependencyRecorder>>,
- cpp_codegen_options: &CppCodegenOptions,
+ codegen_options: &CodegenOptions,
) -> Result<(), ParseError> {
let mut mods_found = HashSet::new();
let inner_dep_recorder: Option<Rc<dyn RebuildDependencyRecorder>> =
@@ -386,7 +404,7 @@
autocxx_inc.clone(),
extra_clang_args,
dep_recorder,
- cpp_codegen_options,
+ codegen_options,
)
.map_err(ParseError::AutocxxCodegenError)?
}
diff --git a/engine/src/rust_pretty_printer.rs b/engine/src/rust_pretty_printer.rs
index 9c23cbc..7c9d36c 100644
--- a/engine/src/rust_pretty_printer.rs
+++ b/engine/src/rust_pretty_printer.rs
@@ -6,35 +6,12 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use proc_macro2::TokenStream;
-use std::io::Write;
-use std::process::{Command, Stdio};
+use syn::{Item, ItemMod};
-enum Error {
- Run(std::io::Error),
- Write(std::io::Error),
- Utf8(std::string::FromUtf8Error),
- Wait(std::io::Error),
-}
-
-pub(crate) fn pretty_print(ts: &TokenStream) -> String {
- reformat_or_else(ts.to_string())
-}
-
-fn reformat_or_else(text: impl std::fmt::Display) -> String {
- match reformat(&text) {
- Ok(s) => s,
- Err(_) => text.to_string(),
- }
-}
-
-fn reformat(text: impl std::fmt::Display) -> Result<String, Error> {
- let mut rustfmt = Command::new("rustfmt")
- .stdin(Stdio::piped())
- .stdout(Stdio::piped())
- .spawn()
- .map_err(Error::Run)?;
- write!(rustfmt.stdin.take().unwrap(), "{}", text).map_err(Error::Write)?;
- let output = rustfmt.wait_with_output().map_err(Error::Wait)?;
- String::from_utf8(output.stdout).map_err(Error::Utf8)
+pub(crate) fn pretty_print(itm: &ItemMod) -> String {
+ prettyplease::unparse(&syn::File {
+ shebang: None,
+ attrs: Vec::new(),
+ items: vec![Item::Mod(itm.clone())],
+ })
}
diff --git a/engine/src/types.rs b/engine/src/types.rs
index 337da14..f7eaae0 100644
--- a/engine/src/types.rs
+++ b/engine/src/types.rs
@@ -6,14 +6,16 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
+use crate::minisyn::Ident;
use itertools::Itertools;
use proc_macro2::Span;
use quote::ToTokens;
use std::iter::Peekable;
use std::{fmt::Display, sync::Arc};
-use syn::{parse_quote, Ident, PathSegment, TypePath};
+use syn::{parse_quote, PathSegment, TypePath};
+use thiserror::Error;
-use crate::{conversion::ConvertError, known_types::known_types};
+use crate::known_types::known_types;
pub(crate) fn make_ident<S: AsRef<str>>(id: S) -> Ident {
Ident::new(id.as_ref(), Span::call_site())
@@ -52,11 +54,15 @@
pub(crate) fn depth(&self) -> usize {
self.0.len()
}
+
+ pub(crate) fn to_cpp_path(&self) -> String {
+ self.0.join("::")
+ }
}
impl Display for Namespace {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- f.write_str(&self.0.join("::"))
+ f.write_str(&self.to_cpp_path())
}
}
@@ -80,7 +86,7 @@
/// either. It doesn't directly have functionality to convert
/// from one to the other; `replace_type_path_without_arguments`
/// does that.
-#[derive(Debug, PartialEq, PartialOrd, Eq, Hash, Clone)]
+#[derive(PartialEq, PartialOrd, Eq, Hash, Clone)]
pub struct QualifiedName(Namespace, String);
impl QualifiedName {
@@ -140,7 +146,7 @@
/// cxx doesn't accept names containing double underscores,
/// but these are OK elsewhere in our output mod.
- pub(crate) fn validate_ok_for_cxx(&self) -> Result<(), ConvertError> {
+ pub(crate) fn validate_ok_for_cxx(&self) -> Result<(), InvalidIdentError> {
validate_ident_ok_for_cxx(self.get_final_item())
}
@@ -172,14 +178,6 @@
}
}
- pub(crate) fn get_final_cpp_item(&self) -> String {
- let special_cpp_name = known_types().special_cpp_name(self);
- match special_cpp_name {
- Some(name) => name,
- None => self.1.to_string(),
- }
- }
-
pub(crate) fn to_type_path(&self) -> TypePath {
if let Some(known_type_path) = known_types().known_type_type_path(self) {
known_type_path
@@ -228,26 +226,44 @@
}
}
+impl std::fmt::Debug for QualifiedName {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ Display::fmt(self, f)
+ }
+}
+
+/// Problems representing C++ identifiers in a way which is compatible with
+/// cxx.
+#[derive(Error, Clone, Debug)]
+pub enum InvalidIdentError {
+ #[error("Names containing __ are reserved by C++ so not acceptable to cxx")]
+ TooManyUnderscores,
+ #[error("bindgen decided to call this type _bindgen_ty_N because it couldn't deduce the correct name for it. That means we can't generate C++ bindings to it.")]
+ BindgenTy,
+ #[error("The item name '{0}' is a reserved word in Rust.")]
+ ReservedName(String),
+}
+
/// cxx doesn't allow identifiers containing __. These are OK elsewhere
/// in our output mod. It would be nice in future to think of a way we
/// can enforce this using the Rust type system, e.g. a newtype
/// wrapper for a CxxCompatibleIdent which is used in any context
/// where code will be output as part of the `#[cxx::bridge]` mod.
-pub fn validate_ident_ok_for_cxx(id: &str) -> Result<(), ConvertError> {
+pub fn validate_ident_ok_for_cxx(id: &str) -> Result<(), InvalidIdentError> {
validate_ident_ok_for_rust(id)?;
if id.contains("__") {
- Err(ConvertError::TooManyUnderscores)
+ Err(InvalidIdentError::TooManyUnderscores)
} else if id.starts_with("_bindgen_ty_") {
- Err(ConvertError::BindgenTy)
+ Err(InvalidIdentError::BindgenTy)
} else {
Ok(())
}
}
-pub fn validate_ident_ok_for_rust(label: &str) -> Result<(), ConvertError> {
+pub fn validate_ident_ok_for_rust(label: &str) -> Result<(), InvalidIdentError> {
let id = make_ident(label);
syn::parse2::<syn::Ident>(id.into_token_stream())
- .map_err(|_| ConvertError::ReservedName(label.to_string()))
+ .map_err(|_| InvalidIdentError::ReservedName(label.to_string()))
.map(|_| ())
}
diff --git a/examples/chromium-fake-render-frame-host/Cargo.toml b/examples/chromium-fake-render-frame-host/Cargo.toml
index 3862f2c..9cc05cd 100644
--- a/examples/chromium-fake-render-frame-host/Cargo.toml
+++ b/examples/chromium-fake-render-frame-host/Cargo.toml
@@ -13,9 +13,9 @@
edition = "2021"
[dependencies]
-cxx = "1.0.68"
-autocxx = { path = "../..", version="0.22.3" }
+cxx = "1.0.78"
+autocxx = { path = "../..", version = "0.26.0" }
[build-dependencies]
-autocxx-build = { path = "../../gen/build", version="0.22.3" }
-miette = { version="4.3", features = [ "fancy" ] }
+autocxx-build = { path = "../../gen/build", version = "0.26.0" }
+miette = { version = "5", features = ["fancy"] }
diff --git a/examples/chromium-fake-render-frame-host/src/render_frame_host.rs b/examples/chromium-fake-render-frame-host/src/render_frame_host.rs
index 9c2bf51..761c8a8 100644
--- a/examples/chromium-fake-render-frame-host/src/render_frame_host.rs
+++ b/examples/chromium-fake-render-frame-host/src/render_frame_host.rs
@@ -209,7 +209,7 @@
}
}
-#[is_subclass(superclass("content::WebContentsObserver"))]
+#[subclass(superclass("content::WebContentsObserver"))]
#[doc(hidden)]
pub struct RenderFrameHostForWebContents {
rfh: *mut ffi::content::RenderFrameHost,
diff --git a/examples/cpp_calling_rust/Cargo.toml b/examples/cpp_calling_rust/Cargo.toml
new file mode 100644
index 0000000..2814194
--- /dev/null
+++ b/examples/cpp_calling_rust/Cargo.toml
@@ -0,0 +1,26 @@
+# Copyright 2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+# https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+# <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+# option. This file may not be copied, modified, or distributed
+# except according to those terms.
+
+[package]
+name = "cpp_calling_rust"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+cxx = "1.0.78"
+autocxx = { path = "../..", version="0.26.0" }
+uwuify = "0.2.2"
+textwrap = "0.14"
+fastrand = "1.5.0"
+
+[build-dependencies]
+autocxx-build = { path = "../../gen/build", version="0.26.0" }
+regex = "1.5.4"
+miette = { version="5", features = [ "fancy" ] }
diff --git a/examples/cpp_calling_rust/build.rs b/examples/cpp_calling_rust/build.rs
new file mode 100644
index 0000000..60cc460
--- /dev/null
+++ b/examples/cpp_calling_rust/build.rs
@@ -0,0 +1,21 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn main() -> miette::Result<()> {
+ let path = std::path::PathBuf::from("src");
+ let mut b = autocxx_build::Builder::new("src/main.rs", &[&path])
+ .auto_allowlist(true)
+ .build()?;
+ b.flag_if_supported("-std=c++17")
+ .file("src/input.cc")
+ .compile("autocxx-cpp-calling-rust-example");
+ println!("cargo:rerun-if-changed=src/main.rs");
+ println!("cargo:rerun-if-changed=src/input.cc");
+ println!("cargo:rerun-if-changed=src/input.h");
+ Ok(())
+}
diff --git a/examples/cpp_calling_rust/src/input.cc b/examples/cpp_calling_rust/src/input.cc
new file mode 100644
index 0000000..64c446a
--- /dev/null
+++ b/examples/cpp_calling_rust/src/input.cc
@@ -0,0 +1,19 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#include "cxxgen.h"
+#include "input.h"
+
+void jurassic() {
+ rust::Box<Dinosaur> prey = new_dinosaur(false);
+ rust::Box<Dinosaur> predator = new_dinosaur(true);
+ prey->roar();
+ predator->roar();
+ predator->eat(std::move(prey));
+ go_extinct();
+}
\ No newline at end of file
diff --git a/examples/cpp_calling_rust/src/input.h b/examples/cpp_calling_rust/src/input.h
new file mode 100644
index 0000000..206adc6
--- /dev/null
+++ b/examples/cpp_calling_rust/src/input.h
@@ -0,0 +1,16 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#pragma once
+
+#include <cstdint>
+#include <sstream>
+#include <stdint.h>
+#include <string>
+
+void jurassic();
\ No newline at end of file
diff --git a/examples/cpp_calling_rust/src/main.rs b/examples/cpp_calling_rust/src/main.rs
new file mode 100644
index 0000000..3fe6404
--- /dev/null
+++ b/examples/cpp_calling_rust/src/main.rs
@@ -0,0 +1,58 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// This example shows calls from C++ back into Rust. That's really not
+// the main purpose of autocxx, and this support is immature. If you're
+// primarily doing this sort of thing, look into other tools such as
+// cbindgen or cxx.
+
+use autocxx::prelude::*;
+use std::pin::Pin;
+
+include_cpp! {
+ #include "input.h"
+ safety!(unsafe_ffi)
+ generate!("jurassic")
+}
+
+fn main() {
+ ffi::jurassic();
+}
+
+#[autocxx::extern_rust::extern_rust_type]
+pub struct Dinosaur {
+ carnivore: bool,
+}
+
+#[autocxx::extern_rust::extern_rust_function]
+pub fn new_dinosaur(carnivore: bool) -> Box<Dinosaur> {
+ Box::new(Dinosaur { carnivore })
+}
+
+impl Dinosaur {
+ #[autocxx::extern_rust::extern_rust_function]
+ fn roar(&self) {
+ println!("Roar");
+ }
+
+ #[autocxx::extern_rust::extern_rust_function]
+ fn eat(self: Pin<&mut Dinosaur>, other_dinosaur: Box<Dinosaur>) {
+ assert!(self.carnivore);
+ other_dinosaur.get_eaten();
+ println!("Nom nom");
+ }
+
+ fn get_eaten(&self) {
+ println!("Uh-oh");
+ }
+}
+
+#[autocxx::extern_rust::extern_rust_function]
+pub fn go_extinct() {
+ println!("Boom")
+}
diff --git a/examples/llvm/Cargo.toml b/examples/llvm/Cargo.toml
index 4c03db7..3c274ac 100644
--- a/examples/llvm/Cargo.toml
+++ b/examples/llvm/Cargo.toml
@@ -13,9 +13,9 @@
edition = "2021"
[dependencies]
-cxx = "1.0.68"
-autocxx = { path = "../..", version="0.17.2" }
+cxx = "1.0.78"
+autocxx = { path = "../..", version = "0.26.0" }
[build-dependencies]
-autocxx-build = { path = "../../gen/build", version="0.17.2" }
-miette = { version="4.3", features = [ "fancy" ] }
+autocxx-build = { path = "../../gen/build", version = "0.26.0" }
+miette = { version = "5", features = ["fancy"] }
diff --git a/examples/non-trivial-type-on-stack/Cargo.toml b/examples/non-trivial-type-on-stack/Cargo.toml
index 110bd1b..7af139e 100644
--- a/examples/non-trivial-type-on-stack/Cargo.toml
+++ b/examples/non-trivial-type-on-stack/Cargo.toml
@@ -13,9 +13,9 @@
edition = "2021"
[dependencies]
-cxx = "1.0.68"
-autocxx = { path = "../..", version="0.22.3" }
+cxx = "1.0.78"
+autocxx = { path = "../..", version = "0.26.0" }
[build-dependencies]
-autocxx-build = { path = "../../gen/build", version="0.22.3" }
-miette = { version="4.3", features = [ "fancy" ] }
+autocxx-build = { path = "../../gen/build", version = "0.26.0" }
+miette = { version = "5", features = ["fancy"] }
diff --git a/examples/pod/Cargo.toml b/examples/pod/Cargo.toml
index 9f4ee5a..e05d261 100644
--- a/examples/pod/Cargo.toml
+++ b/examples/pod/Cargo.toml
@@ -13,9 +13,9 @@
edition = "2021"
[dependencies]
-cxx = "1.0.68"
-autocxx = { path = "../..", version="0.22.3" }
+cxx = "1.0.78"
+autocxx = { path = "../..", version = "0.26.0" }
[build-dependencies]
-autocxx-build = { path = "../../gen/build", version="0.22.3" }
-miette = { version="4.3", features = [ "fancy" ] }
+autocxx-build = { path = "../../gen/build", version = "0.26.0" }
+miette = { version = "5", features = ["fancy"] }
diff --git a/examples/reference-wrappers/Cargo.toml b/examples/reference-wrappers/Cargo.toml
index cb85e80..93c68ba 100644
--- a/examples/reference-wrappers/Cargo.toml
+++ b/examples/reference-wrappers/Cargo.toml
@@ -13,9 +13,9 @@
edition = "2021"
[dependencies]
-cxx = "1.0.68"
-autocxx = { path = "../..", version="0.22.3" }
+cxx = "1.0.78"
+autocxx = { path = "../..", version = "0.26.0" }
[build-dependencies]
-autocxx-build = { path = "../../gen/build", version="0.22.3" }
-miette = { version="4.3", features=["fancy"]}
+autocxx-build = { path = "../../gen/build", version = "0.26.0" }
+miette = { version = "5", features = ["fancy"] }
diff --git a/examples/reference-wrappers/build.rs b/examples/reference-wrappers/build.rs
index 64c573d..8e15dd0 100644
--- a/examples/reference-wrappers/build.rs
+++ b/examples/reference-wrappers/build.rs
@@ -10,9 +10,11 @@
let path = std::path::PathBuf::from("src");
let mut b = autocxx_build::Builder::new("src/main.rs", &[&path]).build()?;
b.flag_if_supported("-std=c++14")
- .file("src/input.cc").compile("autocxx-reference-wrapper-example");
+ .file("src/input.cc")
+ .compile("autocxx-reference-wrapper-example");
println!("cargo:rerun-if-changed=src/main.rs");
println!("cargo:rerun-if-changed=src/input.h");
+
Ok(())
}
diff --git a/examples/reference-wrappers/src/input.h b/examples/reference-wrappers/src/input.h
index 5e3c6e9..dd37dfa 100644
--- a/examples/reference-wrappers/src/input.h
+++ b/examples/reference-wrappers/src/input.h
@@ -18,6 +18,7 @@
Goat() : horns(0) {}
void add_a_horn();
std::string describe() const;
+ uint32_t& get_horns() { return horns; }
private:
uint32_t horns;
};
diff --git a/examples/reference-wrappers/src/main.rs b/examples/reference-wrappers/src/main.rs
index 6bea2ff..ca3e0cd 100644
--- a/examples/reference-wrappers/src/main.rs
+++ b/examples/reference-wrappers/src/main.rs
@@ -23,16 +23,52 @@
// especially in the absence of the Rust "arbitrary self types"
// feature.
+// Necessary to be able to call methods on reference wrappers.
+// For that reason, this example only builds on nightly Rust.
+#![feature(arbitrary_self_types)]
+
use autocxx::prelude::*;
+use std::pin::Pin;
include_cpp! {
#include "input.h"
// This next line enables C++ reference wrappers
+ // This is what requires the 'arbitrary_self_types' feature.
safety!(unsafe_references_wrapped)
generate!("Goat")
generate!("Field")
}
+impl ffi::Goat {
+ // Methods can be called on a CppRef<T> or &CppRef<T>
+ fn bleat(self: CppRef<Self>) {
+ println!("Bleat");
+ }
+}
+
+trait FarmProduce {
+ // Traits can be defined on a CppRef<T> so long as Self: Sized
+ fn sell(self: &CppRef<Self>)
+ where
+ Self: Sized;
+}
+
+impl FarmProduce for ffi::Goat {
+ fn sell(self: &CppRef<Self>) {
+ println!("Selling goat");
+ }
+}
+
+trait FarmArea {
+ fn maintain(self: CppRef<Self>);
+}
+
+impl FarmArea for ffi::Field {
+ fn maintain(self: CppRef<Self>) {
+ println!("Maintaining");
+ }
+}
+
fn main() {
// Create a cxx::UniquePtr as normal for a Field object.
let field = ffi::Field::new().within_unique_ptr();
@@ -47,21 +83,36 @@
// However, as soon as we want to pass a reference to the field
// back to C++, we have to ensure we have no Rust references
// in existence. So: we imprison the object in a "CppPin":
- let field = ffi::cpp_pin_uniqueptr(field);
+ let field = CppUniquePtrPin::new(field);
// We can no longer take Rust references to the field...
// let _field_rust_ref = field.as_ref();
// However, we can take C++ references. And use such references
- // to call methods...
+ // to call methods in C++. Quite often those methods will
+ // return other references, like this.
let another_goat = field.as_cpp_ref().get_goat();
// The 'get_goat' method in C++ returns a reference, so this is
// another CppRef, not a Rust reference.
+
+ // We can still use these C++ references to call Rust methods,
+ // so long as those methods have a "self" type of a
+ // C++ reference not a Rust reference.
+ another_goat.clone().bleat();
+ another_goat.sell();
+
+ // But most commonly, C++ references are simply used as the 'this'
+ // type when calling other C++ methods, like this.
assert_eq!(
another_goat
.describe() // returns a UniquePtr<CxxString>, there
- // are no Rust or C++ references involved at this point.
+ // are no Rust or C++ references involved at this point.
.as_ref()
.unwrap()
.to_string_lossy(),
"This goat has 0 horns."
);
+
+ // We can even use trait objects, though it's a bit of a fiddle.
+ let farm_area: Pin<Box<dyn FarmArea>> = ffi::Field::new().within_box();
+ let farm_area = CppPin::from_pinned_box(farm_area);
+ farm_area.as_cpp_ref().maintain();
}
diff --git a/examples/s2/Cargo.toml b/examples/s2/Cargo.toml
index 04524b1..c93706b 100644
--- a/examples/s2/Cargo.toml
+++ b/examples/s2/Cargo.toml
@@ -15,9 +15,9 @@
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
-cxx = "1.0.68"
-autocxx = { path = "../..", version="0.22.3" }
+cxx = "1.0.78"
+autocxx = { path = "../..", version = "0.26.0" }
[build-dependencies]
-autocxx-build = { path = "../../gen/build", version="0.22.3" }
-miette = { version="4.3", features = [ "fancy" ] }
+autocxx-build = { path = "../../gen/build", version = "0.26.0" }
+miette = { version = "5", features = ["fancy"] }
diff --git a/examples/steam-mini/Cargo.toml b/examples/steam-mini/Cargo.toml
index ceb81ff..5162f96 100644
--- a/examples/steam-mini/Cargo.toml
+++ b/examples/steam-mini/Cargo.toml
@@ -15,9 +15,9 @@
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
-cxx = "1.0.68"
-autocxx = { path = "../..", version="0.22.3" }
+cxx = "1.0.78"
+autocxx = { path = "../..", version = "0.26.0" }
[build-dependencies]
-autocxx-build = { path = "../../gen/build", version="0.22.3" }
-miette = { version="4.3", features = [ "fancy" ] }
+autocxx-build = { path = "../../gen/build", version = "0.26.0" }
+miette = { version = "5", features = ["fancy"] }
diff --git a/examples/subclass/Cargo.toml b/examples/subclass/Cargo.toml
index dc93f53..a19ea27 100644
--- a/examples/subclass/Cargo.toml
+++ b/examples/subclass/Cargo.toml
@@ -15,13 +15,13 @@
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
-cxx = "1.0.68"
-autocxx = { path = "../..", version="0.22.3" }
+cxx = "1.0.78"
+autocxx = { path = "../..", version = "0.26.0" }
uwuify = "0.2.2"
-textwrap = "0.14"
+textwrap = "0.15"
fastrand = "1.5.0"
[build-dependencies]
-autocxx-build = { path = "../../gen/build", version="0.22.3" }
+autocxx-build = { path = "../../gen/build", version = "0.26.0" }
regex = "1.5.4"
-miette = { version="4.3", features = [ "fancy" ] }
+miette = { version = "5", features = ["fancy"] }
diff --git a/examples/subclass/src/main.rs b/examples/subclass/src/main.rs
index 2841598..095abd4 100644
--- a/examples/subclass/src/main.rs
+++ b/examples/subclass/src/main.rs
@@ -34,7 +34,7 @@
// See the main function at the bottom for how this subclass
// is instantiated.
-#[is_subclass(superclass("MessageDisplayer"))]
+#[subclass(superclass("MessageDisplayer"))]
#[derive(Default)]
pub struct UwuDisplayer {}
@@ -60,7 +60,7 @@
// for now, at least, we can't hold non-trivial C++ objects on the Rust stack.)
// All the boxing and unboxing is done automatically by autocxx layers.
-#[is_subclass(superclass("MessageProducer"))]
+#[subclass(superclass("MessageProducer"))]
#[derive(Default)]
pub struct QuoteProducer;
@@ -93,7 +93,7 @@
// doing stuff. In C++ you'd probably need a const_cast. Here we use
// interior mutability.
-#[is_subclass(superclass("MessageDisplayer"))]
+#[subclass(superclass("MessageDisplayer"))]
pub struct BoxDisplayer {
message_count: RefCell<usize>,
}
@@ -103,7 +103,7 @@
Self::new_rust_owned(Self {
// As we're allocating this class ourselves instead of using [`Default`]
// we need to initialize the `cpp_peer` member ourselves. This member is
- // inserted by the `#[is_subclass]` annotation. autocxx will
+ // inserted by the `#[subclass]` annotation. autocxx will
// later use this to store a pointer back to the C++ peer.
cpp_peer: Default::default(),
message_count: RefCell::new(1usize),
diff --git a/examples/subclass/src/messages.h b/examples/subclass/src/messages.h
index 02ff248..bbf6118 100644
--- a/examples/subclass/src/messages.h
+++ b/examples/subclass/src/messages.h
@@ -12,7 +12,6 @@
#pragma once
#include <string>
-#include <memory>
class MessageProducer {
public:
diff --git a/gen/build/Cargo.toml b/gen/build/Cargo.toml
index 678a844..d101f91 100644
--- a/gen/build/Cargo.toml
+++ b/gen/build/Cargo.toml
@@ -8,7 +8,7 @@
[package]
name = "autocxx-build"
-version = "0.22.3"
+version = "0.26.0"
authors = ["Adrian Taylor <adetaylor@chromium.org>"]
edition = "2021"
license = "MIT OR Apache-2.0"
@@ -18,14 +18,16 @@
categories = ["development-tools::ffi", "api-bindings"]
[features]
-runtime = [ "autocxx-engine/runtime" ]
-static = [ "autocxx-engine/static" ]
+runtime = ["autocxx-engine/runtime"]
+static = ["autocxx-engine/static"]
[dependencies]
-autocxx-engine = { version="=0.22.3", path="../../engine", features = ["build"] }
+autocxx-engine = { version = "=0.26.0", path = "../../engine", features = [
+ "build",
+] }
env_logger = "0.9.0"
indexmap = "1.8"
[dependencies.syn]
-version = "1.0"
-features = [ "full" ]
+version = "2.0"
+features = ["full"]
diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs
index c1df580..ccef184 100644
--- a/gen/build/src/lib.rs
+++ b/gen/build/src/lib.rs
@@ -45,7 +45,7 @@
fn record_header_file_dependency(&self, filename: &str) {
let mut already = self.printed_already.lock().unwrap();
if already.insert(filename.into()) {
- println!("cargo:rerun-if-changed={}", filename);
+ println!("cargo:rerun-if-changed={filename}");
}
}
}
diff --git a/gen/cmd/Cargo.toml b/gen/cmd/Cargo.toml
index 51eb56c..7a78ba3 100644
--- a/gen/cmd/Cargo.toml
+++ b/gen/cmd/Cargo.toml
@@ -8,7 +8,7 @@
[package]
name = "autocxx-gen"
-version = "0.22.3"
+version = "0.26.0"
authors = ["Adrian Taylor <adetaylor@chromium.org>"]
edition = "2021"
license = "MIT OR Apache-2.0"
@@ -18,24 +18,24 @@
categories = ["development-tools::ffi", "api-bindings"]
[features]
-runtime = [ "autocxx-engine/runtime" ]
-static = [ "autocxx-engine/static" ]
+runtime = ["autocxx-engine/runtime"]
+static = ["autocxx-engine/static"]
[dependencies]
-autocxx-engine = { version="=0.22.3", path="../../engine" }
+autocxx-engine = { version = "=0.26.0", path = "../../engine" }
clap = { version = "3.1.2", features = ["cargo"] }
proc-macro2 = "1.0"
env_logger = "0.9.0"
-miette = { version="4.3", features=["fancy"]}
+miette = { version = "5", features = ["fancy"] }
pathdiff = "0.2.1"
indexmap = "1.8"
[dev-dependencies]
-assert_cmd = "1.0.3"
+assert_cmd = "2"
tempfile = "3.1"
-autocxx-integration-tests = { path = "../../integration-tests", version="=0.22.3" }
# This is necessary for building the projects created
# by the trybuild test system...
-autocxx = { path="../.." }
-cxx = "1.0.68"
-itertools = "0.10.3"
\ No newline at end of file
+autocxx = { path = "../.." }
+autocxx-integration-tests = { path = "../../integration-tests", version = "=0.26.0" }
+cxx = "1.0.78"
+itertools = "0.10.3"
diff --git a/gen/cmd/src/depfile.rs b/gen/cmd/src/depfile.rs
index d8d3626..315ff89 100644
--- a/gen/cmd/src/depfile.rs
+++ b/gen/cmd/src/depfile.rs
@@ -44,7 +44,7 @@
let dependency_list = self.dependencies.join(" \\\n ");
for output in &self.outputs {
self.file
- .write_all(format!("{}: {}\n\n", output, dependency_list).as_bytes())?
+ .write_all(format!("{output}: {dependency_list}\n\n").as_bytes())?
}
Ok(())
}
diff --git a/gen/cmd/src/main.rs b/gen/cmd/src/main.rs
index 8b109c3..4d533ff 100644
--- a/gen/cmd/src/main.rs
+++ b/gen/cmd/src/main.rs
@@ -11,8 +11,8 @@
mod depfile;
use autocxx_engine::{
- generate_rs_archive, generate_rs_single, parse_file, AutocxxgenHeaderNamer, CxxgenHeaderNamer,
- RebuildDependencyRecorder,
+ generate_rs_archive, generate_rs_single, get_cxx_header_bytes, parse_file,
+ AutocxxgenHeaderNamer, CxxgenHeaderNamer, RebuildDependencyRecorder,
};
use clap::{crate_authors, crate_version, Arg, ArgGroup, Command};
use depfile::Depfile;
@@ -74,7 +74,11 @@
instead of
--gen-rs-include
and you will need to give AUTOCXX_RS_JSON_ARCHIVE when building the Rust code.
-The output filename is named gen.rs.json.
+The output filename is named gen.rs.json. AUTOCXX_RS_JSON_ARCHIVE should be set
+to the path to gen.rs.json. It may optionally have multiple paths separated the
+way as the PATH environment variable for the current platform, see
+[`std::env::split_paths`] for details. The first path which is successfully
+opened will be used.
This teaches rustc (and the autocxx macro) that all the different Rust bindings
for multiple different autocxx macros have been archived into this single file.
@@ -128,7 +132,7 @@
.arg(
Arg::new("gen-rs-include")
.long("gen-rs-include")
- .help("whether to generate Rust files for inclusion using autocxx_macro (suffix will be .include.rs)")
+ .help("whether to generate Rust files for inclusion using autocxx_macro")
)
.arg(
Arg::new("gen-rs-archive")
@@ -152,7 +156,7 @@
.arg(
Arg::new("fix-rs-include-name")
.long("fix-rs-include-name")
- .help("Make the name of the .rs file predictable. You must set AUTOCXX_RS_FILE during Rust build time to educate autocxx_macro about your choice.")
+ .help("Make the name of the .rs file predictable (suffix will be .include.rs). You must set AUTOCXX_RS_FILE during Rust build time to educate autocxx_macro about your choice.")
.requires("gen-rs-include")
)
.arg(
@@ -173,6 +177,11 @@
.takes_value(true),
)
.arg(
+ Arg::new("generate-cxx-h")
+ .long("generate-cxx-h")
+ .help("whether to generate cxx.h header file. If you already knew where to find cxx.h, consider using --cxx-h-path")
+ )
+ .arg(
Arg::new("cxx-h-path")
.long("cxx-h-path")
.value_name("PREFIX")
@@ -243,6 +252,10 @@
autocxxgen_header_namer,
cxxgen_header_namer,
};
+ let codegen_options = autocxx_engine::CodegenOptions {
+ cpp_codegen_options,
+ ..Default::default()
+ };
let depfile = match matches.value_of("depfile") {
None => None,
Some(depfile_path) => {
@@ -273,12 +286,20 @@
incs.clone(),
&extra_clang_args,
dep_recorder,
- &cpp_codegen_options,
+ &codegen_options,
)?;
}
// Finally start to write the C++ and Rust out.
let outdir: PathBuf = matches.value_of_os("outdir").unwrap().into();
+
+ if !outdir.exists() {
+ use miette::WrapErr as _;
+ std::fs::create_dir_all(&outdir)
+ .into_diagnostic()
+ .wrap_err_with(|| format!("Failed to create `outdir` '{}'", outdir.display()))?;
+ }
+
let mut writer = FileWriter {
depfile: &depfile,
outdir: &outdir,
@@ -286,14 +307,14 @@
};
if matches.is_present("gen-cpp") {
let cpp = matches.value_of("cpp-extension").unwrap();
- let name_cc_file = |counter| format!("gen{}.{}", counter, cpp);
+ let name_cc_file = |counter| format!("gen{counter}.{cpp}");
let mut counter = 0usize;
for include_cxx in parsed_files
.iter()
.flat_map(|file| file.get_cpp_buildables())
{
let generations = include_cxx
- .generate_h_and_cxx(&cpp_codegen_options)
+ .generate_h_and_cxx(&codegen_options.cpp_codegen_options)
.expect("Unable to generate header and C++ code");
for pair in generations.0 {
let cppname = name_cc_file(counter);
@@ -302,7 +323,7 @@
counter += 1;
}
}
- drop(cpp_codegen_options);
+ drop(codegen_options);
// Write placeholders to ensure we always make exactly 'n' of each file type.
writer.write_placeholders(counter, desired_number, name_cc_file)?;
writer.write_placeholders(
@@ -316,6 +337,14 @@
name_autocxxgen_h,
)?;
}
+
+ if matches.is_present("generate-cxx-h") {
+ writer.write_to_file(
+ "cxx.h".to_string(),
+ &get_cxx_header_bytes(suppress_system_headers),
+ )?;
+ }
+
if matches.is_present("gen-rs-include") {
if !matches.is_present("fix-rs-include-name") && desired_number.is_some() {
return Err(miette::Report::msg(
@@ -352,15 +381,15 @@
}
fn name_autocxxgen_h(counter: usize) -> String {
- format!("autocxxgen{}.h", counter)
+ format!("autocxxgen{counter}.h")
}
fn name_cxxgen_h(counter: usize) -> String {
- format!("gen{}.h", counter)
+ format!("gen{counter}.h")
}
fn name_include_rs(counter: usize) -> String {
- format!("gen{}.include.rs", counter)
+ format!("gen{counter}.include.rs")
}
fn get_dependency_recorder(depfile: Rc<RefCell<Depfile>>) -> Box<dyn RebuildDependencyRecorder> {
@@ -387,7 +416,7 @@
) -> miette::Result<()> {
if let Some(desired_number) = desired_number {
if counter > desired_number {
- return Err(miette::Report::msg("More files were generated than expected. Increase the value passed to --generate-exact or reduce the number of include_cpp! sections."));
+ return Err(miette::Report::msg(format!("{counter} files were generated. Increase the value passed to --generate-exact or reduce the number of include_cpp! sections.")));
}
while counter < desired_number {
let fname = filename(counter);
@@ -416,7 +445,7 @@
let mut f = File::create(&path).into_diagnostic()?;
f.write_all(content).into_diagnostic()?;
if self.written.contains(&filename) {
- return Err(miette::Report::msg(format!("autocxx_gen would write two files entitled '{}' which would have conflicting contents. Consider using --generate-exact.", filename)));
+ return Err(miette::Report::msg(format!("autocxx_gen would write two files entitled '{filename}' which would have conflicting contents. Consider using --generate-exact.")));
}
self.written.insert(filename);
Ok(())
diff --git a/gen/cmd/tests/cmd_test.rs b/gen/cmd/tests/cmd_test.rs
index 7e455a2..4ec3942 100644
--- a/gen/cmd/tests/cmd_test.rs
+++ b/gen/cmd/tests/cmd_test.rs
@@ -88,7 +88,8 @@
.arg(demo_code_dir.to_str().unwrap())
.arg("--outdir")
.arg(tmp_dir.path().to_str().unwrap())
- .arg("--gen-cpp");
+ .arg("--gen-cpp")
+ .arg("--generate-cxx-h");
cmd.arg(match rs_gen_mode {
RsGenMode::Single => "--gen-rs-include",
RsGenMode::Archive => "--gen-rs-archive",
@@ -109,8 +110,6 @@
fn test_gen() -> Result<(), Box<dyn std::error::Error>> {
let tmp_dir = tempdir()?;
base_test(&tmp_dir, RsGenMode::Single, |_| {})?;
- File::create(tmp_dir.path().join("cxx.h"))
- .and_then(|mut cxx_h| cxx_h.write_all(autocxx_engine::HEADER.as_bytes()))?;
std::env::set_var("OUT_DIR", tmp_dir.path().to_str().unwrap());
let r = build_from_folder(
tmp_dir.path(),
@@ -130,8 +129,6 @@
fn test_gen_archive() -> Result<(), Box<dyn std::error::Error>> {
let tmp_dir = tempdir()?;
base_test(&tmp_dir, RsGenMode::Archive, |_| {})?;
- File::create(tmp_dir.path().join("cxx.h"))
- .and_then(|mut cxx_h| cxx_h.write_all(autocxx_engine::HEADER.as_bytes()))?;
let r = build_from_folder(
tmp_dir.path(),
&tmp_dir.path().join("demo/main.rs"),
@@ -147,6 +144,54 @@
}
#[test]
+fn test_gen_archive_first_entry() -> Result<(), Box<dyn std::error::Error>> {
+ let tmp_dir = tempdir()?;
+ base_test(&tmp_dir, RsGenMode::Archive, |_| {})?;
+ let r = build_from_folder(
+ tmp_dir.path(),
+ &tmp_dir.path().join("demo/main.rs"),
+ vec![tmp_dir.path().join("gen.rs.json")],
+ &["gen0.cc"],
+ RsFindMode::Custom(Box::new(|path: &Path| {
+ std::env::set_var(
+ "AUTOCXX_RS_JSON_ARCHIVE",
+ std::env::join_paths([&path.join("gen.rs.json"), Path::new("/nonexistent")])
+ .unwrap(),
+ )
+ })),
+ );
+ if KEEP_TEMPDIRS {
+ println!("Tempdir: {:?}", tmp_dir.into_path().to_str());
+ }
+ r.unwrap();
+ Ok(())
+}
+
+#[test]
+fn test_gen_archive_second_entry() -> Result<(), Box<dyn std::error::Error>> {
+ let tmp_dir = tempdir()?;
+ base_test(&tmp_dir, RsGenMode::Archive, |_| {})?;
+ let r = build_from_folder(
+ tmp_dir.path(),
+ &tmp_dir.path().join("demo/main.rs"),
+ vec![tmp_dir.path().join("gen.rs.json")],
+ &["gen0.cc"],
+ RsFindMode::Custom(Box::new(|path: &Path| {
+ std::env::set_var(
+ "AUTOCXX_RS_JSON_ARCHIVE",
+ std::env::join_paths([Path::new("/nonexistent"), &path.join("gen.rs.json")])
+ .unwrap(),
+ )
+ })),
+ );
+ if KEEP_TEMPDIRS {
+ println!("Tempdir: {:?}", tmp_dir.into_path().to_str());
+ }
+ r.unwrap();
+ Ok(())
+}
+
+#[test]
fn test_gen_multiple_in_archive() -> Result<(), Box<dyn std::error::Error>> {
let tmp_dir = tempdir()?;
@@ -165,10 +210,8 @@
files,
vec!["directive1.rs", "directive2.rs"],
)?;
- File::create(tmp_dir.path().join("cxx.h"))
- .and_then(|mut cxx_h| cxx_h.write_all(autocxx_engine::HEADER.as_bytes()))?;
// We've asked to create 8 C++ files, mostly blank. Build 'em all.
- let cpp_files = (0..7).map(|id| format!("gen{}.cc", id)).collect_vec();
+ let cpp_files = (0..7).map(|id| format!("gen{id}.cc")).collect_vec();
let cpp_files = cpp_files.iter().map(|s| s.as_str()).collect_vec();
let r = build_from_folder(
tmp_dir.path(),
@@ -221,8 +264,6 @@
assert_not_contentful(&tmp_dir, "autocxxgen1.h");
assert_contentful(&tmp_dir, "gen0.include.rs");
assert_contentful(&tmp_dir, "test.d");
- File::create(tmp_dir.path().join("cxx.h"))
- .and_then(|mut cxx_h| cxx_h.write_all(autocxx_engine::HEADER.as_bytes()))?;
let r = build_from_folder(
tmp_dir.path(),
&tmp_dir.path().join("demo/main.rs"),
@@ -267,7 +308,7 @@
fn write_to_file(dir: &Path, filename: &str, content: &[u8]) {
let path = dir.join(filename);
- let mut f = File::create(&path).expect("Unable to create file");
+ let mut f = File::create(path).expect("Unable to create file");
f.write_all(content).expect("Unable to write file");
}
@@ -278,8 +319,7 @@
}
assert!(
p.metadata().unwrap().len() > BLANK.len().try_into().unwrap(),
- "File {} is empty",
- fname
+ "File {fname} is empty"
);
}
@@ -298,7 +338,7 @@
fn assert_contains(outdir: &TempDir, fname: &str, pattern: &str) {
let p = outdir.path().join(fname);
- let content = std::fs::read_to_string(&p).expect(fname);
- eprintln!("content = {}", content);
+ let content = std::fs::read_to_string(p).expect(fname);
+ eprintln!("content = {content}");
assert!(content.contains(pattern));
}
diff --git a/integration-tests/Cargo.toml b/integration-tests/Cargo.toml
index 7791ea5..78384ce 100644
--- a/integration-tests/Cargo.toml
+++ b/integration-tests/Cargo.toml
@@ -8,7 +8,7 @@
[package]
name = "autocxx-integration-tests"
-version = "0.22.3"
+version = "0.26.0"
autotests = false
edition = "2021"
authors = ["Adrian Taylor <adetaylor@chromium.org>"]
@@ -27,20 +27,25 @@
cc = "1.0"
quote = "1.0"
once_cell = "1.7"
-autocxx-engine = { version="=0.22.3", path="../engine", features = ["build"] }
# This is necessary for building the projects created
# by the trybuild test system...
-autocxx = { path="..", version="=0.22.3" }
+autocxx = { path = "..", version = "=0.26.0" }
+autocxx-engine = { version = "=0.26.0", path = "../engine", features = [
+ "build",
+] }
+moveit = { version = "0.6", features = [ "cxx" ] }
link-cplusplus = "1.0"
-tempfile = "3.1"
+tempfile = "3.4"
indoc = "1.0"
log = "0.4"
-cxx = "1.0.68"
+cxx = "1.0.78"
itertools = "0.10"
+rustversion = "1.0"
+static_assertions = "1.1.0"
[dependencies.syn]
version = "1.0.39"
-features = [ "full" ]
+features = ["full"]
#features = [ "full", "extra-traits" ]
[[test]]
diff --git a/integration-tests/src/lib.rs b/integration-tests/src/lib.rs
index 352c8ab..2335ee5 100644
--- a/integration-tests/src/lib.rs
+++ b/integration-tests/src/lib.rs
@@ -57,6 +57,9 @@
AutocxxRs,
AutocxxRsArchive,
AutocxxRsFile,
+ /// This just calls the callback instead of setting any environment variables. The callback
+ /// receives the path to the temporary directory.
+ Custom(Box<dyn FnOnce(&Path)>),
}
/// API to test building pre-generated files.
@@ -159,7 +162,7 @@
);
}
let temp_path = self.temp_dir.path().to_str().unwrap();
- let mut rustflags = format!("-L {}", temp_path);
+ let mut rustflags = format!("-L {temp_path}");
if std::env::var_os("AUTOCXX_ASAN").is_some() {
rustflags.push_str(" -Z sanitizer=address -Clinker=clang++ -Clink-arg=-fuse-ld=lld");
}
@@ -174,6 +177,7 @@
"AUTOCXX_RS_FILE",
self.temp_dir.path().join("gen0.include.rs"),
),
+ RsFindMode::Custom(f) => f(self.temp_dir.path()),
};
std::panic::catch_unwind(|| {
let test_cases = trybuild::TestCases::new();
@@ -190,6 +194,7 @@
}
/// A positive test, we expect to pass.
+#[track_caller]
pub fn run_test(
cxx_code: &str,
header_code: &str,
@@ -206,6 +211,7 @@
None,
None,
"unsafe_ffi",
+ None,
)
.unwrap()
}
@@ -261,6 +267,7 @@
code_checker,
extra_rust,
"unsafe_ffi",
+ None,
)
.unwrap()
}
@@ -293,6 +300,7 @@
None,
None,
"unsafe_ffi",
+ None,
)
.expect_err("Unexpected success");
}
@@ -315,6 +323,7 @@
code_checker,
extra_rust,
"unsafe_ffi",
+ None,
)
.expect_err("Unexpected success");
}
@@ -365,10 +374,13 @@
rust_code_checker: Option<CodeChecker>,
extra_rust: Option<TokenStream>,
safety_policy: &str,
+ module_attributes: Option<TokenStream>,
) -> Result<(), TestError> {
let hexathorpe = Token);
let safety_policy = format_ident!("{}", safety_policy);
let unexpanded_rust = quote! {
+ #module_attributes
+
use autocxx::prelude::*;
include_cpp!(
@@ -409,14 +421,12 @@
builder_modifier: Option<BuilderModifier>,
rust_code_checker: Option<CodeChecker>,
) -> Result<(), TestError> {
+ let builder_modifier = consider_forcing_wrapper_generation(builder_modifier);
+
const HEADER_NAME: &str = "input.h";
// Step 2: Write the C++ header snippet to a temp file
let tdir = tempdir().unwrap();
- write_to_file(
- &tdir,
- HEADER_NAME,
- &format!("#pragma once\n{}", header_code),
- );
+ write_to_file(&tdir, HEADER_NAME, &format!("#pragma once\n{header_code}"));
write_to_file(&tdir, "cxx.h", HEADER);
rust_code.append_all(quote! {
@@ -427,7 +437,7 @@
let write_rust_to_file = |ts: &TokenStream| -> PathBuf {
// Step 3: Write the Rust code to a temp file
- let rs_code = format!("{}", ts);
+ let rs_code = format!("{ts}");
write_to_file(&tdir, "input.rs", &rs_code)
};
@@ -437,7 +447,7 @@
let rs_path = write_rust_to_file(&rust_code);
info!("Path is {:?}", tdir.path());
- let builder = Builder::<TestBuilderContext>::new(&rs_path, &[tdir.path()])
+ let builder = Builder::<TestBuilderContext>::new(&rs_path, [tdir.path()])
.custom_gendir(target_dir.clone());
let builder = if let Some(builder_modifier) = &builder_modifier {
builder_modifier.modify_autocxx_builder(builder)
@@ -466,7 +476,7 @@
if !cxx_code.is_empty() {
// Step 4: Write the C++ code snippet to a .cc file, along with a #include
// of the header emitted in step 5.
- let cxx_code = format!("#include \"input.h\"\n#include \"cxxgen.h\"\n{}", cxx_code);
+ let cxx_code = format!("#include \"input.h\"\n#include \"cxxgen.h\"\n{cxx_code}");
let cxx_path = write_to_file(&tdir, "input.cxx", &cxx_code);
b.file(cxx_path);
}
@@ -481,7 +491,7 @@
.try_compile("autocxx-demo")
.map_err(TestError::CppBuild)?;
if KEEP_TEMPDIRS {
- println!("Generated .rs files: {:?}", generated_rs_files);
+ println!("Generated .rs files: {generated_rs_files:?}");
}
// Step 8: use the trybuild crate to build the Rust file.
let r = get_builder().lock().unwrap().build(
@@ -502,3 +512,38 @@
}
Ok(())
}
+
+/// If AUTOCXX_FORCE_WRAPPER_GENERATION is set, always force both C++
+/// and Rust side shims, for extra testing of obscure code paths.
+fn consider_forcing_wrapper_generation(
+ existing_builder_modifier: Option<BuilderModifier>,
+) -> Option<BuilderModifier> {
+ if std::env::var("AUTOCXX_FORCE_WRAPPER_GENERATION").is_err() {
+ existing_builder_modifier
+ } else {
+ Some(Box::new(ForceWrapperGeneration(existing_builder_modifier)))
+ }
+}
+
+struct ForceWrapperGeneration(Option<BuilderModifier>);
+
+impl BuilderModifierFns for ForceWrapperGeneration {
+ fn modify_autocxx_builder<'a>(
+ &self,
+ builder: Builder<'a, TestBuilderContext>,
+ ) -> Builder<'a, TestBuilderContext> {
+ let builder = builder.force_wrapper_generation(true);
+ if let Some(modifier) = &self.0 {
+ modifier.modify_autocxx_builder(builder)
+ } else {
+ builder
+ }
+ }
+ fn modify_cc_builder<'a>(&self, builder: &'a mut cc::Build) -> &'a mut cc::Build {
+ if let Some(modifier) = &self.0 {
+ modifier.modify_cc_builder(builder)
+ } else {
+ builder
+ }
+ }
+}
diff --git a/integration-tests/tests/builder_modifiers.rs b/integration-tests/tests/builder_modifiers.rs
index 0cf763e..5fcc458 100644
--- a/integration-tests/tests/builder_modifiers.rs
+++ b/integration-tests/tests/builder_modifiers.rs
@@ -14,11 +14,19 @@
make_clang_arg_adder(&["-std=c++17"])
}
-struct ClangArgAdder(Vec<String>);
+struct ClangArgAdder(Vec<String>, Vec<String>);
pub(crate) fn make_clang_arg_adder(args: &[&str]) -> Option<BuilderModifier> {
+ make_clang_optional_arg_adder(args, &[])
+}
+
+pub(crate) fn make_clang_optional_arg_adder(
+ args: &[&str],
+ optional_args: &[&str],
+) -> Option<BuilderModifier> {
let args: Vec<_> = args.iter().map(|a| a.to_string()).collect();
- Some(Box::new(ClangArgAdder(args)))
+ let optional_args: Vec<_> = optional_args.iter().map(|a| a.to_string()).collect();
+ Some(Box::new(ClangArgAdder(args, optional_args)))
}
impl BuilderModifierFns for ClangArgAdder {
@@ -34,6 +42,9 @@
for f in &self.0 {
builder = builder.flag(f);
}
+ for f in &self.1 {
+ builder = builder.flag_if_supported(f);
+ }
builder
}
}
diff --git a/integration-tests/tests/code_checkers.rs b/integration-tests/tests/code_checkers.rs
index 619c79c..ae12e3c 100644
--- a/integration-tests/tests/code_checkers.rs
+++ b/integration-tests/tests/code_checkers.rs
@@ -87,8 +87,7 @@
for msg in &self.0 {
if !toks.contains(msg) {
return Err(TestError::RsCodeExaminationFail(format!(
- "Couldn't find string '{}'",
- msg
+ "Couldn't find string '{msg}'"
)));
};
}
@@ -110,8 +109,7 @@
let needle = msg.to_string();
if !haystack.contains(&needle) {
return Err(TestError::RsCodeExaminationFail(format!(
- "Couldn't find tokens '{}'",
- needle
+ "Couldn't find tokens '{needle}'"
)));
};
}
@@ -146,7 +144,7 @@
for filename in cpp {
let file = File::open(filename).unwrap();
let lines = BufReader::new(file).lines();
- for l in lines.filter_map(|l| l.ok()) {
+ for l in lines.map_while(Result::ok) {
if self.negative_matches.iter().any(|neg| l.contains(neg)) {
return Err(TestError::CppCodeExaminationFail);
}
diff --git a/integration-tests/tests/cpprefs_test.rs b/integration-tests/tests/cpprefs_test.rs
index 4241dec..9cc6d39 100644
--- a/integration-tests/tests/cpprefs_test.rs
+++ b/integration-tests/tests/cpprefs_test.rs
@@ -13,6 +13,10 @@
use proc_macro2::TokenStream;
use quote::quote;
+const fn arbitrary_self_types_supported() -> bool {
+ rustversion::cfg!(nightly)
+}
+
/// A positive test, we expect to pass.
fn run_cpprefs_test(
cxx_code: &str,
@@ -21,6 +25,10 @@
generate: &[&str],
generate_pods: &[&str],
) {
+ if !arbitrary_self_types_supported() {
+ // "unsafe_references_wrapped" requires arbitrary_self_types, which requires nightly.
+ return;
+ }
do_run_test(
cxx_code,
header_code,
@@ -30,6 +38,9 @@
None,
None,
"unsafe_references_wrapped",
+ Some(quote! {
+ #![feature(arbitrary_self_types)]
+ }),
)
.unwrap()
}
@@ -54,7 +65,7 @@
"},
quote! {
let goat = ffi::Goat::new().within_unique_ptr();
- let mut goat = ffi::CppUniquePtrPin::new(goat);
+ let mut goat = autocxx::CppUniquePtrPin::new(goat);
goat.as_cpp_mut_ref().add_a_horn();
},
&["Goat"],
@@ -87,7 +98,7 @@
"},
quote! {
let goat = ffi::Goat::new().within_unique_ptr();
- let goat = ffi::cpp_pin_uniqueptr(goat);
+ let goat = autocxx::CppUniquePtrPin::new(goat);
goat.as_cpp_ref().describe();
},
&["Goat"],
diff --git a/integration-tests/tests/integration_test.rs b/integration-tests/tests/integration_test.rs
index 16c792b8..ec2b1e7 100644
--- a/integration-tests/tests/integration_test.rs
+++ b/integration-tests/tests/integration_test.rs
@@ -8,7 +8,8 @@
use crate::{
builder_modifiers::{
- make_clang_arg_adder, make_cpp17_adder, EnableAutodiscover, SetSuppressSystemHeaders,
+ make_clang_arg_adder, make_clang_optional_arg_adder, make_cpp17_adder, EnableAutodiscover,
+ SetSuppressSystemHeaders,
},
code_checkers::{
make_error_finder, make_rust_code_finder, make_string_finder, CppMatcher,
@@ -17,7 +18,7 @@
};
use autocxx_integration_tests::{
directives_from_lists, do_run_test, do_run_test_manual, run_generate_all_test, run_test,
- run_test_ex, run_test_expect_fail, run_test_expect_fail_ex, TestError,
+ run_test_ex, run_test_expect_fail, run_test_expect_fail_ex, BuilderModifier, TestError,
};
use indoc::indoc;
use itertools::Itertools;
@@ -501,6 +502,7 @@
run_test_expect_fail(cxx, hdr, rs, &["take_bob"], &["Bob"]);
}
+#[ignore] // https://github.com/google/autocxx/issues/1252
#[test]
fn test_take_as_pod_with_is_relocatable() {
let cxx = indoc! {"
@@ -840,8 +842,7 @@
run_test("", hdr, rs, &["A", "Bob", "C"], &[]);
}
-#[test]
-fn test_take_char_by_ptr_in_wrapped_method() {
+fn run_char_test(builder_modifier: Option<BuilderModifier>) {
let hdr = indoc! {"
#include <cstdint>
#include <memory>
@@ -871,7 +872,25 @@
assert_eq!(unsafe { ch.as_ref()}.unwrap(), &104i8);
assert_eq!(unsafe { a.as_ref().unwrap().take_char(ch, c2) }, 104);
};
- run_test("", hdr, rs, &["A", "C"], &[]);
+ run_test_ex(
+ "",
+ hdr,
+ rs,
+ directives_from_lists(&["A", "C"], &[], None),
+ builder_modifier,
+ None,
+ None,
+ );
+}
+
+#[test]
+fn test_take_char_by_ptr_in_wrapped_method() {
+ run_char_test(None)
+}
+
+#[test]
+fn test_take_char_by_ptr_in_wrapped_method_with_unsigned_chars() {
+ run_char_test(make_clang_arg_adder(&["-funsigned-char"]))
}
#[test]
@@ -1281,7 +1300,7 @@
#define BOB \"foo\"
"};
let rs = quote! {
- assert_eq!(std::str::from_utf8(ffi::BOB).unwrap().trim_end_matches(char::from(0)), "foo");
+ assert_eq!(core::str::from_utf8(ffi::BOB).unwrap().trim_end_matches(char::from(0)), "foo");
};
run_test(cxx, hdr, rs, &["BOB"], &[]);
}
@@ -1428,7 +1447,7 @@
#[test]
fn test_asan_working_as_expected_for_rust_allocations() {
perform_asan_doom_test(
- quote! { Box::into_raw(std::pin::Pin::into_inner_unchecked(a)) },
+ quote! { Box::into_raw(core::pin::Pin::into_inner_unchecked(a)) },
quote! { Box },
)
}
@@ -3042,7 +3061,7 @@
const char* STRING = \"Foo\";
"};
let rs = quote! {
- let a = std::str::from_utf8(ffi::STRING).unwrap().trim_end_matches(char::from(0));
+ let a = core::str::from_utf8(ffi::STRING).unwrap().trim_end_matches(char::from(0));
assert_eq!(a, "Foo");
};
run_test("", hdr, rs, &["STRING"], &[]);
@@ -4221,6 +4240,29 @@
}
#[test]
+fn test_nested_unnamed_enum() {
+ let hdr = indoc! {"
+ namespace N {
+ struct A {
+ enum {
+ LOW_VAL = 1,
+ HIGH_VAL = 1000,
+ };
+ };
+ }
+ "};
+ run_test_ex(
+ "",
+ hdr,
+ quote! {},
+ quote! { generate_ns!("N")},
+ None,
+ None,
+ None,
+ );
+}
+
+#[test]
fn test_nested_type_constructor() {
let hdr = indoc! {"
#include <string>
@@ -4420,6 +4462,31 @@
}
#[test]
+fn test_cycle_up_of_vec() {
+ let hdr = indoc! {"
+ #include <cstdint>
+ #include <vector>
+ #include <memory>
+ struct A {
+ uint32_t a;
+ };
+ inline std::unique_ptr<std::vector<A>> take_vec(std::unique_ptr<std::vector<A>> a) {
+ return a;
+ }
+ inline std::unique_ptr<std::vector<A>> get_vec() {
+ std::unique_ptr<std::vector<A>> items = std::make_unique<std::vector<A>>();
+ items->push_back(A { 3 });
+ items->push_back(A { 4 });
+ return items;
+ }
+ "};
+ let rs = quote! {
+ ffi::take_vec(ffi::get_vec());
+ };
+ run_test("", hdr, rs, &["take_vec", "get_vec"], &[]);
+}
+
+#[test]
fn test_typedef_to_std() {
let hdr = indoc! {"
#include <string>
@@ -4955,6 +5022,78 @@
}
#[test]
+fn test_union_nonpod() {
+ let hdr = indoc! {"
+ #include <cstdint>
+ union A {
+ uint32_t a;
+ float b;
+ };
+ "};
+ let rs = quote! {};
+ run_test("", hdr, rs, &["A"], &[]);
+}
+
+#[test]
+fn test_union_pod() {
+ let hdr = indoc! {"
+ #include <cstdint>
+ union A {
+ uint32_t a;
+ float b;
+ };
+ "};
+ let rs = quote! {};
+ run_test_expect_fail("", hdr, rs, &[], &["A"]);
+}
+
+#[test]
+fn test_type_aliased_anonymous_union_ignored() {
+ let hdr = indoc! {"
+ #include <cstdint>
+ namespace test {
+ typedef union {
+ int a;
+ } Union;
+ };
+ "};
+ let rs = quote! {};
+ run_test("", hdr, rs, &["test::Union"], &[]);
+}
+
+#[test]
+fn test_type_aliased_anonymous_struct_ignored() {
+ let hdr = indoc! {"
+ #include <cstdint>
+ namespace test {
+ typedef struct {
+ int a;
+ } Struct;
+ };
+ "};
+ let rs = quote! {};
+ run_test("", hdr, rs, &["test::Struct"], &[]);
+}
+
+#[test]
+fn test_type_aliased_anonymous_nested_struct_ignored() {
+ let hdr = indoc! {"
+ #include <cstdint>
+ namespace test {
+ struct Outer {
+ typedef struct {
+ int a;
+ } Struct;
+ int b;
+ };
+ };
+ "};
+ let rs = quote! {};
+ run_test("", hdr, rs, &["test::Outer_Struct"], &[]);
+}
+
+#[ignore] // https://github.com/google/autocxx/issues/1251
+#[test]
fn test_double_underscores_ignored() {
let hdr = indoc! {"
#include <cstdint>
@@ -5756,7 +5895,17 @@
} // namespace spanner
"};
let rs = quote! {};
- run_test("", hdr, rs, &["spanner::Database", "spanner::Row"], &[]);
+ run_test_ex(
+ "",
+ hdr,
+ rs,
+ directives_from_lists(&["spanner::Database", "spanner::Row"], &[], None),
+ // This is normally a valid warning for generating bindings for this code, but we're doing
+ // it on purpose as a regression test on minimized code so we'll just ignore it.
+ make_clang_optional_arg_adder(&[], &["-Wno-delete-abstract-non-virtual-dtor"]),
+ None,
+ None,
+ );
}
#[test]
@@ -5921,6 +6070,7 @@
None,
None,
"unsafe_ffi",
+ None,
) {
Err(TestError::CppBuild(_)) => {} // be sure this fails due to a static_assert
// rather than some runtime problem
@@ -6632,6 +6782,48 @@
}
#[test]
+fn test_extern_cpp_type_different_name() {
+ let hdr = indoc! {"
+ #include <cstdint>
+ struct A {
+ A() : a(0) {}
+ int a;
+ };
+ inline void handle_a(const A&) {
+ }
+ inline A create_a() {
+ A a;
+ return a;
+ }
+ "};
+ let hexathorpe = Token);
+ let rs = quote! {
+ use autocxx::prelude::*;
+ include_cpp! {
+ #hexathorpe include "input.h"
+ safety!(unsafe_ffi)
+ generate!("handle_a")
+ generate!("create_a")
+ extern_cpp_opaque_type!("A", crate::DifferentA)
+ }
+ #[cxx::bridge]
+ pub mod ffi2 {
+ unsafe extern "C++" {
+ include!("input.h");
+ type A;
+ }
+ impl UniquePtr<A> {}
+ }
+ pub use ffi2::A as DifferentA;
+ fn main() {
+ let a = ffi::create_a();
+ ffi::handle_a(&a);
+ }
+ };
+ do_run_test_manual("", hdr, rs, None, None).unwrap();
+}
+
+#[test]
fn test_extern_cpp_type_two_include_cpp() {
let hdr = indoc! {"
#include <cstdint>
@@ -6983,6 +7175,35 @@
}
#[test]
+fn test_extern_rust_fn_callback() {
+ let hdr = indoc! {"
+ struct a {};
+ "};
+ let hexathorpe = Token);
+ let rs = quote! {
+ autocxx::include_cpp! {
+ #hexathorpe include "input.h"
+ safety!(unsafe_ffi)
+ generate!("a")
+ }
+
+ use ffi::a;
+ use std::pin::Pin;
+
+ #[autocxx::extern_rust::extern_rust_function]
+ pub fn called_from_cpp(_a: Pin<&mut a>) {}
+
+ fn main() {}
+ };
+ do_run_test_manual("", hdr, rs, None, None).unwrap();
+}
+
+// TODO: there are various other tests for extern_rust_fn we should add:
+// 1) taking mutable and immutable references
+// 2) ensuring that types on which the signature depends as receiver,
+// parameters and return are not garbage collected
+
+#[test]
fn test_rust_reference_no_autodiscover() {
let hdr = indoc! {"
#include <cstdint>
@@ -7012,6 +7233,55 @@
}
#[test]
+fn test_rust_box() {
+ let hdr = indoc! {"
+ #include <cstdint>
+ #include <cxx.h>
+
+ struct RustType;
+ inline uint32_t take_rust_box(rust::Box<RustType>) {
+ return 4;
+ }
+ "};
+ let rs = quote! {
+ let foo = Box::new(RustType(3));
+ let result = ffi::take_rust_box(foo);
+ assert_eq!(result, 4);
+ };
+ run_test_ex(
+ "",
+ hdr,
+ rs,
+ directives_from_lists(&["take_rust_box"], &[], None),
+ None,
+ None,
+ Some(quote! {
+ #[autocxx::extern_rust::extern_rust_type]
+ pub struct RustType(i32);
+ }),
+ );
+}
+
+#[test]
+fn test_rust_reference_no_autodiscover_no_usage() {
+ let rs = quote! {
+ let _ = RustType(3);
+ };
+ run_test_ex(
+ "",
+ "",
+ rs,
+ directives_from_lists(&[], &[], None),
+ None,
+ None,
+ Some(quote! {
+ #[autocxx::extern_rust::extern_rust_type]
+ pub struct RustType(i32);
+ }),
+ );
+}
+
+#[test]
#[cfg_attr(skip_windows_msvc_failing_tests, ignore)]
// TODO - replace make_clang_arg_adder with something that knows how to add an MSVC-suitable
// directive for the cc build.
@@ -7036,7 +7306,7 @@
}
#[test]
-fn test_box() {
+fn test_box_extern_rust_type() {
let hdr = indoc! {"
#include <cxx.h>
struct Foo;
@@ -7064,6 +7334,47 @@
}
#[test]
+fn test_box_return_placement_new() {
+ let hdr = indoc! {"
+ #include <cxx.h>
+ struct Foo;
+ struct Foo2;
+ struct Ret {};
+ inline Ret take_box(rust::Box<Foo>, rust::Box<Foo2>) {
+ return Ret{};
+ }
+ "};
+ run_test_ex(
+ "",
+ hdr,
+ quote! {
+ let _ = ffi::take_box(
+ Box::new(Foo { a: "Hello".into() }),
+ Box::new(bar::Foo2 { a: "Goodbye".into() })
+ );
+ },
+ quote! {
+ generate!("take_box")
+ extern_rust_type!(Foo)
+ generate!("Ret")
+ },
+ None,
+ None,
+ Some(quote! {
+ pub struct Foo {
+ a: String,
+ }
+ mod bar {
+ #[autocxx::extern_rust::extern_rust_type]
+ pub struct Foo2 {
+ pub a: String,
+ }
+ }
+ }),
+ );
+}
+
+#[test]
fn test_box_via_extern_rust() {
let hdr = indoc! {"
#include <cxx.h>
@@ -7090,6 +7401,32 @@
}
#[test]
+fn test_box_via_extern_rust_no_include_cpp() {
+ let hdr = indoc! {"
+ #include <cxx.h>
+ struct Foo;
+ inline void take_box(rust::Box<Foo>) {
+ }
+ "};
+ do_run_test_manual(
+ "",
+ hdr,
+ quote! {
+ #[autocxx::extern_rust::extern_rust_type]
+ pub struct Foo {
+ a: String,
+ }
+
+ fn main() {
+ }
+ },
+ Some(Box::new(EnableAutodiscover)),
+ None,
+ )
+ .unwrap();
+}
+
+#[test]
fn test_box_via_extern_rust_in_mod() {
let hdr = indoc! {"
#include <cxx.h>
@@ -7178,14 +7515,24 @@
let hdr = indoc! {"
#include <cstdint>
inline void take_int(int&) {}
- inline void take_uin16(uint16_t&) {}
- inline void take_char16(char16_t &) {}
+ inline void take_uint16(uint16_t) {}
+ inline void take_us(unsigned short) {}
+ inline void take_char16(char16_t) {}
+ inline void take_uint16_ref(uint16_t&) {}
+ inline void take_char16_ref(char16_t &) {}
"};
run_test(
"",
hdr,
quote! {},
- &["take_int", "take_uin16", "take_char16"],
+ &[
+ "take_int",
+ "take_uint16",
+ "take_char16",
+ "take_uint16_ref",
+ "take_char16_ref",
+ "take_us",
+ ],
&[],
);
}
@@ -7908,10 +8255,10 @@
assert!(Lazy::force(&STATUS).lock().unwrap().rust_allocated);
assert!(!Lazy::force(&STATUS).lock().unwrap().a_called);
let obs_superclass = obs.as_ref().unwrap(); // &subclass
- let obs_superclass = unsafe { std::mem::transmute::<&ffi::MyTestObserverCpp, &ffi::TestObserver>(obs_superclass) };
+ let obs_superclass = unsafe { core::mem::transmute::<&ffi::MyTestObserverCpp, &ffi::TestObserver>(obs_superclass) };
ffi::TriggerTestObserverA(obs_superclass);
assert!(Lazy::force(&STATUS).lock().unwrap().a_called);
- std::mem::drop(obs);
+ core::mem::drop(obs);
Lazy::force(&STATUS).lock().unwrap().a_called = false;
assert!(!Lazy::force(&STATUS).lock().unwrap().rust_allocated);
assert!(!Lazy::force(&STATUS).lock().unwrap().cpp_allocated);
@@ -7928,7 +8275,7 @@
ffi::TriggerTestObserverA(obs.as_ref().borrow().as_ref());
assert!(Lazy::force(&STATUS).lock().unwrap().a_called);
Lazy::force(&STATUS).lock().unwrap().a_called = false;
- std::mem::drop(obs);
+ core::mem::drop(obs);
assert!(!Lazy::force(&STATUS).lock().unwrap().rust_allocated);
assert!(!Lazy::force(&STATUS).lock().unwrap().cpp_allocated);
assert!(!Lazy::force(&STATUS).lock().unwrap().a_called);
@@ -8043,11 +8390,11 @@
assert!(Lazy::force(&STATUS).lock().unwrap().rust_allocated);
assert!(!Lazy::force(&STATUS).lock().unwrap().a_called);
let obs_superclass = obs.as_ref().unwrap(); // &subclass
- let obs_superclass = unsafe { std::mem::transmute::<&ffi::MyTestObserverCpp, &ffi::TestObserver>(obs_superclass) };
+ let obs_superclass = unsafe { core::mem::transmute::<&ffi::MyTestObserverCpp, &ffi::TestObserver>(obs_superclass) };
ffi::TriggerTestObserverA(obs_superclass);
assert!(Lazy::force(&STATUS).lock().unwrap().a_called);
- std::mem::drop(obs);
+ core::mem::drop(obs);
Lazy::force(&STATUS).lock().unwrap().a_called = false;
assert!(!Lazy::force(&STATUS).lock().unwrap().rust_allocated);
assert!(!Lazy::force(&STATUS).lock().unwrap().cpp_allocated);
@@ -8064,7 +8411,7 @@
assert!(Lazy::force(&STATUS).lock().unwrap().a_called);
Lazy::force(&STATUS).lock().unwrap().a_called = false;
- std::mem::drop(obs);
+ core::mem::drop(obs);
assert!(!Lazy::force(&STATUS).lock().unwrap().rust_allocated);
assert!(!Lazy::force(&STATUS).lock().unwrap().cpp_allocated);
assert!(!Lazy::force(&STATUS).lock().unwrap().a_called);
@@ -8076,7 +8423,7 @@
let obs_superclass_ptr: *const ffi::TestObserver = obs.as_ref().borrow().as_ref();
// Retain just a pointer on the Rust side, so there is no Rust-side
// ownership.
- std::mem::drop(obs);
+ core::mem::drop(obs);
assert!(Lazy::force(&STATUS).lock().unwrap().cpp_allocated);
assert!(Lazy::force(&STATUS).lock().unwrap().rust_allocated);
assert!(!Lazy::force(&STATUS).lock().unwrap().a_called);
@@ -9140,7 +9487,7 @@
};
"};
let rs = quote! {
- use autocxx::moveit::EmplaceUnpinned;
+ use autocxx::moveit::Emplace;
let mut up_obj = cxx::UniquePtr::emplace(ffi::A::new());
up_obj.as_mut().unwrap().set(42);
assert_eq!(up_obj.get(), 42);
@@ -9164,7 +9511,6 @@
};
"};
let rs = quote! {
- use autocxx::moveit::EmplaceUnpinned;
use autocxx::moveit::Emplace;
let mut up_obj = cxx::UniquePtr::emplace(ffi::A::new());
up_obj.pin_mut().set(666);
@@ -9228,7 +9574,7 @@
assert!(ffi::was_delete_called());
ffi::reset_flags();
{
- use autocxx::moveit::EmplaceUnpinned;
+ use autocxx::moveit::Emplace;
let _ = cxx::UniquePtr::emplace(ffi::A::new());
}
assert!(ffi::was_delete_called());
@@ -9430,6 +9776,43 @@
}
#[test]
+fn test_abstract_up_multiple_bridge() {
+ let hdr = indoc! {"
+ #include <memory>
+ class A {
+ public:
+ virtual void foo() const = 0;
+ virtual ~A() {}
+ };
+ class B : public A {
+ public:
+ void foo() const {}
+ };
+ inline std::unique_ptr<A> get_a() { return std::make_unique<B>(); }
+ "};
+ let hexathorpe = Token);
+ let rs = quote! {
+ autocxx::include_cpp! {
+ #hexathorpe include "input.h"
+ safety!(unsafe_ffi)
+ generate!("A")
+ }
+ autocxx::include_cpp! {
+ #hexathorpe include "input.h"
+ safety!(unsafe_ffi)
+ name!(ffi2)
+ extern_cpp_type!("A", crate::ffi::A)
+ generate!("get_a")
+ }
+ fn main() {
+ let a = ffi2::get_a();
+ a.foo();
+ }
+ };
+ do_run_test_manual("", hdr, rs, None, None).unwrap();
+}
+
+#[test]
fn test_abstract_private() {
let hdr = indoc! {"
#include <memory>
@@ -9559,8 +9942,7 @@
("F", "struct F { uint32_t a; uint8_t b; };"),
];
let type_definitions = TYPES.iter().map(|(_, def)| *def).join("\n");
- let function_definitions = TYPES.iter().map(|(name, _)| format!("inline size_t get_sizeof_{}() {{ return sizeof({}); }}\ninline size_t get_alignof_{}() {{ return alignof({}); }}\n",
- name, name, name, name)).join("\n");
+ let function_definitions = TYPES.iter().map(|(name, _)| format!("inline size_t get_sizeof_{name}() {{ return sizeof({name}); }}\ninline size_t get_alignof_{name}() {{ return alignof({name}); }}\n")).join("\n");
let hdr = format!(
indoc! {"
#include <cstdint>
@@ -9574,12 +9956,9 @@
let allowlist_fns: Vec<String> = TYPES
.iter()
.flat_map(|(name, _)| {
- [
- format!("get_sizeof_{}", name),
- format!("get_alignof_{}", name),
- ]
- .to_vec()
- .into_iter()
+ [format!("get_sizeof_{name}"), format!("get_alignof_{name}")]
+ .to_vec()
+ .into_iter()
})
.collect_vec();
let allowlist_types: Vec<String> = TYPES.iter().map(|(name, _)| name.to_string()).collect_vec();
@@ -9593,15 +9972,15 @@
let allowlist_both: Vec<&str> = allowlist_both.iter().map(AsRef::as_ref).collect_vec();
let rs = TYPES.iter().fold(quote! {}, |mut accumulator, (name, _)| {
let get_align_symbol =
- proc_macro2::Ident::new(&format!("get_alignof_{}", name), Span::call_site());
+ proc_macro2::Ident::new(&format!("get_alignof_{name}"), Span::call_site());
let get_size_symbol =
- proc_macro2::Ident::new(&format!("get_sizeof_{}", name), Span::call_site());
+ proc_macro2::Ident::new(&format!("get_sizeof_{name}"), Span::call_site());
let type_symbol = proc_macro2::Ident::new(name, Span::call_site());
accumulator.extend(quote! {
let c_size = ffi::#get_size_symbol();
let c_align = ffi::#get_align_symbol();
- assert_eq!(std::mem::size_of::<ffi::#type_symbol>(), c_size);
- assert_eq!(std::mem::align_of::<ffi::#type_symbol>(), c_align);
+ assert_eq!(core::mem::size_of::<ffi::#type_symbol>(), c_size);
+ assert_eq!(core::mem::align_of::<ffi::#type_symbol>(), c_align);
});
accumulator
});
@@ -9696,6 +10075,24 @@
}
#[test]
+fn test_issue_1238() {
+ let hdr = indoc! {"
+ class b;
+ class c;
+ class f {
+ b d();
+ };
+ class S2E {
+ public:
+ f e;
+ b &d(c *) const;
+ };
+ "};
+ let rs = quote! {};
+ run_test("", hdr, rs, &["S2E"], &[]);
+}
+
+#[test]
fn test_issue486_multi_types() {
let hdr = indoc! {"
namespace a {
@@ -9770,6 +10167,13 @@
void a() const {}
};
+ struct AllExplicitlyDefaulted {
+ AllExplicitlyDefaulted() = default;
+ AllExplicitlyDefaulted(const AllExplicitlyDefaulted&) = default;
+ AllExplicitlyDefaulted(AllExplicitlyDefaulted&&) = default;
+ void a() const {};
+ };
+
struct PublicDeleted {
PublicDeleted() = delete;
PublicDeleted(const PublicDeleted&) = delete;
@@ -10311,6 +10715,12 @@
test_movable![ffi::AllImplicitlyDefaulted];
test_call_a![ffi::AllImplicitlyDefaulted];
+ test_constructible![ffi::AllExplicitlyDefaulted];
+ test_make_unique![ffi::AllExplicitlyDefaulted];
+ test_copyable![ffi::AllExplicitlyDefaulted];
+ test_movable![ffi::AllExplicitlyDefaulted];
+ test_call_a![ffi::AllExplicitlyDefaulted];
+
test_call_a![ffi::PublicDeleted];
test_copyable![ffi::PublicDeletedDefault];
@@ -10684,6 +11094,7 @@
rs,
&[
"AllImplicitlyDefaulted",
+ "AllExplicitlyDefaulted",
"PublicDeleted",
"PublicDeletedDefault",
"PublicDeletedCopy",
@@ -11018,7 +11429,7 @@
"Function",
]
.into_iter()
- .flat_map(|l| [format!("{} line A", l), format!("{} line B", l)])
+ .flat_map(|l| [format!("{l} line A"), format!("{l} line B")])
.collect_vec();
run_test_ex(
@@ -11092,6 +11503,43 @@
}
#[test]
+fn test_enum_in_ns() {
+ let hdr = indoc! {"
+ namespace a {
+ enum b {};
+ } // namespace
+ "};
+ run_test("", hdr, quote! {}, &["a::b"], &[]);
+}
+
+#[test]
+fn test_recursive_field() {
+ let hdr = indoc! {"
+ #include <memory>
+ struct A {
+ std::unique_ptr<A> a;
+ };
+ "};
+ run_test("", hdr, quote! {}, &["A"], &[]);
+}
+
+#[test]
+fn test_recursive_field_indirect() {
+ let hdr = indoc! {"
+ #include <memory>
+ struct B;
+ struct A {
+ std::unique_ptr<B> a;
+ };
+ struct B {
+ std::unique_ptr<A> a1;
+ A a2;
+ };
+ "};
+ run_test("", hdr, quote! {}, &["A", "B"], &[]);
+}
+
+#[test]
fn test_typedef_unsupported_type_pub() {
let hdr = indoc! {"
#include <set>
@@ -11493,6 +11941,306 @@
);
}
+#[test]
+fn test_issue_1143() {
+ let hdr = indoc! {
+ "namespace mapnik {
+ class Map {
+ public:
+ int &a(long);
+ };
+ }"
+ };
+
+ run_test("", hdr, quote! {}, &["mapnik::Map"], &[]);
+}
+
+#[test]
+fn test_issue_1170() {
+ let hdr = indoc! {
+ "#include <vector>
+ struct a {
+ enum b {} c;
+ } Loc;
+ struct Arch {
+ std::vector<a> d();
+ } DeterministicRNG;"
+ };
+ run_test("", hdr, quote! {}, &["Arch"], &[]);
+}
+
+// https://github.com/google/autocxx/issues/774
+#[test]
+fn test_virtual_methods() {
+ let hdr = indoc! {"
+ #include <cstdint>
+ #include <memory>
+ class Base {
+ public:
+ Base() {}
+ virtual ~Base() {}
+
+ virtual int a() = 0;
+
+ virtual void b(int) = 0;
+ virtual void b(bool) = 0;
+
+ virtual int c() const = 0;
+ virtual int c() = 0;
+ };
+ class FullyDefined : public Base {
+ public:
+ int a() { return 0; }
+
+ void b(int) { }
+ void b(bool) { }
+
+ int c() const { return 1; }
+ int c() { return 2; }
+ };
+ class Partial1 : public Base {
+ public:
+ int a() { return 0; }
+
+ void b(bool) {}
+ };
+
+ class Partial2 : public Base {
+ public:
+ int a() { return 0; }
+
+ void b(int) { }
+ void b(bool) { }
+
+ int c() const { return 1; }
+ };
+
+ class Partial3 : public Base {
+ public:
+ int a() { return 0; }
+
+ void b(int) { }
+
+ int c() const { return 1; }
+ int c() { return 2; }
+ };
+
+ class Partial4 : public Base {
+ public:
+ int a() { return 0; }
+
+ void b(int) { }
+ void b(bool) = 0;
+
+ int c() const { return 1; }
+ int c() { return 2; }
+ };
+
+ // TODO: currently this class cannot be detected as virtual as there
+ // is no metadata captured to show that this destructor is virtual
+ // uncommenting this (as well as corresponding sections below) gives a
+ // 'instantiation of abstract class' error.
+ // class Partial5 : public Base {
+ // public:
+ // ~Partial5() = 0;
+
+ // int a() { return 0; }
+
+ // void b(int) { }
+ // void b(bool) { }
+
+ // int c() const { return 1; }
+ // int c() { return 2; }
+ // };
+
+ "};
+ let rs = quote! {
+ static_assertions::assert_impl_all!(ffi::FullyDefined: moveit::CopyNew);
+ static_assertions::assert_not_impl_any!(ffi::Partial1: moveit::CopyNew);
+ static_assertions::assert_not_impl_any!(ffi::Partial2: moveit::CopyNew);
+ static_assertions::assert_not_impl_any!(ffi::Partial3: moveit::CopyNew);
+ static_assertions::assert_not_impl_any!(ffi::Partial4: moveit::CopyNew);
+ // static_assertions::assert_not_impl_any!(ffi::Partial5: moveit::CopyNew);
+ let _c1 = ffi::FullyDefined::new().within_unique_ptr();
+ };
+ run_test(
+ "",
+ hdr,
+ rs,
+ &[
+ "FullyDefined",
+ "Partial1",
+ "Partial2",
+ "Partial3",
+ "Partial4",
+ // "Partial5"
+ ],
+ &[],
+ );
+}
+
+#[test]
+fn test_issue_1192() {
+ let hdr = indoc! {
+ "#include <vector>
+ #include <cstdint>
+ template <typename B>
+ struct A {
+ B a;
+ };
+ struct VecThingy {
+ A<uint32_t> contents[2];
+ };
+ struct MyStruct {
+ VecThingy vec;
+ };"
+ };
+ run_test_ex(
+ "",
+ hdr,
+ quote! {},
+ quote! {
+
+ extern_cpp_type!("VecThingy", crate::VecThingy)
+ pod!("VecThingy")
+
+ generate_pod!("MyStruct")
+ },
+ None,
+ None,
+ Some(quote! {
+ // VecThingy isn't necessarily 128 bits long.
+ // This test doesn't actually allocate one.
+ #[repr(transparent)]
+ pub struct VecThingy(pub u128);
+
+ unsafe impl cxx::ExternType for VecThingy {
+ type Id = cxx::type_id!("VecThingy");
+ type Kind = cxx::kind::Trivial;
+ }
+ }),
+ );
+}
+
+#[test]
+fn test_issue_1214() {
+ let hdr = indoc! {"
+ #include <cstdint>
+ enum class C: uint16_t {
+ A,
+ B,
+ };
+ "};
+ run_test("", hdr, quote! {}, &["C"], &[]);
+}
+
+#[test]
+fn test_issue_1229() {
+ let hdr = indoc! {"
+ struct Thing {
+ float id;
+
+ Thing(float id) : id(id) {}
+ };
+
+ struct Item {
+ float id;
+
+ Item(float id) : id(id) {}
+ };
+ "};
+ let hexathorpe = Token);
+ let rs = quote! {
+ use autocxx::WithinUniquePtr;
+
+ autocxx::include_cpp! {
+ #hexathorpe include "input.h"
+ name!(thing)
+ safety!(unsafe)
+ generate!("Thing")
+ }
+ autocxx::include_cpp! {
+ #hexathorpe include "input.h"
+ name!(item)
+ safety!(unsafe)
+ generate!("Item")
+ }
+
+ fn main() {
+ let _thing = thing::Thing::new(15.).within_unique_ptr();
+ let _item = item::Item::new(15.).within_unique_ptr();
+ }
+ };
+
+ do_run_test_manual("", hdr, rs, None, None).unwrap();
+}
+
+#[test]
+#[ignore] // https://github.com/google/autocxx/issues/1265
+fn test_issue_1265() {
+ let hdr = indoc! {"
+ #include <string>
+
+ class Test
+ {
+ public:
+ explicit Test(std::string string)
+ : string(std::move(string))
+ {
+ }
+
+ Test() = delete;
+
+ [[nodiscard]] auto get_string() const -> std::string const& { return this->string; }
+
+ private:
+ std::string string;
+ };
+ "};
+ run_test_ex(
+ "",
+ hdr,
+ quote! {
+ run();
+ },
+ directives_from_lists(&["Test"], &[], None),
+ None,
+ None,
+ Some(quote! {
+ fn run() {
+ let str0 = "string";
+ let str1 = "another string";
+ let ptr0 = UniquePtr::emplace(ffi::Test::new(str0));
+ let ptr1 = UniquePtr::emplace(ffi::Test::new(str1));
+ println!("0: {}", ptr0.get_string());
+ println!("1: {}", ptr1.get_string());
+ moveit!(let mut ref0 = &move *ptr0);
+ moveit!(let mut ref1 = &move *ptr1);
+ println!("0: {}", ref0.get_string());
+ println!("1: {}", ref1.get_string());
+ println!("swap");
+ core::mem::swap(&mut *ref0, &mut *ref1);
+ println!("0: {}", ref0.get_string());
+ println!("1: {}", ref1.get_string());
+ }
+ }),
+ )
+}
+
+#[test]
+fn test_ignore_va_list() {
+ let hdr = indoc! {"
+ #include <stdarg.h>
+ class A {
+ public:
+ A() {}
+ void fn(va_list) {}
+ };
+ "};
+ let rs = quote! {};
+ run_test("", hdr, rs, &["A"], &[]);
+}
+
// Yet to test:
// - Ifdef
// - Out param pointers
diff --git a/macro/Cargo.toml b/macro/Cargo.toml
index 1f81ab1..00f7e28 100644
--- a/macro/Cargo.toml
+++ b/macro/Cargo.toml
@@ -8,7 +8,7 @@
[package]
name = "autocxx-macro"
-version = "0.22.3"
+version = "0.26.0"
authors = ["Adrian Taylor <adetaylor@chromium.org>"]
license = "MIT OR Apache-2.0"
description = "Safe autogenerated interop between Rust and C++"
@@ -21,11 +21,11 @@
proc-macro = true
[dependencies]
-autocxx-parser = { path="../parser", version="=0.22.3" }
+autocxx-parser = { path = "../parser", version = "=0.26.0" }
proc-macro-error = "1.0"
proc-macro2 = "1.0.11"
quote = "1.0"
[dependencies.syn]
-version = "1.0"
-features = [ "full" ]
+version = "2"
+features = ["full"]
diff --git a/macro/src/lib.rs b/macro/src/lib.rs
index 01ccd69..6cdb74a 100644
--- a/macro/src/lib.rs
+++ b/macro/src/lib.rs
@@ -37,7 +37,7 @@
abort!(s.vis.span(), "Rust subclasses of C++ types must by public");
}
let id = &s.ident;
- let cpp_ident = Ident::new(&format!("{}Cpp", id), Span::call_site());
+ let cpp_ident = Ident::new(&format!("{id}Cpp"), Span::call_site());
let input = quote! {
cpp_peer: autocxx::subclass::CppSubclassCppPeerHolder<ffi:: #cpp_ident>
};
diff --git a/parser/Cargo.toml b/parser/Cargo.toml
index 3668e65..07a1f1e 100644
--- a/parser/Cargo.toml
+++ b/parser/Cargo.toml
@@ -8,7 +8,7 @@
[package]
name = "autocxx-parser"
-version = "0.22.3"
+version = "0.26.0"
authors = ["Adrian Taylor <adetaylor@chromium.org>"]
license = "MIT OR Apache-2.0"
description = "Safe autogenerated interop between Rust and C++"
@@ -21,16 +21,16 @@
log = "0.4"
proc-macro2 = "1.0"
quote = "1.0"
-serde = { version = "1.0", features = [ "derive" ]}
+serde = { version = "1.0", features = ["derive"] }
thiserror = "1.0"
once_cell = "1.10"
itertools = "0.10.3"
-indexmap = { version="1.8", features = ["serde"]}
+indexmap = { version = "1.8", features = ["serde"] }
serde_json = "1.0"
[dependencies.syn]
-version = "1.0.39"
-features = [ "full", "extra-traits" ]
+version = "2"
+features = ["full", "extra-traits"]
[features]
reproduction_case = []
diff --git a/parser/src/config.rs b/parser/src/config.rs
index 8d30249..3634295 100644
--- a/parser/src/config.rs
+++ b/parser/src/config.rs
@@ -29,7 +29,7 @@
use quote::quote;
-#[derive(PartialEq, Clone, Debug, Hash)]
+#[derive(PartialEq, Eq, Clone, Debug, Hash)]
pub enum UnsafePolicy {
AllFunctionsSafe,
AllFunctionsUnsafe,
@@ -101,7 +101,7 @@
fn to_bindgen_item(&self) -> String {
match self {
AllowlistEntry::Item(i) => i.clone(),
- AllowlistEntry::Namespace(ns) => format!("{}::.*", ns),
+ AllowlistEntry::Namespace(ns) => format!("{ns}::.*"),
}
}
}
@@ -165,7 +165,7 @@
pub struct RustFun {
pub path: RustPath,
pub sig: Signature,
- pub receiver: Option<Ident>,
+ pub has_receiver: bool,
}
impl std::fmt::Debug for RustFun {
@@ -240,7 +240,7 @@
let (possible_directives, to_parse, parse_completely) = if has_hexathorpe {
(&get_directives().need_hexathorpe, input, false)
} else {
- input.parse::<Option<syn::token::Bang>>()?;
+ input.parse::<Option<syn::token::Not>>()?;
syn::parenthesized!(args in input);
(&get_directives().need_exclamation, &args, true)
};
@@ -250,7 +250,7 @@
None => {
return Err(syn::Error::new(
ident.span(),
- format!("expected {}", all_possible),
+ format!("expected {all_possible}"),
));
}
Some(directive) => directive.parse(to_parse, &mut config, &ident.span())?,
@@ -258,7 +258,7 @@
if parse_completely && !to_parse.is_empty() {
return Err(syn::Error::new(
ident.span(),
- format!("found unexpected input within the directive {}", ident_str),
+ format!("found unexpected input within the directive {ident_str}"),
));
}
if input.is_empty() {
@@ -362,6 +362,7 @@
|| self.is_subclass_holder(cpp_name)
|| self.is_subclass_cpp(cpp_name)
|| self.is_rust_fun(cpp_name)
+ || self.is_rust_type_name(cpp_name)
|| self.is_concrete_type(cpp_name)
|| match &self.allowlist {
Allowlist::Unspecified(_) => panic!("Eek no allowlist yet"),
@@ -408,10 +409,14 @@
}
pub fn is_rust_type(&self, id: &Ident) -> bool {
+ let id_string = id.to_string();
+ self.is_rust_type_name(&id_string) || self.is_subclass_holder(&id_string)
+ }
+
+ fn is_rust_type_name(&self, possible_ty: &str) -> bool {
self.rust_types
.iter()
- .any(|rt| rt.get_final_ident() == &id.to_string())
- || self.is_subclass_holder(&id.to_string())
+ .any(|rt| rt.get_final_ident() == possible_ty)
}
fn is_rust_fun(&self, possible_fun: &str) -> bool {
diff --git a/parser/src/directives.rs b/parser/src/directives.rs
index 70c88fc..1cb35fb 100644
--- a/parser/src/directives.rs
+++ b/parser/src/directives.rs
@@ -275,7 +275,7 @@
}
fn allowlist_err_to_syn_err(err: AllowlistErr, span: &Span) -> syn::Error {
- syn::Error::new(*span, format!("{}", err))
+ syn::Error::new(*span, format!("{err}"))
}
struct StringList<SET, GET>(SET, GET)
@@ -472,7 +472,7 @@
config.extern_rust_funs.push(RustFun {
path,
sig,
- receiver: None,
+ has_receiver: false,
});
Ok(())
}
diff --git a/parser/src/file_locations.rs b/parser/src/file_locations.rs
index a113d0f..29acb48 100644
--- a/parser/src/file_locations.rs
+++ b/parser/src/file_locations.rs
@@ -77,7 +77,7 @@
FileLocationStrategy::Custom(_) => panic!("Should never happen in the macro"),
FileLocationStrategy::UnknownMaybeFromOutdir | FileLocationStrategy::FromOutDir(_) => {
let fname = config.get_rs_filename();
- let fname = format!("/{}/{}/{}", BUILD_DIR_NAME, RS_DIR_NAME, fname);
+ let fname = format!("/{BUILD_DIR_NAME}/{RS_DIR_NAME}/{fname}");
// rust-analyzer works better if we ask Rust to do the path
// concatenation rather than doing it in proc-macro code.
// proc-macro code does not itself have access to the value of
@@ -95,13 +95,13 @@
include!( #fname );
}
}
- FileLocationStrategy::FromAutocxxRsJsonArchive(fname) => {
- let archive = File::open(fname).unwrap_or_else(|_| panic!("Unable to open {}. This may mean you didn't run the codegen tool (autocxx_gen) before building the Rust code.", fname.to_string_lossy()));
+ FileLocationStrategy::FromAutocxxRsJsonArchive(fnames) => {
+ let archive = std::env::split_paths(fnames).flat_map(File::open).next().unwrap_or_else(|| panic!("Unable to open any of the paths listed in {}. This may mean you didn't run the codegen tool (autocxx_gen) before building the Rust code.", fnames.to_string_lossy()));
let multi_bindings: MultiBindings = serde_json::from_reader(archive)
.unwrap_or_else(|_| {
- panic!("Unable to interpret {} as JSON", fname.to_string_lossy())
+ panic!("Unable to interpret {} as JSON", fnames.to_string_lossy())
});
- multi_bindings.get(config).unwrap_or_else(|err| panic!("Unable to find a suitable set of bindings within the JSON archive {} ({}). This likely means that the codegen tool hasn't been rerun since some changes in your include_cpp! macro.", fname.to_string_lossy(), err))
+ multi_bindings.get(config).unwrap_or_else(|err| panic!("Unable to find a suitable set of bindings within the JSON archive {} ({}). This likely means that the codegen tool hasn't been rerun since some changes in your include_cpp! macro.", fnames.to_string_lossy(), err))
}
}
}
diff --git a/parser/src/path.rs b/parser/src/path.rs
index 415b2ef..e3a0f9f 100644
--- a/parser/src/path.rs
+++ b/parser/src/path.rs
@@ -58,7 +58,7 @@
fn parse(input: ParseStream) -> ParseResult<Self> {
let id: Ident = input.parse()?;
let mut p = RustPath::new_from_ident(id);
- while input.parse::<Option<syn::token::Colon2>>()?.is_some() {
+ while input.parse::<Option<syn::token::PathSep>>()?.is_some() {
let id: Ident = input.parse()?;
p = p.append(id);
}
diff --git a/src/lib.rs b/src/lib.rs
index 4b9f26b..fe235a9 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,4 +1,6 @@
#![doc = include_str!("../README.md")]
+#![cfg_attr(nightly, feature(unsize))]
+#![cfg_attr(nightly, feature(dispatch_from_dyn))]
// Copyright 2020 Google LLC
//
@@ -19,7 +21,7 @@
pub mod subclass;
mod value_param;
-pub use reference_wrapper::{CppMutRef, CppPin, CppRef};
+pub use reference_wrapper::{AsCppMutRef, AsCppRef, CppMutRef, CppPin, CppRef, CppUniquePtrPin};
#[cfg_attr(doc, aquamarine::aquamarine)]
/// Include some C++ headers in your Rust project.
@@ -82,7 +84,7 @@
///
/// * *Recommended*: provide various [`generate`] directives in the
/// [`include_cpp`] macro. This can specify functions or types.
-/// * *Not recommended*: in your `build.rs`, call [`Builder::auto_allowlist`].
+/// * *Not recommended*: in your `build.rs`, call `Builder::auto_allowlist`.
/// This will attempt to spot _uses_ of FFI bindings anywhere in your Rust code
/// and build the allowlist that way. This is experimental and has known limitations.
/// * *Strongly not recommended*: use [`generate_all`]. This will attempt to
@@ -216,7 +218,7 @@
/// A concrete type to make, for example
/// `concrete!("Container<Contents>")`.
-/// All types msut already be on the allowlist by having used
+/// All types must already be on the allowlist by having used
/// `generate!` or similar.
///
/// A directive to be included inside
@@ -265,9 +267,12 @@
/// policy available here:
/// `safety!(unsafe_references_wrapped)`
/// This policy treats C++ references as scary and requires
-/// them to be wrapped in a `CppRef` type. This `CppRef`
-/// type is implemented within the generated bindings but
-/// follows the contract of [`CppRef`].
+/// them to be wrapped in a `CppRef` type: see [`CppRef`].
+/// This only works on nightly Rust because it
+/// depends upon an unstable feature
+/// (`arbitrary_self_types`). However, it should
+/// eliminate all undefined behavior related to Rust's
+/// stricter aliasing rules than C++.
#[macro_export]
macro_rules! safety {
($($tt:tt)*) => { $crate::usage!{$($tt)*} };
@@ -286,12 +291,43 @@
/// Indicates that a C++ type is not to be generated by autocxx in this case,
/// but instead should refer to some pre-existing Rust type.
-/// Note that the size and alignment of this type *must* be correct.
-/// If you wish for the type to be POD, you can use a `pod!` directive too.
+///
+/// If you wish for the type to be POD, you can use a `pod!` directive too
+/// (but see the "requirements" section below).
///
/// The syntax is:
/// `extern_cpp_type!("CppNameGoesHere", path::to::rust::type)`
///
+/// Generally speaking, this should be used only to refer to types
+/// generated elsewhere by `autocxx` or `cxx` to ensure that they meet
+/// all the right requirements. It's possible - but fragile - to
+/// define such types yourself.
+///
+/// # Requirements for externally defined Rust types
+///
+/// It's generally expected that you would make such a type
+/// in Rust using a separate `include_cpp!` macro, or
+/// a manual `#[cxx::bridge]` directive somehwere. That is, this
+/// directive is intended mainly for use in cross-linking different
+/// sets of bindings in different mods, rather than truly to point to novel
+/// external types.
+///
+/// But with that in mind, here are the requirements you must stick to.
+///
+/// For non-POD external types:
+/// * The size and alignment of this type *must* be correct.
+///
+/// For POD external types:
+/// * As above
+/// * Your type must correspond to the requirements of
+/// [`cxx::kind::Trivial`]. In general, that means, no move constructor
+/// and no destructor. If you generate this type using `cxx` itself
+/// (or `autocxx`) this will be enforced using `static_assert`s
+/// within the generated C++ code. Without using those tools, you're
+/// on your own for determining this... and it's hard because the presence
+/// of particular fields or base classes may well result in your type
+/// violating those rules.
+///
/// A directive to be included inside
/// [include_cpp] - see [include_cpp] for general information.
#[macro_export]
@@ -495,6 +531,25 @@
/// for instance. This will contribute to an `extern "Rust"` section of the
/// generated `cxx` bindings, and this type will appear in the C++ header
/// generated for use in C++.
+ ///
+ /// # Finding these bindings from C++
+ ///
+ /// You will likely need to forward-declare this type within your C++ headers
+ /// before you can use it in such function signatures. autocxx can't generate
+ /// headers (with this type definition) until it's parsed your header files;
+ /// logically therefore if your header files mention one of these types
+ /// it's impossible for them to see the definition of the type.
+ ///
+ /// If you're using multiple sets of `include_cpp!` directives, or
+ /// a mixture of `include_cpp!` and `#[cxx::bridge]` bindings, then you
+ /// may be able to `#include "cxxgen.h"` to refer to the generated C++
+ /// function prototypes. In this particular circumstance, you'll want to know
+ /// how exactly the `cxxgen.h` header is named, because one will be
+ /// generated for each of the sets of bindings encountered. The pattern
+ /// can be set manually using `autocxxgen`'s command-line options. If you're
+ /// using `autocxx`'s `build.rs` support, those headers will be named
+ /// `cxxgen.h`, `cxxgen1.h`, `cxxgen2.h` according to the order in which
+ /// the `include_cpp` or `cxx::bridge` bindings are encountered.
pub use autocxx_macro::extern_rust_type;
/// Declare that a given function is a Rust function which is to be exported
@@ -504,6 +559,9 @@
/// #[extern_rust_function]
/// pub fn call_me_from_cpp() { }
/// ```
+ ///
+ /// See [`extern_rust_type`] for details of how to find the generated
+ /// declarations from C++.
pub use autocxx_macro::extern_rust_function;
}
@@ -519,6 +577,7 @@
/// and implemented by any (autocxx-related) [`moveit::New`].
pub trait WithinUniquePtr {
type Inner: UniquePtrTarget + MakeCppStorage;
+ /// Create this item within a [`cxx::UniquePtr`].
fn within_unique_ptr(self) -> cxx::UniquePtr<Self::Inner>;
}
@@ -527,13 +586,23 @@
/// and implemented by any (autocxx-related) [`moveit::New`].
pub trait WithinBox {
type Inner;
+ /// Create this item inside a pinned box. This is a good option if you
+ /// want to own this object within Rust, and want to create Rust references
+ /// to it.
fn within_box(self) -> Pin<Box<Self::Inner>>;
+ /// Create this item inside a [`CppPin`]. This is a good option if you
+ /// want to own this option within Rust, but you want to create [`CppRef`]
+ /// C++ references to it. You'd only want to choose that option if you have
+ /// enabled the C++ reference wrapper support by using the
+ /// `safety!(unsafe_references_wrapped`) directive. If you haven't done
+ /// that, ignore this function.
+ fn within_cpp_pin(self) -> CppPin<Self::Inner>;
}
use cxx::kind::Trivial;
use cxx::ExternType;
+use moveit::Emplace;
use moveit::MakeCppStorage;
-use moveit::{Emplace, EmplaceUnpinned};
impl<N, T> WithinUniquePtr for N
where
@@ -554,6 +623,9 @@
fn within_box(self) -> Pin<Box<T>> {
Box::emplace(self)
}
+ fn within_cpp_pin(self) -> CppPin<Self::Inner> {
+ CppPin::from_pinned_box(Box::emplace(self))
+ }
}
/// Emulates the [`WithinUniquePtr`] trait, but for trivial (plain old data) types.
@@ -624,9 +696,12 @@
pub use crate::c_void;
pub use crate::cpp_semantics;
pub use crate::include_cpp;
+ pub use crate::AsCppMutRef;
+ pub use crate::AsCppRef;
pub use crate::CppMutRef;
pub use crate::CppPin;
pub use crate::CppRef;
+ pub use crate::CppUniquePtrPin;
pub use crate::PinMut;
pub use crate::RValueParam;
pub use crate::ValueParam;
@@ -638,7 +713,6 @@
pub use moveit::moveit;
pub use moveit::new::New;
pub use moveit::Emplace;
- pub use moveit::EmplaceUnpinned;
}
/// Re-export moveit for ease of consumers.
diff --git a/src/reference_wrapper.rs b/src/reference_wrapper.rs
index ed943d3..041bd72 100644
--- a/src/reference_wrapper.rs
+++ b/src/reference_wrapper.rs
@@ -6,15 +6,124 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
+use core::{marker::PhantomData, ops::Deref, pin::Pin};
+
+#[cfg(nightly)]
+use std::{marker::Unsize, ops::DispatchFromDyn};
+
+use cxx::{memory::UniquePtrTarget, UniquePtr};
+
/// A C++ const reference. These are different from Rust's `&T` in that
-/// these may exist even while the object is mutated elsewhere.
+/// these may exist even while the object is mutated elsewhere. See also
+/// [`CppMutRef`] for the mutable equivalent.
///
-/// This is a trait not a struct due to the nuances of Rust's orphan rule
-/// - implemntations of this trait are found in each set of generated bindings
-/// but they are essentially the same.
-pub trait CppRef<'a, T> {
+/// The key rule is: we *never* dereference these in Rust. Therefore, any
+/// UB here cannot manifest within Rust, but only across in C++, and therefore
+/// they are equivalently safe to using C++ references in pure-C++ codebases.
+///
+/// *Important*: you might be wondering why you've never encountered this type.
+/// These exist in autocxx-generated bindings only if the `unsafe_references_wrapped`
+/// safety policy is given. This may become the default in future.
+///
+/// # Usage
+///
+/// These types of references are pretty useless in Rust. You can't do
+/// field access. But, you can pass them back into C++! And specifically,
+/// you can call methods on them (i.e. use this type as a `this`). So
+/// the common case here is when C++ gives you a reference to some type,
+/// then you want to call methods on that reference.
+///
+/// # Calling methods
+///
+/// As noted, one of the main reasons for this type is to call methods.
+/// Unfortunately, that depends on unstable Rust features. If you can't
+/// call methods on one of these references, check you're using nightly
+/// and add `#![feature(arbitrary_self_types)]` to your crate.
+///
+/// # Lifetimes and cloneability
+///
+/// Although these references implement C++ aliasing semantics, they
+/// do attempt to give you Rust lifetime tracking. This means if a C++ object
+/// gives you a reference, you won't be able to use that reference after the
+/// C++ object is no longer around.
+///
+/// This is usually what you need, since a C++ object will typically give
+/// you a reference to part of _itself_ or something that it owns. But,
+/// if you know that the returned reference lasts longer than its vendor,
+/// you can use `lifetime_cast` to get a long-lived version.
+///
+/// On the other hand, these references do not give you Rust's exclusivity
+/// guarantees. These references can be freely cloned, and using [`CppRef::const_cast`]
+/// you can even make a mutable reference from an immutable reference.
+///
+/// # Field access
+///
+/// Field access would be achieved by adding C++ `get` and/or `set` methods.
+/// It's possible that a future version of `autocxx` could generate such
+/// getters and setters automatically, but they would need to be `unsafe`
+/// because there is no guarantee that the referent of a `CppRef` is actually
+/// what it's supposed to be, or alive. `CppRef`s may flow from C++ to Rust
+/// via arbitrary means, and with sufficient uses of `get` and `set` it would
+/// even be possible to create a use-after-free in pure Rust code (for instance,
+/// store a [`CppPin`] in a struct field, get a `CppRef` to its referent, then
+/// use a setter to reset that field of the struct.)
+///
+/// # Deref
+///
+/// This type implements [`Deref`] because that's the mechanism that the
+/// unstable Rust `arbitrary_self_types` features uses to determine callable
+/// methods. However, actually calling `Deref::deref` is not permitted and will
+/// result in a compilation failure. If you wish to create a Rust reference
+/// from the C++ reference, see [`CppRef::as_ref`].
+///
+/// # Nullness
+///
+/// Creation of a null C++ reference is undefined behavior (because such
+/// a reference can only be created by dereferencing a null pointer.)
+/// However, in practice, they exist, and we need to be compatible with
+/// pre-existing C++ APIs even if they do naughty things like this.
+/// Therefore this `CppRef` type does allow null values. This is a bit
+/// unfortunate because it means `Option<CppRef<T>>`
+/// occupies more space than `CppRef<T>`.
+///
+/// # Dynamic dispatch
+///
+/// You might wonder if you can do this:
+/// ```ignore
+/// let CppRef<dyn Trait> = ...; // obtain some CppRef<concrete type>
+/// ```
+/// Dynamic dispatch works so long as you're using nightly (we require another
+/// unstable feature, `dispatch_from_dyn`). But we need somewhere to store
+/// the trait object, and `CppRef` isn't it -- a `CppRef` can only store a
+/// simple pointer to something else. So, you need to store the trait object
+/// in a `Box` or similar:
+/// ```ignore
+/// trait SomeTrait {
+/// fn some_method(self: CppRef<Self>)
+/// }
+/// impl SomeTrait for ffi::Concrete {
+/// fn some_method(self: CppRef<Self>) {}
+/// }
+/// let obj: Pin<Box<dyn SomeTrait>> = ffi::Concrete::new().within_box();
+/// let obj = CppPin::from_pinned_box(obj);
+/// farm_area.as_cpp_ref().some_method();
+/// ```
+///
+/// # Implementation notes
+///
+/// Internally, this is represented as a raw pointer in Rust. See the note above
+/// about Nullness for why we don't use [`core::ptr::NonNull`].
+#[repr(transparent)]
+pub struct CppRef<'a, T: ?Sized> {
+ ptr: *const T,
+ phantom: PhantomData<&'a T>,
+}
+
+impl<'a, T: ?Sized> CppRef<'a, T> {
/// Retrieve the underlying C++ pointer.
- fn as_ptr(&self) -> *const T;
+ pub fn as_ptr(&self) -> *const T {
+ self.ptr
+ }
/// Get a regular Rust reference out of this C++ reference.
///
@@ -24,21 +133,111 @@
/// C++ or Rust code while the returned reference exists. Callers must
/// also guarantee that no mutable Rust reference is created to the
/// referent while the returned reference exists.
- unsafe fn as_ref(&self) -> &T {
+ ///
+ /// Callers must also be sure that the C++ reference is properly
+ /// aligned, not null, pointing to valid data, etc.
+ pub unsafe fn as_ref(&self) -> &T {
&*self.as_ptr()
}
+
+ /// Create a C++ reference from a raw pointer.
+ pub fn from_ptr(ptr: *const T) -> Self {
+ Self {
+ ptr,
+ phantom: PhantomData,
+ }
+ }
+
+ /// Create a mutable version of this reference, roughly equivalent
+ /// to C++ `const_cast`.
+ ///
+ /// The opposite is to use [`AsCppRef::as_cpp_ref`] on a [`CppMutRef`]
+ /// to obtain a [`CppRef`].
+ ///
+ /// # Safety
+ ///
+ /// Because we never dereference a `CppRef` in Rust, this cannot create
+ /// undefined behavior _within Rust_ and is therefore not unsafe. It is
+ /// however generally unwise, just as it is in C++. Use sparingly.
+ pub fn const_cast(&self) -> CppMutRef<'a, T> {
+ CppMutRef {
+ ptr: self.ptr as *mut T,
+ phantom: self.phantom,
+ }
+ }
+
+ /// Extend the lifetime of the returned reference beyond normal Rust
+ /// borrow checker rules.
+ ///
+ /// Normally, a reference can't be used beyond the lifetime of the object
+ /// which gave it to you, but sometimes C++ APIs can return references
+ /// to global or other longer-lived objects. In such a case you should
+ /// use this method to get a longer-lived reference.
+ ///
+ /// # Usage
+ ///
+ /// When you're given a C++ reference and you know its referent is valid
+ /// for a long time, use this method. Store the resulting `PhantomReferent`
+ /// somewhere in Rust with an equivalent lifetime.
+ /// That object can then vend longer-lived `CppRef`s using
+ /// [`AsCppRef::as_cpp_ref`].
+ ///
+ /// # Safety
+ ///
+ /// Because `CppRef`s are never dereferenced in Rust, misuse of this API
+ /// cannot lead to undefined behavior _in Rust_ and is therefore not
+ /// unsafe. Nevertheless this can lead to UB in C++, so use carefully.
+ pub fn lifetime_cast(&self) -> PhantomReferent<T> {
+ PhantomReferent(self.ptr)
+ }
}
+impl<'a, T: ?Sized> Deref for CppRef<'a, T> {
+ type Target = *const T;
+ #[inline]
+ fn deref(&self) -> &Self::Target {
+ // With `inline_const` we can simplify this to:
+ // const { panic!("you shouldn't deref CppRef!") }
+ struct C<T: ?Sized>(T);
+ impl<T: ?Sized> C<T> {
+ const V: core::convert::Infallible = panic!(
+ "You cannot directly obtain a Rust reference from a CppRef. Use CppRef::as_ref."
+ );
+ }
+ match C::<T>::V {}
+ }
+}
+
+impl<'a, T: ?Sized> Clone for CppRef<'a, T> {
+ fn clone(&self) -> Self {
+ Self {
+ ptr: self.ptr,
+ phantom: self.phantom,
+ }
+ }
+}
+
+#[cfg(nightly)]
+impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<CppRef<'_, U>> for CppRef<'_, T> {}
+
/// A C++ non-const reference. These are different from Rust's `&mut T` in that
/// several C++ references can exist to the same underlying data ("aliasing")
-/// and that's not permitted in Rust.
+/// and that's not permitted for regular Rust references.
///
-/// This is a trait not a struct due to the nuances of Rust's orphan rule
-/// - implemntations of this trait are found in each set of generated bindings
-/// but they are essentially the same.
-pub trait CppMutRef<'a, T>: CppRef<'a, T> {
+/// See [`CppRef`] for details on safety, usage models and implementation.
+///
+/// You can convert this to a [`CppRef`] using the [`std::convert::Into`] trait.
+#[repr(transparent)]
+pub struct CppMutRef<'a, T: ?Sized> {
+ ptr: *mut T,
+ phantom: PhantomData<&'a T>,
+}
+
+impl<'a, T: ?Sized> CppMutRef<'a, T> {
/// Retrieve the underlying C++ pointer.
- fn as_mut_ptr(&self) -> *mut T;
+ pub fn as_mut_ptr(&self) -> *mut T {
+ self.ptr
+ }
/// Get a regular Rust mutable reference out of this C++ reference.
///
@@ -48,16 +247,98 @@
/// C++ or Rust code while the returned reference exists. Callers must
/// also guarantee that no other Rust reference is created to the referent
/// while the returned reference exists.
- unsafe fn as_mut(&mut self) -> &mut T {
+ ///
+ /// Callers must also be sure that the C++ reference is properly
+ /// aligned, not null, pointing to valid data, etc.
+ pub unsafe fn as_mut(&mut self) -> &mut T {
&mut *self.as_mut_ptr()
}
+
+ /// Create a C++ reference from a raw pointer.
+ pub fn from_ptr(ptr: *mut T) -> Self {
+ Self {
+ ptr,
+ phantom: PhantomData,
+ }
+ }
+
+ /// Extend the lifetime of the returned reference beyond normal Rust
+ /// borrow checker rules. See [`CppRef::lifetime_cast`].
+ pub fn lifetime_cast(&mut self) -> PhantomReferentMut<T> {
+ PhantomReferentMut(self.ptr)
+ }
}
-/// Any newtype wrapper which causes the contained object to obey C++ reference
-/// semantics rather than Rust reference semantics.
-///
-/// The complex generics here are working around the orphan rule - the only
-/// important generic is `T` which is the underlying stored type.
+impl<'a, T: ?Sized> Deref for CppMutRef<'a, T> {
+ type Target = *const T;
+ #[inline]
+ fn deref(&self) -> &Self::Target {
+ // With `inline_const` we can simplify this to:
+ // const { panic!("you shouldn't deref CppRef!") }
+ struct C<T: ?Sized>(T);
+ impl<T: ?Sized> C<T> {
+ const V: core::convert::Infallible = panic!("You cannot directly obtain a Rust reference from a CppMutRef. Use CppMutRef::as_mut.");
+ }
+ match C::<T>::V {}
+ }
+}
+
+impl<'a, T: ?Sized> Clone for CppMutRef<'a, T> {
+ fn clone(&self) -> Self {
+ Self {
+ ptr: self.ptr,
+ phantom: self.phantom,
+ }
+ }
+}
+
+impl<'a, T> From<CppMutRef<'a, T>> for CppRef<'a, T> {
+ fn from(mutable: CppMutRef<'a, T>) -> Self {
+ Self {
+ ptr: mutable.ptr,
+ phantom: mutable.phantom,
+ }
+ }
+}
+
+#[cfg(nightly)]
+impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<CppMutRef<'_, U>> for CppMutRef<'_, T> {}
+
+/// Any type which can return a C++ reference to its contents.
+pub trait AsCppRef<T: ?Sized> {
+ /// Returns a reference which obeys C++ reference semantics
+ fn as_cpp_ref(&self) -> CppRef<T>;
+}
+
+/// Any type which can return a C++ reference to its contents.
+pub trait AsCppMutRef<T: ?Sized>: AsCppRef<T> {
+ /// Returns a mutable reference which obeys C++ reference semantics
+ fn as_cpp_mut_ref(&mut self) -> CppMutRef<T>;
+}
+
+impl<'a, T: ?Sized> AsCppRef<T> for CppMutRef<'a, T> {
+ fn as_cpp_ref(&self) -> CppRef<T> {
+ CppRef::from_ptr(self.ptr)
+ }
+}
+
+/// Workaround for the inability to use std::ptr::addr_of! on the contents
+/// of a box.
+#[repr(transparent)]
+struct CppPinContents<T: ?Sized>(T);
+
+impl<T: ?Sized> CppPinContents<T> {
+ fn addr_of(&self) -> *const T {
+ std::ptr::addr_of!(self.0)
+ }
+ fn addr_of_mut(&mut self) -> *mut T {
+ std::ptr::addr_of_mut!(self.0)
+ }
+}
+
+/// A newtype wrapper which causes the contained object to obey C++ reference
+/// semantics rather than Rust reference semantics. That is, multiple aliasing
+/// mutable C++ references may exist to the contents.
///
/// C++ references are permitted to alias one another, and commonly do.
/// Rust references must alias according only to the narrow rules of the
@@ -65,27 +346,98 @@
///
/// If you need C++ to access your Rust object, first imprison it in one of these
/// objects, then use [`Self::as_cpp_ref`] to obtain C++ references to it.
-pub trait CppPin<'a, T: 'a> {
- /// The type of C++ reference created to the contained object.
- type CppRef: CppRef<'a, T>;
+/// If you need the object back for use in the Rust domain, use [`CppPin::extract`],
+/// but be aware of the safety invariants that you - as a human - will need
+/// to guarantee.
+///
+/// # Usage models
+///
+/// From fairly safe to fairly unsafe:
+///
+/// * *Configure a thing in Rust then give it to C++*. Take your Rust object,
+/// set it up freely using Rust references, methods and data, then imprison
+/// it in a `CppPin` and keep it around while you work with it in C++.
+/// There is no possibility of _aliasing_ UB in this usage model, but you
+/// still need to be careful of use-after-free bugs, just as if you were
+/// to create a reference to any data in C++. The Rust borrow checker will
+/// help you a little by ensuring that your `CppRef` objects don't outlive
+/// the `CppPin`, but once those references pass into C++, it can't help.
+/// * *Pass a thing to C++, have it operate on it synchronously, then take
+/// it back*. To do this, you'd imprison your Rust object in a `CppPin`,
+/// then pass mutable C++ references (using [`AsCppMutRef::as_cpp_mut_ref`])
+/// into a C++ function. C++ would duly operate on the object, and thereafter
+/// you could reclaim the object with `extract()`. At this point, you (as
+/// a human) will need to give a guarantee that no references remain in the
+/// C++ domain. If your object was just locally used by a single C++ function,
+/// which has now returned, this type of local analysis may well be practical.
+/// * *Share a thing between Rust and C++*. This object can vend both C++
+/// references and Rust references (via `as_ref` etc.) It may be possible
+/// for you to guarantee that C++ does not mutate the object while any Rust
+/// reference exists. If you choose this model, you'll need to carefully
+/// track exactly what happens to references and pointers on both sides,
+/// and document your evidence for why you are sure this is safe.
+/// Failure here is bad: Rust makes all sorts of optimization decisions based
+/// upon its borrow checker guarantees, so mistakes can lead to undebuggable
+/// action-at-a-distance crashes.
+///
+/// # See also
+///
+/// See also [`CppUniquePtrPin`], which is equivalent for data which is in
+/// a [`cxx::UniquePtr`].
+pub struct CppPin<T: ?Sized>(Box<CppPinContents<T>>);
- /// The type of C++ mutable reference created to the contained object..
- type CppMutRef: CppMutRef<'a, T>;
+impl<T: ?Sized> CppPin<T> {
+ /// Imprison the Rust data within a `CppPin`. This eliminates any remaining
+ /// Rust references (since we take the item by value) and this object
+ /// subsequently only vends C++ style references, not Rust references,
+ /// until or unless `extract` is called.
+ pub fn new(item: T) -> Self
+ where
+ T: Sized,
+ {
+ Self(Box::new(CppPinContents(item)))
+ }
+
+ /// Imprison the boxed Rust data within a `CppPin`. This eliminates any remaining
+ /// Rust references (since we take the item by value) and this object
+ /// subsequently only vends C++ style references, not Rust references,
+ /// until or unless `extract` is called.
+ ///
+ /// If the item is already in a `Box`, this is slightly more efficient than
+ /// `new` because it will avoid moving/reallocating it.
+ pub fn from_box(item: Box<T>) -> Self {
+ // Safety: CppPinContents<T> is #[repr(transparent)] so
+ // this transmute from
+ // Box<T>
+ // to
+ // Box<CppPinContents<T>>
+ // is safe.
+ let contents = unsafe { std::mem::transmute(item) };
+ Self(contents)
+ }
+
+ // Imprison the boxed Rust data within a `CppPin`. This eliminates any remaining
+ /// Rust references (since we take the item by value) and this object
+ /// subsequently only vends C++ style references, not Rust references,
+ /// until or unless `extract` is called.
+ ///
+ /// If the item is already in a `Box`, this is slightly more efficient than
+ /// `new` because it will avoid moving/reallocating it.
+ pub fn from_pinned_box(item: Pin<Box<T>>) -> Self {
+ // Safety: it's OK to un-pin the Box because we'll be putting it
+ // into a CppPin which upholds the same pinned-ness contract.
+ Self::from_box(unsafe { Pin::into_inner_unchecked(item) })
+ }
/// Get an immutable pointer to the underlying object.
- fn as_ptr(&self) -> *const T;
+ pub fn as_ptr(&self) -> *const T {
+ self.0.addr_of()
+ }
/// Get a mutable pointer to the underlying object.
- fn as_mut_ptr(&mut self) -> *mut T;
-
- /// Returns a reference which obeys C++ reference semantics
- fn as_cpp_ref(&self) -> Self::CppRef;
-
- /// Returns a mutable reference which obeys C++ reference semantics.
- ///
- /// Note that this requires unique ownership of `self`, but this is
- /// advisory since the resulting reference can be cloned.
- fn as_cpp_mut_ref(&mut self) -> Self::CppMutRef;
+ pub fn as_mut_ptr(&mut self) -> *mut T {
+ self.0.addr_of_mut()
+ }
/// Get a normal Rust reference to the underlying object. This is unsafe.
///
@@ -93,7 +445,7 @@
///
/// You must guarantee that C++ will not mutate the object while the
/// reference exists.
- unsafe fn as_ref(&self) -> &T {
+ pub unsafe fn as_ref(&self) -> &T {
&*self.as_ptr()
}
@@ -103,7 +455,183 @@
///
/// You must guarantee that C++ will not mutate the object while the
/// reference exists.
- unsafe fn as_mut(&mut self) -> &mut T {
+ pub unsafe fn as_mut(&mut self) -> &mut T {
&mut *self.as_mut_ptr()
}
+
+ /// Extract the object from within its prison, for re-use again within
+ /// the domain of normal Rust references.
+ ///
+ /// This returns a `Box<T>`: if you want the underlying `T` you can extract
+ /// it using `*`.
+ ///
+ /// # Safety
+ ///
+ /// Callers promise that no remaining C++ references exist either
+ /// in the form of Rust [`CppRef`]/[`CppMutRef`] or any remaining pointers/
+ /// references within C++.
+ pub unsafe fn extract(self) -> Box<T> {
+ // Safety: CppPinContents<T> is #[repr(transparent)] so
+ // this transmute from
+ // Box<CppPinContents<T>>
+ // to
+ // Box<T>
+ // is safe.
+ std::mem::transmute(self.0)
+ }
+}
+
+impl<T: ?Sized> AsCppRef<T> for CppPin<T> {
+ fn as_cpp_ref(&self) -> CppRef<T> {
+ CppRef::from_ptr(self.as_ptr())
+ }
+}
+
+impl<T: ?Sized> AsCppMutRef<T> for CppPin<T> {
+ fn as_cpp_mut_ref(&mut self) -> CppMutRef<T> {
+ CppMutRef::from_ptr(self.as_mut_ptr())
+ }
+}
+
+/// Any newtype wrapper which causes the contained [`UniquePtr`] target to obey C++ reference
+/// semantics rather than Rust reference semantics. That is, multiple aliasing
+/// mutable C++ references may exist to the contents.
+///
+/// C++ references are permitted to alias one another, and commonly do.
+/// Rust references must alias according only to the narrow rules of the
+/// borrow checker.
+pub struct CppUniquePtrPin<T: UniquePtrTarget>(UniquePtr<T>);
+
+impl<T: UniquePtrTarget> CppUniquePtrPin<T> {
+ /// Imprison the type within a `CppPin`. This eliminates any remaining
+ /// Rust references (since we take the item by value) and this object
+ /// subsequently only vends C++ style references, not Rust references.
+ pub fn new(item: UniquePtr<T>) -> Self {
+ Self(item)
+ }
+
+ /// Get an immutable pointer to the underlying object.
+ pub fn as_ptr(&self) -> *const T {
+ // TODO - avoid brief reference here
+ self.0
+ .as_ref()
+ .expect("UniquePtr was null; we can't make a C++ reference")
+ }
+}
+
+impl<T: UniquePtrTarget> AsCppRef<T> for CppUniquePtrPin<T> {
+ fn as_cpp_ref(&self) -> CppRef<T> {
+ CppRef::from_ptr(self.as_ptr())
+ }
+}
+
+impl<T: UniquePtrTarget> AsCppMutRef<T> for CppUniquePtrPin<T> {
+ fn as_cpp_mut_ref(&mut self) -> CppMutRef<T> {
+ let pinnned_ref: Pin<&mut T> = self
+ .0
+ .as_mut()
+ .expect("UniquePtr was null; we can't make a C++ reference");
+ let ptr = unsafe { Pin::into_inner_unchecked(pinnned_ref) };
+ CppMutRef::from_ptr(ptr)
+ }
+}
+
+/// A structure used to extend the lifetime of a returned C++ reference,
+/// to indicate to Rust that it's beyond the normal Rust lifetime rules.
+/// See [`CppRef::lifetime_cast`].
+#[repr(transparent)]
+pub struct PhantomReferent<T: ?Sized>(*const T);
+
+impl<T: ?Sized> AsCppRef<T> for PhantomReferent<T> {
+ fn as_cpp_ref(&self) -> CppRef<T> {
+ CppRef::from_ptr(self.0)
+ }
+}
+
+/// A structure used to extend the lifetime of a returned C++ mutable reference,
+/// to indicate to Rust that it's beyond the normal Rust lifetime rules.
+/// See [`CppRef::lifetime_cast`].
+#[repr(transparent)]
+pub struct PhantomReferentMut<T: ?Sized>(*mut T);
+
+impl<T: ?Sized> AsCppRef<T> for PhantomReferentMut<T> {
+ fn as_cpp_ref(&self) -> CppRef<T> {
+ CppRef::from_ptr(self.0)
+ }
+}
+
+impl<T: ?Sized> AsCppMutRef<T> for PhantomReferentMut<T> {
+ fn as_cpp_mut_ref(&mut self) -> CppMutRef<T> {
+ CppMutRef::from_ptr(self.0)
+ }
+}
+
+#[cfg(all(feature = "arbitrary_self_types", test))]
+mod tests {
+ use super::*;
+
+ struct CppOuter {
+ _a: u32,
+ inner: CppInner,
+ global: *const CppInner,
+ }
+
+ impl CppOuter {
+ fn get_inner_ref<'a>(self: &CppRef<'a, CppOuter>) -> CppRef<'a, CppInner> {
+ // Safety: emulating C++ code for test purposes. This is safe
+ // because we know the data isn't modified during the lifetime of
+ // the returned reference.
+ let self_rust_ref = unsafe { self.as_ref() };
+ CppRef::from_ptr(std::ptr::addr_of!(self_rust_ref.inner))
+ }
+ fn get_global_ref<'a>(self: &CppRef<'a, CppOuter>) -> CppRef<'a, CppInner> {
+ // Safety: emulating C++ code for test purposes. This is safe
+ // because we know the data isn't modified during the lifetime of
+ // the returned reference.
+ let self_rust_ref = unsafe { self.as_ref() };
+ CppRef::from_ptr(self_rust_ref.global)
+ }
+ }
+
+ struct CppInner {
+ b: u32,
+ }
+
+ impl CppInner {
+ fn value_is(self: &CppRef<Self>) -> u32 {
+ // Safety: emulating C++ code for test purposes. This is safe
+ // because we know the data isn't modified during the lifetime of
+ // the returned reference.
+ let self_rust_ref = unsafe { self.as_ref() };
+ self_rust_ref.b
+ }
+ }
+
+ #[test]
+ fn cpp_objects() {
+ let mut global = CppInner { b: 7 };
+ let global_ref_lifetime_phantom;
+ {
+ let outer = CppOuter {
+ _a: 12,
+ inner: CppInner { b: 3 },
+ global: &mut global,
+ };
+ let outer = CppPin::new(outer);
+ let inner_ref = outer.as_cpp_ref().get_inner_ref();
+ assert_eq!(inner_ref.value_is(), 3);
+ global_ref_lifetime_phantom = Some(outer.as_cpp_ref().get_global_ref().lifetime_cast());
+ }
+ let global_ref = global_ref_lifetime_phantom.unwrap();
+ let global_ref = global_ref.as_cpp_ref();
+ assert_eq!(global_ref.value_is(), 7);
+ }
+
+ #[test]
+ fn cpp_pin() {
+ let a = RustThing { _a: 4 };
+ let a = CppPin::new(a);
+ let _ = a.as_cpp_ref();
+ let _ = a.as_cpp_ref();
+ }
}
diff --git a/src/rvalue_param.rs b/src/rvalue_param.rs
index 4acc52e..e380c8a 100644
--- a/src/rvalue_param.rs
+++ b/src/rvalue_param.rs
@@ -78,6 +78,13 @@
/// need to pass a value parameter into C++, and will take responsibility
/// for extracting that value parameter from the [`RValueParam`] and doing
/// any later cleanup.
+///
+/// Because C++ move constructors may modify the original object, we consume
+/// the object and store it, pinned, until the call completes. This avoids any
+/// risk that C++ will mutate objects elsewhere in Rust-land, which could cause
+/// problems in the case of re-entrancy as references might exist to those
+/// other objects and Rust assumes there are no unexpected mutations of objects
+/// where references exist.
#[doc(hidden)]
pub struct RValueParamHandler<T, RVP>
where
diff --git a/src/subclass.rs b/src/subclass.rs
index 6c6ee31..bf905d9 100644
--- a/src/subclass.rs
+++ b/src/subclass.rs
@@ -31,7 +31,7 @@
/// #[subclass(superclass("MyCppSuperclass"))]
/// struct Bar {};
/// ```
-/// * as a directive within the [include_cpp] macro, in which case you
+/// * as a directive within the [crate::include_cpp] macro, in which case you
/// must provide two arguments of the superclass and then the
/// subclass:
/// ```
@@ -102,18 +102,14 @@
/// A type showing how the Rust side of a Rust/C++ subclass pair refers to
/// the C++ side.
#[doc(hidden)]
+#[derive(Default)]
pub enum CppSubclassCppPeerHolder<CppPeer: CppSubclassCppPeer> {
+ #[default]
Empty,
Owned(Box<UniquePtr<CppPeer>>),
Unowned(*mut CppPeer),
}
-impl<CppPeer: CppSubclassCppPeer> Default for CppSubclassCppPeerHolder<CppPeer> {
- fn default() -> Self {
- CppSubclassCppPeerHolder::Empty
- }
-}
-
impl<CppPeer: CppSubclassCppPeer> CppSubclassCppPeerHolder<CppPeer> {
fn pin_mut(&mut self) -> Pin<&mut CppPeer> {
match self {
diff --git a/src/value_param.rs b/src/value_param.rs
index 8e564a1..7386d19 100644
--- a/src/value_param.rs
+++ b/src/value_param.rs
@@ -7,7 +7,7 @@
// except according to those terms.
use cxx::{memory::UniquePtrTarget, UniquePtr};
-use moveit::{CopyNew, DerefMove, MoveNew, New};
+use moveit::{AsMove, CopyNew, MoveNew, New};
use std::{marker::PhantomPinned, mem::MaybeUninit, ops::Deref, pin::Pin};
/// A trait representing a parameter to a C++ function which is received
@@ -192,22 +192,21 @@
/// Explicitly force a value parameter to be taken using any type of [`crate::moveit::new::New`],
/// i.e. a constructor.
-pub fn as_new<N: New<Output = T>, T>(constructor: N) -> impl ValueParam<T> {
+pub fn as_new<N: New>(constructor: N) -> impl ValueParam<N::Output> {
ByNew(constructor)
}
/// Explicitly force a value parameter to be taken by copy.
-pub fn as_copy<P: Deref<Target = T>, T>(ptr: P) -> impl ValueParam<T>
+pub fn as_copy<P: Deref>(ptr: P) -> impl ValueParam<P::Target>
where
- T: CopyNew,
+ P::Target: CopyNew,
{
ByNew(crate::moveit::new::copy(ptr))
}
-/// Explicitly force a value parameter to be taken usign C++ move semantics.
-pub fn as_mov<P: DerefMove + Deref<Target = T>, T>(ptr: P) -> impl ValueParam<T>
+/// Explicitly force a value parameter to be taken using C++ move semantics.
+pub fn as_mov<P: AsMove>(ptr: P) -> impl ValueParam<P::Target>
where
- P: DerefMove,
P::Target: MoveNew,
{
ByNew(crate::moveit::new::mov(ptr))
@@ -216,11 +215,8 @@
#[doc(hidden)]
pub struct ByNew<N: New>(N);
-unsafe impl<N, T> ValueParam<T> for ByNew<N>
-where
- N: New<Output = T>,
-{
- type StackStorage = MaybeUninit<T>;
+unsafe impl<N: New> ValueParam<N::Output> for ByNew<N> {
+ type StackStorage = MaybeUninit<N::Output>;
unsafe fn populate_stack_space(self, mut stack: Pin<&mut Option<Self::StackStorage>>) {
// Safety: we won't move/swap things within the pin.
@@ -228,11 +224,11 @@
*slot = Some(MaybeUninit::uninit());
self.0.new(Pin::new_unchecked(slot.as_mut().unwrap()))
}
- fn get_ptr(stack: Pin<&mut Self::StackStorage>) -> *mut T {
- // Safety: it's OK to (briefly) create a reference to the T because we
+ fn get_ptr(stack: Pin<&mut Self::StackStorage>) -> *mut N::Output {
+ // Safety: it's OK to (briefly) create a reference to the N::Output because we
// populated it within `populate_stack_space`. It's OK to unpack the pin
// because we're not going to move the contents.
- unsafe { Pin::into_inner_unchecked(stack).assume_init_mut() as *mut T }
+ unsafe { Pin::into_inner_unchecked(stack).assume_init_mut() as *mut N::Output }
}
fn do_drop(stack: Pin<&mut Self::StackStorage>) {
diff --git a/tools/mdbook-preprocessor/Cargo.toml b/tools/mdbook-preprocessor/Cargo.toml
index f88e833..b6fb48c 100644
--- a/tools/mdbook-preprocessor/Cargo.toml
+++ b/tools/mdbook-preprocessor/Cargo.toml
@@ -8,28 +8,28 @@
[package]
name = "autocxx-mdbook-preprocessor"
-version = "0.22.3"
+version = "0.26.0"
authors = ["adetaylor <adetaylor@chromium.org>"]
edition = "2021"
[dependencies]
-clap = { version = "3", features = [ "cargo" ] }
+clap = { version = "3", features = ["cargo"] }
serde_json = "1"
itertools = "0.10"
anyhow = "1"
regex = "1"
-autocxx-integration-tests = { path = "../../integration-tests", version="=0.22.3"}
+autocxx-integration-tests = { path = "../../integration-tests", version = "=0.26.0" }
rayon = "1.5"
gag = "1.0"
env_logger = "0.9.0"
[dependencies.syn]
version = "1.0.39"
-features = [ "parsing" ]
+features = ["parsing"]
[dependencies.proc-macro2]
version = "1.0.11"
-features = [ "span-locations" ]
+features = ["span-locations"]
[dependencies.mdbook]
version = "0.4"
diff --git a/tools/mdbook-preprocessor/src/main.rs b/tools/mdbook-preprocessor/src/main.rs
index 1bb4e31..7123ec1 100644
--- a/tools/mdbook-preprocessor/src/main.rs
+++ b/tools/mdbook-preprocessor/src/main.rs
@@ -9,6 +9,7 @@
use std::{
borrow::Cow,
collections::HashSet,
+ ffi::OsString,
fmt::Display,
io::{self, Read},
path::PathBuf,
@@ -135,11 +136,11 @@
&case.cpp,
&case.hdr,
case.rs,
- args.value_of_os("manifest_dir").unwrap(),
+ &OsString::from(args.value_of("manifest_dir").unwrap()),
);
let desc = match err {
Ok(_) => "passed".to_string(),
- Err(ref err) => format!("failed: {:?}", err),
+ Err(ref err) => format!("failed: {err:?}"),
};
eprintln!(
"Doctest {}/{} at {} {}.",
@@ -164,7 +165,7 @@
.read_to_string(&mut stdout_str)
.unwrap();
if !stdout_str.is_empty() {
- eprintln!("Stdout from tests:\n{}", stdout_str);
+ eprintln!("Stdout from tests:\n{stdout_str}");
}
if !fails.is_empty() {
panic!(
@@ -304,7 +305,7 @@
) -> impl Iterator<Item = String> {
let input_str = lines.join("\n");
let fn_call = syn::parse_str::<syn::Expr>(&input_str)
- .unwrap_or_else(|_| panic!("Unable to parse outer function at {}", location));
+ .unwrap_or_else(|_| panic!("Unable to parse outer function at {location}"));
let fn_call = match fn_call {
Expr::Call(expr) => expr,
_ => panic!("Parsing unexpected"),
@@ -338,7 +339,7 @@
cpp,
hdr,
rs: syn::parse_file(&rs)
- .unwrap_or_else(|_| panic!("Unable to parse code at {}", location))
+ .unwrap_or_else(|_| panic!("Unable to parse code at {location}"))
.to_token_stream(),
location,
});
diff --git a/tools/publish-all.sh b/tools/publish-all.sh
index b43b95b..7252b5e 100755
--- a/tools/publish-all.sh
+++ b/tools/publish-all.sh
@@ -11,6 +11,4 @@
echo "Publish: $CRATE"
cargo publish
popd
- sleep 30 # sometimes crates.io takes a moment, and our
- # crates are interdependent.
done
diff --git a/tools/reduce/Cargo.toml b/tools/reduce/Cargo.toml
index 83e3916..c60d2d0 100644
--- a/tools/reduce/Cargo.toml
+++ b/tools/reduce/Cargo.toml
@@ -8,30 +8,32 @@
[package]
name = "autocxx-reduce"
-version = "0.22.3"
+version = "0.26.0"
authors = ["adetaylor <adetaylor@chromium.org>"]
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
-autocxx-engine = { version = "=0.22.3", path="../../engine" }
-autocxx-parser = { version = "=0.22.3", path="../../parser", features = [ "reproduction_case" ] }
+autocxx-engine = { version = "=0.26.0", path = "../../engine" }
+autocxx-parser = { version = "=0.26.0", path = "../../parser", features = [
+ "reproduction_case",
+] }
clap = { version = "3.1.2", features = ["cargo"] }
-tempfile = "3.1"
+tempfile = "3.4"
indoc = "1.0"
itertools = "0.10"
serde_json = "1.0"
serde = "1.0"
serde_derive = "1.0"
-syn = "1.0.39"
+syn = "2.0.1"
quote = "1.0"
-cxx-gen = "0.7.68"
+cxx-gen = "0.7.78"
regex = "1.5"
indexmap = "1.8"
[dev-dependencies]
-assert_cmd = "1.0.3"
+assert_cmd = "2"
tempfile = "3.1"
indoc = "1.0"
-proc-macro2 = "1.0"
\ No newline at end of file
+proc-macro2 = "1.0"
diff --git a/tools/reduce/src/main.rs b/tools/reduce/src/main.rs
index 06110cd..d585dee 100644
--- a/tools/reduce/src/main.rs
+++ b/tools/reduce/src/main.rs
@@ -36,6 +36,13 @@
"};
fn main() {
+ // Assemble some defaults for command line arguments
+ let current_exe = std::env::current_exe().unwrap();
+ let our_dir = current_exe.parent().unwrap();
+ let default_gen_cmd = our_dir.join("autocxx-gen").to_str().unwrap().to_string();
+ let rust_libs_path1 = our_dir.to_str().unwrap().to_string();
+ let rust_libs_path2 = our_dir.join("deps").to_str().unwrap().to_string();
+ let default_rlibs = &[rust_libs_path1.as_str(), rust_libs_path2.as_str()];
let matches = Command::new("autocxx-reduce")
.version(crate_version!())
.author(crate_authors!())
@@ -138,6 +145,24 @@
.long("gen-cmd")
.value_name("GEN-CMD")
.help("where to find autocxx-gen")
+ .default_value(&default_gen_cmd)
+ .takes_value(true),
+ )
+ .arg(
+ Arg::new("rustc")
+ .long("rustc")
+ .value_name("RUSTC")
+ .help("where to find rustc")
+ .default_value("rustc")
+ .takes_value(true),
+ )
+ .arg(
+ Arg::new("rlibs")
+ .long("rlibs")
+ .value_name("LIBDIR")
+ .help("where to find rlibs/rmetas for cxx and autocxx")
+ .default_values(default_rlibs)
+ .multiple_values(true)
.takes_value(true),
)
.arg(
@@ -172,6 +197,11 @@
.help("Do not post-compile the C++ generated by autocxxgen"),
)
.arg(
+ Arg::new("no-rustc")
+ .long("no-rustc")
+ .help("Do not compile the rust generated by autocxxgen"),
+ )
+ .arg(
Arg::new("suppress-cxx-inclusions")
.long("suppress-cxx-inclusions")
.takes_value(true)
@@ -222,8 +252,7 @@
let listing_path = tmp_dir.path().join("listing.h");
create_concatenated_header(&headers, &listing_path)?;
announce_progress(&format!(
- "Preprocessing {:?} to {:?}",
- listing_path, concat_path
+ "Preprocessing {listing_path:?} to {concat_path:?}"
));
preprocess(&listing_path, &concat_path, &incs, &defs)?;
let directives: Vec<_> = std::iter::once("#include \"concat.h\"\n".to_string())
@@ -231,7 +260,7 @@
submatches
.values_of("directive")
.unwrap_or_default()
- .map(|s| format!("{}\n", s)),
+ .map(|s| format!("{s}\n")),
)
.collect();
create_rs_file(&rs_path, &directives)?;
@@ -277,33 +306,25 @@
.collect();
let extra_clang_args: Vec<&str> = extra_clang_args.iter().map(|s| s.as_ref()).collect_vec();
- let default_gen_cmd = std::env::current_exe()?
- .parent()
- .unwrap()
- .join("autocxx-gen")
- .to_str()
- .unwrap()
- .to_string();
- let gen_cmd = matches.value_of("gen-cmd").unwrap_or(&default_gen_cmd);
+ let gen_cmd = matches.value_of("gen-cmd").unwrap();
if !Path::new(gen_cmd).exists() {
panic!(
- "autocxx-gen not found in {}. hint: autocxx-reduce --gen-cmd /path/to/autocxx-gen",
- gen_cmd
+ "autocxx-gen not found in {gen_cmd}. hint: autocxx-reduce --gen-cmd /path/to/autocxx-gen"
);
}
+
run_sample_gen_cmd(gen_cmd, &rs_path, tmp_dir.path(), &extra_clang_args)?;
// Create and run an interestingness test which does not filter its output through grep.
let demo_interestingness_test_dir = tmp_dir.path().join("demo-interestingness-test");
std::fs::create_dir(&demo_interestingness_test_dir).unwrap();
let interestingness_test = demo_interestingness_test_dir.join("test-demo.sh");
create_interestingness_test(
+ &matches,
gen_cmd,
&interestingness_test,
None,
&rs_path,
&extra_clang_args,
- !matches.is_present("no-precompile"),
- !matches.is_present("no-postcompile"),
)?;
let demo_dir_concat_path = demo_interestingness_test_dir.join("concat.h");
std::fs::copy(&concat_path, demo_dir_concat_path).unwrap();
@@ -312,13 +333,12 @@
// Now the main interestingness test
let interestingness_test = tmp_dir.path().join("test.sh");
create_interestingness_test(
+ &matches,
gen_cmd,
&interestingness_test,
Some(matches.value_of("problem").unwrap()),
&rs_path,
&extra_clang_args,
- !matches.is_present("no-precompile"),
- !matches.is_present("no-postcompile"),
)?;
run_creduce(
matches.value_of("creduce").unwrap(),
@@ -331,7 +351,7 @@
match output_path {
None => print_minimized_case(&concat_path)?,
Some(output_path) => {
- std::fs::copy(&concat_path, &PathBuf::from(output_path))?;
+ std::fs::copy(&concat_path, PathBuf::from(output_path))?;
}
};
Ok(())
@@ -349,13 +369,13 @@
}
fn announce_progress(msg: &str) {
- println!("=== {} ===", msg);
+ println!("=== {msg} ===");
}
fn print_minimized_case(concat_path: &Path) -> Result<(), std::io::Error> {
announce_progress("Completed. Minimized test case:");
let contents = std::fs::read_to_string(concat_path)?;
- println!("{}", contents);
+ println!("{contents}");
Ok(())
}
@@ -370,7 +390,7 @@
.arg("--help")
.output();
let msg = match cmd {
- Err(error) => panic!("failed to run creduce. creduce_cmd = {}. hint: autocxx-reduce --creduce /path/to/creduce. error = {}", creduce_cmd, error),
+ Err(error) => panic!("failed to run creduce. creduce_cmd = {creduce_cmd}. hint: autocxx-reduce --creduce /path/to/creduce. error = {error}"),
Ok(result) => result.stdout
};
let msg = std::str::from_utf8(&msg).unwrap();
@@ -413,7 +433,7 @@
let args = format_gen_cmd(rs_file, tmp_dir.to_str().unwrap(), extra_clang_args);
let args = args.collect::<Vec<_>>();
let args_str = args.join(" ");
- announce_progress(&format!("Running sample gen cmd: {} {}", gen_cmd, args_str));
+ announce_progress(&format!("Running sample gen cmd: {gen_cmd} {args_str}"));
std::process::Command::new(gen_cmd).args(args).status()?;
Ok(())
}
@@ -451,15 +471,26 @@
}
fn create_interestingness_test(
+ matches: &ArgMatches,
gen_cmd: &str,
test_path: &Path,
problem: Option<&str>,
rs_file: &Path,
extra_clang_args: &[&str],
- precompile: bool,
- postcompile: bool,
) -> Result<(), std::io::Error> {
announce_progress("Creating interestingness test");
+ let precompile = !matches.is_present("no-precompile");
+ let postcompile = !matches.is_present("no-postcompile");
+ let rustc = !matches.is_present("no-rustc");
+
+ let rustc_path = matches.value_of("rustc").unwrap();
+
+ let rust_libs_path: Vec<String> = matches
+ .get_many::<String>("rlibs")
+ .expect("No rlib path specified")
+ .cloned()
+ .collect();
+
// Ensure we refer to the input header by relative path
// because creduce will invoke us in some other directory with
// a copy thereof.
@@ -469,37 +500,47 @@
// For the compile afterwards, we have to avoid including any system headers.
// We rely on equivalent content being hermetically inside concat.h.
let postcompile_step = make_compile_step(postcompile, "gen0.cc", extra_clang_args);
+ let rustc_step = if rustc {
+ let rust_libs_path = rust_libs_path.iter().map(|p| format!(" -L{p}")).join(" ");
+ format!("{rustc_path} --extern cxx --extern autocxx {rust_libs_path} --crate-type rlib --emit=metadata --edition=2021 autocxx-ffi-default-gen.rs 2>&1")
+ } else {
+ "echo Skipping rustc".to_string()
+ };
+ // -q below to exit immediately as soon as a match is found, to avoid
+ // extra compile/codegen steps
let problem_grep = problem
- .map(|problem| format!("| grep \"{}\" >/dev/null 2>&1", problem))
+ .map(|problem| format!("| grep -q \"{problem}\" >/dev/null 2>&1"))
.unwrap_or_default();
+ // We formerly had a 'trap' below but it seems to have caused problems
+ // (trap \"if [[ \\$? -eq 139 ]]; then echo Segfault; fi\" CHLD; {} {} 2>&1 && cat autocxx-ffi-default-gen.rs && cat autocxxgen*.h && {} && {} 2>&1 ) {}
let content = format!(
indoc! {"
- #!/bin/sh
+ #!/bin/bash
set -e
echo Precompile
{}
echo Move
mv concat.h concat-body.h
- echo Codegen
(echo \"#ifndef __CONCAT_H__\"; echo \"#define __CONCAT_H__\"; echo '#include \"concat-body.h\"'; echo \"#endif\") > concat.h
- (trap \"if [[ \\$? -eq 139 ]]; then echo Segfault; fi\" CHLD; {} {} 2>&1 && cat autocxx-ffi-default-gen.rs && cat autocxxgen*.h && {} 2>&1 ) {}
+ echo Codegen
+ ({} {} 2>&1 && cat autocxx-ffi-default-gen.rs && cat autocxxgen*.h && {} && {} 2>&1) {}
echo Remove
rm concat.h
echo Swap back
mv concat-body.h concat.h
echo Done
"},
- precompile_step, gen_cmd, args, postcompile_step, problem_grep
+ precompile_step, gen_cmd, args, rustc_step, postcompile_step, problem_grep
);
- println!("Interestingness test:\n{}", content);
+ println!("Interestingness test:\n{content}");
{
let mut file = File::create(test_path)?;
file.write_all(content.as_bytes())?;
}
- let mut perms = std::fs::metadata(&test_path)?.permissions();
+ let mut perms = std::fs::metadata(test_path)?.permissions();
perms.set_mode(0o700);
- std::fs::set_permissions(&test_path, perms)?;
+ std::fs::set_permissions(test_path, perms)?;
Ok(())
}
@@ -531,14 +572,14 @@
announce_progress("Creating preprocessed header");
let mut file = File::create(listing_path)?;
for header in headers {
- file.write_all(format!("#include \"{}\"\n", header).as_bytes())?;
+ file.write_all(format!("#include \"{header}\"\n").as_bytes())?;
}
Ok(())
}
fn create_file(path: &Path, content: &str) -> Result<(), std::io::Error> {
let mut file = File::create(path)?;
- write!(file, "{}", content)?;
+ write!(file, "{content}")?;
Ok(())
}
@@ -548,10 +589,7 @@
.find_iter(cxx_gen::HEADER)
.map(|m| m.as_str())
.collect(); // for uniqueness
- defines
- .into_iter()
- .map(|def| format!("-D{}", def))
- .collect()
+ defines.into_iter().map(|def| format!("-D{def}")).collect()
}
#[test]
diff --git a/tools/reduce/tests/reduce_test.rs b/tools/reduce/tests/reduce_test.rs
index b277585..15cd627 100644
--- a/tools/reduce/tests/reduce_test.rs
+++ b/tools/reduce/tests/reduce_test.rs
@@ -55,9 +55,8 @@
do_reduce(
|header, demo_code_dir| {
let config = format!(
- "#include \"{}\" generate_all!() block!(\"First\")
- safety!(unsafe_ffi)",
- header
+ "#include \"{header}\" generate_all!() block!(\"First\")
+ safety!(unsafe_ffi)"
);
let json = serde_json::json!({
"header": INPUT_H,
@@ -173,7 +172,7 @@
let demo_code_dir = tmp_dir.path().join("demo");
std::fs::create_dir(&demo_code_dir).unwrap();
let input_header = if include_cxx_h {
- Cow::Owned(format!("#include \"cxx.h\"\n{}", INPUT_H))
+ Cow::Owned(format!("#include \"cxx.h\"\n{INPUT_H}"))
} else {
Cow::Borrowed(INPUT_H)
};
@@ -205,7 +204,7 @@
cmd = cmd.arg("repro").arg("-r").arg(repro_case);
}
}
- eprintln!("Running {:?}", cmd);
+ eprintln!("Running {cmd:?}");
let o = cmd.output()?;
println!("Reduce output: {}", std::str::from_utf8(&o.stdout).unwrap());
println!("Reduce error: {}", std::str::from_utf8(&o.stderr).unwrap());
@@ -220,7 +219,7 @@
fn write_to_file(dir: &Path, filename: &str, content: &[u8]) {
let path = dir.join(filename);
- let mut f = File::create(&path).expect("Unable to create file");
+ let mut f = File::create(path).expect("Unable to create file");
f.write_all(content).expect("Unable to write file");
}
diff --git a/tools/stress-test/Cargo.toml b/tools/stress-test/Cargo.toml
index a651f60..7788fe3 100644
--- a/tools/stress-test/Cargo.toml
+++ b/tools/stress-test/Cargo.toml
@@ -8,14 +8,14 @@
[package]
name = "autocxx-stress-test"
-version = "0.22.3"
+version = "0.26.0"
authors = ["Adrian Taylor <adetaylor@chromium.org>"]
edition = "2021"
[dependencies]
-cxx = "1.0.68"
-autocxx = { path = "../..", version="0.22.3" }
+cxx = "1.0.78"
+autocxx = { path = "../..", version = "0.26.0" }
[build-dependencies]
-autocxx-build = { path = "../../gen/build", version="0.22.3" }
-miette = { version="4.3", features=["fancy"]}
+autocxx-build = { path = "../../gen/build", version = "0.26.0" }
+miette = { version = "5", features = ["fancy"] }