@@ -120,6 +120,19 @@ impl SolData {
120120 let e = e. to_string ( ) ;
121121 trace ! ( "failed parsing {file:?}: {e}" ) ;
122122 parse_result = Err ( e) ;
123+
124+ if version. is_none ( ) {
125+ version = utils:: capture_outer_and_inner (
126+ content,
127+ & utils:: RE_SOL_PRAGMA_VERSION ,
128+ & [ "version" ] ,
129+ )
130+ . first ( )
131+ . map ( |( cap, name) | Spanned :: new ( name. as_str ( ) . to_owned ( ) , cap. range ( ) ) ) ;
132+ }
133+ if !imports. is_empty ( ) {
134+ imports = capture_imports ( content) ;
135+ }
123136 }
124137 let license = content. lines ( ) . next ( ) . and_then ( |line| {
125138 utils:: capture_outer_and_inner (
@@ -264,3 +277,111 @@ fn library_is_inlined(contract: &ast::ItemContract<'_>) -> bool {
264277 )
265278 } )
266279}
280+
281+ /// Capture the import statement information together with aliases
282+ pub fn capture_imports ( content : & str ) -> Vec < Spanned < SolImport > > {
283+ let mut imports = vec ! [ ] ;
284+ for cap in utils:: RE_SOL_IMPORT . captures_iter ( content) {
285+ if let Some ( name_match) = [ "p1" , "p2" , "p3" , "p4" ] . iter ( ) . find_map ( |name| cap. name ( name) ) {
286+ let statement_match = cap. get ( 0 ) . unwrap ( ) ;
287+ let mut aliases = vec ! [ ] ;
288+ for alias_cap in utils:: RE_SOL_IMPORT_ALIAS . captures_iter ( statement_match. as_str ( ) ) {
289+ if let Some ( alias) = alias_cap. name ( "alias" ) {
290+ let alias = alias. as_str ( ) . to_owned ( ) ;
291+ let import_alias = match alias_cap. name ( "target" ) {
292+ Some ( target) => SolImportAlias :: Contract ( alias, target. as_str ( ) . to_owned ( ) ) ,
293+ None => SolImportAlias :: File ( alias) ,
294+ } ;
295+ aliases. push ( import_alias) ;
296+ }
297+ }
298+ let sol_import =
299+ SolImport :: new ( PathBuf :: from ( name_match. as_str ( ) ) ) . set_aliases ( aliases) ;
300+ imports. push ( Spanned :: new ( sol_import, statement_match. range ( ) ) ) ;
301+ }
302+ }
303+ imports
304+ }
305+
306+ #[ cfg( test) ]
307+ mod tests {
308+ use super :: * ;
309+
310+ #[ track_caller]
311+ fn assert_version ( version_req : Option < & str > , src : & str ) {
312+ let data = SolData :: parse ( src, "test.sol" . as_ref ( ) ) ;
313+ assert_eq ! ( data. version_req, version_req. map( |v| v. parse( ) . unwrap( ) ) , "src:\n {src}" ) ;
314+ }
315+
316+ #[ test]
317+ fn soldata_parsing ( ) {
318+ assert_version ( None , "" ) ;
319+ assert_version ( None , "contract C { }" ) ;
320+
321+ // https://github.com/foundry-rs/foundry/issues/9349
322+ assert_version (
323+ Some ( ">=0.4.22, <0.6" ) ,
324+ r#"
325+ pragma solidity >=0.4.22 <0.6;
326+
327+ contract BugReport {
328+ function() external payable {
329+ deposit();
330+ }
331+ function deposit() public payable {}
332+ }
333+ "# ,
334+ ) ;
335+ }
336+
337+ #[ test]
338+ fn can_capture_curly_imports ( ) {
339+ let content = r#"
340+ import { T } from "../Test.sol";
341+ import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
342+ import {DsTest} from "ds-test/test.sol";
343+ "# ;
344+
345+ let captured_imports =
346+ capture_imports ( content) . into_iter ( ) . map ( |s| s. data . path ) . collect :: < Vec < _ > > ( ) ;
347+
348+ let expected =
349+ utils:: find_import_paths ( content) . map ( |m| m. as_str ( ) . into ( ) ) . collect :: < Vec < PathBuf > > ( ) ;
350+
351+ assert_eq ! ( captured_imports, expected) ;
352+
353+ assert_eq ! (
354+ captured_imports,
355+ vec![
356+ PathBuf :: from( "../Test.sol" ) ,
357+ "@openzeppelin/contracts/utils/ReentrancyGuard.sol" . into( ) ,
358+ "ds-test/test.sol" . into( ) ,
359+ ] ,
360+ ) ;
361+ }
362+
363+ #[ test]
364+ fn cap_capture_aliases ( ) {
365+ let content = r#"
366+ import * as T from "./Test.sol";
367+ import { DsTest as Test } from "ds-test/test.sol";
368+ import "ds-test/test.sol" as Test;
369+ import { FloatMath as Math, Math as FloatMath } from "./Math.sol";
370+ "# ;
371+
372+ let caputred_imports =
373+ capture_imports ( content) . into_iter ( ) . map ( |s| s. data . aliases ) . collect :: < Vec < _ > > ( ) ;
374+ assert_eq ! (
375+ caputred_imports,
376+ vec![
377+ vec![ SolImportAlias :: File ( "T" . into( ) ) ] ,
378+ vec![ SolImportAlias :: Contract ( "Test" . into( ) , "DsTest" . into( ) ) ] ,
379+ vec![ SolImportAlias :: File ( "Test" . into( ) ) ] ,
380+ vec![
381+ SolImportAlias :: Contract ( "Math" . into( ) , "FloatMath" . into( ) ) ,
382+ SolImportAlias :: Contract ( "FloatMath" . into( ) , "Math" . into( ) ) ,
383+ ] ,
384+ ]
385+ ) ;
386+ }
387+ }
0 commit comments