Sfoglia il codice sorgente

Run Ethereum Solidity semantic tests through sema (#1279)

There are many failures. First there are some tests which cause panics
in sema. These are explicitly excluded. There are a further 1062
failures. Note the line:

	assert_eq!(errors, 1062);

To see the failures, see:

	cargo test --test evm ethereum_solidity_tests -- --nocapture

Signed-off-by: Sean Young <sean@mess.org>
Sean Young 2 anni fa
parent
commit
12e3422596
7 ha cambiato i file con 151 aggiunte e 8 eliminazioni
  1. 1 1
      .gitmodules
  2. 2 1
      Cargo.toml
  3. 0 1
      solang-parser/Cargo.toml
  4. 2 2
      solang-parser/src/tests.rs
  5. 7 2
      src/file_resolver.rs
  6. 0 0
      testdata/solidity
  7. 139 1
      tests/evm.rs

+ 1 - 1
.gitmodules

@@ -1,3 +1,3 @@
 [submodule "solang-parser/testdata/solidity"]
-	path = solang-parser/testdata/solidity
+	path = testdata/solidity
 	url = https://github.com/ethereum/solidity

+ 2 - 1
Cargo.toml

@@ -10,7 +10,7 @@ description = "Solang Solidity Compiler"
 keywords = [ "solidity", "compiler", "solana", "substrate" ]
 rust-version = "1.65.0"
 edition = "2021"
-exclude = [ "/.*", "/docs",  "/examples", "/solana-library", "/tests", "/integration", "/vscode" ]
+exclude = [ "/.*", "/docs",  "/examples", "/solana-library", "/tests", "/integration", "/vscode", "/testdata" ]
 
 [build-dependencies]
 cc = "1.0"
@@ -79,6 +79,7 @@ byte-slice-cast = "1.2"
 borsh = "0.10"
 tempfile = "3.3"
 rayon = "1"
+walkdir = "2.3.3"
 
 [package.metadata.docs.rs]
 no-default-features = true

+ 0 - 1
solang-parser/Cargo.toml

@@ -9,7 +9,6 @@ build = "build.rs"
 description = "Solang Solidity Parser"
 keywords = [ "solidity", "parser" ]
 edition = "2021"
-exclude = [ "/testdata" ]
 
 [build-dependencies]
 lalrpop = "0.19"

+ 2 - 2
solang-parser/src/tests.rs

@@ -1184,7 +1184,7 @@ fn test_libsolidity() {
 
     let semantic_tests = WalkDir::new(
         Path::new(env!("CARGO_MANIFEST_DIR"))
-            .join("testdata/solidity/test/libsolidity/semanticTests"),
+            .join("../testdata/solidity/test/libsolidity/semanticTests"),
     )
     .into_iter()
     .collect::<Result<Vec<_>, _>>()
@@ -1194,7 +1194,7 @@ fn test_libsolidity() {
 
     let syntax_tests = WalkDir::new(
         Path::new(env!("CARGO_MANIFEST_DIR"))
-            .join("testdata/solidity/test/libsolidity/syntaxTests"),
+            .join("../testdata/solidity/test/libsolidity/syntaxTests"),
     )
     .into_iter()
     .collect::<Result<Vec<_>, _>>()

+ 7 - 2
src/file_resolver.rs

@@ -131,6 +131,12 @@ impl FileResolver {
     ) -> Result<ResolvedFile, String> {
         let path = PathBuf::from(filename);
 
+        let path = if let Ok(m) = path.strip_prefix("./") {
+            m.to_path_buf()
+        } else {
+            path
+        };
+
         // first check maps
         let mut iter = path.iter();
         if let Some(first_part) = iter.next() {
@@ -195,8 +201,7 @@ impl FileResolver {
             start_import_no = *import_no;
         }
 
-        if self.import_paths.is_empty() {
-            // we have no import paths, resolve by what's in the cache
+        if self.cached_paths.contains_key(&path) {
             let full_path = path;
             let base = full_path
                 .parent()

+ 0 - 0
solang-parser/testdata/solidity → testdata/solidity


+ 139 - 1
tests/evm.rs

@@ -1,7 +1,9 @@
 // SPDX-License-Identifier: Apache-2.0
 
+use rayon::prelude::*;
 use solang::{file_resolver::FileResolver, parse_and_resolve, sema::ast, Target};
-use std::ffi::OsStr;
+use std::{ffi::OsStr, fs, path::Path};
+use walkdir::WalkDir;
 
 fn test_solidity(src: &str) -> ast::Namespace {
     let mut cache = FileResolver::new();
@@ -166,3 +168,139 @@ contract testing  {
 
     assert!(!ns.diagnostics.any_errors());
 }
+
+#[test]
+fn ethereum_solidity_tests() {
+    let error_matcher = regex::Regex::new(r"// ----\r?\n// \w+Error( \d+)?:").unwrap();
+
+    let semantic_tests = WalkDir::new(
+        Path::new(env!("CARGO_MANIFEST_DIR"))
+            .join("testdata/solidity/test/libsolidity/semanticTests"),
+    )
+    .into_iter();
+
+    let syntax_tests = WalkDir::new(
+        Path::new(env!("CARGO_MANIFEST_DIR"))
+            .join("testdata/solidity/test/libsolidity/syntaxTests"),
+    )
+    .into_iter();
+
+    let entries: Vec<_> = semantic_tests
+        .into_iter()
+        .chain(syntax_tests.into_iter())
+        .collect();
+
+    let errors: usize = entries
+        .into_par_iter()
+        .filter_map(|e| {
+            let entry = e.unwrap();
+            let file_name = entry.file_name().to_string_lossy();
+
+            // FIXME: max_depth_reached_4.sol causes a stack overflow in resolve_expression.rs
+            // FIXNE: others listed explicitly cause panics and need fixing
+            if !file_name.ends_with("max_depth_reached_4.sol")
+                && !file_name.ends_with("invalid_utf8_sequence.sol")
+                && !file_name.ends_with("basefee_berlin_function.sol")
+                && !file_name.ends_with("inline_assembly_embedded_function_call.sol")
+                && !file_name.ends_with("cannot_be_function_call.sol")
+                && !file_name.ends_with("complex_cyclic_constant.sol")
+                && !file_name.ends_with("cyclic_constant.sol")
+                && !file_name.ends_with("pure_functions.sol")
+                && !file_name.ends_with("pure_non_rational.sol")
+                && !file_name.ends_with("linkersymbol_function.sol")
+                && !file_name.ends_with("370_shift_constant_left_excessive_rvalue.sol")
+                && file_name.ends_with(".sol")
+            {
+                Some(entry)
+            } else {
+                None
+            }
+        })
+        .map(|entry| {
+            let path = entry.path().parent().unwrap();
+
+            let source = fs::read_to_string(entry.path()).unwrap();
+
+            let expect_error = error_matcher.is_match(&source);
+
+            let (mut cache, names) = set_file_contents(&source, path);
+
+            cache.add_import_path(path).unwrap();
+
+            let errors: usize = names
+                .iter()
+                .map(|name| {
+                    let ns = parse_and_resolve(OsStr::new(&name), &mut cache, Target::EVM);
+
+                    if ns.diagnostics.any_errors() {
+                        if !expect_error {
+                            println!("file: {}", entry.path().display());
+
+                            ns.print_diagnostics_in_plain(&cache, false);
+
+                            1
+                        } else {
+                            0
+                        }
+                    } else if expect_error {
+                        println!("file: {}", entry.path().display());
+
+                        println!("expecting error, none found");
+
+                        1
+                    } else {
+                        0
+                    }
+                })
+                .sum();
+
+            errors
+        })
+        .sum();
+
+    assert_eq!(errors, 1062);
+}
+
+fn set_file_contents(source: &str, path: &Path) -> (FileResolver, Vec<String>) {
+    let mut cache = FileResolver::new();
+    let mut name = "test.sol".to_owned();
+    let mut names = Vec::new();
+    let mut contents = String::new();
+    let source_delimiter = regex::Regex::new(r"==== Source: (.*) ====").unwrap();
+    let external_source_delimiter = regex::Regex::new(r"==== ExternalSource: (.*) ====").unwrap();
+    let equals = regex::Regex::new("([a-zA-Z0-9_]+)=(.*)").unwrap();
+
+    for line in source.lines() {
+        if let Some(cap) = source_delimiter.captures(line) {
+            if !contents.is_empty() {
+                cache.set_file_contents(&name, contents);
+                names.push(name);
+            }
+            name = cap.get(1).unwrap().as_str().to_owned();
+            if name == "////" {
+                name = "test.sol".to_owned();
+            }
+            contents = String::new();
+        } else if let Some(cap) = external_source_delimiter.captures(line) {
+            let mut name = cap.get(1).unwrap().as_str().to_owned();
+            if let Some(cap) = equals.captures(&name) {
+                let mut ext = path.to_path_buf();
+                ext.push(cap.get(2).unwrap().as_str());
+                name = cap.get(1).unwrap().as_str().to_owned();
+                let source = fs::read_to_string(ext).unwrap();
+                cache.set_file_contents(&name, source);
+            }
+            // else rely on file resolver to import stuff
+        } else {
+            contents.push_str(line);
+            contents.push('\n');
+        }
+    }
+
+    if !contents.is_empty() {
+        cache.set_file_contents(&name, contents);
+        names.push(name);
+    }
+
+    (cache, names)
+}