packs/packs/
reference_extractor.rs

1use std::{
2    collections::{HashMap, HashSet},
3    path::PathBuf,
4};
5
6use rayon::prelude::{IntoParallelRefIterator, ParallelIterator};
7use tracing::debug;
8
9use crate::packs::{
10    get_experimental_constant_resolver, get_zeitwerk_constant_resolver,
11    process_files_with_cache, ProcessedFile,
12};
13
14use super::{checker::reference::Reference, Configuration, Sigil};
15
16// It might be nice to have this return a simpler type rather than the tuple
17// This method returns everything we need as input into packwerk checking
18// (references and sigils). Not sure on naming yet.
19#[allow(clippy::type_complexity)]
20pub(crate) fn get_all_references_and_sigils(
21    configuration: &Configuration,
22    absolute_paths: &HashSet<PathBuf>,
23) -> anyhow::Result<(Vec<Reference>, HashMap<PathBuf, Vec<Sigil>>)> {
24    let cache = configuration.get_cache();
25
26    debug!("Getting unresolved references (using cache if possible)");
27
28    let (constant_resolver, processed_files_to_check) = if configuration
29        .experimental_parser
30    {
31        // The experimental parser needs *all* processed files to get definitions
32        let all_processed_files: Vec<ProcessedFile> = process_files_with_cache(
33            &configuration.included_files,
34            cache,
35            configuration,
36        )?;
37
38        let constant_resolver = get_experimental_constant_resolver(
39            &configuration.absolute_root,
40            &all_processed_files,
41            &configuration.ignored_definitions,
42        );
43
44        let processed_files_to_check = all_processed_files
45            .into_iter()
46            .filter(|processed_file| {
47                absolute_paths.contains(&processed_file.absolute_path)
48            })
49            .collect();
50
51        (constant_resolver, processed_files_to_check)
52    } else {
53        let processed_files: Vec<ProcessedFile> =
54            process_files_with_cache(absolute_paths, cache, configuration)?;
55
56        // The zeitwerk constant resolver doesn't look at processed files to get definitions
57        let constant_resolver = get_zeitwerk_constant_resolver(
58            &configuration.pack_set,
59            &configuration.constant_resolver_configuration(),
60        );
61
62        (constant_resolver, processed_files)
63    };
64
65    // Now we're going to get all the files with sigils (i.e. processed_files_to_check where property sigils is not empty)
66    // And then make a separate map of PathBuf => Sigils
67    debug!("Getting sigils");
68    let mut path_to_sigils: HashMap<PathBuf, Vec<Sigil>> = HashMap::new();
69    for processed_file in &processed_files_to_check {
70        if !processed_file.sigils.is_empty() {
71            path_to_sigils.insert(
72                processed_file.absolute_path.to_owned(),
73                processed_file.sigils.to_owned(),
74            );
75        }
76    }
77
78    debug!("Turning unresolved references into fully qualified references");
79    let references: anyhow::Result<Vec<Reference>> = processed_files_to_check
80        .par_iter()
81        .try_fold(
82            Vec::new,
83            // Start with an empty vector for each thread
84            |mut acc, processed_file| {
85                // Try to fold results within a thread
86                for unresolved_ref in &processed_file.unresolved_references {
87                    let mut refs = Reference::from_unresolved_reference(
88                        configuration,
89                        constant_resolver.as_ref(),
90                        unresolved_ref,
91                        &processed_file.absolute_path,
92                    )?;
93                    acc.append(&mut refs); // Collect references, return error if any
94                }
95                Ok(acc)
96            },
97        )
98        .try_reduce(
99            Vec::new, // Start with an empty vector for the reduction
100            |mut acc, mut vec| {
101                // Try to reduce results across threads
102                acc.append(&mut vec); // Combine vectors, no error expected here
103                Ok(acc)
104            },
105        );
106    debug!("Finished turning unresolved references into fully qualified references");
107
108    Ok((references?, path_to_sigils))
109}