Skip to content

Commit

Permalink
Remove Arc<SourceFile>
Browse files Browse the repository at this point in the history
Just SourceFile is enough; it holds the big code buffer in an internal arc
  • Loading branch information
sourcefrog committed Dec 14, 2023
1 parent d6caa27 commit e122913
Show file tree
Hide file tree
Showing 6 changed files with 41 additions and 37 deletions.
7 changes: 3 additions & 4 deletions src/in_diff.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
//! for example from uncommitted or unmerged changes.
use std::collections::HashMap;
use std::sync::Arc;

use anyhow::{anyhow, bail};
use camino::Utf8Path;
Expand Down Expand Up @@ -63,17 +62,17 @@ pub fn diff_filter(mutants: Vec<Mutant>, diff_text: &str) -> Result<Vec<Mutant>>

/// Error if the new text from the diffs doesn't match the source files.
fn check_diff_new_text_matches(patches: &[Patch], mutants: &[Mutant]) -> Result<()> {
let mut source_by_name: HashMap<&Utf8Path, Arc<SourceFile>> = HashMap::new();
let mut source_by_name: HashMap<&Utf8Path, &SourceFile> = HashMap::new();
for mutant in mutants {
source_by_name
.entry(mutant.source_file.path())
.or_insert_with(|| Arc::clone(&mutant.source_file));
.or_insert_with(|| &mutant.source_file);
}
for patch in patches {
let path = strip_patch_path(&patch.new.path);
if let Some(source_file) = source_by_name.get(&path) {
let reconstructed = partial_new_file(patch);
let lines = source_file.code.lines().collect_vec();
let lines = source_file.code().lines().collect_vec();
for (lineno, diff_content) in reconstructed {
let source_content = lines.get(lineno - 1).unwrap_or(&"");
if diff_content != *source_content {
Expand Down
3 changes: 1 addition & 2 deletions src/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
use std::fmt;
use std::io;
use std::sync::Arc;

use serde_json::{json, Value};

Expand Down Expand Up @@ -62,7 +61,7 @@ pub(crate) fn list_mutants<W: fmt::Write>(

pub(crate) fn list_files<W: fmt::Write>(
mut out: W,
source_files: &[Arc<SourceFile>],
source_files: &[SourceFile],
options: &Options,
) -> Result<()> {
if options.emit_json {
Expand Down
10 changes: 5 additions & 5 deletions src/mutate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ pub enum Genre {
#[derive(Clone, Eq, PartialEq)]
pub struct Mutant {
/// Which file is being mutated.
pub source_file: Arc<SourceFile>,
pub source_file: SourceFile,

/// The function that's being mutated: the nearest enclosing function, if they are nested.
///
Expand Down Expand Up @@ -71,7 +71,7 @@ impl Mutant {
/// Return text of the whole file with the mutation applied.
pub fn mutated_code(&self) -> String {
self.span.replace(
&self.source_file.code,
self.source_file.code(),
&format!("{} {}", &self.replacement, MUTATION_MARKER_COMMENT),
)
}
Expand Down Expand Up @@ -143,7 +143,7 @@ impl Mutant {
}

pub fn original_text(&self) -> String {
self.span.extract(&self.source_file.code)
self.span.extract(self.source_file.code())
}

/// Return the text inserted for this mutation.
Expand All @@ -165,7 +165,7 @@ impl Mutant {
let old_label = self.source_file.tree_relative_slashes();
// There shouldn't be any newlines, but just in case...
let new_label = self.describe_change().replace('\n', " ");
TextDiff::from_lines(&self.source_file.code, &self.mutated_code())
TextDiff::from_lines(self.source_file.code(), &self.mutated_code())
.unified_diff()
.context_radius(8)
.header(&old_label, &new_label)
Expand All @@ -178,7 +178,7 @@ impl Mutant {
}

pub fn unapply(&self, build_dir: &mut BuildDir) -> Result<()> {
self.write_in_dir(build_dir, &self.source_file.code)
self.write_in_dir(build_dir, self.source_file.code())
}

#[allow(unknown_lints, clippy::needless_pass_by_ref_mut)]
Expand Down
23 changes: 16 additions & 7 deletions src/source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,19 @@ use crate::path::Utf8PathSlashes;
///
/// Code is normalized to Unix line endings as it's read in, and modified
/// files are written with Unix line endings.
#[derive(Debug, Eq, PartialEq)]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct SourceFile {
/// Package within the workspace.
pub package: Arc<Package>,

/// Path of this source file relative to workspace.
pub tree_relative_path: Utf8PathBuf,

/// Full copy of the source.
pub code: String,
/// Full copy of the unmodified source.
///
/// This is held in an [Arc] so that SourceFiles can be cloned without using excessive
/// amounts of memory.
code: Arc<String>,
}

impl SourceFile {
Expand All @@ -41,9 +44,11 @@ impl SourceFile {
package: &Arc<Package>,
) -> Result<SourceFile> {
let full_path = tree_path.join(&tree_relative_path);
let code = std::fs::read_to_string(&full_path)
.with_context(|| format!("failed to read source of {full_path:?}"))?
.replace("\r\n", "\n");
let code = Arc::new(
std::fs::read_to_string(&full_path)
.with_context(|| format!("failed to read source of {full_path:?}"))?
.replace("\r\n", "\n"),
);
Ok(SourceFile {
tree_relative_path,
code,
Expand All @@ -59,6 +64,10 @@ impl SourceFile {
pub fn path(&self) -> &Utf8Path {
self.tree_relative_path.as_path()
}

pub fn code(&self) -> &str {
self.code.as_str()
}
}

#[cfg(test)]
Expand Down Expand Up @@ -89,6 +98,6 @@ mod test {
}),
)
.unwrap();
assert_eq!(source_file.code, "fn main() {\n 640 << 10;\n}\n");
assert_eq!(source_file.code(), "fn main() {\n 640 << 10;\n}\n");
}
}
29 changes: 13 additions & 16 deletions src/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ use crate::*;
/// were visited but that produced no mutants.
pub struct Discovered {
pub mutants: Vec<Mutant>,
pub files: Vec<Arc<SourceFile>>,
pub files: Vec<SourceFile>,
}

/// Discover all mutants and all source files.
Expand All @@ -42,7 +42,7 @@ pub struct Discovered {
///
pub fn walk_tree(
workspace_dir: &Utf8Path,
top_source_files: &[Arc<SourceFile>],
top_source_files: &[SourceFile],
options: &Options,
console: &Console,
) -> Result<Discovered> {
Expand All @@ -52,24 +52,24 @@ pub fn walk_tree(
.map(|e| syn::parse_str(e).with_context(|| format!("Failed to parse error value {e:?}")))
.collect::<Result<Vec<Expr>>>()?;
console.walk_tree_start();
let mut file_queue: VecDeque<Arc<SourceFile>> = top_source_files.iter().cloned().collect();
let mut file_queue: VecDeque<SourceFile> = top_source_files.iter().cloned().collect();
let mut mutants = Vec::new();
let mut files: Vec<Arc<SourceFile>> = Vec::new();
let mut files: Vec<SourceFile> = Vec::new();
while let Some(source_file) = file_queue.pop_front() {
console.walk_tree_update(files.len(), mutants.len());
check_interrupted()?;
let (mut file_mutants, external_mods) = walk_file(Arc::clone(&source_file), &error_exprs)?;
let (mut file_mutants, external_mods) = walk_file(&source_file, &error_exprs)?;
// We'll still walk down through files that don't match globs, so that
// we have a chance to find modules underneath them. However, we won't
// collect any mutants from them, and they don't count as "seen" for
// `--list-files`.
for mod_name in &external_mods {
if let Some(mod_path) = find_mod_source(workspace_dir, &source_file, mod_name)? {
file_queue.push_back(Arc::new(SourceFile::new(
file_queue.push_back(SourceFile::new(
workspace_dir,
mod_path,
&source_file.package,
)?))
)?)
}
}
let path = &source_file.tree_relative_path;
Expand Down Expand Up @@ -101,21 +101,18 @@ pub fn walk_tree(
///
/// Returns the mutants found, and the names of modules referenced by `mod` statements
/// that should be visited later.
fn walk_file(
source_file: Arc<SourceFile>,
error_exprs: &[Expr],
) -> Result<(Vec<Mutant>, Vec<String>)> {
fn walk_file(source_file: &SourceFile, error_exprs: &[Expr]) -> Result<(Vec<Mutant>, Vec<String>)> {
let _span = debug_span!("source_file", path = source_file.tree_relative_slashes()).entered();
debug!("visit source file");
let syn_file = syn::parse_str::<syn::File>(&source_file.code)
let syn_file = syn::parse_str::<syn::File>(source_file.code())
.with_context(|| format!("failed to parse {}", source_file.tree_relative_slashes()))?;
let mut visitor = DiscoveryVisitor {
error_exprs,
external_mods: Vec::new(),
mutants: Vec::new(),
namespace_stack: Vec::new(),
fn_stack: Vec::new(),
source_file: Arc::clone(&source_file),
source_file: source_file.clone(),
};
visitor.visit_file(&syn_file);
Ok((visitor.mutants, visitor.external_mods))
Expand All @@ -131,7 +128,7 @@ struct DiscoveryVisitor<'o> {
mutants: Vec<Mutant>,

/// The file being visited.
source_file: Arc<SourceFile>,
source_file: SourceFile,

/// The stack of namespaces we're currently inside.
namespace_stack: Vec<String>,
Expand Down Expand Up @@ -180,7 +177,7 @@ impl<'o> DiscoveryVisitor<'o> {
let body_span = function_body_span(block).expect("Empty function body");
let mut new_mutants = return_type_replacements(&sig.output, self.error_exprs)
.map(|rep| Mutant {
source_file: Arc::clone(&self.source_file),
source_file: self.source_file.clone(),
function: Some(Arc::clone(function)),
span: body_span,
replacement: rep.to_pretty_string(),
Expand Down Expand Up @@ -355,7 +352,7 @@ impl<'ast> Visit<'ast> for DiscoveryVisitor<'_> {
let mut new_mutants = replacements
.into_iter()
.map(|rep| Mutant {
source_file: Arc::clone(&self.source_file),
source_file: self.source_file.clone(),
function: self.fn_stack.last().map(Arc::clone),
replacement: rep.to_pretty_string(),
span: i.op.span().into(),
Expand Down
6 changes: 3 additions & 3 deletions src/workspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,19 +182,19 @@ impl Workspace {
}

/// Find all the top source files for selected packages.
fn top_sources(&self, package_filter: &PackageFilter) -> Result<Vec<Arc<SourceFile>>> {
fn top_sources(&self, package_filter: &PackageFilter) -> Result<Vec<SourceFile>> {
let mut sources = Vec::new();
for PackageTop {
package,
top_sources,
} in self.package_tops(package_filter)?
{
for source_path in top_sources {
sources.push(Arc::new(SourceFile::new(
sources.push(SourceFile::new(
&self.dir,
source_path.to_owned(),
&package,
)?));
)?);
}
}
Ok(sources)
Expand Down

0 comments on commit e122913

Please sign in to comment.