Skip to content

Commit de1204d

Browse files
committed
Refactor + add JSON [de]serializartion support
1 parent 1383bb2 commit de1204d

File tree

13 files changed

+419
-424
lines changed

13 files changed

+419
-424
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,6 @@ soroban-security-rules-sdk = { path = "./sdk", version = "0.0.1" }
2222
soroban-security-rules-runner = { path = "./rules-runner", version = "0.0.1" }
2323
soroban-security-rules-macro-lib = { path = "./macro-lib", version = "0.0.1" }
2424

25-
syn = { version = "2.0.95", features = ["full"] }
25+
syn = { version = "2.0.96", features = ["full"] }
2626
quote = "1.0.38"
2727
proc-macro2 = { version = "1.0.92", features = ["span-locations"] }

macro-lib/src/lib.rs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@ use syn::{parse_macro_input, Ident, ItemStruct, LitStr};
1414
pub fn node_location(args: TokenStream, input: TokenStream) -> TokenStream {
1515
let input = parse_macro_input!(input as ItemStruct);
1616
let struct_name = &input.ident;
17-
let mut inner_field_name: Ident = Ident::new("inner_item", proc_macro2::Span::call_site());
17+
let mut inner_field_name: Ident = Ident::new("location", proc_macro2::Span::call_site());
1818

1919
let name_parser = syn::meta::parser(|meta| -> Result<(), syn::Error> {
20-
if meta.path.is_ident("inner") {
20+
if meta.path.is_ident("location") {
2121
let name: LitStr = meta.value()?.parse()?;
2222
inner_field_name = Ident::new(name.value().as_str(), inner_field_name.span());
2323
Ok(())
@@ -32,27 +32,31 @@ pub fn node_location(args: TokenStream, input: TokenStream) -> TokenStream {
3232
let expanded = quote! {
3333
#input
3434

35-
impl Location for #struct_name {
35+
impl TLocation for #struct_name {
3636
fn source_code(&self) -> Option<String> {
37-
self.#inner_field_name.span().source_text()
37+
match &self.#inner_field_name.source_code {
38+
Some(source_code) => Some(source_code.clone()),
39+
None => None,
40+
}
3841
}
3942

4043
fn start_line(&self) -> usize {
41-
self.#inner_field_name.span().start().line as usize
44+
self.#inner_field_name.start_line
4245
}
4346

4447
fn start_col(&self) -> usize {
45-
self.#inner_field_name.span().start().column as usize
48+
self.#inner_field_name.start_col
4649
}
4750

4851
fn end_line(&self) -> usize {
49-
self.#inner_field_name.span().end().line as usize
52+
self.#inner_field_name.end_line
5053
}
5154

5255
fn end_col(&self) -> usize {
53-
self.#inner_field_name.span().end().column as usize
56+
self.#inner_field_name.end_col
5457
}
5558
}
59+
5660
};
5761

5862
TokenStream::from(expanded)

rules/src/lib.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
use std::{cell::RefCell, collections::HashMap, rc::Rc};
22

3-
use soroban_security_rules_sdk::{function::Function, node::Location, Codebase, Rule, SealedState};
3+
use soroban_security_rules_sdk::{
4+
function::Function, node::TLocation, Codebase, Rule, SealedState,
5+
};
46

57
pub struct FileWithoutNoStd;
68

@@ -13,7 +15,7 @@ impl Rule for FileWithoutNoStd {
1315
let mut errors = HashMap::new();
1416
for file in codebase.files() {
1517
if !file.has_no_std() {
16-
errors.insert(file.name().to_string(), vec![(0, 0)]);
18+
errors.insert(file.name.to_string(), vec![(0, 0)]);
1719
}
1820
}
1921
if errors.is_empty() {
@@ -48,7 +50,7 @@ impl Rule for ContractWithoutFunctions {
4850
.is_empty()
4951
{
5052
errors.insert(
51-
contract.name().to_string(),
53+
contract.name.to_string(),
5254
vec![(contract.start_line(), contract.start_col())],
5355
);
5456
}

sdk/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ repository = { workspace = true }
88

99
[dependencies]
1010
thiserror = "2.0.9"
11-
serde = "1.0.217"
11+
serde = { version = "1.0.217", features = ["derive", "rc"] }
1212
serde_json = "1.0.135"
1313
uuid = { version = "1.1", features = ["v4"] }
1414
syn.workspace = true

sdk/src/ast/contract.rs

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,26 @@
11
#![warn(clippy::pedantic)]
22
use super::function::Function;
3-
use super::node::{Location, Node};
4-
use super::node_type::{ContractChildType, ContractParentType, NodeType};
3+
use super::node::{Location, Node, TLocation};
4+
use super::node_type::{ContractChildType, ContractParentType, NodeKind};
5+
56
use soroban_security_rules_macro_lib::node_location;
67
use std::cell::RefCell;
78
use std::rc::Rc;
8-
use syn::spanned::Spanned;
9-
use syn::ItemStruct;
109

11-
#[node_location(inner = "inner_struct")]
10+
#[node_location]
11+
#[derive(Clone, serde::Serialize, serde::Deserialize)]
1212
pub struct Contract {
1313
pub id: usize,
14-
pub(crate) inner_struct: Rc<ItemStruct>,
14+
pub location: Location,
15+
pub name: String,
1516
pub parent: ContractParentType,
1617
pub children: RefCell<Vec<ContractChildType>>,
1718
}
1819

1920
impl Node for Contract {
20-
fn parent(&self) -> Option<NodeType> {
21+
fn parent(&self) -> Option<NodeKind> {
2122
match &self.parent {
22-
ContractParentType::File(file) => Some(NodeType::File(file.clone())),
23+
ContractParentType::File(file) => Some(NodeKind::File(file.clone())),
2324
}
2425
}
2526

@@ -31,8 +32,8 @@ impl Node for Contract {
3132

3233
impl Contract {
3334
#[must_use]
34-
pub fn name(&self) -> String {
35-
self.inner_struct.ident.to_string()
35+
pub fn contract_name_from_syn_item(contract: &syn::ItemStruct) -> String {
36+
contract.ident.to_string()
3637
}
3738

3839
pub fn functions(&self) -> impl Iterator<Item = Rc<Function>> {
@@ -54,13 +55,16 @@ impl Contract {
5455

5556
#[cfg(test)]
5657
mod tests {
57-
use crate::utils::test::{
58-
create_mock_contract, create_mock_contract_with_inner_struct,
59-
create_mock_contract_with_parent, create_mock_file, create_mock_function,
58+
use crate::{
59+
location,
60+
utils::test::{
61+
create_mock_contract, create_mock_contract_with_inner_struct,
62+
create_mock_contract_with_parent, create_mock_file, create_mock_function,
63+
},
6064
};
6165

6266
use super::*;
63-
use syn::parse_quote;
67+
use syn::{parse_quote, ItemStruct};
6468

6569
#[test]
6670
fn test_contract_parent() {
@@ -70,7 +74,7 @@ mod tests {
7074
let parent = contract.parent();
7175
assert!(parent.is_some(), "Contract should have a parent node");
7276
match parent.unwrap() {
73-
NodeType::File(file) => {
77+
NodeKind::File(file) => {
7478
assert_eq!(Rc::as_ptr(&parent_file), Rc::as_ptr(&file));
7579
}
7680
_ => panic!("Contract parent should be a file"),
@@ -125,7 +129,11 @@ mod tests {
125129
field: u32,
126130
}
127131
};
128-
let contract = create_mock_contract_with_inner_struct(1, item_struct);
129-
assert_eq!(contract.name(), "TestStruct");
132+
let contract = create_mock_contract_with_inner_struct(
133+
1,
134+
item_struct.ident.to_string(),
135+
location!(item_struct),
136+
);
137+
assert_eq!(contract.name, "TestStruct");
130138
}
131139
}

sdk/src/ast/expression.rs

Lines changed: 75 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
11
#![warn(clippy::pedantic)]
22
use super::function::Function;
3-
use super::node::{Location, Node};
3+
use super::node::{Location, Node, TLocation};
44
use super::node_type::{
5-
FunctionCallChildType, FunctionCallParentType, MethodCallChildType, MethodCallParentType,
6-
NodeType,
5+
FunctionCallChildType, FunctionCallParentType, MemberAccessChildType, MemberAccessParentType,
6+
MethodCallChildType, MethodCallParentType, NodeKind,
77
};
88
use soroban_security_rules_macro_lib::node_location;
99
use std::cell::RefCell;
1010
use std::rc::Rc;
11-
use syn::spanned::Spanned;
1211
use syn::{Expr, ExprCall, ExprMethodCall};
1312

14-
#[derive(Clone)]
13+
#[derive(Clone, serde::Serialize, serde::Deserialize)]
1514
pub enum Expression {
1615
FunctionCall(Rc<FunctionCall>),
1716
MethodCall(Rc<MethodCall>),
@@ -24,19 +23,21 @@ pub enum ExpressionParentType {
2423
Expression(Rc<Expression>),
2524
}
2625

27-
#[node_location(inner = "inner_struct")]
26+
#[node_location]
27+
#[derive(Clone, serde::Serialize, serde::Deserialize)]
2828
pub struct FunctionCall {
2929
pub id: usize,
30-
pub(crate) inner_struct: Rc<ExprCall>,
30+
pub location: Location,
31+
pub function_name: String,
3132
pub parent: FunctionCallParentType,
3233
pub children: Vec<FunctionCallChildType>,
3334
pub is_tried: bool,
3435
}
3536

3637
impl Node for FunctionCall {
37-
fn parent(&self) -> Option<NodeType> {
38+
fn parent(&self) -> Option<NodeKind> {
3839
match &self.parent {
39-
FunctionCallParentType::Function(parent) => Some(NodeType::Function(parent.clone())),
40+
FunctionCallParentType::Function(parent) => Some(NodeKind::Function(parent.clone())),
4041
}
4142
}
4243

@@ -48,8 +49,8 @@ impl Node for FunctionCall {
4849

4950
impl FunctionCall {
5051
#[must_use]
51-
pub fn function_name(&self) -> String {
52-
match self.inner_struct.func.as_ref() {
52+
pub fn function_name_from_syn_item(function_call: &ExprCall) -> String {
53+
match function_call.func.as_ref() {
5354
Expr::Path(ref expr_path) => expr_path
5455
.path
5556
.segments
@@ -61,22 +62,24 @@ impl FunctionCall {
6162
}
6263
}
6364

64-
#[node_location(inner = "inner_struct")]
65+
#[node_location]
66+
#[derive(Clone, serde::Serialize, serde::Deserialize)]
6567
pub struct MethodCall {
6668
pub id: usize,
67-
pub(crate) inner_struct: Rc<ExprMethodCall>,
69+
pub location: Location,
70+
pub method_name: String,
6871
pub parent: MethodCallParentType,
6972
pub children: RefCell<Vec<MethodCallChildType>>,
7073
pub is_tried: bool,
7174
}
7275

7376
impl Node for MethodCall {
74-
fn parent(&self) -> Option<NodeType> {
77+
fn parent(&self) -> Option<NodeKind> {
7578
match &self.parent {
76-
MethodCallParentType::Function(parent) => Some(NodeType::Function(parent.clone())),
79+
MethodCallParentType::Function(parent) => Some(NodeKind::Function(parent.clone())),
7780
MethodCallParentType::Expression(parent) => match &**parent {
7881
Expression::MethodCall(parent) => parent.parent(),
79-
Expression::FunctionCall(parent) => NodeType::FunctionCall(parent.clone()).into(),
82+
Expression::FunctionCall(parent) => NodeKind::FunctionCall(parent.clone()).into(),
8083
Expression::Empty => None,
8184
},
8285
}
@@ -88,25 +91,77 @@ impl Node for MethodCall {
8891
}
8992
}
9093

94+
impl MethodCall {
95+
#[must_use]
96+
pub fn method_name_from_syn_item(method_call: &ExprMethodCall) -> String {
97+
method_call.method.to_string()
98+
}
99+
}
100+
101+
#[node_location]
102+
#[derive(serde::Serialize, serde::Deserialize)]
103+
pub struct MemberAccess {
104+
pub id: usize,
105+
pub location: Location,
106+
pub member_name: String,
107+
pub parent: MemberAccessParentType,
108+
pub children: Vec<MemberAccessChildType>,
109+
pub is_tried: bool,
110+
}
111+
112+
impl Node for MemberAccess {
113+
fn parent(&self) -> Option<NodeKind> {
114+
match &self.parent {
115+
MemberAccessParentType::Function(parent) => Some(NodeKind::Function(parent.clone())),
116+
MemberAccessParentType::Expression(parent) => match &**parent {
117+
Expression::MethodCall(parent) => parent.parent(),
118+
Expression::FunctionCall(parent) => NodeKind::FunctionCall(parent.clone()).into(),
119+
Expression::Empty => None,
120+
},
121+
}
122+
}
123+
124+
#[allow(refining_impl_trait)]
125+
fn children(&self) -> impl Iterator<Item = MemberAccessChildType> {
126+
self.children.iter().cloned()
127+
}
128+
}
129+
130+
impl MemberAccess {
131+
// #[must_use]
132+
// pub fn member_name(&self) -> String {
133+
// match &self.inner_item.member {
134+
// syn::Member::Named(ident) => ident.to_string(),
135+
// syn::Member::Unnamed(index) => index.index.to_string(),
136+
// }
137+
// }
138+
139+
// #[must_use]
140+
// pub fn base(&self) -> Expression {
141+
// self.inner_struct.base
142+
// }
143+
}
144+
91145
#[cfg(test)]
92146
mod function_call_tests {
93147
use super::*;
94-
use crate::utils::test::create_mock_function;
148+
use crate::{location, utils::test::create_mock_function};
95149
use syn::parse_quote;
96150

97151
#[test]
98152
fn test_function_call_function_name() {
99-
let inner_struct = parse_quote! {
153+
let inner_struct: ExprCall = parse_quote! {
100154
execute("Hello, world!")
101155
};
102156
let function_call = FunctionCall {
103157
id: 0,
104-
inner_struct: Rc::new(inner_struct),
158+
location: location!(inner_struct),
159+
function_name: FunctionCall::function_name_from_syn_item(&inner_struct),
105160
parent: FunctionCallParentType::Function(Rc::new(create_mock_function(0))),
106161
children: vec![],
107162
is_tried: false,
108163
};
109164

110-
assert_eq!(function_call.function_name(), "execute");
165+
assert_eq!(function_call.function_name, "execute");
111166
}
112167
}

0 commit comments

Comments
 (0)