diff --git a/libs/ast-references/.gitignore b/libs/ast-references/.gitignore new file mode 100644 index 00000000..71ab9a43 --- /dev/null +++ b/libs/ast-references/.gitignore @@ -0,0 +1,10 @@ +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb \ No newline at end of file diff --git a/libs/ast-references/Cargo.lock b/libs/ast-references/Cargo.lock new file mode 100644 index 00000000..b156f712 --- /dev/null +++ b/libs/ast-references/Cargo.lock @@ -0,0 +1,95 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ast-extractor" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "syn-solidity", + "thiserror", +] + +[[package]] +name = "ast-references" +version = "0.1.0" +dependencies = [ + "ast-extractor", + "proc-macro2", +] + +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + +[[package]] +name = "proc-macro2" +version = "1.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "2.0.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn-solidity" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8a5a633f1172a0c80b1516a988e7e8efa7ce9cededf56590f54e593e4513b3" +dependencies = [ + "paste", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thiserror" +version = "1.0.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d6d7a740b8a666a7e828dd00da9c0dc290dff53154ea77ac109281de90589b7" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" diff --git a/libs/ast-references/Cargo.toml b/libs/ast-references/Cargo.toml new file mode 100644 index 00000000..cef452db --- /dev/null +++ b/libs/ast-references/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "ast-references" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +proc-macro2 = { version = "1.0.66", features = ["span-locations"]} +ast-extractor = { path = "../ast-extractor"} \ No newline at end of file diff --git a/libs/ast-references/src/file_retriever.rs b/libs/ast-references/src/file_retriever.rs new file mode 100644 index 00000000..7efabdb1 --- /dev/null +++ b/libs/ast-references/src/file_retriever.rs @@ -0,0 +1,595 @@ +/** + * file_retriever.rs + * Function to retrieve a file reference from AST + * author: ByFish + */ +use crate::types::contract_reference::ContractReference; +use crate::types::enum_reference::EnumReference; +use crate::types::error_reference::ErrorReference; +use crate::types::event_reference::EventReference; +use crate::types::file_reference::FileReference; +use crate::types::function_reference::FunctionReference; +use crate::types::location::{Bound, Location}; +use crate::types::struct_reference::StructReference; +use crate::types::variable_reference::VariableReference; +use proc_macro2::TokenStream; +use std::cell::RefCell; +use std::fs; +use std::rc::Rc; +use std::str::FromStr; +use ast_extractor::{ + ItemContract, ItemEnum, ItemError, ItemEvent, ItemFunction, ItemStruct, Spanned, + VariableDefinition, Visit, +}; + +struct FileVisitor { + file_reference: Rc>, + current_contract: Option>>, +} + +impl FileVisitor { + pub fn new(path: String) -> Self { + Self { + file_reference: Rc::new(RefCell::new(FileReference::new(path.to_string()))), + current_contract: None, + } + } +} + +impl<'ast> Visit<'ast> for FileVisitor { + fn visit_variable_definition(&mut self, i: &'ast VariableDefinition) { + let variable_reference = VariableReference::new( + i.name.0.to_string().clone(), + i.ty.clone(), + Location::new( + self.file_reference.borrow_mut().path.clone(), + Bound::new( + i.name.span().start().line as u32, + i.name.span().start().column as u32, + ), + Bound::new( + i.name.span().end().line as u32, + i.name.span().end().column as u32, + ), + ), + self.current_contract.as_ref(), + Some(&self.file_reference), + ); + if self.current_contract.is_some() { + self.current_contract + .as_ref() + .unwrap() + .borrow_mut() + .add_property(&Rc::new(RefCell::new(variable_reference))); + } else { + self.file_reference + .borrow_mut() + .add_variable(variable_reference); + } + ast_extractor::visit::visit_variable_definition(self, i) + } + + fn visit_item_enum(&mut self, i: &'ast ItemEnum) { + let enum_reference = EnumReference::new( + i.name.to_string(), + Location::new( + self.file_reference.borrow_mut().path.clone(), + Bound::new( + i.name.span().start().line as u32, + i.name.span().start().column as u32, + ), + Bound::new( + i.name.span().start().line as u32, + i.name.span().start().column as u32, + ), + ), + self.current_contract.as_ref(), + Some(&self.file_reference), + ); + if self.current_contract.is_some() { + self.current_contract + .as_ref() + .unwrap() + .borrow_mut() + .add_enum(&Rc::new(RefCell::new(enum_reference))); + } else { + self.file_reference.borrow_mut().add_enum(enum_reference); + } + ast_extractor::visit::visit_item_enum(self, i) + } + + fn visit_item_contract(&mut self, i: &ItemContract) { + let contract_reference = ContractReference::new( + i.name.to_string(), + Location::new( + self.file_reference.borrow_mut().path.clone(), + Bound::new( + i.name.span().start().line as u32, + i.name.span().start().column as u32, + ), + Bound::new( + i.name.span().end().line as u32, + i.name.span().end().column as u32, + ), + ), + &self.file_reference, + ); + self.file_reference + .borrow_mut() + .add_contract(contract_reference); + self.current_contract = Some( + self.file_reference + .borrow() + .contracts + .last() + .unwrap() + .clone(), + ); + ast_extractor::visit::visit_item_contract(self, i); + self.current_contract = None; + } + + fn visit_item_event(&mut self, i: &'ast ItemEvent) { + let event_reference = EventReference::new( + i.name.to_string(), + Location::new( + self.file_reference.borrow_mut().path.clone(), + Bound::new( + i.name.span().start().line as u32, + i.name.span().start().column as u32, + ), + Bound::new( + i.name.span().end().line as u32, + i.name.span().end().column as u32, + ), + ), + self.current_contract.as_ref(), + Some(&self.file_reference), + ); + if self.current_contract.is_some() { + self.current_contract + .as_ref() + .unwrap() + .borrow_mut() + .add_event(&Rc::new(RefCell::new(event_reference))); + } else { + self.file_reference.borrow_mut().add_event(event_reference); + } + ast_extractor::visit::visit_item_event(self, i) + } + + fn visit_item_function(&mut self, i: &'ast ItemFunction) { + if self.current_contract.is_some() { + let function_reference = FunctionReference::new( + i.name.as_ref().unwrap().0.to_string().clone(), + i.kind.clone(), + Location::new( + self.file_reference.borrow_mut().path.clone(), + Bound::new( + i.name.span().start().line as u32, + i.name.span().start().column as u32, + ), + Bound::new( + i.name.span().end().line as u32, + i.name.span().end().column as u32, + ), + ), + self.current_contract.as_ref().unwrap(), + ); + self.current_contract + .as_ref() + .unwrap() + .borrow_mut() + .add_function(&Rc::new(RefCell::new(function_reference))); + } + ast_extractor::visit::visit_item_function(self, i); + } + + fn visit_item_struct(&mut self, i: &'ast ItemStruct) { + let struct_reference = StructReference::new( + i.name.to_string(), + Location::new( + self.file_reference.borrow_mut().path.clone(), + Bound::new( + i.name.span().start().line as u32, + i.name.span().start().column as u32, + ), + Bound::new( + i.name.span().end().line as u32, + i.name.span().end().column as u32, + ), + ), + self.current_contract.as_ref(), + Some(&self.file_reference), + ); + if self.current_contract.is_some() { + self.current_contract + .as_ref() + .unwrap() + .borrow_mut() + .add_struct(&Rc::new(RefCell::new(struct_reference))); + } else { + self.file_reference + .borrow_mut() + .add_struct(struct_reference); + } + ast_extractor::visit::visit_item_struct(self, i) + } + + fn visit_item_error(&mut self, i: &'ast ItemError) { + let error_reference = ErrorReference::new( + i.name.to_string(), + Location::new( + self.file_reference.borrow_mut().path.clone(), + Bound::new( + i.name.span().start().line as u32, + i.name.span().start().column as u32, + ), + Bound::new( + i.name.span().end().line as u32, + i.name.span().end().column as u32, + ), + ), + self.current_contract.as_ref(), + Some(&self.file_reference), + ); + if self.current_contract.is_some() { + self.current_contract + .as_ref() + .unwrap() + .borrow_mut() + .add_error(&Rc::new(RefCell::new(error_reference))); + } else { + self.file_reference.borrow_mut().add_error(error_reference); + } + ast_extractor::visit::visit_item_error(self, i) + } +} + +pub fn retrieve_file_reference_from_path(path: String) -> Rc> { + let source = fs::read_to_string(path.to_string()).unwrap(); + let tokens = TokenStream::from_str(source.as_str()).unwrap(); + let ast = ast_extractor::parse2(tokens).unwrap(); + let mut visitor = FileVisitor::new(path.to_string()); + visitor.visit_file(&ast); + visitor.file_reference +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_retrieve_contract_variables() { + let mut path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); + path.push("tests"); + path.push("variables"); + path.push("contract.sol"); + let path_str = path.to_str().unwrap().to_string(); + let source = fs::read_to_string(&path).unwrap(); + + let mut visitor = FileVisitor::new(path_str.clone()); + let contract_ref = ContractReference::new( + "Good".to_string(), + Location::new(path_str, Bound { line: 1, column: 1 }, Bound::new(1, 10)), + &visitor.file_reference, + ); + visitor + .file_reference + .borrow_mut() + .add_contract(contract_ref); + visitor.current_contract = Some(visitor.file_reference.borrow().contracts[0].clone()); + let file = ast_extractor::extract::extract_ast_from_content(&source).unwrap(); + let contract = file.items.iter().find(|item| match item { + ast_extractor::Item::Contract(_) => true, + _ => false, + }); + let contract = match contract { + Some(ast_extractor::Item::Contract(contract)) => contract, + _ => panic!("No contract found"), + }; + let variables = contract.body.iter().find(|item| match item { + ast_extractor::Item::Variable(_) => true, + _ => false, + }); + let variables = match variables { + Some(ast_extractor::Item::Variable(variables)) => variables, + _ => panic!("No variables found"), + }; + visitor.visit_variable_definition(variables); + assert_eq!( + visitor.file_reference.borrow().contracts[0] + .borrow() + .properties + .len(), + 1 + ); + } + + #[test] + fn test_retrieve_file_variables() { + let mut path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); + path.push("tests"); + path.push("variables"); + path.push("file.sol"); + let path_str = path.to_str().unwrap().to_string(); + let source = fs::read_to_string(&path).unwrap(); + + let mut visitor = FileVisitor::new(path_str.clone()); + let file = ast_extractor::extract::extract_ast_from_content(&source).unwrap(); + let variable = file.items.iter().find(|item| match item { + ast_extractor::Item::Variable(_) => true, + _ => false, + }); + let variable = match variable { + Some(ast_extractor::Item::Variable(var)) => var, + _ => panic!("Expect variable declaration"), + }; + visitor.visit_variable_definition(variable); + assert_eq!(visitor.file_reference.borrow().variables.len(), 1); + } + + #[test] + fn test_retrieve_contract_enums() { + let mut path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); + path.push("tests"); + path.push("enums"); + path.push("contract.sol"); + let path_str = path.to_str().unwrap().to_string(); + let source = fs::read_to_string(&path).unwrap(); + + let mut visitor = FileVisitor::new(path_str.clone()); + let contract_ref = ContractReference::new( + "Good".to_string(), + Location::new(path_str, Bound { line: 1, column: 1 }, Bound::new(1, 10)), + &visitor.file_reference, + ); + visitor + .file_reference + .borrow_mut() + .add_contract(contract_ref); + visitor.current_contract = Some(visitor.file_reference.borrow().contracts[0].clone()); + let file = ast_extractor::extract::extract_ast_from_content(&source).unwrap(); + let contract = file.items.iter().find(|item| match item { + ast_extractor::Item::Contract(_) => true, + _ => false, + }); + let contract = match contract { + Some(ast_extractor::Item::Contract(contract)) => contract, + _ => panic!("No contract found"), + }; + let enums = contract.body.iter().find(|item| match item { + ast_extractor::Item::Enum(_) => true, + _ => false, + }); + let enums = match enums { + Some(ast_extractor::Item::Enum(enums)) => enums, + _ => panic!("No enums found"), + }; + visitor.visit_item_enum(enums); + assert_eq!( + visitor.file_reference.borrow().contracts[0] + .borrow() + .enums + .len(), + 1 + ); + } + + #[test] + fn test_retrieve_file_enums() { + let mut path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); + path.push("tests"); + path.push("enums"); + path.push("file.sol"); + let path_str = path.to_str().unwrap().to_string(); + let source = fs::read_to_string(&path).unwrap(); + + let mut visitor = FileVisitor::new(path_str.clone()); + let file = ast_extractor::extract::extract_ast_from_content(&source).unwrap(); + let enums = file.items.iter().find(|item| match item { + ast_extractor::Item::Enum(_) => true, + _ => false, + }); + let enums = match enums { + Some(ast_extractor::Item::Enum(var)) => var, + _ => panic!("Expect enums declaration"), + }; + visitor.visit_item_enum(enums); + assert_eq!(visitor.file_reference.borrow().enums.len(), 1); + } + + #[test] + fn test_retrieve_contract_nodes_empty() { + let mut path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); + path.push("tests"); + path.push("contracts"); + path.push("file.sol"); + let path_str = path.to_str().unwrap().to_string(); + let source = fs::read_to_string(&path).unwrap(); + + let mut visitor = FileVisitor::new(path_str.clone()); + let file = ast_extractor::extract::extract_ast_from_content(&source).unwrap(); + let contracts = file.items.iter().find(|item| match item { + ast_extractor::Item::Contract(_) => true, + _ => false, + }); + let contracts = match contracts { + Some(ast_extractor::Item::Contract(var)) => var, + _ => panic!("Expect contracts declaration"), + }; + visitor.visit_item_contract(contracts); + assert_eq!(visitor.file_reference.borrow().contracts.len(), 1); + } + + #[test] + fn test_retrieve_contract_event() { + let mut path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); + path.push("tests"); + path.push("events"); + path.push("contract.sol"); + let path_str = path.to_str().unwrap().to_string(); + let source = fs::read_to_string(&path).unwrap(); + + let mut visitor = FileVisitor::new(path_str.clone()); + let file = ast_extractor::extract::extract_ast_from_content(&source).unwrap(); + let contract = file.items.iter().find(|item| match item { + ast_extractor::Item::Contract(_) => true, + _ => false, + }); + let contract = match contract { + Some(ast_extractor::Item::Contract(contract)) => contract, + _ => panic!("No contract found"), + }; + let events = contract.body.iter().find(|item| match item { + ast_extractor::Item::Event(_) => true, + _ => false, + }); + let events = match events { + Some(ast_extractor::Item::Event(var)) => var, + _ => panic!("Expect events declaration"), + }; + visitor.visit_item_event(events); + assert_eq!(visitor.file_reference.borrow().events.len(), 1); + } + + #[test] + fn test_retrieve_functions() { + let mut path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); + path.push("tests"); + path.push("functions"); + path.push("contract.sol"); + let path_str = path.to_str().unwrap().to_string(); + let source = fs::read_to_string(&path).unwrap(); + + let mut visitor = FileVisitor::new(path_str.clone()); + let contract_ref = ContractReference::new( + "Good".to_string(), + Location::new(path_str, Bound { line: 1, column: 1 }, Bound::new(10, 100)), + &visitor.file_reference, + ); + visitor + .file_reference + .borrow_mut() + .add_contract(contract_ref); + visitor.current_contract = Some(visitor.file_reference.borrow().contracts[0].clone()); + let file = ast_extractor::extract::extract_ast_from_content(&source).unwrap(); + let contract = file.items.iter().find(|item| match item { + ast_extractor::Item::Contract(_) => true, + _ => false, + }); + let contract = match contract { + Some(ast_extractor::Item::Contract(contract)) => contract, + _ => panic!("No contract found"), + }; + let function = contract.body.iter().find(|item| match item { + ast_extractor::Item::Function(_) => true, + _ => false, + }); + let function = match function { + Some(ast_extractor::Item::Function(function)) => function, + _ => panic!("No function found"), + }; + visitor.visit_item_function(function); + assert_eq!( + visitor.file_reference.borrow().contracts[0] + .borrow() + .functions + .len(), + 1 + ); + } + + #[test] + fn test_retrieve_contract_structs() { + let mut path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); + path.push("tests"); + path.push("structs"); + path.push("contract.sol"); + let path_str = path.to_str().unwrap().to_string(); + let source = fs::read_to_string(&path).unwrap(); + + let mut visitor = FileVisitor::new(path_str.clone()); + let contract_ref = ContractReference::new( + "Good".to_string(), + Location::new(path_str, Bound { line: 1, column: 1 }, Bound::new(1, 10)), + &visitor.file_reference, + ); + visitor + .file_reference + .borrow_mut() + .add_contract(contract_ref); + visitor.current_contract = Some(visitor.file_reference.borrow().contracts[0].clone()); + let file = ast_extractor::extract::extract_ast_from_content(&source).unwrap(); + let contract = file.items.iter().find(|item| match item { + ast_extractor::Item::Contract(_) => true, + _ => false, + }); + let contract = match contract { + Some(ast_extractor::Item::Contract(contract)) => contract, + _ => panic!("No contract found"), + }; + let structs = contract.body.iter().find(|item| match item { + ast_extractor::Item::Struct(_) => true, + _ => false, + }); + let structs = match structs { + Some(ast_extractor::Item::Struct(structs)) => structs, + _ => panic!("No structs found"), + }; + visitor.visit_item_struct(structs); + assert_eq!( + visitor.file_reference.borrow().contracts[0] + .borrow() + .structs + .len(), + 1 + ); + } + + #[test] + fn test_retrieve_file_structs() { + let mut path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); + path.push("tests"); + path.push("structs"); + path.push("file.sol"); + let path_str = path.to_str().unwrap().to_string(); + let source = fs::read_to_string(&path).unwrap(); + + let mut visitor = FileVisitor::new(path_str.clone()); + let file = ast_extractor::extract::extract_ast_from_content(&source).unwrap(); + let structs = file.items.iter().find(|item| match item { + ast_extractor::Item::Struct(_) => true, + _ => false, + }); + let structs = match structs { + Some(ast_extractor::Item::Struct(var)) => var, + _ => panic!("Expect structs declaration"), + }; + visitor.visit_item_struct(structs); + assert_eq!(visitor.file_reference.borrow().structs.len(), 1); + } + + #[test] + fn test_retrieve_errors() { + let mut path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); + path.push("tests"); + path.push("errors"); + path.push("contract.sol"); + let path_str = path.to_str().unwrap().to_string(); + let source = fs::read_to_string(&path).unwrap(); + + let mut visitor = FileVisitor::new(path_str.clone()); + let file = ast_extractor::extract::extract_ast_from_content(&source).unwrap(); + let errors = file.items.iter().find(|item| match item { + ast_extractor::Item::Error(_) => true, + _ => false, + }); + let errors = match errors { + Some(ast_extractor::Item::Error(var)) => var, + _ => panic!("Expect errors declaration"), + }; + visitor.visit_item_error(errors); + assert_eq!(visitor.file_reference.borrow().errors.len(), 1); + } +} diff --git a/libs/ast-references/src/lib.rs b/libs/ast-references/src/lib.rs new file mode 100644 index 00000000..03782f9a --- /dev/null +++ b/libs/ast-references/src/lib.rs @@ -0,0 +1,2 @@ +pub mod file_retriever; +pub mod types; diff --git a/libs/ast-references/src/types.rs b/libs/ast-references/src/types.rs new file mode 100644 index 00000000..9ebda400 --- /dev/null +++ b/libs/ast-references/src/types.rs @@ -0,0 +1,9 @@ +pub mod contract_reference; +pub mod enum_reference; +pub mod error_reference; +pub mod event_reference; +pub mod file_reference; +pub mod function_reference; +pub mod location; +pub mod variable_reference; +pub mod struct_reference; \ No newline at end of file diff --git a/libs/ast-references/src/types/contract_reference.rs b/libs/ast-references/src/types/contract_reference.rs new file mode 100644 index 00000000..819a66a4 --- /dev/null +++ b/libs/ast-references/src/types/contract_reference.rs @@ -0,0 +1,293 @@ +use crate::types::file_reference::FileReference; +use crate::types::function_reference::FunctionReference; +use crate::types::location::Location; +use crate::types::struct_reference::StructReference; +use crate::types::variable_reference::VariableReference; +use std::cell::RefCell; +/** + * ContractReference.rs + * Definition of ContractReference struct + * author: 0xMemoryGrinder +*/ +use std::fmt; +use std::hash::{Hash, Hasher}; +use std::rc::Rc; + +use super::enum_reference::EnumReference; +use super::error_reference::ErrorReference; +use super::event_reference::EventReference; + +/****************************************************************************** + * Types * + *****************************************************************************/ + +pub struct ContractReference { + pub name: String, + pub location: Location, + pub file: Rc>, + pub structs: Vec>>, + pub functions: Vec>>, + pub properties: Vec>>, + pub errors: Vec>>, + pub events: Vec>>, + pub enums: Vec>>, +} + +/****************************************************************************** + * Methods / Trait implementation * + *****************************************************************************/ + +impl ContractReference { + pub fn new( + name: String, + location: Location, + file: &Rc>, + ) -> ContractReference { + ContractReference { + name: name, + location: location, + file: file.clone(), + structs: Vec::new(), + functions: Vec::new(), + properties: Vec::new(), + errors: Vec::new(), + events: Vec::new(), + enums: Vec::new(), + } + } + + pub fn add_struct(&mut self, strct: &Rc>) { + self.structs.push(strct.clone()); + } + + pub fn add_function(&mut self, function: &Rc>) { + self.functions.push(function.clone()); + } + + pub fn add_property(&mut self, property: &Rc>) { + self.properties.push(property.clone()); + } + + pub fn add_error(&mut self, error: &Rc>) { + self.errors.push(error.clone()); + } + + pub fn add_event(&mut self, event: &Rc>) { + self.events.push(event.clone()); + } + + pub fn add_enum(&mut self, enums: &Rc>) { + self.enums.push(enums.clone()); + } +} + +impl fmt::Display for ContractReference { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Contract {} at {}", self.name, self.location) + } +} + +impl fmt::Debug for ContractReference { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Contract {} at {}", self.name, self.location) + } +} + +impl PartialEq for ContractReference { + fn eq(&self, other: &ContractReference) -> bool { + self.name == other.name && self.location == other.location && self.file == other.file + } +} + +impl Eq for ContractReference {} + +impl Hash for ContractReference { + fn hash(&self, state: &mut H) { + self.name.hash(state); + self.location.hash(state); + } +} + +/****************************************************************************** + * Tests * + *****************************************************************************/ + +#[cfg(test)] +mod tests { + use std::cell::RefCell; + + use proc_macro2::Span; + use ast_extractor::{kw::function, Type}; + + use crate::types::location::Bound; + + use super::*; + + #[test] + fn new_good_construct() { + let file = Rc::new(RefCell::new(FileReference::new("File.test".to_string()))); + let result = ContractReference::new( + "Test".to_string(), + Location::new( + "File.test".to_string(), + Bound { line: 0, column: 0 }, + Bound { line: 0, column: 0 }, + ), + &file, + ); + + assert_eq!(result.file, file); + assert_eq!(result.name, "Test"); + assert_eq!(result.location.file, "File.test"); + assert_eq!(result.location.start.line, 0); + assert_eq!(result.location.start.column, 0); + assert_eq!(result.location.end.line, 0); + assert_eq!(result.location.end.column, 0); + } + + #[test] + fn add_struct() { + let file = Rc::new(RefCell::new(FileReference::new("File.test".to_string()))); + let result = Rc::new(RefCell::new(ContractReference::new( + "Test".to_string(), + Location::new( + "File.test".to_string(), + Bound { line: 0, column: 0 }, + Bound { line: 0, column: 0 }, + ), + &file, + ))); + let strct = Rc::new(RefCell::new(StructReference::new( + "TestStruct".to_string(), + Location::new( + "File.test".to_string(), + Bound { line: 0, column: 0 }, + Bound { line: 0, column: 0 }, + ), + Some(&result), + Some(&file), + ))); + + (*result).borrow_mut().add_struct(&strct); + + assert_eq!(result.borrow().structs.len(), 1); + assert_eq!(result.borrow().structs[0], strct); + } + + #[test] + fn add_function() { + let file = Rc::new(RefCell::new(FileReference::new("File.test".to_string()))); + let result = Rc::new(RefCell::new(ContractReference::new( + "Test".to_string(), + Location::new( + "File.test".to_string(), + Bound { line: 0, column: 0 }, + Bound { line: 0, column: 0 }, + ), + &file, + ))); + let function = Rc::new(RefCell::new(FunctionReference::new( + "TestFunction".to_string(), + ast_extractor::FunctionKind::Function(function(proc_macro2::Span::call_site())), + Location::new( + "File.test".to_string(), + Bound { line: 0, column: 0 }, + Bound { line: 0, column: 0 }, + ), + &result, + ))); + + (*result).borrow_mut().add_function(&function); + + assert_eq!(result.borrow().functions.len(), 1); + assert_eq!(result.borrow().functions[0], function); + } + + #[test] + fn add_property() { + let file = Rc::new(RefCell::new(FileReference::new("File.test".to_string()))); + let result = Rc::new(RefCell::new(ContractReference::new( + "Test".to_string(), + Location::new( + "File.test".to_string(), + Bound { line: 0, column: 0 }, + Bound { line: 0, column: 0 }, + ), + &file, + ))); + let property = Rc::new(RefCell::new(VariableReference::new( + "TestProperty".to_string(), + Type::Bool(Span::call_site()), + Location::new( + "File.test".to_string(), + Bound { line: 0, column: 0 }, + Bound { line: 0, column: 0 }, + ), + Some(&result), + None, + ))); + + (*result).borrow_mut().add_property(&property); + + assert_eq!(result.borrow().properties.len(), 1); + assert_eq!(result.borrow().properties[0], property); + } + + #[test] + fn add_error() { + let file = Rc::new(RefCell::new(FileReference::new("File.test".to_string()))); + let result = Rc::new(RefCell::new(ContractReference::new( + "Test".to_string(), + Location::new( + "File.test".to_string(), + Bound { line: 0, column: 0 }, + Bound { line: 0, column: 0 }, + ), + &file, + ))); + let error = Rc::new(RefCell::new(ErrorReference::new( + "TestError".to_string(), + Location::new( + "File.test".to_string(), + Bound { line: 0, column: 0 }, + Bound { line: 0, column: 0 }, + ), + Some(&result), + None, + ))); + + (*result).borrow_mut().add_error(&error); + + assert_eq!(result.borrow().errors.len(), 1); + assert_eq!(result.borrow().errors[0], error); + } + + #[test] + fn add_event() { + let file = Rc::new(RefCell::new(FileReference::new("File.test".to_string()))); + let result = Rc::new(RefCell::new(ContractReference::new( + "Test".to_string(), + Location::new( + "File.test".to_string(), + Bound { line: 0, column: 0 }, + Bound { line: 0, column: 0 }, + ), + &file, + ))); + let event = Rc::new(RefCell::new(EventReference::new( + "TestEvent".to_string(), + Location::new( + "File.test".to_string(), + Bound { line: 0, column: 0 }, + Bound { line: 0, column: 0 }, + ), + Some(&result), + None, + ))); + + (*result).borrow_mut().add_event(&event); + + assert_eq!(result.borrow().events.len(), 1); + assert_eq!(result.borrow().events[0], event); + } +} diff --git a/libs/ast-references/src/types/enum_reference.rs b/libs/ast-references/src/types/enum_reference.rs new file mode 100644 index 00000000..5fb40685 --- /dev/null +++ b/libs/ast-references/src/types/enum_reference.rs @@ -0,0 +1,210 @@ +/** + * EnumReference.rs + * Definition of EnumReference struct + * author: 0xMemoryGrinder + */ + +use std::fmt; +use std::rc::Rc; +use std::cell::RefCell; +use crate::types::location::Location; +use crate::types::file_reference::FileReference; +use crate::types::contract_reference::ContractReference; + +/****************************************************************************** + * Types * + *****************************************************************************/ + +pub struct EnumValueReference { + pub name: String, + pub location: Location, +} + +pub struct EnumReference { + pub name: String, + pub location: Location, + pub values : Vec, + pub contract: Option>>, + pub file: Option>>, +} + +/****************************************************************************** + * Methods / Trait implementation * + *****************************************************************************/ + + impl EnumReference { + pub fn new(name: String, location: Location, contract: Option<&Rc>>, file: Option<&Rc>>) -> EnumReference { + EnumReference { + name: name, + location: location, + values: Vec::new(), + contract: match contract { + Some(c) => Some(c.clone()), + None => None, + }, + file: match file { + Some(f) => Some(f.clone()), + None => None, + }, + } + } + + pub fn add_value(&mut self, name: String, location: Location) { + self.values.push(EnumValueReference { + name: name, + location: location, + }); + } + } + +impl fmt::Display for EnumReference { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Enum {} at {}", self.name, self.location) + } +} + +impl fmt::Debug for EnumReference { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Enum {} at {}", self.name, self.location) + } +} + +impl PartialEq for EnumReference { + fn eq(&self, other: &EnumReference) -> bool { + self.name == other.name && self.location == other.location && self.values == other.values && self.file == other.file + } +} + +impl PartialEq for EnumValueReference { + fn eq(&self, other: &EnumValueReference) -> bool { + self.name == other.name && self.location == other.location + } +} + +/****************************************************************************** + * Tests * + *****************************************************************************/ + +#[cfg(test)] +mod tests { + use crate::types::location::Bound; + + use super::*; + + #[test] + fn new_good_construct() { + let result = EnumReference::new( + String::from("Test"), + Location::new( + String::from("test.sol"), + Bound::new(0, 0), + Bound::new(0, 0), + ), + None, + None, + ); + + assert_eq!(result.name, "Test"); + assert_eq!(result.location.file, "test.sol"); + assert_eq!(result.location.start.line, 0); + assert_eq!(result.location.start.column, 0); + assert_eq!(result.location.end.line, 0); + assert_eq!(result.location.end.column, 0); + assert!(result.values.is_empty()); + } + + #[test] + fn new_contract_enum() { + let file = Rc::new(RefCell::new(FileReference::new("File.test".to_string()))); + let contract = ContractReference::new( + "Contract".to_string(), + Location::new( + "File.test".to_string(), + Bound::new(0, 0), + Bound::new(0, 0), + ), + &file, + ); + let result = EnumReference::new( + String::from("Test"), + Location::new( + String::from("test.sol"), + Bound::new(0, 0), + Bound::new(0, 0), + ), + Some(&Rc::new(RefCell::new(contract))), + None, + ); + + assert_eq!(result.name, "Test"); + assert_eq!(result.location.file, "test.sol"); + assert_eq!(result.location.start.line, 0); + assert_eq!(result.location.start.column, 0); + assert_eq!(result.location.end.line, 0); + assert_eq!(result.location.end.column, 0); + assert!(result.values.is_empty()); + + assert_eq!(result.contract.as_ref().unwrap().borrow().name, "Contract"); + assert_eq!(result.contract.as_ref().unwrap().borrow().location.file, "File.test".to_string()); + assert_eq!(result.contract.as_ref().unwrap().borrow().location.start.line, 0); + assert_eq!(result.contract.as_ref().unwrap().borrow().location.start.column, 0); + assert_eq!(result.contract.as_ref().unwrap().borrow().location.end.line, 0); + assert_eq!(result.contract.as_ref().unwrap().borrow().location.end.column, 0); + } + + #[test] + fn new_standalone_enum() { + let file = Rc::new(RefCell::new(FileReference::new("File.test".to_string()))); + let result = EnumReference::new( + String::from("Test"), + Location::new( + String::from("test.sol"), + Bound::new(0, 0), + Bound::new(0, 0), + ), + None, + Some(&file), + ); + + assert_eq!(result.name, "Test"); + assert_eq!(result.location.file, "test.sol"); + assert_eq!(result.location.start.line, 0); + assert_eq!(result.location.start.column, 0); + assert_eq!(result.location.end.line, 0); + assert_eq!(result.location.end.column, 0); + assert!(result.values.is_empty()); + + assert_eq!(result.file.as_ref().unwrap().borrow().path, "File.test"); + } + + #[test] + fn add_value() { + let mut result = EnumReference::new( + String::from("Test"), + Location::new( + String::from("test.sol"), + Bound::new(0, 0), + Bound::new(0, 0), + ), + None, + None, + ); + + result.add_value( + String::from("TestValue"), + Location::new( + String::from("test.sol"), + Bound::new(0, 0), + Bound::new(0, 0), + ), + ); + + assert_eq!(result.values.len(), 1); + assert_eq!(result.values[0].name, "TestValue"); + assert_eq!(result.values[0].location.file, "test.sol"); + assert_eq!(result.values[0].location.start.line, 0); + assert_eq!(result.values[0].location.start.column, 0); + assert_eq!(result.values[0].location.end.line, 0); + assert_eq!(result.values[0].location.end.column, 0); + } +} \ No newline at end of file diff --git a/libs/ast-references/src/types/error_reference.rs b/libs/ast-references/src/types/error_reference.rs new file mode 100644 index 00000000..8f48a929 --- /dev/null +++ b/libs/ast-references/src/types/error_reference.rs @@ -0,0 +1,220 @@ +/** + * ErrorReference.rs + * Definition of ErrorReference struct + * author: 0xMemoryGrinder + */ + +use std::fmt; +use std::rc::Rc; +use std::cell::RefCell; +use ast_extractor::{Type, Storage}; +use crate::types::location::Location; +use crate::types::file_reference::FileReference; +use crate::types::contract_reference::ContractReference; + +/****************************************************************************** + * Types * + *****************************************************************************/ + +pub struct ErrorParameterReference { + pub name: String, + pub ty: Type, + pub storage: Option, + pub location: Location, +} + +pub struct ErrorReference { + pub name: String, + pub location: Location, + pub parameters : Vec, + pub contract: Option>>, + pub file: Option>>, +} + +/****************************************************************************** + * Methods / Trait implementation * + *****************************************************************************/ + + impl ErrorReference { + pub fn new(name: String, location: Location, contract: Option<&Rc>>, file: Option<&Rc>>) -> ErrorReference { + ErrorReference { + name: name, + location: location, + parameters: Vec::new(), + contract: match contract { + Some(c) => Some(c.clone()), + None => None, + }, + file: match file { + Some(f) => Some(f.clone()), + None => None, + }, + } + } + + pub fn add_value(&mut self, name: String, ty: Type, storage: Option, location: Location) { + self.parameters.push(ErrorParameterReference { + name: name, + ty: ty, + storage: storage, + location: location, + }); + } + } + +impl fmt::Display for ErrorReference { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Error {} at {}", self.name, self.location) + } +} + +impl fmt::Debug for ErrorReference { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Error {} at {}", self.name, self.location) + } +} + +impl PartialEq for ErrorReference { + fn eq(&self, other: &ErrorReference) -> bool { + self.name == other.name && self.location == other.location && self.parameters == other.parameters && self.file == other.file + } +} + +impl PartialEq for ErrorParameterReference { + fn eq(&self, other: &ErrorParameterReference) -> bool { + self.name == other.name && self.location == other.location && self.ty == other.ty && self.storage == other.storage + } +} + +/****************************************************************************** + * Tests * + *****************************************************************************/ + +#[cfg(test)] +mod tests { + use std::num::NonZeroU16; + use proc_macro2::Span; + + use crate::types::location::Bound; + + use super::*; + + #[test] + fn new_good_construct() { + let result = ErrorReference::new( + String::from("Test"), + Location::new( + String::from("test.sol"), + Bound::new(0, 0), + Bound::new(0, 0), + ), + None, + None, + ); + + assert_eq!(result.name, "Test"); + assert_eq!(result.location.file, "test.sol"); + assert_eq!(result.location.start.line, 0); + assert_eq!(result.location.start.column, 0); + assert_eq!(result.location.end.line, 0); + assert_eq!(result.location.end.column, 0); + assert!(result.parameters.is_empty()); + } + + #[test] + fn new_contract_enum() { + let file = Rc::new(RefCell::new(FileReference::new("File.test".to_string()))); + let contract = ContractReference::new( + "Contract".to_string(), + Location::new( + "File.test".to_string(), + Bound::new(0, 0), + Bound::new(0, 0), + ), + &file, + ); + let result = ErrorReference::new( + String::from("Test"), + Location::new( + String::from("test.sol"), + Bound::new(0, 0), + Bound::new(0, 0), + ), + Some(&Rc::new(RefCell::new(contract))), + None, + ); + + assert_eq!(result.name, "Test"); + assert_eq!(result.location.file, "test.sol"); + assert_eq!(result.location.start.line, 0); + assert_eq!(result.location.start.column, 0); + assert_eq!(result.location.end.line, 0); + assert_eq!(result.location.end.column, 0); + assert!(result.parameters.is_empty()); + + assert_eq!(result.contract.as_ref().unwrap().borrow().name, "Contract"); + assert_eq!(result.contract.as_ref().unwrap().borrow().location.file, "File.test".to_string()); + assert_eq!(result.contract.as_ref().unwrap().borrow().location.start.line, 0); + assert_eq!(result.contract.as_ref().unwrap().borrow().location.start.column, 0); + assert_eq!(result.contract.as_ref().unwrap().borrow().location.end.line, 0); + assert_eq!(result.contract.as_ref().unwrap().borrow().location.end.column, 0); + } + + #[test] + fn new_standalone_enum() { + let file = Rc::new(RefCell::new(FileReference::new("File.test".to_string()))); + let result = ErrorReference::new( + String::from("Test"), + Location::new( + String::from("test.sol"), + Bound::new(0, 0), + Bound::new(0, 0), + ), + None, + Some(&file), + ); + + assert_eq!(result.name, "Test"); + assert_eq!(result.location.file, "test.sol"); + assert_eq!(result.location.start.line, 0); + assert_eq!(result.location.start.column, 0); + assert_eq!(result.location.end.line, 0); + assert_eq!(result.location.end.column, 0); + assert!(result.parameters.is_empty()); + + assert_eq!(result.file.as_ref().unwrap().borrow().path, "File.test"); + } + + #[test] + fn add_value() { + let mut result = ErrorReference::new( + String::from("Test"), + Location::new( + String::from("test.sol"), + Bound::new(0, 0), + Bound::new(0, 0), + ), + None, + None, + ); + + result.add_value( + String::from("TestValue"), + Type::Uint(Span::call_site(), NonZeroU16::new(256)), + None, + Location::new( + String::from("test.sol"), + Bound::new(0, 0), + Bound::new(0, 0), + ), + ); + + assert_eq!(result.parameters.len(), 1); + assert_eq!(result.parameters[0].name, "TestValue"); + assert_eq!(result.parameters[0].location.file, "test.sol"); + assert_eq!(result.parameters[0].location.start.line, 0); + assert_eq!(result.parameters[0].location.start.column, 0); + assert_eq!(result.parameters[0].location.end.line, 0); + assert_eq!(result.parameters[0].location.end.column, 0); + } +} \ No newline at end of file diff --git a/libs/ast-references/src/types/event_reference.rs b/libs/ast-references/src/types/event_reference.rs new file mode 100644 index 00000000..b525c3ad --- /dev/null +++ b/libs/ast-references/src/types/event_reference.rs @@ -0,0 +1,220 @@ +/** + * EventReference.rs + * Definition of EventReference struct + * author: 0xMemoryGrinder + */ + +use std::fmt; +use std::rc::Rc; +use std::cell::RefCell; +use ast_extractor::{Type, Storage}; +use crate::types::location::Location; +use crate::types::file_reference::FileReference; +use crate::types::contract_reference::ContractReference; + +/****************************************************************************** + * Types * + *****************************************************************************/ + +pub struct EventParameterReference { + pub name: String, + pub ty: Type, + pub storage: Option, + pub location: Location, +} + +pub struct EventReference { + pub name: String, + pub location: Location, + pub parameters : Vec, + pub contract: Option>>, + pub file: Option>>, +} + +/****************************************************************************** + * Methods / Trait implementation * + *****************************************************************************/ + + impl EventReference { + pub fn new(name: String, location: Location, contract: Option<&Rc>>, file: Option<&Rc>>) -> EventReference { + EventReference { + name: name, + location: location, + parameters: Vec::new(), + contract: match contract { + Some(c) => Some(c.clone()), + None => None, + }, + file: match file { + Some(f) => Some(f.clone()), + None => None, + }, + } + } + + pub fn add_value(&mut self, name: String, ty: Type, storage: Option, location: Location) { + self.parameters.push(EventParameterReference { + name: name, + ty: ty, + storage: storage, + location: location, + }); + } + } + +impl fmt::Display for EventReference { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Event {} at {}", self.name, self.location) + } +} + +impl fmt::Debug for EventReference { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Event {} at {}", self.name, self.location) + } +} + +impl PartialEq for EventReference { + fn eq(&self, other: &EventReference) -> bool { + self.name == other.name && self.location == other.location && self.parameters == other.parameters && self.file == other.file + } +} + +impl PartialEq for EventParameterReference { + fn eq(&self, other: &EventParameterReference) -> bool { + self.name == other.name && self.location == other.location && self.ty == other.ty && self.storage == other.storage + } +} + +/****************************************************************************** + * Tests * + *****************************************************************************/ + +#[cfg(test)] +mod tests { + use std::num::NonZeroU16; + use proc_macro2::Span; + + use crate::types::location::Bound; + + use super::*; + + #[test] + fn new_good_construct() { + let result = EventReference::new( + String::from("Test"), + Location::new( + String::from("test.sol"), + Bound::new(0, 0), + Bound::new(0, 0), + ), + None, + None, + ); + + assert_eq!(result.name, "Test"); + assert_eq!(result.location.file, "test.sol"); + assert_eq!(result.location.start.line, 0); + assert_eq!(result.location.start.column, 0); + assert_eq!(result.location.end.line, 0); + assert_eq!(result.location.end.column, 0); + assert!(result.parameters.is_empty()); + } + + #[test] + fn new_contract_enum() { + let file = Rc::new(RefCell::new(FileReference::new("File.test".to_string()))); + let contract = ContractReference::new( + "Contract".to_string(), + Location::new( + "File.test".to_string(), + Bound::new(0, 0), + Bound::new(0, 0), + ), + &file, + ); + let result = EventReference::new( + String::from("Test"), + Location::new( + String::from("test.sol"), + Bound::new(0, 0), + Bound::new(0, 0), + ), + Some(&Rc::new(RefCell::new(contract))), + None, + ); + + assert_eq!(result.name, "Test"); + assert_eq!(result.location.file, "test.sol"); + assert_eq!(result.location.start.line, 0); + assert_eq!(result.location.start.column, 0); + assert_eq!(result.location.end.line, 0); + assert_eq!(result.location.end.column, 0); + assert!(result.parameters.is_empty()); + + assert_eq!(result.contract.as_ref().unwrap().borrow().name, "Contract"); + assert_eq!(result.contract.as_ref().unwrap().borrow().location.file, "File.test".to_string()); + assert_eq!(result.contract.as_ref().unwrap().borrow().location.start.line, 0); + assert_eq!(result.contract.as_ref().unwrap().borrow().location.start.column, 0); + assert_eq!(result.contract.as_ref().unwrap().borrow().location.end.line, 0); + assert_eq!(result.contract.as_ref().unwrap().borrow().location.end.column, 0); + } + + #[test] + fn new_standalone_enum() { + let file = Rc::new(RefCell::new(FileReference::new("File.test".to_string()))); + let result = EventReference::new( + String::from("Test"), + Location::new( + String::from("test.sol"), + Bound::new(0, 0), + Bound::new(0, 0), + ), + None, + Some(&file), + ); + + assert_eq!(result.name, "Test"); + assert_eq!(result.location.file, "test.sol"); + assert_eq!(result.location.start.line, 0); + assert_eq!(result.location.start.column, 0); + assert_eq!(result.location.end.line, 0); + assert_eq!(result.location.end.column, 0); + assert!(result.parameters.is_empty()); + + assert_eq!(result.file.as_ref().unwrap().borrow().path, "File.test"); + } + + #[test] + fn add_value() { + let mut result = EventReference::new( + String::from("Test"), + Location::new( + String::from("test.sol"), + Bound::new(0, 0), + Bound::new(0, 0), + ), + None, + None, + ); + + result.add_value( + String::from("TestValue"), + Type::Uint(Span::call_site(), NonZeroU16::new(256)), + None, + Location::new( + String::from("test.sol"), + Bound::new(0, 0), + Bound::new(0, 0), + ), + ); + + assert_eq!(result.parameters.len(), 1); + assert_eq!(result.parameters[0].name, "TestValue"); + assert_eq!(result.parameters[0].location.file, "test.sol"); + assert_eq!(result.parameters[0].location.start.line, 0); + assert_eq!(result.parameters[0].location.start.column, 0); + assert_eq!(result.parameters[0].location.end.line, 0); + assert_eq!(result.parameters[0].location.end.column, 0); + } +} \ No newline at end of file diff --git a/libs/ast-references/src/types/file_reference.rs b/libs/ast-references/src/types/file_reference.rs new file mode 100644 index 00000000..83a51642 --- /dev/null +++ b/libs/ast-references/src/types/file_reference.rs @@ -0,0 +1,175 @@ +use std::cell::RefCell; +/** + * FileReference.rs + * Definition of FileReference struct + * author: 0xMemoryGrinder +*/ + +use std::fmt; +use std::hash::{Hash, Hasher}; +use std::rc::Rc; +use super::contract_reference::ContractReference; +use super::struct_reference::StructReference; +use super::enum_reference::EnumReference; +use super::error_reference::ErrorReference; +use super::event_reference::EventReference; +use super::variable_reference::VariableReference; + +/****************************************************************************** + * Types * + *****************************************************************************/ + +pub struct FileReference { + pub path: String, + pub contracts: Vec>>, + pub structs: Vec>>, + pub enums: Vec>>, + pub errors: Vec>>, + pub events: Vec>>, + pub variables: Vec>>, +} + +/****************************************************************************** + * Methods / Trait implementation * + *****************************************************************************/ + +impl FileReference { + pub fn new(path: String) -> FileReference { + FileReference { + path: path, + contracts: Vec::new(), + structs: Vec::new(), + enums: Vec::new(), + errors: Vec::new(), + events: Vec::new(), + variables: Vec::new(), + } + } + + pub fn add_contract(&mut self, contract: ContractReference) { + self.contracts.push(Rc::new(RefCell::new(contract))); + } + + pub fn add_struct(&mut self, strct: StructReference) { + self.structs.push(Rc::new(RefCell::new(strct))); + } + + pub fn add_enum(&mut self, enm: EnumReference) { + self.enums.push(Rc::new(RefCell::new(enm))); + } + + pub fn add_error(&mut self, error: ErrorReference) { + self.errors.push(Rc::new(RefCell::new(error))); + } + + pub fn add_event(&mut self, event: EventReference) { + self.events.push(Rc::new(RefCell::new(event))); + } + + pub fn add_variable(&mut self, variable: VariableReference) { + self.variables.push(Rc::new(RefCell::new(variable))); + } +} + +impl fmt::Display for FileReference { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "File {}", self.path) + } +} + +impl fmt::Debug for FileReference { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "File {}", self.path) + } +} + +impl PartialEq for FileReference { + fn eq(&self, other: &FileReference) -> bool { + self.path == other.path + } +} + +impl Eq for FileReference {} + +impl Hash for FileReference { + fn hash(&self, state: &mut H) { + self.path.hash(state); + } +} + + +/****************************************************************************** + * Tests * + *****************************************************************************/ + + #[cfg(test)] + mod tests { + use std::cell::RefCell; + use proc_macro2::Span; + use ast_extractor::Type; + + use crate::types::location::{Bound, Location}; + + use super::*; + + #[test] + fn new_good_construct() { + let file = Rc::new(RefCell::new(FileReference::new("File.test".to_string()))); + + assert_eq!(file.borrow().path, "File.test"); + } + + #[test] + fn add_contract() { + let file = Rc::new(RefCell::new(FileReference::new("File.test".to_string()))); + let contract = ContractReference::new("Contract".to_string(), Location::new("File.test".to_string(), Bound::new(0, 0), Bound::new(0, 0)), &file); + file.borrow_mut().add_contract(contract); + + assert_eq!(file.borrow().contracts.len(), 1); + } + + #[test] + fn add_struct() { + let file = Rc::new(RefCell::new(FileReference::new("File.test".to_string()))); + let strct = StructReference::new("Struct".to_string(), Location::new("File.test".to_string(), Bound::new(0, 0), Bound::new(0, 0)), None, Some(&file)); + file.borrow_mut().add_struct(strct); + + assert_eq!(file.borrow().structs.len(), 1); + } + + #[test] + fn add_enum() { + let file = Rc::new(RefCell::new(FileReference::new("File.test".to_string()))); + let enm = EnumReference::new("Enum".to_string(), Location::new("File.test".to_string(), Bound::new(0, 0), Bound::new(0, 0)), None, Some(&file)); + file.borrow_mut().add_enum(enm); + + assert_eq!(file.borrow().enums.len(), 1); + } + + #[test] + fn add_error() { + let file = Rc::new(RefCell::new(FileReference::new("File.test".to_string()))); + let error = ErrorReference::new("Error".to_string(), Location::new("File.test".to_string(), Bound::new(0, 0), Bound::new(0, 0)), None, None); + file.borrow_mut().add_error(error); + + assert_eq!(file.borrow().errors.len(), 1); + } + + #[test] + fn add_event() { + let file = Rc::new(RefCell::new(FileReference::new("File.test".to_string()))); + let event = EventReference::new("Event".to_string(), Location::new("File.test".to_string(), Bound::new(0, 0), Bound::new(0, 0)), None, None); + file.borrow_mut().add_event(event); + + assert_eq!(file.borrow().events.len(), 1); + } + + #[test] + fn add_variable() { + let file = Rc::new(RefCell::new(FileReference::new("File.test".to_string()))); + let variable = VariableReference::new("Variable".to_string(), Type::Bool(Span::call_site()), Location::new("File.test".to_string(), Bound::new(0, 0), Bound::new(0, 0)), None, None); + file.borrow_mut().add_variable(variable); + + assert_eq!(file.borrow().variables.len(), 1); + } +} \ No newline at end of file diff --git a/libs/ast-references/src/types/function_reference.rs b/libs/ast-references/src/types/function_reference.rs new file mode 100644 index 00000000..bda6123d --- /dev/null +++ b/libs/ast-references/src/types/function_reference.rs @@ -0,0 +1,90 @@ +use std::cell::RefCell; +/** + * FunctionReference.rs + * Definition of FuntionReference struct + * author: 0xMemoryGrinder +*/ + +use std::fmt; +use std::rc::Rc; +use ast_extractor::FunctionKind; + +use crate::types::location::Location; +use crate::types::contract_reference::ContractReference; + +/****************************************************************************** + * Types * + *****************************************************************************/ + +pub struct FunctionReference { + pub name: String, + pub kind: FunctionKind, + pub location: Location, + pub contract: Rc>, +} + +/****************************************************************************** + * Methods / Trait implementation * + *****************************************************************************/ + + impl FunctionReference { + pub fn new(name: String, kind: FunctionKind, location: Location, contract: &Rc>) -> FunctionReference { + FunctionReference { + name: name, + kind: kind, + location: location, + contract: contract.clone(), + } + } + } + +impl fmt::Display for FunctionReference { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Function {} at {}", self.name, self.location) + } +} + +impl fmt::Debug for FunctionReference { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Function {} at {} in contract {:?}", self.name, self.location, self.contract) + } +} + +impl PartialEq for FunctionReference { + fn eq(&self, other: &FunctionReference) -> bool { + self.name == other.name && self.location == other.location && self.contract == other.contract + } +} + +/****************************************************************************** + * Tests * + *****************************************************************************/ + + #[cfg(test)] + mod tests { + use std::cell::RefCell; + use proc_macro2::Span; + use ast_extractor::kw::function; + + use crate::types::{location::{Bound, Location}, file_reference::FileReference}; + + use super::*; + + #[test] + fn new_good_construct() { + let file = Rc::new(RefCell::new(FileReference::new("Test.sol".to_string()))); + let contract = Rc::new(RefCell::new(ContractReference::new("contract".to_string(), Location::new("Test.sol".to_string(), Bound::new(0, 0), Bound::new(0, 0)), &file))); + let function = FunctionReference::new( + "function".to_string(), + FunctionKind::Function(function(Span::call_site())), + Location::new("Test.sol".to_string(), + Bound::new(0, 0), + Bound::new(0, 0)), + &contract + ); + + assert_eq!(function.name, "function".to_string()); + assert_eq!(function.location, Location::new("Test.sol".to_string(), Bound::new(0, 0), Bound::new(0, 0))); + assert_eq!(function.contract, contract); + } +} \ No newline at end of file diff --git a/libs/ast-references/src/types/location.rs b/libs/ast-references/src/types/location.rs new file mode 100644 index 00000000..9a7b9939 --- /dev/null +++ b/libs/ast-references/src/types/location.rs @@ -0,0 +1,144 @@ +/** + * Location.rs + * Definition of Location and Bound structs + * author: 0xMemoryGrinder +*/ + +use std::fmt; +use std::hash::{Hash, Hasher}; + +/****************************************************************************** + * Types * + *****************************************************************************/ + +pub struct Bound { + pub line: u32, + pub column: u32, +} + +pub struct Location { + pub file: String, // path of the file + pub start: Bound, + pub end: Bound, +} + +/****************************************************************************** + * Methods / Trait implementation * + *****************************************************************************/ + +impl Bound { + pub fn new(line: u32, column: u32) -> Bound { + Bound { + line: line, + column: column, + } + } +} + +impl Location { + pub fn new(file: String, start: Bound, end: Bound) -> Location { + Location { + file: file, + start: start, + end: end, + } + } +} + +impl fmt::Display for Location { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}:({}:{}-{}:{})", self.file, self.start.line, self.start.column, self.end.line, self.end.column) + } +} + +impl fmt::Debug for Location { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}:({}:{}-{}:{})", self.file, self.start.line, self.start.column, self.end.line, self.end.column) + } +} + +impl PartialEq for Location { + fn eq(&self, other: &Location) -> bool { + self.file == other.file && self.start == other.start && self.end == other.end + } +} + +impl PartialEq for Bound { + fn eq(&self, other: &Bound) -> bool { + self.line == other.line && self.column == other.column + } +} + +impl Eq for Location {} + +impl Hash for Location { + fn hash(&self, state: &mut H) { + self.file.hash(state); + self.start.hash(state); + self.end.hash(state); + + } +} + +impl Hash for Bound { + fn hash(&self, state: &mut H) { + self.line.hash(state); + self.column.hash(state); + } +} + +impl Clone for Location { + fn clone(&self) -> Location { + Location { + file: self.file.clone(), + start: Bound { + line: self.start.line, + column: self.start.column, + }, + end: Bound { + line: self.end.line, + column: self.end.column, + }, + } + } +} + +impl Default for Location { + fn default() -> Location { + Location { + file: String::new(), + start: Bound { + line: 0, + column: 0, + }, + end: Bound { + line: 0, + column: 0, + }, + } + } +} + +/****************************************************************************** + * Tests * + *****************************************************************************/ + + #[cfg(test)] + mod tests { + use super::*; + + #[test] + fn new_good_construct() { + let result = Location::new( + String::from("test.sol"), + Bound::new(0, 0), + Bound::new(0, 0), + ); + + assert_eq!(result.file, "test.sol"); + assert_eq!(result.start.line, 0); + assert_eq!(result.start.column, 0); + assert_eq!(result.end.line, 0); + assert_eq!(result.end.column, 0); + } +} \ No newline at end of file diff --git a/libs/ast-references/src/types/struct_reference.rs b/libs/ast-references/src/types/struct_reference.rs new file mode 100644 index 00000000..89332755 --- /dev/null +++ b/libs/ast-references/src/types/struct_reference.rs @@ -0,0 +1,156 @@ +use std::cell::RefCell; +/** + * StructReference.rs + * Definition of StructReference struct + * author: 0xMemoryGrinder +*/ + +use std::fmt; +use std::rc::Rc; +use crate::types::location::Location; +use crate::types::file_reference::FileReference; +use crate::types::contract_reference::ContractReference; + +/****************************************************************************** + * Types * + *****************************************************************************/ + +pub struct StructReference { + pub name: String, + pub location: Location, + pub contract: Option>>, + pub file: Option>>, +} + +/****************************************************************************** + * Methods / Trait implementation * + *****************************************************************************/ + + impl StructReference { + pub fn new(name: String, location: Location, contract: Option<&Rc>>, file: Option<&Rc>>) -> StructReference { + StructReference { + name: name, + location: location, + contract: match contract { + Some(c) => Some(c.clone()), + None => None, + }, + file: match file { + Some(f) => Some(f.clone()), + None => None, + }, + } + } + } + +impl fmt::Display for StructReference { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Struct {} at {}", self.name, self.location) + } +} + +impl fmt::Debug for StructReference { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Struct {} at {}", self.name, self.location) + } +} + +impl PartialEq for StructReference { + fn eq(&self, other: &StructReference) -> bool { + self.name == other.name && self.location == other.location && self.contract == other.contract && self.file == other.file + } +} + +/****************************************************************************** + * Tests * + *****************************************************************************/ + +#[cfg(test)] +mod tests { + use crate::types::location::Bound; + + use super::*; + + #[test] + fn new_good_construct() { + let result = StructReference::new( + String::from("Test"), + Location::new( + String::from("test.sol"), + Bound::new(0, 0), + Bound::new(0, 0), + ), + None, + None, + ); + + assert_eq!(result.name, "Test"); + assert_eq!(result.location.file, "test.sol"); + assert_eq!(result.location.start.line, 0); + assert_eq!(result.location.start.column, 0); + assert_eq!(result.location.end.line, 0); + assert_eq!(result.location.end.column, 0); + } + + #[test] + fn new_contract_struct() { + let file = Rc::new(RefCell::new(FileReference::new("File.test".to_string()))); + let contract = ContractReference::new( + "Contract".to_string(), + Location::new( + "File.test".to_string(), + Bound::new(0, 0), + Bound::new(0, 0), + ), + &file, + ); + let result = StructReference::new( + String::from("Test"), + Location::new( + String::from("test.sol"), + Bound::new(0, 0), + Bound::new(0, 0), + ), + Some(&Rc::new(RefCell::new(contract))), + None, + ); + + assert_eq!(result.name, "Test"); + assert_eq!(result.location.file, "test.sol"); + assert_eq!(result.location.start.line, 0); + assert_eq!(result.location.start.column, 0); + assert_eq!(result.location.end.line, 0); + assert_eq!(result.location.end.column, 0); + + assert_eq!(result.contract.as_ref().unwrap().borrow().name, "Contract"); + assert_eq!(result.contract.as_ref().unwrap().borrow().location.file, "File.test".to_string()); + assert_eq!(result.contract.as_ref().unwrap().borrow().location.start.line, 0); + assert_eq!(result.contract.as_ref().unwrap().borrow().location.start.column, 0); + assert_eq!(result.contract.as_ref().unwrap().borrow().location.end.line, 0); + assert_eq!(result.contract.as_ref().unwrap().borrow().location.end.column, 0); + } + + #[test] + fn new_standalone_enum() { + let file = Rc::new(RefCell::new(FileReference::new("File.test".to_string()))); + let result = StructReference::new( + String::from("Test"), + Location::new( + String::from("test.sol"), + Bound::new(0, 0), + Bound::new(0, 0), + ), + None, + Some(&file), + ); + + assert_eq!(result.name, "Test"); + assert_eq!(result.location.file, "test.sol"); + assert_eq!(result.location.start.line, 0); + assert_eq!(result.location.start.column, 0); + assert_eq!(result.location.end.line, 0); + assert_eq!(result.location.end.column, 0); + + assert_eq!(result.file.as_ref().unwrap().borrow().path, "File.test"); + } +} \ No newline at end of file diff --git a/libs/ast-references/src/types/variable_reference.rs b/libs/ast-references/src/types/variable_reference.rs new file mode 100644 index 00000000..861a7a7d --- /dev/null +++ b/libs/ast-references/src/types/variable_reference.rs @@ -0,0 +1,105 @@ +/** + * VariableReference.rs + * Definition of VariableReference struct + * author: 0xMemoryGrinder + */ + +use std::fmt; +use std::rc::Rc; +use std::cell::RefCell; +use ast_extractor::Type; + +use crate::types::location::Location; +use crate::types::contract_reference::ContractReference; + +use super::file_reference::FileReference; + + +/****************************************************************************** + * Types * + *****************************************************************************/ + +pub struct VariableReference { + pub name: String, + pub ty: Type, + pub location: Location, + pub contract: Option>>, + pub file: Option>>, +} + +/****************************************************************************** + * Methods / Trait implementation * + *****************************************************************************/ + + impl VariableReference { + pub fn new(name: String, ty: Type, location: Location, contract: Option<&Rc>>, file: Option<&Rc>>) -> VariableReference { + VariableReference { + name: name, + ty, + location: location, + contract: match contract { + Some(c) => Some(c.clone()), + None => None, + }, + file: match file { + Some(f) => Some(f.clone()), + None => None, + }, + } + } + } + +impl fmt::Display for VariableReference { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Property {} at {}", self.name, self.location) + } +} + +impl fmt::Debug for VariableReference { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Property {} at {} in contract {:?}", self.name, self.location, self.contract) + } +} + +impl PartialEq for VariableReference { + fn eq(&self, other: &VariableReference) -> bool { + self.name == other.name && self.location == other.location && self.contract == other.contract + } +} + +/****************************************************************************** + * Tests * + *****************************************************************************/ + + #[cfg(test)] + mod tests { + use std::cell::RefCell; + use proc_macro2::Span; + + use crate::types::{location::{Bound, Location}, file_reference::FileReference}; + + use super::*; + + #[test] + fn new_good_construct() { + let file = Rc::new(RefCell::new(FileReference::new("test.sol".to_string()))); + let contract = Rc::new(RefCell::new(ContractReference::new("Test".to_string(), Location::new("test.sol".to_string(), Bound::new(0, 0), Bound::new(0, 0)), &file))); + let property = VariableReference::new("test".to_string(), Type::Bool(Span::call_site()), Location::new("test.sol".to_string(), Bound::new(0, 0), Bound::new(0, 0)), Some(&contract), None); + assert_eq!(property.name, "test"); + assert_eq!(property.ty, Type::Bool(Span::call_site())); + assert_eq!(property.location, Location::new("test.sol".to_string(), Bound::new(0, 0), Bound::new(0, 0))); + assert_eq!(property.contract, Some(contract)); + assert_eq!(property.file, None); + } + + #[test] + fn new_standalone_variable() { + let file = Rc::new(RefCell::new(FileReference::new("test.sol".to_string()))); + let property = VariableReference::new("test".to_string(), Type::Bool(Span::call_site()), Location::new("test.sol".to_string(), Bound::new(0, 0), Bound::new(0, 0)), None, Some(&file)); + assert_eq!(property.name, "test"); + assert_eq!(property.ty, Type::Bool(Span::call_site())); + assert_eq!(property.location, Location::new("test.sol".to_string(), Bound::new(0, 0), Bound::new(0, 0))); + assert_eq!(property.contract, None); + assert_eq!(property.file, Some(file)); + } +} \ No newline at end of file diff --git a/libs/ast-references/tests/contracts/file.sol b/libs/ast-references/tests/contracts/file.sol new file mode 100644 index 00000000..551bb919 --- /dev/null +++ b/libs/ast-references/tests/contracts/file.sol @@ -0,0 +1,3 @@ +pragma solidity ^0.8.0; + +contract Good {} diff --git a/libs/ast-references/tests/enums/contract.sol b/libs/ast-references/tests/enums/contract.sol new file mode 100644 index 00000000..49d3b990 --- /dev/null +++ b/libs/ast-references/tests/enums/contract.sol @@ -0,0 +1,9 @@ +pragma solidity ^0.8.0; + +contract Good { + enum State { + Waiting, + Ready, + Active + } +} diff --git a/libs/ast-references/tests/enums/file.sol b/libs/ast-references/tests/enums/file.sol new file mode 100644 index 00000000..42714f2d --- /dev/null +++ b/libs/ast-references/tests/enums/file.sol @@ -0,0 +1,7 @@ +pragma solidity ^0.8.0; + +enum State { + Waiting, + Ready, + Active +} diff --git a/libs/ast-references/tests/errors/contract.sol b/libs/ast-references/tests/errors/contract.sol new file mode 100644 index 00000000..194c9853 --- /dev/null +++ b/libs/ast-references/tests/errors/contract.sol @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +error InsufficientBalance(uint256 available, uint256 required); + +contract Good { +} diff --git a/libs/ast-references/tests/events/contract.sol b/libs/ast-references/tests/events/contract.sol new file mode 100644 index 00000000..5d9cd154 --- /dev/null +++ b/libs/ast-references/tests/events/contract.sol @@ -0,0 +1,5 @@ +pragma solidity ^0.8.0; + +contract Good { + event Deposit(address indexed _from, bytes32 indexed _id, uint256 _value); +} diff --git a/libs/ast-references/tests/functions/contract.sol b/libs/ast-references/tests/functions/contract.sol new file mode 100644 index 00000000..d66927ce --- /dev/null +++ b/libs/ast-references/tests/functions/contract.sol @@ -0,0 +1,7 @@ +pragma solidity ^0.8.0; + +contract Good { + function f() public pure returns (uint) { + return 1; + } +} diff --git a/libs/ast-references/tests/structs/contract.sol b/libs/ast-references/tests/structs/contract.sol new file mode 100644 index 00000000..7cb64f89 --- /dev/null +++ b/libs/ast-references/tests/structs/contract.sol @@ -0,0 +1,10 @@ +pragma solidity ^0.8.0; + +contract Good { + struct User { + uint id; + string name; + uint age; + uint balance; + } +} diff --git a/libs/ast-references/tests/structs/file.sol b/libs/ast-references/tests/structs/file.sol new file mode 100644 index 00000000..bcc1d2c1 --- /dev/null +++ b/libs/ast-references/tests/structs/file.sol @@ -0,0 +1,8 @@ +pragma solidity ^0.8.0; + +struct User { + uint id; + string name; + uint age; + uint balance; +} diff --git a/libs/ast-references/tests/variables/contract.sol b/libs/ast-references/tests/variables/contract.sol new file mode 100644 index 00000000..f85c3d4b --- /dev/null +++ b/libs/ast-references/tests/variables/contract.sol @@ -0,0 +1,5 @@ +pragma solidity ^0.8.0; + +contract Good { + uint256 public x; +} diff --git a/libs/ast-references/tests/variables/file.sol b/libs/ast-references/tests/variables/file.sol new file mode 100644 index 00000000..b065d6d0 --- /dev/null +++ b/libs/ast-references/tests/variables/file.sol @@ -0,0 +1,3 @@ +pragma solidity ^0.8.0; + +uint256 constant a = 1; diff --git a/remove-me-ed5dccf7608a4c40bb11.txt b/remove-me-ed5dccf7608a4c40bb11.txt new file mode 100644 index 00000000..75bea5bc --- /dev/null +++ b/remove-me-ed5dccf7608a4c40bb11.txt @@ -0,0 +1 @@ +ed5dccf7608a4c40bb11 diff --git a/toolchains/solidity/linter/core/Cargo.lock b/toolchains/solidity/linter/core/Cargo.lock index 715f9323..0d21a114 100644 --- a/toolchains/solidity/linter/core/Cargo.lock +++ b/toolchains/solidity/linter/core/Cargo.lock @@ -631,9 +631,9 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "0.3.2" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "397f229dc34c7b8231b6ef85502f9ca4e3425b8625e6d403bb74779e6b1917b5" +checksum = "1b8a5a633f1172a0c80b1516a988e7e8efa7ce9cededf56590f54e593e4513b3" dependencies = [ "paste", "proc-macro2",