Skip to content

Commit 3785334

Browse files
authored
Fixed bug where crate_universe could match aliases to bench/example deps (#2600)
A dependency which aliases a transitive dependency with a name that happens to match another Cargo target in that (Cargo) package will result in an incorrect dependency being collected. For the example of [surrealdb-core](https://github.com/surrealdb/surrealdb/blob/v1.3.1/core/Cargo.toml#L83), the `executor = { package "async-executor" }` dependency was incorrectly matching [a benchmark target](https://github.com/smol-rs/async-executor/blob/v1.9.1/Cargo.toml#L38-L40), thus causing a broken dep to be rendered. This PR fixes this issue.
1 parent ee339e2 commit 3785334

File tree

5 files changed

+96726
-5187
lines changed

5 files changed

+96726
-5187
lines changed

crate_universe/src/context.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,7 @@ mod test {
285285
(&CrateId::new("log".to_owned(), Version::new(0, 4, 21)), false),
286286
(&CrateId::new("names".to_owned(), Version::parse("0.12.1-dev").unwrap()), false),
287287
(&CrateId::new("names".to_owned(), Version::new(0, 13, 0)), false),
288+
(&CrateId::new("surrealdb".to_owned(), Version::new(1, 3, 1)), false),
288289
(&CrateId::new("value-bag".to_owned(), Version::parse("1.0.0-alpha.7").unwrap()), false),
289290
],
290291
}

crate_universe/src/metadata/dependency.rs

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::collections::BTreeSet;
33

44
use anyhow::{bail, Result};
55
use cargo_metadata::{
6-
DependencyKind, Metadata as CargoMetadata, Node, NodeDep, Package, PackageId,
6+
DependencyKind, Metadata as CargoMetadata, Node, NodeDep, Package, PackageId, Target,
77
};
88
use cargo_platform::Platform;
99
use serde::{Deserialize, Serialize};
@@ -187,19 +187,28 @@ fn collect_deps_selectable(
187187
select
188188
}
189189

190+
/// Packages may have targets that match aliases of dependents. This function
191+
/// checks a target to see if it's an unexpected type for a dependency.
192+
fn is_ignored_package_target(target: &Target) -> bool {
193+
target
194+
.kind
195+
.iter()
196+
.any(|t| ["example", "bench", "test"].contains(&t.as_str()))
197+
}
198+
190199
fn is_lib_package(package: &Package) -> bool {
191200
package.targets.iter().any(|target| {
192201
target
193202
.crate_types
194203
.iter()
195204
.any(|t| ["lib", "rlib"].contains(&t.as_str()))
205+
&& !is_ignored_package_target(target)
196206
})
197207
}
198208

199209
fn is_proc_macro_package(package: &Package) -> bool {
200210
package.targets.iter().any(|target| {
201-
target.crate_types.iter().any(|t| t == "proc-macro")
202-
&& !target.kind.iter().any(|t| t == "example")
211+
target.crate_types.iter().any(|t| t == "proc-macro") && !is_ignored_package_target(target)
203212
})
204213
}
205214

@@ -238,8 +247,13 @@ fn is_workspace_member(node_dep: &NodeDep, metadata: &CargoMetadata) -> bool {
238247

239248
fn get_library_target_name(package: &Package, potential_name: &str) -> Result<String> {
240249
// If the potential name is not an alias in a dependent's package, a target's name
241-
// should match which means we already know what the target library name is.
242-
if package.targets.iter().any(|t| t.name == potential_name) {
250+
// should match which means we already know what the target library name is. The
251+
// only exception is for targets that are otherwise ignored (like benchmarks or examples).
252+
if package
253+
.targets
254+
.iter()
255+
.any(|t| t.name == potential_name && !is_ignored_package_target(t))
256+
{
243257
return Ok(potential_name.to_string());
244258
}
245259

@@ -271,11 +285,13 @@ fn get_library_target_name(package: &Package, potential_name: &str) -> Result<St
271285
/// for targets where packages (packages[#].targets[#].name) uses crate names. In order to
272286
/// determine whether or not a dependency is aliased, we compare it with all available targets
273287
/// on it's package. Note that target names are not guaranteed to be module names where Node
274-
/// dependencies are, so we need to do a conversion to check for this
288+
/// dependencies are, so we need to do a conversion to check for this. This function will
289+
/// return the name of a target's alias in the content of the current dependent if it is aliased.
275290
fn get_target_alias(target_name: &str, package: &Package) -> Option<String> {
276291
match package
277292
.targets
278293
.iter()
294+
.filter(|t| !is_ignored_package_target(t))
279295
.all(|t| sanitize_module_name(&t.name) != target_name)
280296
{
281297
true => Some(target_name.to_string()),
@@ -449,6 +465,30 @@ mod test {
449465
assert_eq!(proc_macro_deps, Vec::<&str>::new());
450466
}
451467

468+
#[test]
469+
fn bench_name_alias_dep() {
470+
let metadata = metadata::alias();
471+
472+
let node = find_metadata_node("surrealdb-core", &metadata);
473+
let dependencies = DependencySet::new_for_node(node, &metadata);
474+
475+
println!("{:#?}", dependencies);
476+
477+
let bindings = dependencies.normal_deps.items();
478+
479+
// It's critical that the dep be found with the correct name and not the
480+
// alias that the `aliases` package is using that coincidentally matches the
481+
// `bench` target `executor` in the `async-executor` package.
482+
let async_executor = bindings
483+
.iter()
484+
.find(|(_, dep)| dep.target_name == "async-executor")
485+
.map(|(_, dep)| dep)
486+
.unwrap();
487+
488+
// Ensure alias data is still tracked.
489+
assert_eq!(async_executor.alias, Some("executor".to_owned()));
490+
}
491+
452492
#[test]
453493
fn sys_dependencies() {
454494
let metadata = metadata::build_scripts();

0 commit comments

Comments
 (0)