1use foundry_compilers::artifacts::{
18 ast::SourceLocation, Block, BlockOrStatement, StateMutability, Statement, TypeName, Visibility,
19};
20use semver::VersionReq;
21
22use crate::analysis::stmt_src;
23
24pub fn source_string_at_location<'a>(
32 id: u32,
33 source: &'a str,
34 location: &SourceLocation,
35) -> &'a str {
36 if let Some(index) = location.index {
37 assert_eq!(index as u32, id, "Source index mismatch");
38 }
39
40 source_string_at_location_unchecked(source, location)
41}
42
43pub fn source_string_at_location_unchecked<'a>(
50 source: &'a str,
51 location: &SourceLocation,
52) -> &'a str {
53 let start = location.start.unwrap_or(0);
54 let end = location.length.map(|l| start + l).unwrap_or(source.len() - 1);
55 &source[start..end]
56}
57
58pub fn slice_source_location(src: &SourceLocation, start: usize, length: usize) -> SourceLocation {
80 assert!(
81 src.length.map(|l| l >= start).unwrap_or(true),
82 "Sliced start is greater than the original source length"
83 );
84 assert!(
85 src.length.map(|l| l >= start + length).unwrap_or(true),
86 "Sliced source length is greater than the original source length"
87 );
88
89 SourceLocation {
90 start: src.start.map(|s| s + start).or(Some(start)),
91 length: Some(length),
92 index: src.index,
93 }
94}
95
96pub fn visibility_to_str(visibility: &Visibility) -> &'static str {
114 match visibility {
115 Visibility::Public => "public",
116 Visibility::Internal => "internal",
117 Visibility::Private => "private",
118 Visibility::External => "external",
119 }
120}
121
122pub fn mutability_to_str(mutability: &StateMutability) -> &'static str {
140 match mutability {
141 StateMutability::Pure => "pure",
142 StateMutability::View => "view",
143 StateMutability::Payable => "payable",
144 StateMutability::Nonpayable => "nonpayable",
145 }
146}
147
148pub fn find_next_index_of_source_location(src: &SourceLocation) -> Option<usize> {
159 if let Some(start) = src.start {
160 if let Some(length) = src.length {
161 return Some(start + length);
162 }
163 }
164 None
165}
166
167pub fn find_next_semicolon_after_source_location(
187 source: &str,
188 src: &SourceLocation,
189) -> Option<usize> {
190 let start = src.start.unwrap_or(0);
191 let end = src.length.map(|l| start + l).unwrap_or(start);
192 let substr = &source[end..];
193 substr.find(";").map(|i| i + end)
194}
195
196pub fn find_next_index_of_block_or_statement(
207 source: &str,
208 block_or_statement: &BlockOrStatement,
209) -> Option<usize> {
210 match block_or_statement {
211 BlockOrStatement::Statement(statement) => find_next_index_of_statement(source, statement),
212 BlockOrStatement::Block(block) => find_next_index_of_source_location(&block.src),
213 }
214}
215
216pub fn find_index_of_first_statement_in_block_or_statement(
226 block_or_statement: &BlockOrStatement,
227) -> Option<usize> {
228 match block_or_statement {
229 BlockOrStatement::Statement(statement) => match statement {
230 Statement::Block(block) => find_index_of_first_statement_in_block(block),
231 _ => stmt_src(statement).start,
232 },
233 BlockOrStatement::Block(block) => find_index_of_first_statement_in_block(block),
234 }
235}
236
237pub fn find_index_of_first_statement_in_block(block: &Block) -> Option<usize> {
247 block.statements.first().map_or(
248 block.src.start.map(|s| s + 1),
250 |stmt| stmt_src(stmt).start,
251 )
252}
253
254pub fn find_next_index_of_last_statement_in_block(source: &str, block: &Block) -> Option<usize> {
265 block.statements.last().map_or(
266 find_next_index_of_source_location(&block.src).map(|s| s - 1),
268 |stmt| find_next_index_of_statement(source, stmt),
269 )
270}
271
272pub fn find_next_index_of_statement(source: &str, stmt: &Statement) -> Option<usize> {
283 match stmt {
284 Statement::Block(block) => find_next_index_of_source_location(&block.src),
285 Statement::Break(break_stmt) => {
286 find_next_semicolon_after_source_location(source, &break_stmt.src).map(|i| i + 1)
287 }
288 Statement::Continue(continue_stmt) => {
289 find_next_semicolon_after_source_location(source, &continue_stmt.src).map(|i| i + 1)
290 }
291 Statement::DoWhileStatement(do_while_statement) => {
292 find_next_index_of_source_location(&do_while_statement.src)
293 }
294 Statement::EmitStatement(emit_statement) => {
295 find_next_semicolon_after_source_location(source, &emit_statement.src).map(|i| i + 1)
296 }
297 Statement::ExpressionStatement(expression_statement) => {
298 find_next_semicolon_after_source_location(source, &expression_statement.src)
299 .map(|i| i + 1)
300 }
301 Statement::ForStatement(for_statement) => {
302 find_next_index_of_block_or_statement(source, &for_statement.body)
303 }
304 Statement::IfStatement(if_statement) => match &if_statement.false_body {
305 Some(false_body) => find_next_index_of_block_or_statement(source, false_body),
306 None => find_next_index_of_block_or_statement(source, &if_statement.true_body),
307 },
308 Statement::InlineAssembly(inline_assembly) => {
309 find_next_index_of_source_location(&inline_assembly.src)
310 }
311 Statement::PlaceholderStatement(placeholder_statement) => {
312 find_next_semicolon_after_source_location(source, &placeholder_statement.src)
313 .map(|i| i + 1)
314 }
315 Statement::Return(return_stmt) => {
316 let return_str = source_string_at_location_unchecked(source, &return_stmt.src);
317 if return_str.trim_end().ends_with(";") {
318 find_next_index_of_source_location(&return_stmt.src)
319 } else {
320 find_next_semicolon_after_source_location(source, &return_stmt.src).map(|i| i + 1)
321 }
322 }
323 Statement::RevertStatement(revert_statement) => {
324 find_next_semicolon_after_source_location(source, &revert_statement.src).map(|i| i + 1)
325 }
326 Statement::TryStatement(try_statement) => {
327 find_next_index_of_source_location(&try_statement.src)
328 }
329 Statement::UncheckedBlock(unchecked_block) => {
330 find_next_index_of_source_location(&unchecked_block.src)
331 }
332 Statement::VariableDeclarationStatement(variable_declaration_statement) => {
333 find_next_semicolon_after_source_location(source, &variable_declaration_statement.src)
334 .map(|i| i + 1)
335 }
336 Statement::WhileStatement(while_statement) => {
337 find_next_index_of_block_or_statement(source, &while_statement.body)
338 }
339 }
340}
341
342pub fn contains_user_defined_type(type_name: &TypeName) -> bool {
360 match type_name {
361 TypeName::ArrayTypeName(array_type_name) => {
362 contains_user_defined_type(&array_type_name.base_type)
363 }
364 TypeName::ElementaryTypeName(_) => false,
365 TypeName::FunctionTypeName(_) => false,
366 TypeName::Mapping(mapping) => {
367 contains_user_defined_type(&mapping.key_type)
368 || contains_user_defined_type(&mapping.value_type)
369 }
370 TypeName::UserDefinedTypeName(_) => true,
371 }
372}
373
374pub fn contains_function_type(type_name: &TypeName) -> bool {
392 match type_name {
393 TypeName::FunctionTypeName(_) => true,
394 TypeName::ArrayTypeName(array_type_name) => {
395 contains_function_type(&array_type_name.base_type)
396 }
397 TypeName::ElementaryTypeName(_) => false,
398 TypeName::Mapping(mapping) => {
399 contains_function_type(&mapping.key_type) || contains_function_type(&mapping.value_type)
400 }
401 TypeName::UserDefinedTypeName(_) => false,
402 }
403}
404
405pub fn contains_mapping_type(type_name: &TypeName) -> bool {
415 match type_name {
416 TypeName::Mapping(_) => true,
417 TypeName::ArrayTypeName(array_type_name) => {
418 contains_mapping_type(&array_type_name.base_type)
419 }
420 TypeName::ElementaryTypeName(_) => false,
421 TypeName::FunctionTypeName(_) => false,
422 TypeName::UserDefinedTypeName(_) => false,
424 }
425}
426
427pub fn abi_encode_available(version_req: &VersionReq) -> bool {
445 let min_version = semver::Version::parse("0.4.24").unwrap();
447
448 !version_req.comparators.iter().all(|cmp| allows_any_version_lt(&min_version, cmp))
450}
451
452fn allows_any_version_lt(min_version: &semver::Version, comparator: &semver::Comparator) -> bool {
454 use semver::Op;
455
456 match comparator.op {
457 Op::Exact | Op::Greater | Op::GreaterEq | Op::Tilde | Op::Caret | Op::Wildcard => {
458 let exact_version = semver::Version {
460 major: comparator.major,
461 minor: comparator.minor.unwrap_or(0),
462 patch: comparator.patch.unwrap_or(0),
463 pre: semver::Prerelease::EMPTY,
464 build: semver::BuildMetadata::EMPTY,
465 };
466 exact_version < *min_version
467 }
468 Op::Less | Op::LessEq => true,
469 _ => true,
470 }
471}
472
473#[cfg(test)]
474mod tests {
475 use super::*;
476 use semver::VersionReq;
477
478 #[test]
479 fn test_abi_encode_available_exact_versions() {
480 assert!(abi_encode_available(&VersionReq::parse("=0.4.24").unwrap()));
482 assert!(abi_encode_available(&VersionReq::parse("=0.4.25").unwrap()));
483 assert!(abi_encode_available(&VersionReq::parse("=0.5.0").unwrap()));
484 assert!(abi_encode_available(&VersionReq::parse("=0.5.10").unwrap()));
485 assert!(abi_encode_available(&VersionReq::parse("=0.6.0").unwrap()));
486 assert!(abi_encode_available(&VersionReq::parse("=0.7.0").unwrap()));
487 assert!(abi_encode_available(&VersionReq::parse("=0.8.0").unwrap()));
488 assert!(abi_encode_available(&VersionReq::parse("=0.8.19").unwrap()));
489
490 assert!(!abi_encode_available(&VersionReq::parse("=0.4.23").unwrap()));
492 assert!(!abi_encode_available(&VersionReq::parse("=0.4.20").unwrap()));
493 assert!(!abi_encode_available(&VersionReq::parse("=0.3.0").unwrap()));
494 }
495
496 #[test]
497 fn test_abi_encode_available_greater_than() {
498 assert!(!abi_encode_available(&VersionReq::parse(">0.4.23").unwrap())); assert!(!abi_encode_available(&VersionReq::parse(">0.4.20").unwrap())); assert!(!abi_encode_available(&VersionReq::parse(">0.3.0").unwrap())); assert!(!abi_encode_available(&VersionReq::parse(">0.1.0").unwrap())); assert!(abi_encode_available(&VersionReq::parse(">0.4.24").unwrap())); assert!(abi_encode_available(&VersionReq::parse(">0.4.25").unwrap())); assert!(abi_encode_available(&VersionReq::parse(">0.5.0").unwrap())); }
509
510 #[test]
511 fn test_abi_encode_available_greater_equal() {
512 assert!(abi_encode_available(&VersionReq::parse(">=0.4.24").unwrap()));
514 assert!(abi_encode_available(&VersionReq::parse(">=0.4.25").unwrap()));
515 assert!(abi_encode_available(&VersionReq::parse(">=0.5.0").unwrap()));
516 assert!(abi_encode_available(&VersionReq::parse(">=0.6.0").unwrap()));
517
518 assert!(!abi_encode_available(&VersionReq::parse(">=0.4.23").unwrap()));
520 assert!(!abi_encode_available(&VersionReq::parse(">=0.4.20").unwrap()));
521 }
522
523 #[test]
524 fn test_abi_encode_available_less_than() {
525 assert!(!abi_encode_available(&VersionReq::parse("<0.4.24").unwrap()));
527 assert!(!abi_encode_available(&VersionReq::parse("<0.4.23").unwrap()));
528 assert!(!abi_encode_available(&VersionReq::parse("<0.3.0").unwrap()));
529
530 assert!(!abi_encode_available(&VersionReq::parse("<0.4.25").unwrap()));
532 assert!(!abi_encode_available(&VersionReq::parse("<0.5.0").unwrap()));
533 assert!(!abi_encode_available(&VersionReq::parse("<0.6.0").unwrap()));
534 }
535
536 #[test]
537 fn test_abi_encode_available_less_equal() {
538 assert!(!abi_encode_available(&VersionReq::parse("<=0.4.24").unwrap()));
540 assert!(!abi_encode_available(&VersionReq::parse("<=0.4.25").unwrap()));
541 assert!(!abi_encode_available(&VersionReq::parse("<=0.5.0").unwrap()));
542
543 assert!(!abi_encode_available(&VersionReq::parse("<=0.4.23").unwrap()));
545 assert!(!abi_encode_available(&VersionReq::parse("<=0.4.20").unwrap()));
546 }
547
548 #[test]
549 fn test_abi_encode_available_tilde() {
550 assert!(abi_encode_available(&VersionReq::parse("~0.4.24").unwrap()));
552 assert!(abi_encode_available(&VersionReq::parse("~0.4.25").unwrap()));
553 assert!(abi_encode_available(&VersionReq::parse("~0.5.0").unwrap()));
554 assert!(abi_encode_available(&VersionReq::parse("~0.5.1").unwrap()));
555
556 assert!(!abi_encode_available(&VersionReq::parse("~0.4.23").unwrap()));
558 assert!(!abi_encode_available(&VersionReq::parse("~0.4.20").unwrap()));
559
560 assert!(!abi_encode_available(&VersionReq::parse("~0").unwrap()));
562 assert!(!abi_encode_available(&VersionReq::parse("~0.3").unwrap()));
563 }
564
565 #[test]
566 fn test_abi_encode_available_caret() {
567 assert!(abi_encode_available(&VersionReq::parse("^0.4.24").unwrap()));
569 assert!(abi_encode_available(&VersionReq::parse("^0.4.25").unwrap()));
570 assert!(abi_encode_available(&VersionReq::parse("^0.5.0").unwrap()));
571 assert!(abi_encode_available(&VersionReq::parse("^0.6.0").unwrap()));
572
573 assert!(!abi_encode_available(&VersionReq::parse("^0.4.23").unwrap()));
575 assert!(!abi_encode_available(&VersionReq::parse("^0.4.20").unwrap()));
576
577 assert!(!abi_encode_available(&VersionReq::parse("^0").unwrap()));
579 assert!(!abi_encode_available(&VersionReq::parse("^0.3").unwrap()));
580 }
581
582 #[test]
583 fn test_abi_encode_available_wildcard() {
584 assert!(abi_encode_available(&VersionReq::parse("0.4.24").unwrap()));
586 assert!(abi_encode_available(&VersionReq::parse("0.4.25").unwrap()));
587 assert!(abi_encode_available(&VersionReq::parse("0.5.0").unwrap()));
588
589 assert!(!abi_encode_available(&VersionReq::parse("0.4.23").unwrap()));
591 assert!(!abi_encode_available(&VersionReq::parse("0.4.20").unwrap()));
592 }
593
594 #[test]
595 fn test_abi_encode_available_complex_ranges() {
596 assert!(abi_encode_available(&VersionReq::parse(">=0.4.24, <0.9.0").unwrap()));
598 assert!(abi_encode_available(&VersionReq::parse(">=0.5.0, <0.8.0").unwrap()));
599
600 assert!(!abi_encode_available(&VersionReq::parse(">=0.4.20, <0.4.24").unwrap()));
602 assert!(!abi_encode_available(&VersionReq::parse(">=0.3.0, <0.4.24").unwrap()));
603 }
604
605 #[test]
606 fn test_abi_encode_available_edge_cases() {
607 assert!(abi_encode_available(&VersionReq::parse(">=0.4.24").unwrap())); assert!(abi_encode_available(&VersionReq::parse(">0.4.24").unwrap())); assert!(!abi_encode_available(&VersionReq::parse("<=0.4.24").unwrap())); assert!(!abi_encode_available(&VersionReq::parse("<0.4.24").unwrap())); assert!(abi_encode_available(&VersionReq::parse(">=0.4.24-alpha").unwrap()));
615 }
616}