Skip to content

Commit

Permalink
Return 'extra file tags' in report info (#98)
Browse files Browse the repository at this point in the history
This returns info on ignored files in the `report`. The intent here is
to also make it possible for the consumer to show information on files
that are only used in tests.
  • Loading branch information
Adjective-Object authored Nov 13, 2024
1 parent 9b19dd7 commit f2ee1e7
Show file tree
Hide file tree
Showing 11 changed files with 567 additions and 238 deletions.
19 changes: 18 additions & 1 deletion Cargo.lock

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

Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "minor",
"comment": "unused_finder: return tagged symbols",
"packageName": "@good-fences/api",
"email": "mhuan13@gmail.com",
"dependentChangeType": "patch"
}
2 changes: 2 additions & 0 deletions crates/unused_finder/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ ahashmap = { path = "../ahashmap" }
bitflags = "2.6.0"
ignore = "0.4.23"
abspath = { version = "0.2.0", path = "../abspath" }
itertools = "0.13.0"
debug_print = "1.0.0"

[dev-dependencies]
stringreader = "0.1.1"
Expand Down
35 changes: 21 additions & 14 deletions crates/unused_finder/src/cfg/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use std::fmt::{Display, Formatter};

use itertools::Itertools;
use package_match_rules::PackageMatchRules;
use rayon::iter::Either;
use serde::Deserialize;

pub mod package_match_rules;
Expand Down Expand Up @@ -101,13 +103,14 @@ pub struct UnusedFinderJSONConfig {
/// 3. Otherwise, the item is treated as the name of an individual package, and matched
/// literally.
pub entry_packages: Vec<String>,
/// List of globs that will be matched against files in the repository
/// List of glob patterns to mark as "tests".
/// These files will be marked as used, and all of their transitive
/// dependencies will also be marked as used
///
/// Matches are made against the relative file paths from the repo root.
/// A matching file will be tagged as a "test" file, and will be excluded
/// from the list of unused files
/// glob patterns are matched against the relative file path from the
/// root of the repository
#[serde(default)]
pub test_file_patterns: Vec<String>,
pub test_files: Vec<String>,
}

/// Configuration for the unused symbols finder
Expand All @@ -134,7 +137,7 @@ pub struct UnusedFinderConfig {
/// Matches are made against the relative file paths from the repo root.
/// A matching file will be tagged as a "test" file, and will be excluded
/// from the list of unused files
pub test_file_patterns: Vec<glob::Pattern>,
pub test_files: Vec<glob::Pattern>,

/// Globs of individual files & directories to skip during the file walk.
///
Expand All @@ -146,6 +149,17 @@ pub struct UnusedFinderConfig {
impl TryFrom<UnusedFinderJSONConfig> for UnusedFinderConfig {
type Error = ConfigError;
fn try_from(value: UnusedFinderJSONConfig) -> std::result::Result<Self, Self::Error> {
let (test_globs, test_glob_errs): (Vec<glob::Pattern>, Vec<_>) = value
.test_files
.iter()
.partition_map(|pat| match glob::Pattern::new(pat) {
Ok(pat) => Either::Left(pat),
Err(err) => Either::Right(PatErr(0, GlobInterp::Path, err)),
});
if !test_glob_errs.is_empty() {
return Err(ConfigError::InvalidGlobPatterns(ErrList(test_glob_errs)));
}

Ok(UnusedFinderConfig {
// raw fields that are copied from the JSON config
report_exported_symbols: value.report_exported_symbols,
Expand All @@ -154,14 +168,7 @@ impl TryFrom<UnusedFinderJSONConfig> for UnusedFinderConfig {
repo_root: value.repo_root,
// other fields that are processed before use
entry_packages: value.entry_packages.try_into()?,
test_file_patterns: value
.test_file_patterns
.iter()
.map(|p| glob::Pattern::new(p))
.collect::<Result<_, _>>()
.map_err(|e| {
ConfigError::InvalidGlobPatterns(ErrList(vec![PatErr(0, GlobInterp::Path, e)]))
})?,
test_files: test_globs,
skip: value.skip,
})
}
Expand Down
51 changes: 8 additions & 43 deletions crates/unused_finder/src/graph.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
use core::{fmt, option::Option::None};
use std::{
collections::HashSet,
fmt::Display,
path::{Path, PathBuf},
};
use core::option::Option::None;
use std::{collections::HashSet, path::Path, path::PathBuf};

use ahashmap::{AHashMap, AHashSet};
use anyhow::Result;
Expand All @@ -12,41 +8,10 @@ use rayon::prelude::*;
use crate::{
logger::Logger,
parse::{ExportedSymbol, ResolvedImportExportInfo},
tag::UsedTag,
walked_file::ResolvedSourceFile,
};

bitflags::bitflags! {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
pub struct UsedTag: u8 {
/// True if this file or symbol was used recursively by an
/// "entry package" (a package that was passed as an entry point).
const FROM_ENTRY = 0x01;
/// True if this file or symbol was used recursively by a test file.
const FROM_TEST = 0x02;
/// True if this file or symbol was used recursively by an
/// ignored symbol or file.
const FROM_IGNORED = 0x04;
// True if this symbol is a type-only symbol
const TYPE_ONLY = 0x08;
}
}

impl Display for UsedTag {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut tags = Vec::new();
if self.contains(Self::FROM_ENTRY) {
tags.push("entry");
};
if self.contains(Self::FROM_IGNORED) {
tags.push("ignored");
};
if self.contains(Self::TYPE_ONLY) {
tags.push("type-only");
}
write!(f, "{}", tags.join("+"))
}
}

// graph node used to represent a file during the "used file" walk
#[derive(Debug, Clone, Default)]
pub struct GraphFile {
Expand Down Expand Up @@ -168,13 +133,13 @@ impl Graph {
pub fn traverse_bfs(
&mut self,
logger: impl Logger,
initial_frontier_files: Vec<PathBuf>,
initial_frontier_symbols: Vec<(PathBuf, Vec<ExportedSymbol>)>,
initial_frontier_files: Vec<&Path>,
initial_frontier_symbols: Vec<(&Path, Vec<ExportedSymbol>)>,
tag: UsedTag,
) -> Result<()> {
let initial_file_edges = initial_frontier_files
.into_iter()
.filter_map(|path| match self.path_to_id.get(&path) {
.filter_map(|path| match self.path_to_id.get(path) {
Some(file_id) => Some(*file_id),
None => {
logger.log(format!(
Expand All @@ -189,8 +154,8 @@ impl Graph {
let initial_symbol_edges = initial_frontier_symbols
.into_iter()
.filter_map(
|(path, symbols): (PathBuf, Vec<ExportedSymbol>)| -> Option<Vec<Edge>> {
match self.path_to_id.get(&path).cloned() {
|(path, symbols): (&Path, Vec<ExportedSymbol>)| -> Option<Vec<Edge>> {
match self.path_to_id.get(path).cloned() {
Some(file_id) => Some(
symbols
.into_iter()
Expand Down
4 changes: 3 additions & 1 deletion crates/unused_finder/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ mod ignore_file;
pub mod logger;
mod parse;
mod report;
mod tag;
#[cfg(test)]
mod test;
mod unused_finder;
Expand All @@ -26,7 +27,8 @@ mod walked_file;

pub use cfg::{UnusedFinderConfig, UnusedFinderJSONConfig};
pub use parse::data::ResolvedImportExportInfo;
pub use report::{SymbolReport, UnusedFinderReport, UsedTagEnum};
pub use report::{SymbolReport, SymbolReportWithTags, UnusedFinderReport};
pub use tag::UsedTagEnum;
pub use unused_finder::{UnusedFinder, UnusedFinderResult};

pub fn find_unused_items(
Expand Down
Loading

0 comments on commit f2ee1e7

Please sign in to comment.