foundry_common/preprocessor/
mod.rs1use foundry_compilers::{
2 Compiler, ProjectPathsConfig, SourceParser, apply_updates,
3 artifacts::SolcLanguage,
4 error::Result,
5 multi::{MultiCompiler, MultiCompilerInput, MultiCompilerLanguage},
6 project::Preprocessor,
7 solc::{SolcCompiler, SolcVersionedInput},
8};
9use solar::parse::{ast::Span, interface::SourceMap};
10use std::{
11 collections::HashSet,
12 ops::{ControlFlow, Range},
13 path::PathBuf,
14};
15
16mod data;
17use data::{collect_preprocessor_data, create_deploy_helpers};
18
19mod deps;
20use deps::{PreprocessorDependencies, remove_bytecode_dependencies};
21
22#[track_caller]
24fn span_to_range(source_map: &SourceMap, span: Span) -> Range<usize> {
25 source_map.span_to_source(span).unwrap().1
26}
27
28#[derive(Debug)]
35pub struct DynamicTestLinkingPreprocessor;
36
37impl Preprocessor<SolcCompiler> for DynamicTestLinkingPreprocessor {
38 #[instrument(name = "DynamicTestLinkingPreprocessor::preprocess", skip_all)]
39 fn preprocess(
40 &self,
41 _solc: &SolcCompiler,
42 input: &mut SolcVersionedInput,
43 paths: &ProjectPathsConfig<SolcLanguage>,
44 mocks: &mut HashSet<PathBuf>,
45 ) -> Result<()> {
46 if !input.input.sources.iter().any(|(path, _)| paths.is_test_or_script(path)) {
48 trace!("no tests or sources to preprocess");
49 return Ok(());
50 }
51
52 let mut compiler =
53 foundry_compilers::resolver::parse::SolParser::new(paths.with_language_ref())
54 .into_compiler();
55 let _ = compiler.enter_mut(|compiler| -> solar::parse::interface::Result {
56 let mut pcx = compiler.parse();
57
58 let mut preprocessed_paths = vec![];
62 let sources = &mut input.input.sources;
63 for (path, source) in sources.iter() {
64 if let Ok(src_file) = compiler
65 .sess()
66 .source_map()
67 .new_source_file(path.clone(), source.content.as_str())
68 && paths.is_test_or_script(path)
69 {
70 pcx.add_file(src_file);
71 preprocessed_paths.push(path.clone());
72 }
73 }
74
75 pcx.parse();
77 let ControlFlow::Continue(()) = compiler.lower_asts()? else { return Ok(()) };
78 let gcx = compiler.gcx();
79 let deps = PreprocessorDependencies::new(
81 gcx,
82 &preprocessed_paths,
83 &paths.paths_relative().sources,
84 &paths.root,
85 mocks,
86 );
87 let data = collect_preprocessor_data(gcx, &deps.referenced_contracts);
89
90 sources.extend(create_deploy_helpers(&data));
92
93 apply_updates(sources, remove_bytecode_dependencies(gcx, &deps, &data));
95
96 Ok(())
97 });
98
99 if let Err(err) = compiler.sess().emitted_errors().unwrap() {
101 warn!("failed preprocessing:\n{err}");
102 }
103
104 Ok(())
105 }
106}
107
108impl Preprocessor<MultiCompiler> for DynamicTestLinkingPreprocessor {
109 fn preprocess(
110 &self,
111 compiler: &MultiCompiler,
112 input: &mut <MultiCompiler as Compiler>::Input,
113 paths: &ProjectPathsConfig<MultiCompilerLanguage>,
114 mocks: &mut HashSet<PathBuf>,
115 ) -> Result<()> {
116 let MultiCompilerInput::Solc(input) = input else { return Ok(()) };
118
119 let Some(solc) = &compiler.solc else { return Ok(()) };
120
121 let paths = paths.clone().with_language::<SolcLanguage>();
122 self.preprocess(solc, input, &paths, mocks)
123 }
124}