use super::super::*;
use super::async_exposure::async_mod;
use super::async_exposure::async_subtree_labels;
use super::dyn_trait::dyn_mod;
use super::forbidden_marker::marker_findings;
use super::helpers::*;
use super::impl_trait::impl_trait_mod;
use super::trait_impl::locality_findings;
use super::unsafe_confinement::unsafe_labels;
use super::visibility::vis_findings;
#[test]
pub(super) fn cfg_if_if_else_arms_both_expose_forbidden_types() {
let out = findings(
"cfg-if-both-arms",
&[
("lib.rs", "pub mod api;\npub mod infra;\n"),
(
"api.rs",
"cfg_if::cfg_if! {\n\
if #[cfg(unix)] {\n\
pub fn unix_leak() -> crate::infra::Secret { loop {} }\n\
} else {\n\
pub fn fallback_leak() -> crate::infra::Secret { loop {} }\n\
}\n\
}\n",
),
("infra.rs", "pub struct Secret;\n"),
],
"crate::api",
&["crate::infra"],
)
.unwrap();
assert_eq!(
out,
vec![
"crate::infra::Secret exposed by fn crate::api::fallback_leak",
"crate::infra::Secret exposed by fn crate::api::unix_leak",
],
"both cfg_if arms' exposures must react (cfg-blind union): {out:?}"
);
}
#[test]
pub(super) fn cfg_if_if_only_arm_exposes_a_forbidden_type() {
let out = findings(
"cfg-if-if-only",
&[
("lib.rs", "pub mod api;\npub mod infra;\n"),
(
"api.rs",
"cfg_if::cfg_if! {\n\
if #[cfg(unix)] {\n\
pub fn leak() -> crate::infra::Secret { loop {} }\n\
}\n\
}\n",
),
("infra.rs", "pub struct Secret;\n"),
],
"crate::api",
&["crate::infra"],
)
.unwrap();
assert_eq!(
out,
vec!["crate::infra::Secret exposed by fn crate::api::leak"],
"an if-only cfg_if arm must be observed: {out:?}"
);
}
#[test]
pub(super) fn cfg_if_else_if_chain_exposes_every_arm() {
let out = findings(
"cfg-if-chain",
&[
("lib.rs", "pub mod api;\npub mod infra;\n"),
(
"api.rs",
"cfg_if::cfg_if! {\n\
if #[cfg(unix)] {\n\
pub fn a_leak() -> crate::infra::Secret { loop {} }\n\
} else if #[cfg(windows)] {\n\
pub fn b_leak() -> crate::infra::Secret { loop {} }\n\
} else {\n\
pub fn c_leak() -> crate::infra::Secret { loop {} }\n\
}\n\
}\n",
),
("infra.rs", "pub struct Secret;\n"),
],
"crate::api",
&["crate::infra"],
)
.unwrap();
assert_eq!(
out,
vec![
"crate::infra::Secret exposed by fn crate::api::a_leak",
"crate::infra::Secret exposed by fn crate::api::b_leak",
"crate::infra::Secret exposed by fn crate::api::c_leak",
],
"every arm of an else-if chain must be observed: {out:?}"
);
}
#[test]
pub(super) fn nested_cfg_if_inside_an_arm_exposes_a_forbidden_type() {
let out = findings(
"cfg-if-nested",
&[
("lib.rs", "pub mod api;\npub mod infra;\n"),
(
"api.rs",
"cfg_if::cfg_if! {\n\
if #[cfg(unix)] {\n\
cfg_if::cfg_if! {\n\
if #[cfg(target_pointer_width = \"64\")] {\n\
pub fn inner_leak() -> crate::infra::Secret { loop {} }\n\
}\n\
}\n\
}\n\
}\n",
),
("infra.rs", "pub struct Secret;\n"),
],
"crate::api",
&["crate::infra"],
)
.unwrap();
assert_eq!(
out,
vec!["crate::infra::Secret exposed by fn crate::api::inner_leak"],
"a nested cfg_if's arm must be observed: {out:?}"
);
}
#[test]
pub(super) fn a_cfg_if_arm_declared_file_module_is_walked() {
let out = findings(
"cfg-if-arm-mod",
&[
(
"lib.rs",
"pub mod infra;\n\
cfg_if::cfg_if! {\n\
if #[cfg(unix)] {\n\
pub mod api;\n\
}\n\
}\n",
),
(
"api.rs",
"pub fn leak() -> crate::infra::Secret { loop {} }\n",
),
("infra.rs", "pub struct Secret;\n"),
],
"crate::api",
&["crate::infra"],
)
.unwrap();
assert_eq!(
out,
vec!["crate::infra::Secret exposed by fn crate::api::leak"],
"a mod declared only inside a cfg_if arm must be descended: {out:?}"
);
}
#[test]
pub(super) fn a_cfg_if_arm_declared_inline_module_is_walked() {
let out = findings(
"cfg-if-arm-inline-mod",
&[
(
"lib.rs",
"pub mod infra;\n\
cfg_if::cfg_if! {\n\
if #[cfg(unix)] {\n\
pub mod api {\n\
pub fn leak() -> crate::infra::Secret { loop {} }\n\
}\n\
}\n\
}\n",
),
("infra.rs", "pub struct Secret;\n"),
],
"crate::api",
&["crate::infra"],
)
.unwrap();
assert_eq!(
out,
vec!["crate::infra::Secret exposed by fn crate::api::leak"],
"an inline mod declared inside a cfg_if arm must be descended: {out:?}"
);
}
#[test]
pub(super) fn a_paren_delimited_cfg_if_invocation_is_transparent() {
let out = findings(
"cfg-if-paren",
&[
("lib.rs", "pub mod api;\npub mod infra;\n"),
(
"api.rs",
"cfg_if::cfg_if!(\n\
if #[cfg(unix)] {\n\
pub fn leak() -> crate::infra::Secret { loop {} }\n\
}\n\
);\n",
),
("infra.rs", "pub struct Secret;\n"),
],
"crate::api",
&["crate::infra"],
)
.unwrap();
assert_eq!(
out,
vec!["crate::infra::Secret exposed by fn crate::api::leak"],
"a paren-delimited cfg_if invocation must be observed: {out:?}"
);
}
#[test]
pub(super) fn a_cfg_if_inside_an_inline_module_is_transparent() {
let out = findings(
"cfg-if-in-inline-mod",
&[
("lib.rs", "pub mod api;\npub mod infra;\n"),
(
"api.rs",
"pub mod inner {\n\
cfg_if::cfg_if! {\n\
if #[cfg(unix)] {\n\
pub fn leak() -> crate::infra::Secret { loop {} }\n\
}\n\
}\n\
}\n",
),
("infra.rs", "pub struct Secret;\n"),
],
"crate::api::inner",
&["crate::infra"],
)
.unwrap();
assert_eq!(
out,
vec!["crate::infra::Secret exposed by fn crate::api::inner::leak"],
"a cfg_if inside an inline module must be observed: {out:?}"
);
}
#[test]
pub(super) fn a_cfg_if_arm_body_that_does_not_parse_as_items_is_not_a_scan_error() {
let out = findings(
"cfg-if-unparseable-arm",
&[
("lib.rs", "pub mod api;\npub mod infra;\n"),
(
"api.rs",
"pub fn control_leak() -> crate::infra::Secret { loop {} }\n\
cfg_if::cfg_if! {\n\
if #[cfg(unix)] {\n\
let _not_an_item = 1;\n\
}\n\
}\n",
),
("infra.rs", "pub struct Secret;\n"),
],
"crate::api",
&["crate::infra"],
)
.unwrap();
assert_eq!(
out,
vec!["crate::infra::Secret exposed by fn crate::api::control_leak"],
"an unparseable arm body must yield no items and no error, leaving the rest observed: {out:?}"
);
}
#[test]
pub(super) fn an_arbitrary_macro_body_is_not_read_as_transparent_arms() {
let out = findings(
"arbitrary-macro-body",
&[
("lib.rs", "pub mod api;\npub mod infra;\n"),
(
"api.rs",
"generate_wrapper! {\n\
impl Foo {\n\
pub fn hidden() -> crate::infra::Secret { loop {} }\n\
}\n\
}\n",
),
("infra.rs", "pub struct Secret;\n"),
],
"crate::api",
&["crate::infra"],
)
.unwrap();
assert!(
out.is_empty(),
"only cfg_if is transparent: an arbitrary macro's body must not be read as arms: {out:?}"
);
}
#[test]
pub(super) fn the_same_exposure_written_as_an_item_reacts() {
let out = findings(
"arbitrary-macro-control",
&[
("lib.rs", "pub mod api;\npub mod infra;\n"),
(
"api.rs",
"pub struct Foo;\n\
impl Foo {\n\
pub fn hidden() -> crate::infra::Secret { loop {} }\n\
}\n\
pub fn hidden() -> crate::infra::Secret { loop {} }\n",
),
("infra.rs", "pub struct Secret;\n"),
],
"crate::api",
&["crate::infra"],
)
.unwrap();
assert_eq!(
out,
vec![
"crate::infra::Secret exposed by fn <crate::api::Foo>::hidden",
"crate::infra::Secret exposed by fn crate::api::hidden",
],
"the control exposure must react as an item — both the inherent-impl method the macro \
fixture wrapped and the free function: {out:?}"
);
}
#[test]
pub(super) fn a_cfg_if_arm_declared_module_with_no_source_file_is_tolerated() {
let out = findings(
"cfg-if-arm-absent-file",
&[
(
"lib.rs",
"pub mod api;\npub mod infra;\n\
cfg_if::cfg_if! {\n\
if #[cfg(unix)] {\n\
pub mod unix_only;\n\
}\n\
}\n",
),
(
"api.rs",
"pub fn leak() -> crate::infra::Secret { loop {} }\n",
),
("infra.rs", "pub struct Secret;\n"),
],
"crate::api",
&["crate::infra"],
)
.unwrap();
assert_eq!(
out,
vec!["crate::infra::Secret exposed by fn crate::api::leak"],
"an arm-declared module with no file must be tolerated, not a scan error: {out:?}"
);
}
#[test]
pub(super) fn the_same_module_declaration_outside_an_arm_still_fails_loud() {
let error = findings(
"cfg-if-arm-absent-control",
&[
(
"lib.rs",
"pub mod api;\npub mod infra;\npub mod unix_only;\n",
),
(
"api.rs",
"pub fn leak() -> crate::infra::Secret { loop {} }\n",
),
("infra.rs", "pub struct Secret;\n"),
],
"crate::api",
&["crate::infra"],
)
.unwrap_err();
assert!(
error.contains("unix_only") || error.contains("no source file"),
"an unconditional declaration with no file must stay a scan error: {error}"
);
}
#[test]
pub(super) fn a_cfg_if_arm_declared_dual_backed_module_is_still_a_scan_error() {
let error = findings(
"cfg-if-arm-dual-backed",
&[
(
"lib.rs",
"pub mod api;\npub mod infra;\n\
cfg_if::cfg_if! {\n\
if #[cfg(unix)] {\n\
pub mod dual;\n\
}\n\
}\n",
),
(
"api.rs",
"pub fn leak() -> crate::infra::Secret { loop {} }\n",
),
("infra.rs", "pub struct Secret;\n"),
("dual.rs", "pub struct A;\n"),
("dual/mod.rs", "pub struct A;\n"),
],
"crate::api",
&["crate::infra"],
)
.unwrap_err();
assert!(
error.contains("resolves to both"),
"a dual-backed arm-declared module must stay a scan error: {error}"
);
}
#[test]
pub(super) fn an_unsafe_site_inside_a_cfg_if_arm_is_confined() {
let out = unsafe_labels(
"cfg-if-arm",
&[
("lib.rs", "pub mod ffi;\npub mod net;\n"),
("ffi.rs", ""),
(
"net.rs",
"cfg_if::cfg_if! {\n\
if #[cfg(unix)] {\n\
pub unsafe fn decode() {}\n\
}\n\
}\n",
),
],
&["crate::ffi"],
)
.unwrap();
assert_eq!(
out,
vec!["unsafe fn decode in crate::net"],
"an unsafe site inside a cfg_if arm must react: {out:?}"
);
}
#[test]
pub(super) fn a_trait_impl_inside_a_cfg_if_arm_is_located() {
let out = locality_findings(
"cfg-if-arm",
&[
("lib.rs", "pub mod command;\npub mod domain;\n"),
("command.rs", "pub trait Command {}\n"),
(
"domain.rs",
"use crate::command::Command;\n\
pub struct Foo;\n\
cfg_if::cfg_if! {\n\
if #[cfg(unix)] {\n\
impl Command for Foo {}\n\
}\n\
}\n",
),
],
"crate::command::Command",
&["crate::commands"],
)
.unwrap();
assert_eq!(
out,
vec!["crate::domain (impl crate::command::Command for crate::domain::Foo)"],
"a trait impl inside a cfg_if arm must be located: {out:?}"
);
}
#[test]
pub(super) fn a_bare_pub_inside_a_cfg_if_arm_reacts() {
let out = vis_findings(
"cfg-if-arm",
&[
("lib.rs", "pub mod m;\n"),
(
"m.rs",
"cfg_if::cfg_if! {\n\
if #[cfg(unix)] {\n\
pub fn wide() {}\n\
}\n\
}\n",
),
],
"crate::m",
)
.unwrap();
assert_eq!(
out,
vec!["pub fn wide"],
"a bare pub inside a cfg_if arm must react: {out:?}"
);
}
#[test]
pub(super) fn a_dyn_seam_inside_a_cfg_if_arm_reacts() {
let out = dyn_mod(
"cfg-if-arm",
"pub trait Port {}\n\
cfg_if::cfg_if! {\n\
if #[cfg(unix)] {\n\
pub fn get() -> Box<dyn Port> { loop {} }\n\
}\n\
}\n",
)
.unwrap();
assert_eq!(
out.len(),
1,
"a dyn seam inside a cfg_if arm must react: {out:?}"
);
}
#[test]
pub(super) fn an_impl_trait_seam_inside_a_cfg_if_arm_reacts() {
let out = impl_trait_mod(
"cfg-if-arm",
"pub trait Port {}\n\
cfg_if::cfg_if! {\n\
if #[cfg(unix)] {\n\
pub fn get() -> impl Port { loop {} }\n\
}\n\
}\n",
)
.unwrap();
assert_eq!(
out.len(),
1,
"an impl-trait seam inside a cfg_if arm must react: {out:?}"
);
}
#[test]
pub(super) fn an_async_fn_inside_a_cfg_if_arm_reacts() {
let out = async_mod(
"cfg-if-arm",
"cfg_if::cfg_if! {\n\
if #[cfg(unix)] {\n\
pub async fn serve() {}\n\
}\n\
}\n",
)
.unwrap();
assert_eq!(
out.len(),
1,
"an async fn inside a cfg_if arm must react: {out:?}"
);
}
#[test]
pub(super) fn a_forbidden_marker_inside_a_cfg_if_arm_reacts() {
let out = marker_findings(
"cfg-if-arm",
&[
("lib.rs", "pub mod domain;\n"),
(
"domain.rs",
"cfg_if::cfg_if! {\n\
if #[cfg(unix)] {\n\
#[derive(serde::Serialize)]\n\
pub struct Order;\n\
}\n\
}\n",
),
],
"crate::domain",
&["serde::Serialize"],
)
.unwrap();
assert_eq!(
out.len(),
1,
"a forbidden derive inside a cfg_if arm must react: {out:?}"
);
}
#[test]
pub(super) fn a_subtree_scope_observes_an_async_fn_inside_a_cfg_if_arm() {
let out = async_subtree_labels(
"cfg-if-arm",
&[
("lib.rs", "pub mod net;\n"),
(
"net.rs",
"cfg_if::cfg_if! {\n\
if #[cfg(unix)] {\n\
pub async fn connect() {}\n\
}\n\
}\n",
),
],
"crate",
);
assert_eq!(
out.len(),
1,
"an async fn inside a submodule's cfg_if arm must react at the subtree scope: {out:?}"
);
}
#[test]
pub(super) fn an_unqualified_cfg_if_invocation_is_transparent() {
let out = findings(
"cfg-if-unqualified",
&[
("lib.rs", "pub mod api;\npub mod infra;\n"),
(
"api.rs",
"use cfg_if::cfg_if;\n\
cfg_if! {\n\
if #[cfg(unix)] {\n\
pub fn leak() -> crate::infra::Secret { loop {} }\n\
}\n\
}\n",
),
("infra.rs", "pub struct Secret;\n"),
],
"crate::api",
&["crate::infra"],
)
.unwrap();
assert_eq!(
out,
vec!["crate::infra::Secret exposed by fn crate::api::leak"],
"the unqualified cfg_if! spelling must be transparent too: {out:?}"
);
}
#[test]
pub(super) fn two_cfg_if_arms_declaring_one_module_name_do_not_double_report() {
let out = findings(
"cfg-if-arm-twin-mod",
&[
(
"lib.rs",
"pub mod infra;\n\
cfg_if::cfg_if! {\n\
if #[cfg(unix)] {\n\
pub mod api;\n\
} else {\n\
pub mod api;\n\
}\n\
}\n",
),
(
"api.rs",
"pub fn leak() -> crate::infra::Secret { loop {} }\n",
),
("infra.rs", "pub struct Secret;\n"),
],
"crate::api",
&["crate::infra"],
)
.unwrap();
assert_eq!(
out,
vec!["crate::infra::Secret exposed by fn crate::api::leak"],
"two arms declaring the same module must dedup to one finding: {out:?}"
);
}
#[test]
pub(super) fn a_cfg_if_inside_an_impl_body_is_a_stated_bound() {
let out = findings(
"cfg-if-impl-body",
&[
("lib.rs", "pub mod api;\npub mod infra;\n"),
(
"api.rs",
"pub struct Api;\n\
impl Api {\n\
cfg_if::cfg_if! {\n\
if #[cfg(unix)] {\n\
pub fn leak() -> crate::infra::Secret { loop {} }\n\
}\n\
}\n\
}\n",
),
("infra.rs", "pub struct Secret;\n"),
],
"crate::api",
&["crate::infra"],
)
.unwrap();
assert!(
out.is_empty(),
"an impl-body cfg_if is a stated bound, not a claimed reaction: {out:?}"
);
}
#[test]
pub(super) fn an_absent_path_remap_target_inside_a_cfg_if_arm_is_tolerated() {
let out = findings(
"cfg-if-arm-absent-path",
&[
(
"lib.rs",
"pub mod api;\npub mod infra;\n\
cfg_if::cfg_if! {\n\
if #[cfg(windows)] {\n\
#[path = \"windows_impl.rs\"]\n\
pub mod imp;\n\
}\n\
}\n",
),
(
"api.rs",
"pub fn leak() -> crate::infra::Secret { loop {} }\n",
),
("infra.rs", "pub struct Secret;\n"),
],
"crate::api",
&["crate::infra"],
)
.unwrap();
assert_eq!(
out,
vec!["crate::infra::Secret exposed by fn crate::api::leak"],
"an absent unconditional #[path] target inside an arm must be tolerated: {out:?}"
);
}
#[test]
pub(super) fn two_crates_with_the_identical_async_exposure_boundary_stay_distinct_violations() {
fn tree_and_metadata(label: &str, package: &str) -> (TempSrcTree, Value) {
let tree = TempSrcTree::new(label);
tree.write_all(&[("lib.rs", "pub mod registry;\npub async fn register() {}\n")]);
let metadata = serde_json::json!({
"packages": [{
"name": package,
"dependencies": [],
"targets": [{ "kind": ["lib"], "src_path": tree.root().to_string_lossy().into_owned() }],
}],
});
(tree, metadata)
}
let (_alpha_tree, alpha_metadata) = tree_and_metadata("async-identity-alpha", "alpha");
let (_beta_tree, beta_metadata) = tree_and_metadata("async-identity-beta", "beta");
let combined_metadata = serde_json::json!({
"packages": [
alpha_metadata["packages"][0].clone(),
beta_metadata["packages"][0].clone(),
],
});
fn boundary_for(package: &str) -> AsyncExposureBoundary {
AsyncExposureBoundary::in_crate(package)
.module("crate")
.must_not_expose_async_fn()
.because("no async seam here")
}
let mut violations = Vec::new();
eval_into(
&combined_metadata,
&[boundary_for("alpha"), boundary_for("beta")],
check_async_exposure_boundary,
&mut violations,
)
.expect("both boundaries resolve");
let outcome = outcome_from(violations, 1, 1);
let report = match outcome {
Outcome::Violations(report) => report,
other => panic!("expected two violations, got {other:?}"),
};
assert_eq!(
report.violations.len(),
2,
"each crate's async-exposure violation must survive dedup: {:?}",
report.violations
);
let ids: std::collections::BTreeSet<_> = report.violations.iter().map(Violation::id).collect();
assert_eq!(
ids.len(),
2,
"identity must differ by crate, not collapse to one"
);
}
#[test]
pub(super) fn a_forbidden_type_in_an_extern_block_pub_fn_signature_is_observed() {
let out = semantic_findings(
"extern-block-fn-exposure",
&[
("lib.rs", "pub mod infra;\npub mod api;\n"),
("infra.rs", "pub struct Secret;\n"),
(
"api.rs",
"extern \"C\" {\n pub fn handle() -> crate::infra::Secret;\n}\n",
),
],
"crate::api",
&["crate::infra"],
false,
&[],
)
.unwrap();
assert_eq!(
out,
vec!["crate::infra::Secret exposed by fn crate::api::handle"]
);
}
#[test]
pub(super) fn a_forbidden_type_in_an_extern_block_pub_static_is_observed() {
let out = semantic_findings(
"extern-block-static-exposure",
&[
("lib.rs", "pub mod infra;\npub mod api;\n"),
("infra.rs", "pub struct Secret;\n"),
(
"api.rs",
"extern \"C\" {\n pub static S: crate::infra::Secret;\n}\n",
),
],
"crate::api",
&["crate::infra"],
false,
&[],
)
.unwrap();
assert_eq!(
out,
vec!["crate::infra::Secret exposed by static crate::api::S"]
);
}
#[test]
pub(super) fn a_non_pub_extern_block_item_is_not_observed() {
let out = semantic_findings(
"extern-block-private-not-observed",
&[
("lib.rs", "pub mod infra;\npub mod api;\n"),
("infra.rs", "pub struct Secret;\n"),
(
"api.rs",
"extern \"C\" {\n fn handle() -> crate::infra::Secret;\n static S: crate::infra::Secret;\n}\n",
),
],
"crate::api",
&["crate::infra"],
false,
&[],
)
.unwrap();
assert_eq!(out, Vec::<String>::new());
}
#[test]
pub(super) fn forbidden_marker_self_type_landing_reacts_when_the_forbidden_alias_is_declared_first()
{
let out = marker_findings(
"self-type-landing-cfg-forbidden-first",
&[
("lib.rs", "pub mod domain;\npub mod wire;\n"),
("domain.rs", "pub struct Order;\n"),
(
"wire.rs",
"#[cfg(unix)]\nuse crate::domain::Order as Y;\n#[cfg(not(unix))]\nuse crate::domain::NotOrder as Y;\ntype X = Y;\nimpl serde::Serialize for X {}\n",
),
],
"crate::domain",
&["serde::Serialize"],
)
.unwrap();
assert_eq!(
out,
["impl serde::Serialize for crate::wire::X in crate::wire"],
"the marker-acquisition REACTION now checks every landing candidate (X resolves to the \
governed crate::domain::Order under one cfg branch), even though the finding's OWNER \
label renders the self type as written (`X`), a separate, deliberate identity concern \
from the landing/gating check: {out:?}"
);
}
#[test]
pub(super) fn forbidden_marker_self_type_landing_reacts_when_the_forbidden_alias_is_declared_second()
{
let out = marker_findings(
"self-type-landing-cfg-forbidden-second",
&[
("lib.rs", "pub mod domain;\npub mod wire;\n"),
("domain.rs", "pub struct Order;\n"),
(
"wire.rs",
"#[cfg(not(unix))]\nuse crate::domain::NotOrder as Y;\n#[cfg(unix)]\nuse crate::domain::Order as Y;\ntype X = Y;\nimpl serde::Serialize for X {}\n",
),
],
"crate::domain",
&["serde::Serialize"],
)
.unwrap();
assert_eq!(
out,
["impl serde::Serialize for crate::wire::X in crate::wire"],
"the marker-acquisition REACTION now checks every landing candidate (X resolves to the \
governed crate::domain::Order under one cfg branch), even though the finding's OWNER \
label renders the self type as written (`X`), a separate, deliberate identity concern \
from the landing/gating check: {out:?}"
);
}
#[test]
pub(super) fn type_alias_exposure_reacts_when_the_forbidden_alias_is_declared_first() {
let out = semantic_findings(
"type-alias-cfg-forbidden-first",
&[
("lib.rs", "pub mod safe;\npub mod infra;\npub mod api;\n"),
("safe.rs", "pub struct Handle;\n"),
("infra.rs", "pub struct Secret;\n"),
(
"api.rs",
"#[cfg(unix)]\nuse crate::infra::Secret as Y;\n#[cfg(not(unix))]\nuse crate::safe::Handle as Y;\ntype X = Y;\npub fn leak() -> X { loop {} }\n",
),
],
"crate::api",
&["crate::infra"],
false,
&[],
)
.unwrap();
assert_eq!(
out,
vec!["crate::infra::Secret exposed by fn crate::api::leak"]
);
}
#[test]
pub(super) fn type_alias_exposure_reacts_when_the_forbidden_alias_is_declared_second() {
let out = semantic_findings(
"type-alias-cfg-forbidden-second",
&[
("lib.rs", "pub mod safe;\npub mod infra;\npub mod api;\n"),
("safe.rs", "pub struct Handle;\n"),
("infra.rs", "pub struct Secret;\n"),
(
"api.rs",
"#[cfg(not(unix))]\nuse crate::safe::Handle as Y;\n#[cfg(unix)]\nuse crate::infra::Secret as Y;\ntype X = Y;\npub fn leak() -> X { loop {} }\n",
),
],
"crate::api",
&["crate::infra"],
false,
&[],
)
.unwrap();
assert_eq!(
out,
vec!["crate::infra::Secret exposed by fn crate::api::leak"]
);
}
#[test]
pub(super) fn signature_coupling_reacts_through_a_cfg_attr_path_hidden_reexport() {
let out = semantic_findings(
"cfg-attr-exposure-reexport",
&[
(
"lib.rs",
"pub mod infra;\n#[cfg_attr(windows, path = \"weird.rs\")]\npub mod facade;\npub mod api;\n",
),
("infra.rs", "pub struct Secret;\n"),
("facade.rs", "pub use crate::infra::Secret;\n"),
(
"api.rs",
"pub fn leak() -> crate::facade::Secret { loop {} }\n",
),
],
"crate::api",
&["crate::infra"],
false,
&[],
)
.unwrap();
assert_eq!(
out,
vec!["crate::infra::Secret exposed by fn crate::api::leak"]
);
}
#[test]
pub(super) fn cfg_attr_wrapped_path_resolves_through_its_own_target_with_no_sibling_at_all() {
let out = semantic_findings(
"cfg-attr-lone-target-resolves",
&[
(
"lib.rs",
"pub mod infra;\n#[cfg_attr(windows, path = \"win.rs\")]\nmod foo;\n",
),
("infra.rs", "pub struct Secret;\n"),
(
"win.rs",
"pub fn leak() -> crate::infra::Secret { loop {} }\n",
),
],
"crate::foo",
&["crate::infra"],
false,
&[],
)
.unwrap();
assert_eq!(
out,
vec!["crate::infra::Secret exposed by fn crate::foo::leak"]
);
}
#[test]
pub(super) fn cfg_attr_wrapped_path_sibling_reacts_through_its_own_file_not_absorbed_by_a_sibling()
{
let out = semantic_findings(
"cfg-attr-sibling-anchor",
&[
("lib.rs", "pub mod infra;\n#[cfg(windows)]\n#[cfg_attr(target_arch = \"x86\", path = \"foo_x86.rs\")]\nmod foo;\n#[cfg(not(windows))]\nmod foo;\n"),
("infra.rs", "pub struct Secret;\n"),
("foo.rs", "pub fn safe() {}\n"),
(
"foo_x86.rs",
"pub fn leak() -> crate::infra::Secret { loop {} }\n",
),
],
"crate::foo",
&["crate::infra"],
false,
&[],
)
.unwrap();
assert_eq!(
out,
vec!["crate::infra::Secret exposed by fn crate::foo::leak"]
);
}
#[test]
pub(super) fn cfg_attr_wrapped_path_sibling_reacts_through_a_cfg_if_arm() {
let out = semantic_findings(
"cfg-attr-sibling-anchor-cfg-if",
&[
(
"lib.rs",
"pub mod infra;\ncfg_if::cfg_if! {\n if #[cfg(windows)] {\n #[cfg_attr(target_arch = \"x86\", path = \"foo_x86.rs\")]\n mod foo;\n } else {\n mod foo;\n }\n}\n",
),
("infra.rs", "pub struct Secret;\n"),
("foo.rs", "pub fn safe() {}\n"),
(
"foo_x86.rs",
"pub fn leak() -> crate::infra::Secret { loop {} }\n",
),
],
"crate::foo",
&["crate::infra"],
false,
&[],
)
.unwrap();
assert_eq!(
out,
vec!["crate::infra::Secret exposed by fn crate::foo::leak"]
);
}
#[test]
pub(super) fn cfg_attr_wrapped_path_with_no_sibling_and_no_backing_file_still_fails_loud() {
let err = semantic_findings(
"cfg-attr-lone-fails-loud",
&[(
"lib.rs",
"#[cfg_attr(windows, path = \"win.rs\")]\nmod foo;\n",
)],
"crate::foo",
&[],
false,
&[],
)
.unwrap_err();
assert!(
err.contains("not found") || err.contains("could not"),
"a lone unbacked cfg_attr(path) declaration must still fail loud: {err}"
);
}
#[test]
pub(super) fn stacked_cfg_attr_wrapped_path_attributes_are_all_read_not_only_the_first() {
let out = semantic_findings(
"stacked-cfg-attr-path",
&[
("lib.rs", "pub mod infra;\n#[cfg_attr(windows, path = \"win.rs\")]\n#[cfg_attr(target_os = \"macos\", path = \"mac.rs\")]\nmod foo;\n"),
("infra.rs", "pub struct Secret;\n"),
(
"mac.rs",
"pub fn leak() -> crate::infra::Secret { loop {} }\n",
),
],
"crate::foo",
&["crate::infra"],
false,
&[],
)
.unwrap();
assert_eq!(
out,
vec!["crate::infra::Secret exposed by fn crate::foo::leak"]
);
}
#[test]
pub(super) fn mutually_exclusive_cfg_gated_use_aliases_both_react() {
let out = semantic_findings(
"cfg-use-alias-merge-forbidden-first",
&[
("lib.rs", "pub mod safe;\npub mod infra;\npub mod api;\n"),
("safe.rs", "pub struct Handle;\n"),
("infra.rs", "pub struct Secret;\n"),
(
"api.rs",
"#[cfg(unix)]\nuse crate::infra::Secret as Handle;\n#[cfg(not(unix))]\nuse crate::safe::Handle;\npub fn leak() -> Handle { loop {} }\n",
),
],
"crate::api",
&["crate::infra"],
false,
&[],
)
.unwrap();
assert_eq!(
out,
vec!["crate::infra::Secret exposed by fn crate::api::leak"]
);
}
#[test]
pub(super) fn mutually_exclusive_cfg_gated_use_aliases_react_regardless_of_declaration_order() {
let out = semantic_findings(
"cfg-use-alias-merge-forbidden-second",
&[
("lib.rs", "pub mod safe;\npub mod infra;\npub mod api;\n"),
("safe.rs", "pub struct Handle;\n"),
("infra.rs", "pub struct Secret;\n"),
(
"api.rs",
"#[cfg(not(unix))]\nuse crate::safe::Handle;\n#[cfg(unix)]\nuse crate::infra::Secret as Handle;\npub fn leak() -> Handle { loop {} }\n",
),
],
"crate::api",
&["crate::infra"],
false,
&[],
)
.unwrap();
assert_eq!(
out,
vec!["crate::infra::Secret exposed by fn crate::api::leak"]
);
}
#[test]
pub(super) fn mutually_exclusive_cfg_if_use_aliases_both_react() {
let out = semantic_findings(
"cfg-if-use-alias-merge",
&[
("lib.rs", "pub mod safe;\npub mod infra;\npub mod api;\n"),
("safe.rs", "pub struct Handle;\n"),
("infra.rs", "pub struct Secret;\n"),
(
"api.rs",
"cfg_if::cfg_if! { if #[cfg(unix)] { use crate::infra::Secret as Handle; } else { use crate::safe::Handle; } }\npub fn leak() -> Handle { loop {} }\n",
),
],
"crate::api",
&["crate::infra"],
false,
&[],
)
.unwrap();
assert_eq!(
out,
vec!["crate::infra::Secret exposed by fn crate::api::leak"]
);
}
#[test]
pub(super) fn mutually_exclusive_reexport_targets_both_canonicalize_correctly() {
let out = semantic_findings(
"reexport-map-merge-forbidden-first",
&[
(
"lib.rs",
"pub mod safe;\npub mod infra;\npub mod api;\npub mod facade;\n",
),
("safe.rs", "pub struct Thing;\n"),
("infra.rs", "pub struct Secret;\n"),
(
"api.rs",
"#[cfg(unix)]\npub use crate::infra::Secret as Handle;\n#[cfg(not(unix))]\npub use crate::safe::Thing as Handle;\n",
),
("facade.rs", "pub fn f() -> crate::api::Handle { loop {} }\n"),
],
"crate::facade",
&["crate::infra"],
false,
&[],
)
.unwrap();
assert_eq!(
out,
vec!["crate::infra::Secret exposed by fn crate::facade::f"]
);
}
#[test]
pub(super) fn mutually_exclusive_reexport_targets_react_regardless_of_declaration_order() {
let out = semantic_findings(
"reexport-map-merge-forbidden-second",
&[
(
"lib.rs",
"pub mod safe;\npub mod infra;\npub mod api;\npub mod facade;\n",
),
("safe.rs", "pub struct Thing;\n"),
("infra.rs", "pub struct Secret;\n"),
(
"api.rs",
"#[cfg(not(unix))]\npub use crate::safe::Thing as Handle;\n#[cfg(unix)]\npub use crate::infra::Secret as Handle;\n",
),
("facade.rs", "pub fn f() -> crate::api::Handle { loop {} }\n"),
],
"crate::facade",
&["crate::infra"],
false,
&[],
)
.unwrap();
assert_eq!(
out,
vec!["crate::infra::Secret exposed by fn crate::facade::f"]
);
}
#[test]
pub(super) fn dyn_trait_operand_resolution_reacts_through_a_cfg_attr_path_hidden_reexport() {
let tree = TempSrcTree::new("dyn-trait-cfg-attr-path-reexport");
tree.write_all(&[
(
"lib.rs",
"pub mod infra;\n#[cfg_attr(windows, path = \"weird.rs\")]\npub mod facade;\npub mod api;\n",
),
("infra.rs", "pub trait Port {}\n"),
("facade.rs", "pub use crate::infra::Port;\n"),
(
"api.rs",
"pub fn f() -> Box<dyn crate::facade::Port> { loop {} }\n",
),
]);
let out = crate::dyn_trait::dyn_operand_module_findings(
tree.src(),
&tree.root(),
"crate::api",
&["crate::infra::Port".to_string()],
"x",
&[],
)
.unwrap();
assert_eq!(out.len(), 1, "{out:?}");
assert_eq!(
out[0].0,
crate::finding::SemanticFact::Exposed {
kind: crate::finding::ExposureKind::DynTrait,
subject: "dyn crate::facade::Port".to_string(),
seam: crate::finding::PublicSeam::FreeFn {
module: "crate::api".to_string(),
name: "f".to_string(),
},
}
);
}
#[test]
pub(super) fn dyn_trait_operand_resolution_reacts_regardless_of_cfg_gated_use_alias_order() {
let tree = TempSrcTree::new("dyn-trait-principal-cfg-use-merge");
tree.write_all(&[
("lib.rs", "pub mod safe;\npub mod infra;\npub mod api;\n"),
("safe.rs", "pub trait SafePort {}\n"),
("infra.rs", "pub trait Port {}\n"),
(
"api.rs",
"#[cfg(not(unix))]\nuse crate::safe::SafePort as P;\n#[cfg(unix)]\nuse crate::infra::Port as P;\npub fn f() -> Box<dyn P> { loop {} }\n",
),
]);
let out = crate::dyn_trait::dyn_operand_module_findings(
tree.src(),
&tree.root(),
"crate::api",
&["crate::infra::Port".to_string()],
"x",
&[],
)
.unwrap();
assert_eq!(out.len(), 1, "{out:?}");
assert_eq!(
out[0].0,
crate::finding::SemanticFact::Exposed {
kind: crate::finding::ExposureKind::DynTrait,
subject: "dyn P".to_string(),
seam: crate::finding::PublicSeam::FreeFn {
module: "crate::api".to_string(),
name: "f".to_string(),
},
}
);
}
#[test]
pub(super) fn impl_trait_operand_resolution_reacts_regardless_of_cfg_gated_use_alias_order() {
let tree = TempSrcTree::new("impl-trait-principal-cfg-use-merge");
tree.write_all(&[
("lib.rs", "pub mod safe;\npub mod infra;\npub mod api;\n"),
("safe.rs", "pub trait SafePort {}\n"),
("infra.rs", "pub trait Port {}\n"),
(
"api.rs",
"#[cfg(not(unix))]\nuse crate::safe::SafePort as P;\n#[cfg(unix)]\nuse crate::infra::Port as P;\npub fn f() -> impl P { loop {} }\n",
),
]);
let out = crate::impl_trait::impl_trait_operand_module_findings(
tree.src(),
&tree.root(),
"crate::api",
&["crate::infra::Port".to_string()],
"x",
&[],
)
.unwrap();
assert_eq!(out.len(), 1, "{out:?}");
assert_eq!(
out[0].0,
crate::finding::SemanticFact::Exposed {
kind: crate::finding::ExposureKind::ImplTrait,
subject: "impl P".to_string(),
seam: crate::finding::PublicSeam::FreeFn {
module: "crate::api".to_string(),
name: "f".to_string(),
},
}
);
}
#[test]
pub(super) fn dyn_trait_operand_resolution_reacts_regardless_of_cfg_gated_use_alias_order_reversed()
{
let tree = TempSrcTree::new("dyn-trait-principal-cfg-use-merge-reversed");
tree.write_all(&[
("lib.rs", "pub mod safe;\npub mod infra;\npub mod api;\n"),
("safe.rs", "pub trait SafePort {}\n"),
("infra.rs", "pub trait Port {}\n"),
(
"api.rs",
"#[cfg(unix)]\nuse crate::infra::Port as P;\n#[cfg(not(unix))]\nuse crate::safe::SafePort as P;\npub fn f() -> Box<dyn P> { loop {} }\n",
),
]);
let out = crate::dyn_trait::dyn_operand_module_findings(
tree.src(),
&tree.root(),
"crate::api",
&["crate::infra::Port".to_string()],
"x",
&[],
)
.unwrap();
assert_eq!(out.len(), 1, "{out:?}");
assert_eq!(
out[0].0,
crate::finding::SemanticFact::Exposed {
kind: crate::finding::ExposureKind::DynTrait,
subject: "dyn P".to_string(),
seam: crate::finding::PublicSeam::FreeFn {
module: "crate::api".to_string(),
name: "f".to_string(),
},
}
);
}
#[test]
pub(super) fn impl_trait_operand_resolution_reacts_regardless_of_cfg_gated_use_alias_order_reversed()
{
let tree = TempSrcTree::new("impl-trait-principal-cfg-use-merge-reversed");
tree.write_all(&[
("lib.rs", "pub mod safe;\npub mod infra;\npub mod api;\n"),
("safe.rs", "pub trait SafePort {}\n"),
("infra.rs", "pub trait Port {}\n"),
(
"api.rs",
"#[cfg(unix)]\nuse crate::infra::Port as P;\n#[cfg(not(unix))]\nuse crate::safe::SafePort as P;\npub fn f() -> impl P { loop {} }\n",
),
]);
let out = crate::impl_trait::impl_trait_operand_module_findings(
tree.src(),
&tree.root(),
"crate::api",
&["crate::infra::Port".to_string()],
"x",
&[],
)
.unwrap();
assert_eq!(out.len(), 1, "{out:?}");
assert_eq!(
out[0].0,
crate::finding::SemanticFact::Exposed {
kind: crate::finding::ExposureKind::ImplTrait,
subject: "impl P".to_string(),
seam: crate::finding::PublicSeam::FreeFn {
module: "crate::api".to_string(),
name: "f".to_string(),
},
}
);
}
#[test]
pub(super) fn signature_coupling_reacts_on_a_const_wrapped_inherent_impl() {
assert_eq!(
findings(
"body-nested-const-signature",
&[
("lib.rs", "pub mod infra;\npub mod api;\n"),
("infra.rs", "pub struct Db;\n"),
(
"api.rs",
"pub struct Svc;\nconst _: () = {\n impl Svc {\n pub fn leak(&self) -> crate::infra::Db { unimplemented!() }\n }\n};\n",
),
],
"crate::api",
&["crate::infra"],
)
.unwrap(),
["crate::infra::Db exposed by fn <crate::api::Svc>::leak"],
);
}
#[test]
pub(super) fn signature_coupling_reacts_on_a_fn_body_wrapped_inherent_impl() {
assert_eq!(
findings(
"body-nested-fnbody-signature",
&[
("lib.rs", "pub mod infra;\npub mod api;\n"),
("infra.rs", "pub struct Db;\n"),
(
"api.rs",
"pub struct Svc;\nfn _also() {\n impl Svc {\n pub fn leak(&self) -> crate::infra::Db { unimplemented!() }\n }\n}\n",
),
],
"crate::api",
&["crate::infra"],
)
.unwrap(),
["crate::infra::Db exposed by fn <crate::api::Svc>::leak"],
);
}
#[test]
pub(super) fn async_exposure_reacts_on_a_const_wrapped_inherent_impl() {
assert_eq!(
async_mod(
"body-nested-const-async",
"pub struct Svc;\nconst _: () = {\n impl Svc {\n pub async fn run(&self) {}\n }\n};\n",
)
.unwrap(),
["async fn <crate::m::Svc>::run(&self)"],
);
}
#[test]
pub(super) fn async_exposure_reacts_on_a_fn_body_wrapped_inherent_impl() {
assert_eq!(
async_mod(
"body-nested-fnbody-async",
"pub struct Svc;\nfn _also() {\n impl Svc {\n pub async fn run(&self) {}\n }\n}\n",
)
.unwrap(),
["async fn <crate::m::Svc>::run(&self)"],
);
}
#[test]
pub(super) fn dyn_trait_reacts_on_a_const_wrapped_inherent_impl() {
assert_eq!(
dyn_mod(
"body-nested-const-dyn",
"pub struct Svc;\nconst _: () = {\n impl Svc {\n pub fn dynamic(&self) -> Box<dyn crate::Port> { unimplemented!() }\n }\n};\n",
)
.unwrap(),
["dyn crate::Port exposed by fn <crate::m::Svc>::dynamic"],
);
}
#[test]
pub(super) fn dyn_trait_reacts_on_a_fn_body_wrapped_inherent_impl() {
assert_eq!(
dyn_mod(
"body-nested-fnbody-dyn",
"pub struct Svc;\nfn _also() {\n impl Svc {\n pub fn dynamic(&self) -> Box<dyn crate::Port> { unimplemented!() }\n }\n}\n",
)
.unwrap(),
["dyn crate::Port exposed by fn <crate::m::Svc>::dynamic"],
);
}
#[test]
pub(super) fn impl_trait_reacts_on_a_const_wrapped_inherent_impl() {
assert_eq!(
impl_trait_mod(
"body-nested-const-impltrait",
"pub struct Svc;\nconst _: () = {\n impl Svc {\n pub fn existential(&self) -> impl crate::Port { unimplemented!() }\n }\n};\n",
)
.unwrap(),
["impl crate::Port exposed by fn <crate::m::Svc>::existential"],
);
}
#[test]
pub(super) fn impl_trait_reacts_on_a_fn_body_wrapped_inherent_impl() {
assert_eq!(
impl_trait_mod(
"body-nested-fnbody-impltrait",
"pub struct Svc;\nfn _also() {\n impl Svc {\n pub fn existential(&self) -> impl crate::Port { unimplemented!() }\n }\n}\n",
)
.unwrap(),
["impl crate::Port exposed by fn <crate::m::Svc>::existential"],
);
}
#[test]
pub(super) fn trait_impl_locality_reacts_on_a_const_wrapped_trait_impl() {
let out = locality_findings(
"body-nested-const-locality",
&[
(
"lib.rs",
"pub mod command;\npub mod commands;\npub mod rogue;\n",
),
("command.rs", "pub trait Command { fn run(&self); }\n"),
(
"commands.rs",
"pub struct Ok1;\nimpl crate::command::Command for Ok1 { fn run(&self) {} }\n",
),
(
"rogue.rs",
"pub struct Rogue;\nconst _: () = {\n impl crate::command::Command for Rogue {\n fn run(&self) {}\n }\n};\n",
),
],
"crate::command::Command",
&["crate::commands"],
)
.unwrap();
assert_eq!(
out,
["crate::rogue (impl crate::command::Command for crate::rogue::Rogue)"],
);
}
#[test]
pub(super) fn trait_impl_locality_reacts_on_a_fn_body_wrapped_trait_impl() {
let out = locality_findings(
"body-nested-fnbody-locality",
&[
(
"lib.rs",
"pub mod command;\npub mod commands;\npub mod rogue;\n",
),
("command.rs", "pub trait Command { fn run(&self); }\n"),
(
"commands.rs",
"pub struct Ok1;\nimpl crate::command::Command for Ok1 { fn run(&self) {} }\n",
),
(
"rogue.rs",
"pub struct Rogue2;\nfn _also() {\n impl crate::command::Command for Rogue2 {\n fn run(&self) {}\n }\n}\n",
),
],
"crate::command::Command",
&["crate::commands"],
)
.unwrap();
assert_eq!(
out,
["crate::rogue (impl crate::command::Command for crate::rogue::Rogue2)"],
);
}
#[test]
pub(super) fn forbidden_marker_reacts_on_a_const_wrapped_hand_impl() {
let out = marker_findings(
"body-nested-const-marker",
&[
("lib.rs", "pub mod domain;\npub mod wire;\n"),
("domain.rs", "pub struct Order;\n"),
(
"wire.rs",
"const _: () = {\n impl serde::Serialize for crate::domain::Order {}\n};\n",
),
],
"crate::domain",
&["serde::Serialize"],
)
.unwrap();
assert_eq!(
out,
["impl serde::Serialize for crate::domain::Order in crate::wire"],
);
}
#[test]
pub(super) fn forbidden_marker_reacts_on_a_fn_body_wrapped_hand_impl() {
let out = marker_findings(
"body-nested-fnbody-marker",
&[
("lib.rs", "pub mod domain;\npub mod wire;\n"),
("domain.rs", "pub struct Order;\n"),
(
"wire.rs",
"fn _also() {\n impl serde::Serialize for crate::domain::Order {}\n}\n",
),
],
"crate::domain",
&["serde::Serialize"],
)
.unwrap();
assert_eq!(
out,
["impl serde::Serialize for crate::domain::Order in crate::wire"],
);
}
#[test]
pub(super) fn signature_coupling_control_the_identical_unwrapped_impl_also_reacts() {
assert_eq!(
findings(
"body-nested-control-unwrapped",
&[
("lib.rs", "pub mod infra;\npub mod api;\n"),
("infra.rs", "pub struct Db;\n"),
(
"api.rs",
"pub struct Svc;\nimpl Svc {\n pub fn leak(&self) -> crate::infra::Db { unimplemented!() }\n}\n",
),
],
"crate::api",
&["crate::infra"],
)
.unwrap(),
["crate::infra::Db exposed by fn <crate::api::Svc>::leak"],
);
}
#[test]
pub(super) fn a_plain_fn_directly_in_a_const_body_stays_a_stated_bound() {
assert_eq!(
findings(
"body-nested-bound-plain-fn",
&[
("lib.rs", "pub mod infra;\npub mod api;\n"),
("infra.rs", "pub struct Db;\n"),
(
"api.rs",
"const _: () = {\n pub fn also_hidden() -> crate::infra::Db { unimplemented!() }\n};\n",
),
],
"crate::api",
&["crate::infra"],
)
.unwrap(),
Vec::<String>::new(),
);
}
#[test]
pub(super) fn an_impl_nested_one_level_further_stays_a_stated_bound() {
assert_eq!(
findings(
"body-nested-bound-two-levels",
&[
("lib.rs", "pub mod infra;\npub mod api;\n"),
("infra.rs", "pub struct Db;\n"),
(
"api.rs",
"pub struct Svc;\nfn _also() {\n if true {\n impl Svc {\n pub fn leak(&self) -> crate::infra::Db { unimplemented!() }\n }\n }\n}\n",
),
],
"crate::api",
&["crate::infra"],
)
.unwrap(),
Vec::<String>::new(),
);
}
#[test]
pub(super) fn a_static_wrapped_impl_stays_a_stated_bound() {
assert_eq!(
findings(
"body-nested-bound-static",
&[
("lib.rs", "pub mod infra;\npub mod api;\n"),
("infra.rs", "pub struct Db;\n"),
(
"api.rs",
"pub struct Svc;\nstatic S: () = {\n impl Svc {\n pub fn leak(&self) -> crate::infra::Db { unimplemented!() }\n }\n};\n",
),
],
"crate::api",
&["crate::infra"],
)
.unwrap(),
Vec::<String>::new(),
);
}
#[test]
pub(super) fn a_struct_directly_in_a_const_body_stays_a_stated_bound() {
assert_eq!(
findings(
"body-nested-bound-struct",
&[
("lib.rs", "pub mod infra;\npub mod api;\n"),
("infra.rs", "pub struct Db;\n"),
(
"api.rs",
"const _: () = {\n pub struct AlsoHidden { pub field: crate::infra::Db }\n};\n",
),
],
"crate::api",
&["crate::infra"],
)
.unwrap(),
Vec::<String>::new(),
);
}
#[test]
pub(super) fn a_mod_directly_in_a_const_body_stays_a_stated_bound() {
assert_eq!(
findings(
"body-nested-bound-mod",
&[
("lib.rs", "pub mod infra;\npub mod api;\n"),
("infra.rs", "pub struct Db;\n"),
(
"api.rs",
"const _: () = {\n pub mod inner {\n pub struct AlsoHidden { pub field: crate::infra::Db }\n }\n};\n",
),
],
"crate::api",
&["crate::infra"],
)
.unwrap(),
Vec::<String>::new(),
);
}