Skip to content

Commit

Permalink
Merge pull request #31 from bkchr/bkchr-fix-multiple-crates
Browse files Browse the repository at this point in the history
Fix finding crates which import itself
  • Loading branch information
bkchr authored Jan 24, 2023
2 parents 2309ded + a9aa118 commit 27e449c
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 10 deletions.
35 changes: 32 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# proc-macro-crate


[![](https://docs.rs/proc-macro-crate/badge.svg)](https://docs.rs/proc-macro-crate) [![](https://img.shields.io/crates/v/proc-macro-crate.svg)](https://crates.io/crates/proc-macro-crate) [![](https://img.shields.io/crates/d/proc-macro-crate.svg)](https://crates.io/crates/proc-macro-crate) [![Build Status](https://travis-ci.org/bkchr/proc-macro-crate.svg?branch=master)](https://travis-ci.org/bkchr/proc-macro-crate)
[![](https://docs.rs/proc-macro-crate/badge.svg)](https://docs.rs/proc-macro-crate/) [![](https://img.shields.io/crates/v/proc-macro-crate.svg)](https://crates.io/crates/proc-macro-crate) [![](https://img.shields.io/crates/d/proc-macro-crate.png)](https://crates.io/crates/proc-macro-crate) [![Build Status](https://travis-ci.org/bkchr/proc-macro-crate.png?branch=master)](https://travis-ci.org/bkchr/proc-macro-crate)

Providing support for `$crate` in procedural macros.

Expand All @@ -13,7 +13,7 @@ Providing support for `$crate` in procedural macros.

In `macro_rules!` `$crate` is used to get the path of the crate where a macro is declared in. In
procedural macros there is currently no easy way to get this path. A common hack is to import the
desired crate with a know name and use this. However, with Rust edition 2018 and dropping
desired crate with a know name and use this. However, with rust edition 2018 and dropping
`extern crate` declarations from `lib.rs`, people start to rename crates in `Cargo.toml` directly.
However, this breaks importing the crate, as the proc-macro developer does not know the renamed
name of the crate that should be imported.
Expand Down Expand Up @@ -45,6 +45,35 @@ fn import_my_crate() {

```

### Edge cases

There are multiple edge cases when it comes to determining the correct crate. If you for example
import a crate as its own dependency, like this:

```toml
[package]
name = "my_crate"

[dev-dependencies]
my_crate = { version = "0.1", features = [ "test-feature" ] }
```

The crate will return `FoundCrate::Itself` and you will not be able to find the other instance
of your crate in `dev-dependencies`. Other similar cases are when one crate is imported multiple
times:

```toml
[package]
name = "my_crate"

[dependencies]
some-crate = { version = "0.5" }
some-crate-old = { package = "some-crate", version = "0.1" }
```

When searching for `some-crate` in this `Cargo.toml` it will return `FoundCrate::Name("some_old_crate")`,
aka the last definition of the crate in the `Cargo.toml`.

### License

Licensed under either of
Expand All @@ -55,4 +84,4 @@ Licensed under either of

at your option.

License: Apache-2.0/MIT
License: MIT OR Apache-2.0
70 changes: 63 additions & 7 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,42 @@ fn import_my_crate() {
# fn main() {}
```
## Edge cases
There are multiple edge cases when it comes to determining the correct crate. If you for example
import a crate as its own dependency, like this:
```toml
[package]
name = "my_crate"
[dev-dependencies]
my_crate = { version = "0.1", features = [ "test-feature" ] }
```
The crate will return `FoundCrate::Itself` and you will not be able to find the other instance
of your crate in `dev-dependencies`. Other similar cases are when one crate is imported multiple
times:
```toml
[package]
name = "my_crate"
[dependencies]
some-crate = { version = "0.5" }
some-crate-old = { package = "some-crate", version = "0.1" }
```
When searching for `some-crate` in this `Cargo.toml` it will return `FoundCrate::Name("some_old_crate")`,
aka the last definition of the crate in the `Cargo.toml`.
## License
Licensed under either of
* [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0)
* [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0)
* [MIT license](http://opensource.org/licenses/MIT)
* [MIT license](https://opensource.org/licenses/MIT)
at your option.
*/
Expand Down Expand Up @@ -227,26 +256,31 @@ fn open_cargo_toml(path: &Path) -> Result<Document, Error> {
/// `dev-dependencies`.
fn extract_crate_names(cargo_toml: &Document) -> Result<CrateNames, Error> {
let package_name = extract_package_name(cargo_toml);
let root_pkg = package_name.map(|name| {
let root_pkg = package_name.as_ref().map(|name| {
let cr = match env::var_os("CARGO_TARGET_TMPDIR") {
// We're running for a library/binary crate
None => FoundCrate::Itself,
// We're running for an integration test
Some(_) => FoundCrate::Name(sanitize_crate_name(name)),
};

(name.to_owned(), cr)
(name.to_string(), cr)
});

let dep_tables = dep_tables(cargo_toml).chain(target_dep_tables(cargo_toml));
let dep_pkgs = dep_tables.flatten().map(|(dep_name, dep_value)| {
let dep_pkgs = dep_tables.flatten().filter_map(move |(dep_name, dep_value)| {
let pkg_name = dep_value
.get("package")
.and_then(|i| i.as_str())
.unwrap_or(dep_name);

if package_name.as_ref().map_or(false, |n| *n == pkg_name) {
return None;
}

let cr = FoundCrate::Name(sanitize_crate_name(dep_name));

(pkg_name.to_owned(), cr)
Some((pkg_name.to_owned(), cr))
});

Ok(root_pkg.into_iter().chain(dep_pkgs).collect())
Expand Down Expand Up @@ -291,7 +325,7 @@ mod tests {
fn $name() {
let cargo_toml = $cargo_toml.parse::<Document>().expect("Parses `Cargo.toml`");

match extract_crate_names(&cargo_toml).map(|mut map| map.remove("my_crate")) {
match extract_crate_names(&cargo_toml).map(|mut map| map.remove("my_crate")) {
$( $result )* => (),
o => panic!("Invalid result: {:?}", o),
}
Expand Down Expand Up @@ -379,4 +413,26 @@ mod tests {
"#,
Ok(Some(FoundCrate::Itself))
}

create_test! {
own_crate_and_in_deps,
r#"
[package]
name = "my_crate"
[dev-dependencies]
my_crate = "0.1"
"#,
Ok(Some(FoundCrate::Itself))
}

create_test! {
multiple_times,
r#"
[dependencies]
my_crate = { version = "0.5" }
my-crate-old = { package = "my_crate", version = "0.1" }
"#,
Ok(Some(FoundCrate::Name(name))) if name == "my_crate_old"
}
}

0 comments on commit 27e449c

Please sign in to comment.