Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update krates again #590

Merged
merged 2 commits into from
Jan 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 20 additions & 38 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions deny.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ deny = [
{ name = "libssh2-sys" },
# There is literally never a reason to use this
{ name = "cmake" },
# https://github.com/Byron/gitoxide/pull/1245
{ name = "windows", wrappers = ["gix-sec"] },
# Ditto
{ name = "windows" },
]
skip = [
# strum_macros + maybe-async
Expand Down
101 changes: 86 additions & 15 deletions src/bans.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,8 +214,61 @@ pub fn check(
sink.push(build_diags);
}

let (denied_ids, ban_wrappers): (Vec<_>, Vec<_>) =
denied.into_iter().map(|kb| (kb.id, kb.wrappers)).unzip();
struct BanWrappers {
map: std::collections::BTreeMap<usize, (usize, Vec<crate::Spanned<String>>)>,
hits: BitVec,
}

impl BanWrappers {
fn new(
mut map: std::collections::BTreeMap<usize, (usize, Vec<crate::Spanned<String>>)>,
) -> Self {
let hits = BitVec::repeat(
false,
map.values_mut().fold(0, |sum, v| {
v.0 = sum;
sum + v.1.len()
}),
);

Self { map, hits }
}

#[inline]
fn has_wrappers(&self, i: usize) -> bool {
self.map.contains_key(&i)
}

#[inline]
fn check(&mut self, i: usize, name: &str) -> Option<crate::diag::Span> {
let (offset, wrappers) = &self.map[&i];
if let Some(pos) = wrappers.iter().position(|wrapper| wrapper.value == name) {
self.hits.set(*offset + pos, true);
Some(wrappers[pos].span.clone())
} else {
None
}
}
}

let (denied_ids, mut ban_wrappers) = {
let mut bw = std::collections::BTreeMap::new();

(
denied
.into_iter()
.enumerate()
.map(|(i, kb)| {
if let Some(wrappers) = kb.wrappers.filter(|v| !v.is_empty()) {
bw.insert(i, (0, wrappers));
}

kb.id
})
.collect::<Vec<_>>(),
BanWrappers::new(bw),
)
};

let (feature_ids, features): (Vec<_>, Vec<_>) =
features.into_iter().map(|cf| (cf.id, cf.features)).unzip();
Expand Down Expand Up @@ -390,6 +443,7 @@ pub fn check(
let (_, build_packs) = rayon::join(
|| {
let last = ctx.krates.len() - 1;

for (i, krate) in ctx.krates.krates().enumerate() {
let mut pack = Pack::with_kid(Check::Bans, krate.id.clone());

Expand All @@ -401,24 +455,25 @@ pub fn check(
span: rm.id.span.clone(),
};

// The crate is banned, but it might have be allowed if it's wrapped
// by one or more particular crates
let is_allowed_by_wrapper = if let Some(wrappers) =
ban_wrappers.get(rm.index).and_then(|bw| bw.as_ref())
{
// The crate is banned, but it might be allowed if it's
// wrapped by one or more particular crates
let is_allowed_by_wrapper = if ban_wrappers.has_wrappers(rm.index) {
let nid = ctx.krates.nid_for_kid(&krate.id).unwrap();

// Ensure that every single crate that has a direct dependency
// on the banned crate is an allowed wrapper
ctx.krates.direct_dependents(nid).into_iter().all(|src| {
// on the banned crate is an allowed wrapper, note we
// check every one even after a failure so we don't get
// extra warnings about unmatched wrappers
let mut all = true;
for src in ctx.krates.direct_dependents(nid) {
let (diag, is_allowed): (Diag, _) =
match wrappers.iter().find(|aw| aw.value == src.krate.name) {
Some(aw) => (
match ban_wrappers.check(rm.index, &src.krate.name) {
Some(span) => (
diags::BannedAllowedByWrapper {
ban_cfg: ban_cfg.clone(),
ban_exception_cfg: CfgCoord {
file: file_id,
span: aw.span.clone(),
span,
},
banned_krate: krate,
wrapper_krate: src.krate,
Expand All @@ -438,8 +493,10 @@ pub fn check(
};

pack.push(diag);
is_allowed
})
all = all && is_allowed;
}

all
} else {
false
};
Expand Down Expand Up @@ -846,7 +903,7 @@ pub fn check(
for skip in skip_hit
.into_iter()
.zip(skipped.into_iter())
.filter_map(|(hit, skip)| if !hit { Some(skip) } else { None })
.filter_map(|(hit, skip)| (!hit).then_some(skip))
{
pack.push(diags::UnmatchedSkip {
skip_cfg: CfgCoord {
Expand All @@ -857,6 +914,20 @@ pub fn check(
});
}

for wrapper in ban_wrappers
.hits
.into_iter()
.zip(ban_wrappers.map.into_values().flat_map(|(_, w)| w))
.filter_map(|(hit, wrapper)| (!hit).then_some(wrapper))
{
pack.push(diags::UnusedWrapper {
wrapper_cfg: CfgCoord {
file: file_id,
span: wrapper.span,
},
});
}

sink.push(pack);
}

Expand Down
18 changes: 18 additions & 0 deletions src/bans/diags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ pub enum Code {
UnmatchedBypass,
UnmatchedPathBypass,
UnmatchedGlob,
UnusedWrapper,
}

impl From<Code> for String {
Expand Down Expand Up @@ -214,6 +215,23 @@ impl<'a> From<UnmatchedSkip<'a>> for Diag {
}
}

pub(crate) struct UnusedWrapper {
pub(crate) wrapper_cfg: CfgCoord,
}

impl From<UnusedWrapper> for Diag {
fn from(us: UnusedWrapper) -> Self {
Diagnostic::new(Severity::Warning)
.with_message("wrapper for banned crate was not enountered")
.with_code(Code::UnusedWrapper)
.with_labels(vec![us
.wrapper_cfg
.into_label()
.with_message("unmatched wrapper")])
.into()
}
}

pub(crate) struct BannedAllowedByWrapper<'a> {
pub(crate) ban_cfg: CfgCoord,
pub(crate) banned_krate: &'a Krate,
Expand Down
15 changes: 15 additions & 0 deletions tests/bans.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,21 @@ wrappers = ["safe-wrapper"]
insta::assert_json_snapshot!(diags);
}

#[test]
fn warns_on_unused_wrappers() {
let diags = gather_bans(
func_name!(),
KrateGather::new("allow_wrappers/maincrate"),
r#"
[[deny]]
name = "dangerous-dep"
wrappers = ["safe-wrapper", "other-crate"]
"#,
);

insta::assert_json_snapshot!(diags);
}

#[test]
fn disallows_denied() {
let diags = gather_bans(
Expand Down
Loading