diff --git a/Cargo.lock b/Cargo.lock index 5d33ba3a..77871b1a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -202,6 +202,7 @@ dependencies = [ "insta", "petgraph", "pico-args", + "rand", "regex", "rstest", "semver 0.10.0", diff --git a/cargo-geiger-serde/src/lib.rs b/cargo-geiger-serde/src/lib.rs index c9d29867..4556106d 100644 --- a/cargo-geiger-serde/src/lib.rs +++ b/cargo-geiger-serde/src/lib.rs @@ -12,8 +12,7 @@ mod source; pub use package_id::PackageId; pub use report::{ - Count, CounterBlock, DependencyKind, PackageInfo, QuickReportEntry, QuickSafetyReport, - ReportEntry, SafetyReport, UnsafeInfo, + Count, CounterBlock, DependencyKind, PackageInfo, QuickReportEntry, + QuickSafetyReport, ReportEntry, SafetyReport, UnsafeInfo, }; pub use source::Source; - diff --git a/cargo-geiger-serde/src/package_id.rs b/cargo-geiger-serde/src/package_id.rs index d79e5558..4f838dd0 100644 --- a/cargo-geiger-serde/src/package_id.rs +++ b/cargo-geiger-serde/src/package_id.rs @@ -3,7 +3,9 @@ use semver::Version; use serde::{Deserialize, Serialize}; /// Identifies a package in the dependency tree -#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] +#[derive( + Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize, +)] pub struct PackageId { /// Package name pub name: String, diff --git a/cargo-geiger-serde/src/report.rs b/cargo-geiger-serde/src/report.rs index 70ddb1a0..fa118df3 100644 --- a/cargo-geiger-serde/src/report.rs +++ b/cargo-geiger-serde/src/report.rs @@ -193,14 +193,9 @@ impl Entry for QuickReportEntry { mod entry_serde { use crate::PackageId; use serde::{ - ser::SerializeSeq, - Deserialize, Deserializer, Serialize, Serializer, - }; - use std::{ - collections::HashMap, - fmt, - marker::PhantomData, + ser::SerializeSeq, Deserialize, Deserializer, Serialize, Serializer, }; + use std::{collections::HashMap, fmt, marker::PhantomData}; pub(super) fn serialize( map: &HashMap, @@ -219,7 +214,9 @@ mod entry_serde { seq.end() } - pub(super) fn deserialize<'de, T, D>(deserializer: D) -> Result, D::Error> + pub(super) fn deserialize<'de, T, D>( + deserializer: D, + ) -> Result, D::Error> where T: Deserialize<'de> + super::Entry, D: Deserializer<'de>, @@ -253,10 +250,7 @@ mod entry_serde { } mod set_serde { - use serde::{ - ser::SerializeSeq, - Serialize, Serializer, - }; + use serde::{ser::SerializeSeq, Serialize, Serializer}; use std::collections::HashSet; pub(super) fn serialize( diff --git a/cargo-geiger-serde/src/source.rs b/cargo-geiger-serde/src/source.rs index 10df0596..bfd99a54 100644 --- a/cargo-geiger-serde/src/source.rs +++ b/cargo-geiger-serde/src/source.rs @@ -2,15 +2,11 @@ use serde::{Deserialize, Serialize}; use url::Url; /// Source of a package (where it is fetched from) -#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] +#[derive( + Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize, +)] pub enum Source { - Git { - url: Url, - rev: String, - }, - Registry { - name: String, - url: Url, - }, + Git { url: Url, rev: String }, + Registry { name: String, url: Url }, Path(Url), } diff --git a/cargo-geiger/Cargo.toml b/cargo-geiger/Cargo.toml index 16df6f5d..0f7766ac 100644 --- a/cargo-geiger/Cargo.toml +++ b/cargo-geiger/Cargo.toml @@ -39,6 +39,7 @@ assert_cmd = "1.0.1" better-panic = "0.2.0" fs_extra = "1.2.0" insta = "0.16.1" +rand = "0.7.3" regex = "1.3.9" rstest = "0.6.4" semver = "0.10.0" diff --git a/cargo-geiger/src/cli.rs b/cargo-geiger/src/cli.rs index 8e85e01f..222ceaaf 100644 --- a/cargo-geiger/src/cli.rs +++ b/cargo-geiger/src/cli.rs @@ -107,7 +107,6 @@ pub fn resolve<'a, 'cfg>( #[cfg(test)] mod cli_tests { use super::*; - use rstest::*; #[rstest] @@ -179,4 +178,28 @@ mod cli_tests { assert_eq!(package.package_id().name(), "cargo-geiger"); } + + #[rstest] + fn resolve_test() { + let config = Config::default().unwrap(); + let manifest_path: Option = None; + let workspace = get_workspace(&config, manifest_path).unwrap(); + let package = workspace.current().unwrap(); + let mut registry = get_registry(&config, &package).unwrap(); + + let features: Vec = vec![]; + let all_features = false; + let no_default_features = false; + + let resolve_cargo_result = resolve( + package.package_id(), + &mut registry, + &workspace, + &features, + all_features, + no_default_features, + ); + + assert!(resolve_cargo_result.is_ok()); + } } diff --git a/cargo-geiger/src/format/print_config.rs b/cargo-geiger/src/format/print_config.rs index 88a22d8d..6d719400 100644 --- a/cargo-geiger/src/format/print_config.rs +++ b/cargo-geiger/src/format/print_config.rs @@ -115,18 +115,12 @@ mod print_config_tests { #[rstest( input_invert_bool, expected_edge_direction, - case( - true, - EdgeDirection::Incoming - ), - case( - false, - EdgeDirection::Outgoing - ) + case(true, EdgeDirection::Incoming), + case(false, EdgeDirection::Outgoing) )] fn print_config_new_test_invert( input_invert_bool: bool, - expected_edge_direction: EdgeDirection + expected_edge_direction: EdgeDirection, ) { let mut args = create_args(); args.invert = input_invert_bool; @@ -143,18 +137,12 @@ mod print_config_tests { #[rstest( input_include_tests_bool, expected_include_tests, - case( - true, - IncludeTests::Yes - ), - case( - false, - IncludeTests::No - ), + case(true, IncludeTests::Yes), + case(false, IncludeTests::No) )] fn print_config_new_test_include_tests( input_include_tests_bool: bool, - expected_include_tests: IncludeTests + expected_include_tests: IncludeTests, ) { let mut args = create_args(); args.include_tests = input_include_tests_bool; @@ -172,31 +160,15 @@ mod print_config_tests { input_prefix_depth_bool, input_no_indent_bool, expected_output_prefix, - case( - true, - false, - Prefix::Depth, - ), - case( - true, - false, - Prefix::Depth, - ), - case( - false, - true, - Prefix::None, - ), - case( - false, - false, - Prefix::Indent, - ), + case(true, false, Prefix::Depth,), + case(true, false, Prefix::Depth,), + case(false, true, Prefix::None,), + case(false, false, Prefix::Indent,) )] fn print_config_new_test_prefix( input_prefix_depth_bool: bool, input_no_indent_bool: bool, - expected_output_prefix: Prefix + expected_output_prefix: Prefix, ) { let mut args = create_args(); args.prefix_depth = input_prefix_depth_bool; @@ -205,27 +177,15 @@ mod print_config_tests { let print_config_result = PrintConfig::new(&args); assert!(print_config_result.is_ok()); - assert_eq!( - print_config_result.unwrap().prefix, - expected_output_prefix - ); + assert_eq!(print_config_result.unwrap().prefix, expected_output_prefix); } #[rstest( input_verbosity_u32, expected_verbosity, - case( - 0, - Verbosity::Normal - ), - case( - 1, - Verbosity::Verbose - ), - case( - 1, - Verbosity::Verbose - ) + case(0, Verbosity::Normal), + case(1, Verbosity::Verbose), + case(1, Verbosity::Verbose) )] fn print_config_new_test_verbosity( input_verbosity_u32: u32, @@ -237,10 +197,7 @@ mod print_config_tests { let print_config_result = PrintConfig::new(&args); assert!(print_config_result.is_ok()); - assert_eq!( - print_config_result.unwrap().verbosity, - expected_verbosity - ); + assert_eq!(print_config_result.unwrap().verbosity, expected_verbosity); } #[rstest( @@ -266,16 +223,13 @@ mod print_config_tests { let string_value = String::from("string_value"); assert_eq!( - colorize( - string_value, - &input_crate_detection_status - ), + colorize(string_value, &input_crate_detection_status), expected_colorized_string ); } fn create_args() -> Args { - Args{ + Args { all: false, all_deps: false, all_features: false, @@ -303,7 +257,7 @@ mod print_config_tests { unstable_flags: vec![], verbose: 0, version: false, - output_format: None + output_format: None, } } } diff --git a/cargo-geiger/src/graph.rs b/cargo-geiger/src/graph.rs index 0c12c428..efb640ab 100644 --- a/cargo-geiger/src/graph.rs +++ b/cargo-geiger/src/graph.rs @@ -254,9 +254,7 @@ mod graph_tests { let result = build_graph_prerequisites(&args, &config_host); assert!(result.is_ok()); - let (extra_deps, _) = result.unwrap(); - assert_eq!(extra_deps, expected_extra_deps); } @@ -294,7 +292,7 @@ mod graph_tests { } fn create_args() -> Args { - Args{ + Args { all: false, all_deps: false, all_features: false, @@ -322,7 +320,7 @@ mod graph_tests { unstable_flags: vec![], verbose: 0, version: false, - output_format: None + output_format: None, } } } diff --git a/cargo-geiger/src/rs_file.rs b/cargo-geiger/src/rs_file.rs index f79db969..a6222c53 100644 --- a/cargo-geiger/src/rs_file.rs +++ b/cargo-geiger/src/rs_file.rs @@ -23,15 +23,15 @@ use walkdir::{DirEntry, WalkDir}; /// The wrapped PathBufs are canonicalized. #[derive(Debug, PartialEq)] pub enum RsFile { - /// Library entry point source file, usually src/lib.rs - LibRoot(PathBuf), - /// Executable entry point source file, usually src/main.rs BinRoot(PathBuf), /// Not sure if this is relevant but let's be conservative for now. CustomBuildRoot(PathBuf), + /// Library entry point source file, usually src/lib.rs + LibRoot(PathBuf), + /// All other .rs files. Other(PathBuf), } @@ -97,6 +97,15 @@ pub fn into_rs_code_file(kind: &TargetKind, path: PathBuf) -> RsFile { } } +pub fn into_is_entry_point_and_path_buf(rs_file: RsFile) -> (bool, PathBuf) { + match rs_file { + RsFile::BinRoot(pb) => (true, pb), + RsFile::CustomBuildRoot(pb) => (true, pb), + RsFile::LibRoot(pb) => (true, pb), + RsFile::Other(pb) => (false, pb), + } +} + pub fn is_file_with_ext(entry: &DirEntry, file_ext: &str) -> bool { if !entry.file_type().is_file() { return false; @@ -307,7 +316,7 @@ mod rs_file_tests { )] fn into_rs_code_file_test( input_target_kind: TargetKind, - expected_rs_file: RsFile + expected_rs_file: RsFile, ) { let path_buf = Path::new("test_path.ext").to_path_buf(); @@ -317,6 +326,24 @@ mod rs_file_tests { ); } + #[rstest( + input_rs_file, + expected_is_entry_point, + case(RsFile::BinRoot(PathBuf::from("test.txt")), true), + case(RsFile::CustomBuildRoot(PathBuf::from("test.txt")), true), + case(RsFile::LibRoot(PathBuf::from("test.txt")), true), + case(RsFile::Other(PathBuf::from("test.txt")), false) + )] + fn into_is_entry_point_and_path_buf_test( + input_rs_file: RsFile, + expected_is_entry_point: bool, + ) { + let (is_entry_point, _path_buf) = + into_is_entry_point_and_path_buf(input_rs_file); + assert_eq!(is_entry_point, expected_is_entry_point); + assert_eq!(_path_buf, PathBuf::from("test.txt")); + } + #[rstest] fn is_file_with_ext_test() { let config = Config::default().unwrap(); diff --git a/cargo-geiger/src/scan.rs b/cargo-geiger/src/scan.rs index baead95a..5459b3db 100644 --- a/cargo-geiger/src/scan.rs +++ b/cargo-geiger/src/scan.rs @@ -10,10 +10,12 @@ use crate::rs_file::RsFileMetricsWrapper; use default::scan_unsafe; use forbid::scan_forbid_unsafe; -use cargo::core::{PackageId, PackageSet, Workspace}; use cargo::core::dependency::DepKind; +use cargo::core::{PackageId, PackageSet, Workspace}; use cargo::{CliResult, Config}; -use cargo_geiger_serde::{CounterBlock, DependencyKind, PackageInfo, UnsafeInfo}; +use cargo_geiger_serde::{ + CounterBlock, DependencyKind, PackageInfo, UnsafeInfo, +}; use petgraph::visit::EdgeRef; use std::collections::{HashMap, HashSet}; use std::path::PathBuf; @@ -32,13 +34,13 @@ pub struct PackageMetrics { } pub enum ScanMode { - // The default scan mode, scan every .rs file. - Full, - // An optimization to allow skipping everything except the entry points. // This is only useful for the "--forbid-only" mode since that mode only // depends on entry point .rs files. EntryPointsOnly, + + // The default scan mode, scan every .rs file. + Full, } pub struct ScanParameters<'a> { @@ -172,7 +174,10 @@ fn package_metrics<'a>( indices.push(dep_index); } let dep = from_cargo_package_id(graph.graph[dep_index].id); - package.add_dependency(dep, from_cargo_dependency_kind(*edge.weight())); + package.add_dependency( + dep, + from_cargo_dependency_kind(*edge.weight()), + ); } match geiger_context.package_id_to_metrics.get(&id) { Some(m) => Some((package, Some(m))), @@ -191,8 +196,11 @@ fn from_cargo_package_id(id: PackageId) -> cargo_geiger_serde::PackageId { let source_url = if source_url.scheme() == "file" { match source_url.to_file_path() { Ok(p) => { - let p = p.canonicalize().expect("A package source path could not be canonicalized"); - Url::from_file_path(p).expect("A URL could not be created from a file path") + let p = p + .canonicalize() + .expect("A package source path could not be canonicalized"); + Url::from_file_path(p) + .expect("A URL could not be created from a file path") } Err(_) => source_url.clone(), } @@ -202,7 +210,10 @@ fn from_cargo_package_id(id: PackageId) -> cargo_geiger_serde::PackageId { let source = if source.is_git() { cargo_geiger_serde::Source::Git { url: source_url, - rev: source.precise().expect("Git revision should be known").to_string(), + rev: source + .precise() + .expect("Git revision should be known") + .to_string(), } } else if source.is_path() { cargo_geiger_serde::Source::Path(source_url) @@ -233,10 +244,7 @@ fn from_cargo_dependency_kind(kind: DepKind) -> DependencyKind { mod scan_tests { use super::*; - use crate::{ - rs_file::RsFileMetricsWrapper, - scan::PackageMetrics, - }; + use crate::{rs_file::RsFileMetricsWrapper, scan::PackageMetrics}; use cargo_geiger_serde::{Count, UnsafeInfo}; use rstest::*; diff --git a/cargo-geiger/src/scan/default.rs b/cargo-geiger/src/scan/default.rs index 35774324..4fc43b8d 100644 --- a/cargo-geiger/src/scan/default.rs +++ b/cargo-geiger/src/scan/default.rs @@ -115,7 +115,7 @@ fn scan( fn scan_to_report( workspace: &Workspace, packages: &PackageSet, - root_pack_id: PackageId, + root_package_id: PackageId, graph: &Graph, scan_parameters: &ScanParameters, output_format: OutputFormat, @@ -125,22 +125,28 @@ fn scan_to_report( geiger_context, } = scan(workspace, packages, scan_parameters)?; let mut report = SafetyReport::default(); - for (package, pack_metrics) in - package_metrics(&geiger_context, graph, root_pack_id) + for (package, package_metrics_option) in + package_metrics(&geiger_context, graph, root_package_id) { - let pack_metrics = match pack_metrics { + let package_metrics = match package_metrics_option { Some(m) => m, None => { report.packages_without_metrics.insert(package.id); continue; } }; - let unsafety = unsafe_stats(pack_metrics, &rs_files_used); - let entry = ReportEntry { package, unsafety }; + let unsafe_info = unsafe_stats(package_metrics, &rs_files_used); + let entry = ReportEntry { + package, + unsafety: unsafe_info, + }; report.packages.insert(entry.package.id.clone(), entry); } report.used_but_not_scanned_files = - list_files_used_but_not_scanned(&geiger_context, &rs_files_used).into_iter().collect(); + list_files_used_but_not_scanned(&geiger_context, &rs_files_used) + .into_iter() + .collect(); + let s = match output_format { OutputFormat::Json => serde_json::to_string(&report).unwrap(), }; @@ -148,29 +154,55 @@ fn scan_to_report( Ok(()) } -#[cfg(tests)] +#[cfg(test)] mod default_tests { use super::*; use crate::format::Charset; - use rstest::*; - #[rstest] - fn build_compile_options_test() { - let args_all_features = rand::random(); - let args_features = Some(String::from("unit test features")); - let args_no_default_features = rand::random(); + #[rstest( + input_features, + expected_compile_features, + case( + Some(String::from("unit test features")), + vec!["unit", "test", "features"], + ), + case( + Some(String::from("")), + vec![""], + ) + )] + fn build_compile_options_test( + input_features: Option, + expected_compile_features: Vec<&str>, + ) { + let mut args = create_args(); + args.all_features = rand::random(); + args.features = input_features; + args.no_default_features = rand::random(); + + let config = Config::default().unwrap(); + let compile_options = build_compile_options(&args, &config); + + assert_eq!(compile_options.all_features, args.all_features); + assert_eq!(compile_options.features, expected_compile_features); + assert_eq!( + compile_options.no_default_features, + args.no_default_features + ); + } - let args = Args { + fn create_args() -> Args { + Args { all: false, all_deps: false, - all_features: args_all_features, + all_features: false, all_targets: false, build_deps: false, charset: Charset::Utf8, color: None, dev_deps: false, - features: args_features, + features: None, forbid_only: false, format: "".to_string(), frozen: false, @@ -179,7 +211,7 @@ mod default_tests { invert: false, locked: false, manifest_path: None, - no_default_features: args_no_default_features, + no_default_features: false, no_indent: false, offline: false, package: None, @@ -190,26 +222,6 @@ mod default_tests { verbose: 0, version: false, output_format: None, - }; - - let config = Config::default().unwrap(); - - let compile_options = build_compile_options(&args, &config); - - assert_eq!(compile_options.all_features, args_all_features); - assert_eq!(compile_options.features, vec!["unit", "test", "features"]); - assert_eq!( - compile_options.no_default_features, - args_no_default_features - ); - } - - #[rstest] - fn construct_scan_mode_default_output_key_lines_test() { - let emoji_symbols = EmojiSymbols::new(Charset::Utf8); - let output_key_lines = - construct_scan_mode_default_output_key_lines(&emoji_symbols); - - assert_eq!(output_key_lines.len(), 12); + } } } diff --git a/cargo-geiger/src/scan/default/table.rs b/cargo-geiger/src/scan/default/table.rs index 1c329129..e206c0aa 100644 --- a/cargo-geiger/src/scan/default/table.rs +++ b/cargo-geiger/src/scan/default/table.rs @@ -153,4 +153,4 @@ fn construct_key_lines(emoji_symbols: &EmojiSymbols) -> Vec { output_key_lines.push(String::new()); output_key_lines -} \ No newline at end of file +} diff --git a/cargo-geiger/src/scan/find.rs b/cargo-geiger/src/scan/find.rs index b3f9f8a8..b837007f 100644 --- a/cargo-geiger/src/scan/find.rs +++ b/cargo-geiger/src/scan/find.rs @@ -1,6 +1,7 @@ use crate::format::print_config::PrintConfig; use crate::rs_file::{ - into_rs_code_file, is_file_with_ext, RsFile, RsFileMetricsWrapper, + into_is_entry_point_and_path_buf, into_rs_code_file, is_file_with_ext, + RsFile, RsFileMetricsWrapper, }; use crate::scan::PackageMetrics; @@ -10,7 +11,7 @@ use cargo::core::package::PackageSet; use cargo::core::{Package, PackageId}; use cargo::util::CargoResult; use cargo::{CliError, Config}; -use geiger::{find_unsafe_in_file, IncludeTests}; +use geiger::{find_unsafe_in_file, IncludeTests, RsFileMetrics, ScanFileError}; use std::collections::HashMap; use std::path::Path; use std::path::PathBuf; @@ -36,7 +37,7 @@ pub fn find_unsafe( } fn find_unsafe_in_packages( - packs: &PackageSet, + packages: &PackageSet, allow_partial_results: bool, include_tests: IncludeTests, mode: ScanMode, @@ -45,48 +46,42 @@ fn find_unsafe_in_packages( where F: FnMut(usize, usize) -> CargoResult<()>, { - let mut pack_id_to_metrics = HashMap::new(); - let packs = packs.get_many(packs.package_ids()).unwrap(); - let pack_code_files: Vec<_> = find_rs_files_in_packages(&packs).collect(); - let pack_code_file_count = pack_code_files.len(); - for (i, (pack_id, rs_code_file)) in pack_code_files.into_iter().enumerate() + let mut package_id_to_metrics = HashMap::new(); + let package_ids = packages.get_many(packages.package_ids()).unwrap(); + let package_code_files: Vec<_> = + find_rs_files_in_packages(&package_ids).collect(); + let package_code_file_count = package_code_files.len(); + for (i, (package_id, rs_code_file)) in + package_code_files.into_iter().enumerate() { - let (is_entry_point, p) = match rs_code_file { - RsFile::LibRoot(pb) => (true, pb), - RsFile::BinRoot(pb) => (true, pb), - RsFile::CustomBuildRoot(pb) => (true, pb), - RsFile::Other(pb) => (false, pb), - }; + let (is_entry_point, path_buf) = + into_is_entry_point_and_path_buf(rs_code_file); if let (false, ScanMode::EntryPointsOnly) = (is_entry_point, &mode) { continue; } - match find_unsafe_in_file(&p, include_tests) { - Err(e) => { - if allow_partial_results { - eprintln!( - "Failed to parse file: {}, {:?} ", - &p.display(), - e - ); - } else { - panic!("Failed to parse file: {}, {:?} ", &p.display(), e); - } + match find_unsafe_in_file(&path_buf, include_tests) { + Err(error) => { + handle_unsafe_in_file_error( + allow_partial_results, + error, + &path_buf, + ); } - Ok(file_metrics) => { - let package_metrics = pack_id_to_metrics - .entry(pack_id) - .or_insert_with(PackageMetrics::default); - let wrapper = package_metrics - .rs_path_to_metrics - .entry(p) - .or_insert_with(RsFileMetricsWrapper::default); - wrapper.metrics = file_metrics; - wrapper.is_crate_entry_point = is_entry_point; + Ok(rs_file_metrics) => { + update_package_id_to_metrics_with_rs_file_metrics( + is_entry_point, + package_id, + &mut package_id_to_metrics, + path_buf, + rs_file_metrics, + ); } } - let _ = progress_step(i, pack_code_file_count); + let _ = progress_step(i, package_code_file_count); + } + GeigerContext { + package_id_to_metrics, } - GeigerContext { package_id_to_metrics: pack_id_to_metrics } } fn find_rs_files_in_dir(dir: &Path) -> impl Iterator { @@ -105,11 +100,11 @@ fn find_rs_files_in_dir(dir: &Path) -> impl Iterator { }) } -fn find_rs_files_in_package(pack: &Package) -> Vec { +fn find_rs_files_in_package(package: &Package) -> Vec { // Find all build target entry point source files. let mut canon_targets = HashMap::new(); - for t in pack.targets() { - let path = t.src_path().path(); + for target in package.targets() { + let path = target.src_path().path(); let path = match path { None => continue, Some(p) => p, @@ -123,28 +118,213 @@ fn find_rs_files_in_package(pack: &Package) -> Vec { .canonicalize() // will Err on non-existing paths. .expect("canonicalize for build target path failed."); // FIXME let targets = canon_targets.entry(canon).or_insert_with(Vec::new); - targets.push(t); + targets.push(target); } - let mut out = Vec::new(); - for p in find_rs_files_in_dir(pack.root()) { - if !canon_targets.contains_key(&p) { - out.push(RsFile::Other(p)); + let mut rs_files = Vec::new(); + for path_bufs in find_rs_files_in_dir(package.root()) { + if !canon_targets.contains_key(&path_bufs) { + rs_files.push(RsFile::Other(path_bufs)); } } - for (k, v) in canon_targets.into_iter() { - for target in v { - out.push(into_rs_code_file(target.kind(), k.clone())); + for (path_buf, targets) in canon_targets.into_iter() { + for target in targets { + rs_files.push(into_rs_code_file(target.kind(), path_buf.clone())); } } - out + rs_files } fn find_rs_files_in_packages<'a>( - packs: &'a [&Package], + packages: &'a [&Package], ) -> impl Iterator + 'a { - packs.iter().flat_map(|pack| { - find_rs_files_in_package(pack) + packages.iter().flat_map(|package| { + find_rs_files_in_package(package) .into_iter() - .map(move |path| (pack.package_id(), path)) + .map(move |p| (package.package_id(), p)) }) } + +fn handle_unsafe_in_file_error( + allow_partial_results: bool, + error: ScanFileError, + path_buf: &PathBuf, +) { + if allow_partial_results { + eprintln!("Failed to parse file: {}, {:?} ", path_buf.display(), error); + } else { + panic!("Failed to parse file: {}, {:?} ", path_buf.display(), error); + } +} + +fn update_package_id_to_metrics_with_rs_file_metrics( + is_entry_point: bool, + package_id: PackageId, + package_id_to_metrics: &mut HashMap, + path_buf: PathBuf, + rs_file_metrics: RsFileMetrics, +) { + let package_metrics = package_id_to_metrics + .entry(package_id) + .or_insert_with(PackageMetrics::default); + let wrapper = package_metrics + .rs_path_to_metrics + .entry(path_buf) + .or_insert_with(RsFileMetricsWrapper::default); + wrapper.metrics = rs_file_metrics; + wrapper.is_crate_entry_point = is_entry_point; +} + +#[cfg(test)] +mod find_tests { + use super::*; + + use crate::cli::get_workspace; + + use rstest::*; + use std::env; + use std::fs::File; + use std::io; + use std::io::ErrorKind; + use tempfile::tempdir; + + #[rstest] + fn find_rs_files_in_dir_test() { + let temp_dir = tempdir().unwrap(); + + let mut rs_file_names = + vec!["rs_file_1.rs", "rs_file_2.rs", "rs_file_3.rs"]; + + for file_name in &rs_file_names { + let file_path = temp_dir.path().join(file_name); + File::create(file_path).unwrap(); + } + + let non_rs_file_names = + vec!["non_rs_file_1.txt", "non_rs_file_2.ext", "non_rs_file"]; + + for file_name in &non_rs_file_names { + let file_path = temp_dir.path().join(file_name); + File::create(file_path).unwrap(); + } + + let actual_rs_files = find_rs_files_in_dir(temp_dir.path()); + + let mut actual_rs_file_names = actual_rs_files + .into_iter() + .map(|f| { + String::from(f.as_path().file_name().unwrap().to_str().unwrap()) + }) + .collect::>(); + + rs_file_names.sort_unstable(); + actual_rs_file_names.sort(); + + assert_eq!(actual_rs_file_names, rs_file_names); + } + + #[rstest] + fn find_rs_file_in_package() { + let package = get_current_workspace_package(); + let rs_files_in_package = find_rs_files_in_package(&package); + + let path_bufs_in_package = rs_files_in_package + .iter() + .map(|f| match f { + RsFile::BinRoot(path_buf) => path_buf, + RsFile::CustomBuildRoot(path_buf) => path_buf, + RsFile::LibRoot(path_buf) => path_buf, + RsFile::Other(path_buf) => path_buf, + }) + .collect::>(); + + for path_buf in &path_bufs_in_package { + assert_eq!(path_buf.extension().unwrap().to_str().unwrap(), "rs"); + } + } + + #[rstest] + fn handle_unsafe_in_file_error_doesnt_panic_when_allow_partial_results_is_true( + ) { + let path_buf = PathBuf::from("test_path"); + handle_unsafe_in_file_error( + true, + ScanFileError::Io( + io::Error::new(ErrorKind::Other, "test"), + path_buf.clone(), + ), + &path_buf, + ); + } + + #[rstest] + #[should_panic] + fn handle_unsafe_in_file_error_panics_when_allow_partial_results_is_false() + { + let path_buf = PathBuf::from("test_path"); + handle_unsafe_in_file_error( + false, + ScanFileError::Io( + io::Error::new(ErrorKind::Other, "test"), + path_buf.clone(), + ), + &path_buf, + ); + } + + #[rstest( + input_is_entry_point, + expected_is_crate_entry_point, + package, + case(true, true, get_current_workspace_package()), + case(false, false, get_current_workspace_package()) + )] + fn update_package_id_to_metrics_with_rs_file_metrics_test( + input_is_entry_point: bool, + expected_is_crate_entry_point: bool, + package: Package, + ) { + //let package = get_current_workspace_package(); + let mut package_id_to_metrics = + HashMap::::new(); + + let mut rs_files_in_package = find_rs_files_in_package(&package); + let rs_file = rs_files_in_package.pop().unwrap(); + let (_, path_buf) = into_is_entry_point_and_path_buf(rs_file); + + let rs_file_metrics = + find_unsafe_in_file(path_buf.as_path(), IncludeTests::Yes).unwrap(); + + update_package_id_to_metrics_with_rs_file_metrics( + input_is_entry_point, + package.package_id(), + &mut package_id_to_metrics, + package.manifest_path().to_path_buf(), + rs_file_metrics.clone(), + ); + + assert!(package_id_to_metrics.contains_key(&package.package_id())); + let package_metrics = + package_id_to_metrics.get(&package.package_id()).unwrap(); + + let wrapper = package_metrics + .rs_path_to_metrics + .get(package.manifest_path()) + .unwrap(); + + assert_eq!(wrapper.metrics, rs_file_metrics); + assert_eq!(wrapper.is_crate_entry_point, expected_is_crate_entry_point); + } + + #[fixture] + fn get_current_workspace_package() -> Package { + let config = Config::default().unwrap(); + + let current_working_dir = + env::current_dir().unwrap().join("Cargo.toml"); + + let manifest_path_option = Some(current_working_dir); + + let workspace = get_workspace(&config, manifest_path_option).unwrap(); + workspace.current().unwrap().clone() + } +} diff --git a/cargo-geiger/src/tree.rs b/cargo-geiger/src/tree.rs index 6309e80f..908723d2 100644 --- a/cargo-geiger/src/tree.rs +++ b/cargo-geiger/src/tree.rs @@ -8,6 +8,7 @@ use cargo::core::PackageId; /// A step towards decoupling some parts of the table-tree printing from the /// dependency graph traversal. +#[derive(Debug, PartialEq)] pub enum TextTreeLine { /// A text line for a package Package { id: PackageId, tree_vines: String }, @@ -89,22 +90,13 @@ mod tree_tests { #[rstest( input_prefix, expected_tree_vines_string, - case( - Prefix::Depth, - "3 " - ), - case( - Prefix::Indent, - "| |-- " - ), - case( - Prefix::None, - "" - ) + case(Prefix::Depth, "3 "), + case(Prefix::Indent, "| |-- "), + case(Prefix::None, "") )] fn construct_tree_vines_string_test( input_prefix: Prefix, - expected_tree_vines_string: &str + expected_tree_vines_string: &str, ) { let mut levels_continue = vec![true, false, true]; @@ -118,18 +110,12 @@ mod tree_tests { #[rstest( input_charset, expected_tree_symbols, - case( - Charset::Utf8, - UTF8_TREE_SYMBOLS - ), - case( - Charset::Ascii, - ASCII_TREE_SYMBOLS - ) + case(Charset::Utf8, UTF8_TREE_SYMBOLS), + case(Charset::Ascii, ASCII_TREE_SYMBOLS) )] fn get_tree_symbols_test( input_charset: Charset, - expected_tree_symbols: TreeSymbols + expected_tree_symbols: TreeSymbols, ) { assert_eq!(get_tree_symbols(input_charset), expected_tree_symbols); } diff --git a/cargo-geiger/src/tree/traversal.rs b/cargo-geiger/src/tree/traversal.rs index c81bbc84..7992d123 100644 --- a/cargo-geiger/src/tree/traversal.rs +++ b/cargo-geiger/src/tree/traversal.rs @@ -1,14 +1,16 @@ -use crate::format::print_config::{Prefix, PrintConfig}; -use crate::graph::{Graph, Node}; -use crate::tree::{get_tree_symbols, TextTreeLine}; +mod dependency_kind; +mod dependency_node; + +use crate::format::print_config::PrintConfig; +use crate::graph::Graph; +use crate::tree::TextTreeLine; use super::construct_tree_vines_string; +use dependency_kind::walk_dependency_kind; +use dependency_node::walk_dependency_node; -use cargo::core::dependency::DepKind; use cargo::core::PackageId; -use petgraph::visit::EdgeRef; -use petgraph::EdgeDirection; -use std::collections::{HashMap, HashSet}; +use std::collections::HashSet; /// Printing the returned TextTreeLines in order is expected to produce a nice /// looking tree structure. @@ -32,119 +34,3 @@ pub fn walk_dependency_tree( print_config, ) } - -fn construct_dependency_type_nodes_hashmap<'a>( - graph: &'a Graph, - package: &Node, - print_config: &PrintConfig, -) -> HashMap> { - let mut dependency_type_nodes: HashMap> = [ - (DepKind::Build, vec![]), - (DepKind::Development, vec![]), - (DepKind::Normal, vec![]), - ] - .iter() - .cloned() - .collect(); - - for edge in graph - .graph - .edges_directed(graph.nodes[&package.id], print_config.direction) - { - let dependency = match print_config.direction { - EdgeDirection::Incoming => &graph.graph[edge.source()], - EdgeDirection::Outgoing => &graph.graph[edge.target()], - }; - - dependency_type_nodes - .get_mut(edge.weight()) - .unwrap() - .push(dependency); - } - - dependency_type_nodes -} - -fn walk_dependency_kind( - kind: DepKind, - deps: &mut Vec<&Node>, - graph: &Graph, - visited_deps: &mut HashSet, - levels_continue: &mut Vec, - print_config: &PrintConfig, -) -> Vec { - if deps.is_empty() { - return Vec::new(); - } - - // Resolve uses Hash data types internally but we want consistent output ordering - deps.sort_by_key(|n| n.id); - - let tree_symbols = get_tree_symbols(print_config.charset); - let mut output = Vec::new(); - if let Prefix::Indent = print_config.prefix { - match kind { - DepKind::Normal => (), - _ => { - let mut tree_vines = String::new(); - for &continues in &**levels_continue { - let c = if continues { tree_symbols.down } else { " " }; - tree_vines.push_str(&format!("{} ", c)); - } - output.push(TextTreeLine::ExtraDepsGroup { kind, tree_vines }); - } - } - } - - let mut node_iterator = deps.iter().peekable(); - while let Some(dependency) = node_iterator.next() { - levels_continue.push(node_iterator.peek().is_some()); - output.append(&mut walk_dependency_node( - dependency, - graph, - visited_deps, - levels_continue, - print_config, - )); - levels_continue.pop(); - } - output -} - -fn walk_dependency_node( - package: &Node, - graph: &Graph, - visited_deps: &mut HashSet, - levels_continue: &mut Vec, - print_config: &PrintConfig, -) -> Vec { - let new = print_config.all || visited_deps.insert(package.id); - let tree_vines = construct_tree_vines_string(levels_continue, print_config); - - let mut all_out_text_tree_lines = vec![TextTreeLine::Package { - id: package.id, - tree_vines, - }]; - - if !new { - return all_out_text_tree_lines; - } - - let mut dependency_type_nodes = - construct_dependency_type_nodes_hashmap(graph, package, print_config); - - for (dep_kind, nodes) in dependency_type_nodes.iter_mut() { - let mut dep_kind_out = walk_dependency_kind( - *dep_kind, - nodes, - graph, - visited_deps, - levels_continue, - print_config, - ); - - all_out_text_tree_lines.append(&mut dep_kind_out); - } - - all_out_text_tree_lines -} diff --git a/cargo-geiger/src/tree/traversal/dependency_kind.rs b/cargo-geiger/src/tree/traversal/dependency_kind.rs new file mode 100644 index 00000000..11a8dca0 --- /dev/null +++ b/cargo-geiger/src/tree/traversal/dependency_kind.rs @@ -0,0 +1,178 @@ +use crate::format::print_config::{Prefix, PrintConfig}; +use crate::graph::{Graph, Node}; +use crate::tree::{get_tree_symbols, TextTreeLine, TreeSymbols}; + +use super::dependency_node::walk_dependency_node; + +use cargo::core::dependency::DepKind; +use cargo::core::PackageId; +use std::collections::HashSet; +use std::iter::Peekable; +use std::slice::Iter; + +pub fn walk_dependency_kind( + dep_kind: DepKind, + deps: &mut Vec<&Node>, + graph: &Graph, + visited_deps: &mut HashSet, + levels_continue: &mut Vec, + print_config: &PrintConfig, +) -> Vec { + if deps.is_empty() { + return Vec::new(); + } + + // Resolve uses Hash data types internally but we want consistent output ordering + deps.sort_by_key(|n| n.id); + + let tree_symbols = get_tree_symbols(print_config.charset); + let mut text_tree_lines = Vec::new(); + if let Prefix::Indent = print_config.prefix { + push_extra_deps_group_text_tree_line_for_non_normal_dependencies( + dep_kind, + levels_continue, + &tree_symbols, + &mut text_tree_lines, + ) + } + + let mut node_iterator = deps.iter().peekable(); + while let Some(dependency) = node_iterator.next() { + handle_walk_dependency_node( + dependency, + graph, + levels_continue, + &mut node_iterator, + print_config, + &mut text_tree_lines, + visited_deps, + ); + } + text_tree_lines +} + +fn handle_walk_dependency_node( + dependency: &Node, + graph: &Graph, + levels_continue: &mut Vec, + node_iterator: &mut Peekable>, + print_config: &PrintConfig, + text_tree_lines: &mut Vec, + visited_deps: &mut HashSet, +) { + levels_continue.push(node_iterator.peek().is_some()); + text_tree_lines.append(&mut walk_dependency_node( + dependency, + graph, + visited_deps, + levels_continue, + print_config, + )); + levels_continue.pop(); +} + +fn push_extra_deps_group_text_tree_line_for_non_normal_dependencies( + dep_kind: DepKind, + levels_continue: &[bool], + tree_symbols: &TreeSymbols, + text_tree_lines: &mut Vec, +) { + match dep_kind { + DepKind::Normal => (), + _ => { + let mut tree_vines = String::new(); + for &continues in &*levels_continue { + let c = if continues { tree_symbols.down } else { " " }; + tree_vines.push_str(&format!("{} ", c)) + } + text_tree_lines.push(TextTreeLine::ExtraDepsGroup { + kind: dep_kind, + tree_vines, + }); + } + } +} + +#[cfg(test)] +mod traversal_tests { + use super::*; + + use crate::format::Charset; + use crate::tree::TextTreeLine::ExtraDepsGroup; + + use rstest::*; + + #[rstest( + input_dep_kind, + input_levels_continue, + expected_text_tree_lines, + case( + DepKind::Build, + vec![], + vec![ + ExtraDepsGroup { + kind: DepKind::Build, + tree_vines: String::from("") + } + ] + ), + case( + DepKind::Build, + vec![false, true], + vec![ + ExtraDepsGroup { + kind: DepKind::Build, + tree_vines: format!( + " {} ", + get_tree_symbols(Charset::Utf8).down + ) + } + ] + ), + case( + DepKind::Development, + vec![true], + vec![ + ExtraDepsGroup { + kind: DepKind::Development, + tree_vines: format!( + "{} ", + get_tree_symbols(Charset::Utf8).down + ) + } + ] + ), + case( + DepKind::Development, + vec![false], + vec![ + ExtraDepsGroup { + kind: DepKind::Development, + tree_vines: String::from(" ") + } + ] + ), + case( + DepKind::Normal, + vec![], + vec![] + ) + )] + fn push_extra_deps_group_text_tree_line_for_non_normal_dependencies_test( + input_dep_kind: DepKind, + input_levels_continue: Vec, + expected_text_tree_lines: Vec, + ) { + let mut text_tree_lines: Vec = vec![]; + let tree_symbols = get_tree_symbols(Charset::Utf8); + + push_extra_deps_group_text_tree_line_for_non_normal_dependencies( + input_dep_kind, + &input_levels_continue, + &tree_symbols, + &mut text_tree_lines, + ); + + assert_eq!(text_tree_lines, expected_text_tree_lines); + } +} diff --git a/cargo-geiger/src/tree/traversal/dependency_node.rs b/cargo-geiger/src/tree/traversal/dependency_node.rs new file mode 100644 index 00000000..f7fec540 --- /dev/null +++ b/cargo-geiger/src/tree/traversal/dependency_node.rs @@ -0,0 +1,246 @@ +use crate::format::print_config::PrintConfig; +use crate::graph::{Graph, Node}; +use crate::tree::TextTreeLine; + +use super::construct_tree_vines_string; +use super::walk_dependency_kind; + +use cargo::core::dependency::DepKind; +use cargo::core::PackageId; +use petgraph::visit::EdgeRef; +use petgraph::EdgeDirection; +use std::collections::{HashMap, HashSet}; + +pub fn walk_dependency_node( + package: &Node, + graph: &Graph, + visited_deps: &mut HashSet, + levels_continue: &mut Vec, + print_config: &PrintConfig, +) -> Vec { + let new = print_config.all || visited_deps.insert(package.id); + let tree_vines = construct_tree_vines_string(levels_continue, print_config); + + let mut all_out_text_tree_lines = vec![TextTreeLine::Package { + id: package.id, + tree_vines, + }]; + + if !new { + return all_out_text_tree_lines; + } + + let mut dependency_type_nodes = + construct_dependency_type_nodes_hashmap(graph, package, print_config); + + for (dep_kind, nodes) in dependency_type_nodes.iter_mut() { + let mut dep_kind_out = walk_dependency_kind( + *dep_kind, + nodes, + graph, + visited_deps, + levels_continue, + print_config, + ); + + all_out_text_tree_lines.append(&mut dep_kind_out); + } + + all_out_text_tree_lines +} + +fn construct_dependency_type_nodes_hashmap<'a>( + graph: &'a Graph, + package: &Node, + print_config: &PrintConfig, +) -> HashMap> { + let mut dependency_type_nodes: HashMap> = [ + (DepKind::Build, vec![]), + (DepKind::Development, vec![]), + (DepKind::Normal, vec![]), + ] + .iter() + .cloned() + .collect(); + + for edge in graph + .graph + .edges_directed(graph.nodes[&package.id], print_config.direction) + { + let dependency = match print_config.direction { + EdgeDirection::Incoming => &graph.graph[edge.source()], + EdgeDirection::Outgoing => &graph.graph[edge.target()], + }; + + dependency_type_nodes + .get_mut(edge.weight()) + .unwrap() + .push(dependency); + } + + dependency_type_nodes +} + +#[cfg(test)] +mod dependency_node_tests { + use super::*; + + use crate::cli::get_workspace; + use crate::format::pattern::Pattern; + use crate::format::print_config::{Prefix, PrintConfig}; + use crate::format::Charset; + + use cargo::core::Verbosity; + use cargo::Config; + use geiger::IncludeTests; + use petgraph::graph::NodeIndex; + use rstest::*; + use std::env; + + #[rstest( + input_directed_edges, + input_edge_direction, + expected_build_nodes_length, + expected_development_nodes_length, + expected_normal_nodes_length, + case( + vec![ + (1, 0, DepKind::Build), + (2, 0, DepKind::Build), + (3, 0, DepKind::Build), + (4, 0, DepKind::Development), + (5, 0, DepKind::Development), + (6, 0, DepKind::Normal) + ], + EdgeDirection::Incoming, + 3, + 2, + 1 + ), + case( + vec![ + (0, 1, DepKind::Build), + (0, 2, DepKind::Development), + (0, 3, DepKind::Development), + (0, 4, DepKind::Normal), + (0, 5, DepKind::Normal), + (0, 6, DepKind::Normal) + ], + EdgeDirection::Outgoing, + 1, + 2, + 3 + ) + )] + fn construct_dependency_type_nodes_hashmap_test( + input_directed_edges: Vec<(usize, usize, DepKind)>, + input_edge_direction: EdgeDirection, + expected_build_nodes_length: usize, + expected_development_nodes_length: usize, + expected_normal_nodes_length: usize, + ) { + let mut inner_graph = petgraph::Graph::::new(); + let mut nodes = HashMap::::new(); + + let package_ids = create_package_id_vec(7); + let print_config = create_print_config(input_edge_direction); + + for package_id in &package_ids { + nodes.insert( + *package_id, + inner_graph.add_node(Node { id: *package_id }), + ); + } + + add_edges_to_graph( + &input_directed_edges, + &mut inner_graph, + &nodes, + &package_ids, + ); + + let graph = Graph { + graph: inner_graph, + nodes, + }; + + let dependency_type_nodes_hashmap = + construct_dependency_type_nodes_hashmap( + &graph, + &Node { id: package_ids[0] }, + &print_config, + ); + + assert_eq!( + dependency_type_nodes_hashmap[&DepKind::Build].len(), + expected_build_nodes_length + ); + assert_eq!( + dependency_type_nodes_hashmap[&DepKind::Development].len(), + expected_development_nodes_length + ); + assert_eq!( + dependency_type_nodes_hashmap[&DepKind::Normal].len(), + expected_normal_nodes_length + ); + } + + fn add_edges_to_graph( + directed_edges: &[(usize, usize, DepKind)], + graph: &mut petgraph::Graph, + nodes: &HashMap, + package_ids: &[PackageId], + ) { + for (source_index, target_index, dep_kind) in directed_edges { + graph.add_edge( + nodes[&package_ids[*source_index]], + nodes[&package_ids[*target_index]], + *dep_kind, + ); + } + } + + fn create_package_id_vec(count: i32) -> Vec { + let config = Config::default().unwrap(); + + let current_working_dir = + env::current_dir().unwrap().join("Cargo.toml"); + + let manifest_path_option = Some(current_working_dir); + + let workspace = get_workspace(&config, manifest_path_option).unwrap(); + + let package = workspace.current().unwrap(); + + let source_id = package.dependencies().first().unwrap().source_id(); + + let mut package_id_vec: Vec = vec![]; + + for i in 0..count { + package_id_vec.push( + PackageId::new( + format!("test_name_{}", i), + format!("1.2.{}", i).as_str(), + source_id, + ) + .unwrap(), + ) + } + + package_id_vec + } + + fn create_print_config(edge_direction: EdgeDirection) -> PrintConfig { + PrintConfig { + all: false, + allow_partial_results: false, + charset: Charset::Ascii, + direction: edge_direction, + format: Pattern(vec![]), + include_tests: IncludeTests::Yes, + prefix: Prefix::Depth, + output_format: None, + verbosity: Verbosity::Verbose, + } + } +} diff --git a/cargo-geiger/tests/mod.rs b/cargo-geiger/tests/mod.rs index e07f16a0..b2d3aa44 100644 --- a/cargo-geiger/tests/mod.rs +++ b/cargo-geiger/tests/mod.rs @@ -3,8 +3,8 @@ use assert_cmd::prelude::*; use cargo_geiger_serde::{ - Count, CounterBlock, PackageId, PackageInfo, QuickReportEntry, QuickSafetyReport, ReportEntry, - SafetyReport, Source, UnsafeInfo, + Count, CounterBlock, PackageId, PackageInfo, QuickReportEntry, + QuickSafetyReport, ReportEntry, SafetyReport, Source, UnsafeInfo, }; use insta::assert_snapshot; use rstest::rstest; @@ -126,14 +126,17 @@ trait Test { fn run(&self) { let (output, cx) = run_geiger_json(Self::NAME); assert!(output.status.success()); - let actual = serde_json::from_slice::(&output.stdout).unwrap(); + let actual = + serde_json::from_slice::(&output.stdout).unwrap(); assert_eq!(actual, self.expected_report(&cx)); } fn run_quick(&self) { let (output, cx) = run_geiger_json_quick(Self::NAME); assert!(output.status.success()); - let actual = serde_json::from_slice::(&output.stdout).unwrap(); + let actual = + serde_json::from_slice::(&output.stdout) + .unwrap(); assert_eq!(actual, self.expected_quick_report(&cx)); } } @@ -152,8 +155,14 @@ impl Test for Test1 { package: PackageInfo::new(make_package_id(cx, Self::NAME)), unsafety: UnsafeInfo { used: CounterBlock { - functions: Count { safe: 1, unsafe_: 1 }, - exprs: Count { safe: 4, unsafe_: 2 }, + functions: Count { + safe: 1, + unsafe_: 1, + }, + exprs: Count { + safe: 4, + unsafe_: 2, + }, ..Default::default() }, ..Default::default() @@ -168,7 +177,8 @@ impl Test for Test2 { const NAME: &'static str = "test2_package_with_shallow_deps"; fn expected_report(&self, cx: &Context) -> SafetyReport { - let mut report = single_entry_safety_report(self.expected_report_entry(cx)); + let mut report = + single_entry_safety_report(self.expected_report_entry(cx)); merge_test_reports(&mut report, Test1.expected_report(cx)); merge_test_reports(&mut report, external::ref_slice_safety_report()); report @@ -185,8 +195,14 @@ impl Test for Test2 { }, unsafety: UnsafeInfo { used: CounterBlock { - functions: Count { safe: 0, unsafe_: 1 }, - exprs: Count { safe: 0, unsafe_: 4 }, + functions: Count { + safe: 0, + unsafe_: 1, + }, + exprs: Count { + safe: 0, + unsafe_: 4, + }, ..Default::default() }, ..Default::default() @@ -201,7 +217,8 @@ impl Test for Test3 { const NAME: &'static str = "test3_package_with_nested_deps"; fn expected_report(&self, cx: &Context) -> SafetyReport { - let mut report = single_entry_safety_report(self.expected_report_entry(cx)); + let mut report = + single_entry_safety_report(self.expected_report_entry(cx)); merge_test_reports(&mut report, external::itertools_safety_report()); merge_test_reports(&mut report, external::doc_comment_safety_report()); merge_test_reports(&mut report, Test2.expected_report(cx)); @@ -220,8 +237,14 @@ impl Test for Test3 { }, unsafety: UnsafeInfo { used: CounterBlock { - functions: Count { safe: 1, unsafe_: 0 }, - exprs: Count { safe: 6, unsafe_: 1 }, + functions: Count { + safe: 1, + unsafe_: 0, + }, + exprs: Count { + safe: 6, + unsafe_: 1, + }, ..Default::default() }, ..Default::default() @@ -236,7 +259,8 @@ impl Test for Test4 { const NAME: &'static str = "test4_workspace_with_top_level_package"; fn expected_report(&self, cx: &Context) -> SafetyReport { - let mut report = single_entry_safety_report(self.expected_report_entry(cx)); + let mut report = + single_entry_safety_report(self.expected_report_entry(cx)); merge_test_reports(&mut report, Test1.expected_report(cx)); report } @@ -249,13 +273,25 @@ impl Test for Test4 { }, unsafety: UnsafeInfo { used: CounterBlock { - functions: Count { safe: 1, unsafe_: 0 }, - exprs: Count { safe: 1, unsafe_: 0 }, + functions: Count { + safe: 1, + unsafe_: 0, + }, + exprs: Count { + safe: 1, + unsafe_: 0, + }, ..Default::default() }, unused: CounterBlock { - functions: Count { safe: 1, unsafe_: 0 }, - exprs: Count { safe: 1, unsafe_: 1 }, + functions: Count { + safe: 1, + unsafe_: 0, + }, + exprs: Count { + safe: 1, + unsafe_: 1, + }, ..Default::default() }, ..Default::default() @@ -270,8 +306,12 @@ impl Test for Test6 { const NAME: &'static str = "test6_cargo_lock_out_of_date"; fn expected_report(&self, cx: &Context) -> SafetyReport { - let mut report = single_entry_safety_report(self.expected_report_entry(cx)); - merge_test_reports(&mut report, external::generational_arena_safety_report()); + let mut report = + single_entry_safety_report(self.expected_report_entry(cx)); + merge_test_reports( + &mut report, + external::generational_arena_safety_report(), + ); merge_test_reports(&mut report, external::idna_safety_report()); report } @@ -287,8 +327,14 @@ impl Test for Test6 { }, unsafety: UnsafeInfo { used: CounterBlock { - functions: Count { safe: 1, unsafe_: 0 }, - exprs: Count { safe: 1, unsafe_: 0 }, + functions: Count { + safe: 1, + unsafe_: 0, + }, + exprs: Count { + safe: 1, + unsafe_: 0, + }, ..Default::default() }, forbids_unsafe: true, @@ -304,7 +350,8 @@ impl Test for Test7 { const NAME: &'static str = "test7_package_with_patched_dep"; fn expected_report(&self, cx: &Context) -> SafetyReport { - let mut report = single_entry_safety_report(self.expected_report_entry(cx)); + let mut report = + single_entry_safety_report(self.expected_report_entry(cx)); merge_test_reports(&mut report, external::num_cpus_safety_report(cx)); report } @@ -317,8 +364,14 @@ impl Test for Test7 { }, unsafety: UnsafeInfo { used: CounterBlock { - functions: Count { safe: 1, unsafe_: 0 }, - exprs: Count { safe: 1, unsafe_: 0 }, + functions: Count { + safe: 1, + unsafe_: 0, + }, + exprs: Count { + safe: 1, + unsafe_: 0, + }, ..Default::default() }, forbids_unsafe: true, @@ -366,7 +419,9 @@ fn make_source(cx: &Context, name: &str) -> Source { } fn make_workspace_source(cx: &Context, workspace: &str, name: &str) -> Source { - Source::Path(Url::from_file_path(cx.workspace_crate_dir(workspace, name)).unwrap()) + Source::Path( + Url::from_file_path(cx.workspace_crate_dir(workspace, name)).unwrap(), + ) } struct Context { @@ -383,8 +438,12 @@ impl Context { content_only: true, ..Default::default() }; - fs_extra::dir::copy(&src_path, dir.path(), ©_options).expect("Failed to copy tests"); - let path = dir.path().canonicalize().expect("Failed to canonicalize temporary path"); + fs_extra::dir::copy(&src_path, dir.path(), ©_options) + .expect("Failed to copy tests"); + let path = dir + .path() + .canonicalize() + .expect("Failed to canonicalize temporary path"); // Canonicalizing on Windows returns a UNC path (starting with `\\?\`). // `cargo build` (as of 1.47.0) fails to use an overriding path dependency if the manifest // given to `cargo build` is a UNC path. Roudtripping to URL gets rid of the UNC prefix. @@ -423,7 +482,10 @@ fn report_entry_list_to_map(entries: I) -> HashMap where I: IntoIterator, { - entries.into_iter().map(|e| (e.package.id.clone(), e)).collect() + entries + .into_iter() + .map(|e| (e.package.id.clone(), e)) + .collect() } fn to_set(items: I) -> HashSet @@ -438,8 +500,12 @@ where // tests. fn merge_test_reports(report: &mut SafetyReport, other: SafetyReport) { report.packages.extend(other.packages); - report.packages_without_metrics.extend(other.packages_without_metrics); - report.used_but_not_scanned_files.extend(other.used_but_not_scanned_files); + report + .packages_without_metrics + .extend(other.packages_without_metrics); + report + .used_but_not_scanned_files + .extend(other.used_but_not_scanned_files); } fn to_quick_report(report: SafetyReport) -> QuickSafetyReport { @@ -468,17 +534,21 @@ fn single_entry_safety_report(entry: ReportEntry) -> SafetyReport { } mod external { + use super::{ + merge_test_reports, single_entry_safety_report, to_set, Context, Test, + }; use cargo_geiger_serde::{ - Count, CounterBlock, PackageId, PackageInfo, ReportEntry, SafetyReport, Source, UnsafeInfo, + Count, CounterBlock, PackageId, PackageInfo, ReportEntry, SafetyReport, + Source, UnsafeInfo, }; use semver::Version; - use super::{merge_test_reports, single_entry_safety_report, to_set, Context, Test}; use url::Url; fn crates_io_source() -> Source { Source::Registry { name: "crates.io".into(), - url: Url::parse("https://github.com/rust-lang/crates.io-index").unwrap(), + url: Url::parse("https://github.com/rust-lang/crates.io-index") + .unwrap(), } } @@ -495,8 +565,14 @@ mod external { package: PackageInfo::new(ref_slice_package_id()), unsafety: UnsafeInfo { used: CounterBlock { - functions: Count { safe: 4, unsafe_: 0 }, - exprs: Count { safe: 10, unsafe_: 2 }, + functions: Count { + safe: 4, + unsafe_: 0, + }, + exprs: Count { + safe: 10, + unsafe_: 2, + }, ..Default::default() }, ..Default::default() @@ -518,10 +594,22 @@ mod external { package: PackageInfo::new(either_package_id()), unsafety: UnsafeInfo { used: CounterBlock { - functions: Count { safe: 6, unsafe_: 0 }, - exprs: Count { safe: 102, unsafe_: 0 }, - item_impls: Count { safe: 21, unsafe_: 0 }, - methods: Count { safe: 50, unsafe_: 0 }, + functions: Count { + safe: 6, + unsafe_: 0, + }, + exprs: Count { + safe: 102, + unsafe_: 0, + }, + item_impls: Count { + safe: 21, + unsafe_: 0, + }, + methods: Count { + safe: 50, + unsafe_: 0, + }, ..Default::default() }, ..Default::default() @@ -543,8 +631,14 @@ mod external { package: PackageInfo::new(doc_comment_package_id()), unsafety: UnsafeInfo { used: CounterBlock { - functions: Count { safe: 1, unsafe_: 0 }, - exprs: Count { safe: 37, unsafe_: 0 }, + functions: Count { + safe: 1, + unsafe_: 0, + }, + exprs: Count { + safe: 37, + unsafe_: 0, + }, ..Default::default() }, ..Default::default() @@ -558,7 +652,10 @@ mod external { name: "itertools".into(), version: Version::new(0, 8, 0), source: Source::Git { - url: Url::parse("https://github.com/rust-itertools/itertools.git").unwrap(), + url: Url::parse( + "https://github.com/rust-itertools/itertools.git", + ) + .unwrap(), rev: "8761fbefb3b209cf41829f8dba38044b69c1d8dd".into(), }, } @@ -572,21 +669,51 @@ mod external { }, unsafety: UnsafeInfo { used: CounterBlock { - functions: Count { safe: 79, unsafe_: 0 }, - exprs: Count { safe: 2413, unsafe_: 0 }, - item_impls: Count { safe: 129, unsafe_: 0 }, - item_traits: Count { safe: 7, unsafe_: 0 }, - methods: Count { safe: 180, unsafe_: 0 }, + functions: Count { + safe: 79, + unsafe_: 0, + }, + exprs: Count { + safe: 2413, + unsafe_: 0, + }, + item_impls: Count { + safe: 129, + unsafe_: 0, + }, + item_traits: Count { + safe: 7, + unsafe_: 0, + }, + methods: Count { + safe: 180, + unsafe_: 0, + }, }, unused: CounterBlock { - functions: Count { safe: 67, unsafe_: 0 }, - exprs: Count { safe: 1210, unsafe_: 72 }, - item_impls: Count { safe: 24, unsafe_: 3 }, - item_traits: Count { safe: 2, unsafe_: 1 }, - methods: Count { safe: 29, unsafe_: 3 }, + functions: Count { + safe: 67, + unsafe_: 0, + }, + exprs: Count { + safe: 1210, + unsafe_: 72, + }, + item_impls: Count { + safe: 24, + unsafe_: 3, + }, + item_traits: Count { + safe: 2, + unsafe_: 1, + }, + methods: Count { + safe: 29, + unsafe_: 3, + }, }, ..Default::default() - } + }, }; let mut report = single_entry_safety_report(entry); merge_test_reports(&mut report, either_safety_report()); @@ -625,16 +752,37 @@ mod external { }, unsafety: UnsafeInfo { used: CounterBlock { - exprs: Count { safe: 372, unsafe_: 0 }, - item_impls: Count { safe: 21, unsafe_: 0 }, - methods: Count { safe: 39, unsafe_: 0 }, + exprs: Count { + safe: 372, + unsafe_: 0, + }, + item_impls: Count { + safe: 21, + unsafe_: 0, + }, + methods: Count { + safe: 39, + unsafe_: 0, + }, ..Default::default() }, unused: CounterBlock { - functions: Count { safe: 6, unsafe_: 0 }, - exprs: Count { safe: 243, unsafe_: 0 }, - item_impls: Count { safe: 7, unsafe_: 0 }, - methods: Count { safe: 8, unsafe_: 0 }, + functions: Count { + safe: 6, + unsafe_: 0, + }, + exprs: Count { + safe: 243, + unsafe_: 0, + }, + item_impls: Count { + safe: 7, + unsafe_: 0, + }, + methods: Count { + safe: 8, + unsafe_: 0, + }, ..Default::default() }, forbids_unsafe: true, @@ -665,13 +813,25 @@ mod external { }, unsafety: UnsafeInfo { used: CounterBlock { - functions: Count { safe: 17, unsafe_: 0 }, - exprs: Count { safe: 13596, unsafe_: 1 }, + functions: Count { + safe: 17, + unsafe_: 0, + }, + exprs: Count { + safe: 13596, + unsafe_: 1, + }, ..Default::default() }, unused: CounterBlock { - functions: Count { safe: 7, unsafe_: 0 }, - exprs: Count { safe: 185, unsafe_: 0 }, + functions: Count { + safe: 7, + unsafe_: 0, + }, + exprs: Count { + safe: 185, + unsafe_: 0, + }, ..Default::default() }, ..Default::default() @@ -713,21 +873,51 @@ mod external { package: PackageInfo::new(smallvec_package_id()), unsafety: UnsafeInfo { used: CounterBlock { - functions: Count { safe: 0, unsafe_: 2 }, - exprs: Count { safe: 291, unsafe_: 354 }, - item_impls: Count { safe: 48, unsafe_: 4 }, - item_traits: Count { safe: 3, unsafe_: 1 }, - methods: Count { safe: 92, unsafe_: 13 }, + functions: Count { + safe: 0, + unsafe_: 2, + }, + exprs: Count { + safe: 291, + unsafe_: 354, + }, + item_impls: Count { + safe: 48, + unsafe_: 4, + }, + item_traits: Count { + safe: 3, + unsafe_: 1, + }, + methods: Count { + safe: 92, + unsafe_: 13, + }, }, unused: CounterBlock { - functions: Count { safe: 18, unsafe_: 0 }, - exprs: Count { safe: 126, unsafe_: 0 }, - item_impls: Count { safe: 2, unsafe_: 0 }, - item_traits: Count { safe: 1, unsafe_: 0 }, - methods: Count { safe: 14, unsafe_: 0 }, + functions: Count { + safe: 18, + unsafe_: 0, + }, + exprs: Count { + safe: 126, + unsafe_: 0, + }, + item_impls: Count { + safe: 2, + unsafe_: 0, + }, + item_traits: Count { + safe: 1, + unsafe_: 0, + }, + methods: Count { + safe: 14, + unsafe_: 0, + }, }, ..Default::default() - } + }, }; single_entry_safety_report(entry) } @@ -748,10 +938,22 @@ mod external { }, unsafety: UnsafeInfo { used: CounterBlock { - functions: Count { safe: 16, unsafe_: 0 }, - exprs: Count { safe: 2119, unsafe_: 0 }, - item_impls: Count { safe: 8, unsafe_: 0 }, - methods: Count { safe: 31, unsafe_: 0 }, + functions: Count { + safe: 16, + unsafe_: 0, + }, + exprs: Count { + safe: 2119, + unsafe_: 0, + }, + item_impls: Count { + safe: 8, + unsafe_: 0, + }, + methods: Count { + safe: 31, + unsafe_: 0, + }, ..Default::default() }, forbids_unsafe: true, @@ -779,15 +981,36 @@ mod external { }, unsafety: UnsafeInfo { used: CounterBlock { - functions: Count { safe: 37, unsafe_: 0 }, - exprs: Count { safe: 12901, unsafe_: 20 }, - item_impls: Count { safe: 9, unsafe_: 0 }, - item_traits: Count { safe: 1, unsafe_: 0 }, - methods: Count { safe: 21, unsafe_: 0 }, + functions: Count { + safe: 37, + unsafe_: 0, + }, + exprs: Count { + safe: 12901, + unsafe_: 20, + }, + item_impls: Count { + safe: 9, + unsafe_: 0, + }, + item_traits: Count { + safe: 1, + unsafe_: 0, + }, + methods: Count { + safe: 21, + unsafe_: 0, + }, }, unused: CounterBlock { - functions: Count { safe: 22, unsafe_: 0 }, - exprs: Count { safe: 84, unsafe_: 0 }, + functions: Count { + safe: 22, + unsafe_: 0, + }, + exprs: Count { + safe: 84, + unsafe_: 0, + }, ..Default::default() }, ..Default::default() @@ -809,12 +1032,18 @@ mod external { pub(super) fn num_cpus_safety_report(cx: &Context) -> SafetyReport { let entry = ReportEntry { package: PackageInfo { - dependencies: to_set(vec![super::make_package_id(cx, super::Test1::NAME)]), + dependencies: to_set(vec![super::make_package_id( + cx, + super::Test1::NAME, + )]), ..PackageInfo::new(num_cpus_package_id(cx)) }, unsafety: UnsafeInfo { used: CounterBlock { - functions: Count { safe: 1, unsafe_: 0 }, + functions: Count { + safe: 1, + unsafe_: 0, + }, ..Default::default() }, ..Default::default() diff --git a/geiger/src/lib.rs b/geiger/src/lib.rs index 49488a64..dff964ed 100644 --- a/geiger/src/lib.rs +++ b/geiger/src/lib.rs @@ -36,7 +36,7 @@ impl fmt::Display for ScanFileError { } /// Scan result for a single `.rs` file. -#[derive(Debug, Default)] +#[derive(Clone, Debug, Default, PartialEq)] pub struct RsFileMetrics { /// Metrics storage. pub counters: CounterBlock,