Skip to content

Commit 035ac86

Browse files
authored
AST Rust API (#1477)
This PR defines an AST API as a façade over the last IR language currently available (`ir2_flat_contracts`) that allows opaque access to the IR nodes and the semantic information computed in the frontend passes. - From the `SemanticAnalysis` object that is obtainable from the `CompilationUnit` it's possible to retrieve the root of the AST of each file in the unit `get_file_ast_root()`, which returns `ast::SourceUnit`. - Navigation from the `SourceUnit` down through the intermediate AST nodes, down to the terminal nodes. - Non-terminal nodes are wrapped in objects which contain a reference to the IR node and the `SemanticAnalysis`. - Terminal nodes are not wrapped by default except for `Identifier` nodes, which allow attempting to resolve to a `Definition` object. - A `Visitor` trait can be implemented and used via `accept_source_unit` (and related functions) to traverse the wrapped AST. - Non-terminal nodes define methods to access the child nodes. - Certain non-terminal nodes also define other methods to access other semantic information (eg. `SourceUnit` defines `contracts()` to retrieve the list of contracts defined in the file)
1 parent 7dd0da8 commit 035ac86

File tree

24 files changed

+8837
-118
lines changed

24 files changed

+8837
-118
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/solidity/outputs/cargo/crate/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ categories = [
2727
[features]
2828
default = []
2929
__private_ariadne_errors = ["dep:ariadne"]
30-
__private_backend_api = ["dep:indexmap"]
30+
__private_backend_api = ["dep:indexmap", "dep:paste"]
3131
__private_wasm_apis = []
3232
__private_testing_utils = ["metaslang_bindings/__private_testing_utils"]
3333

@@ -36,6 +36,7 @@ ariadne = { workspace = true, optional = true }
3636
indexmap = { workspace = true, optional = true }
3737
metaslang_bindings = { workspace = true }
3838
metaslang_cst = { workspace = true }
39+
paste = { workspace = true, optional = true }
3940
semver = { workspace = true }
4041
serde = { workspace = true }
4142
strum = { workspace = true }

crates/solidity/outputs/cargo/crate/src/backend/binder/definitions.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use crate::cst::{NodeId, TerminalNode};
88
//////////////////////////////////////////////////////////////////////////////
99
// Definitions
1010

11+
// __SLANG_DEFINITION_TYPES__ keep in sync with AST type
1112
#[derive(Debug)]
1213
pub enum Definition {
1314
Constant(ConstantDefinition),

crates/solidity/outputs/cargo/crate/src/backend/binder/mod.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@ pub struct Binder {
109109
/// Linearisations, as a vector of definitions, indexed by the
110110
/// contract/interface definition's `NodeId`
111111
linearisations: HashMap<NodeId, Vec<NodeId>>,
112+
/// Reverse mapping from definition `NodeId` to the set of references that bind to it
113+
definitions_to_references: HashMap<NodeId, Vec<NodeId>>,
112114
}
113115

114116
/// This controls visibility filtering and how to use the linearisation when
@@ -141,6 +143,7 @@ impl Binder {
141143
references: HashMap::new(),
142144
node_typing: HashMap::new(),
143145
linearisations: HashMap::new(),
146+
definitions_to_references: HashMap::new(),
144147
}
145148
}
146149

@@ -262,6 +265,31 @@ impl Binder {
262265
&self.references
263266
}
264267

268+
pub(crate) fn update_definitions_to_references_index(&mut self) {
269+
// Build reverse mapping from definitions to references
270+
let mut definitions: HashMap<NodeId, Vec<NodeId>> = HashMap::new();
271+
for (reference_id, reference) in &self.references {
272+
match &reference.resolution {
273+
Resolution::Definition(node_id) => {
274+
if let Some(references) = definitions.get_mut(node_id) {
275+
references.push(*reference_id);
276+
} else {
277+
definitions.insert(*node_id, vec![*reference_id]);
278+
}
279+
}
280+
Resolution::Ambiguous(_) | Resolution::BuiltIn(_) | Resolution::Unresolved => {}
281+
}
282+
}
283+
self.definitions_to_references = definitions;
284+
}
285+
286+
pub fn get_references_by_definition_id(&self, node_id: NodeId) -> Vec<NodeId> {
287+
self.definitions_to_references
288+
.get(&node_id)
289+
.cloned()
290+
.unwrap_or_default()
291+
}
292+
265293
pub(crate) fn insert_using_directive_in_scope(
266294
&mut self,
267295
directive: UsingDirective,
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#[path = "nodes.generated.rs"]
2+
mod nodes;
3+
pub use nodes::*;
4+
5+
mod node_extensions;
6+
pub use node_extensions::*;
7+
8+
use super::ir2_flat_contracts as input;
9+
10+
#[path = "visitor.generated.rs"]
11+
pub mod visitor;
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
use std::rc::Rc;
2+
3+
use super::super::{ContractDefinition, ContractDefinitionStruct, Definition, InterfaceDefinition};
4+
5+
pub enum ContractBase {
6+
Contract(ContractDefinition),
7+
Interface(InterfaceDefinition),
8+
}
9+
10+
impl ContractBase {
11+
fn from_definition(definition: &Definition) -> Option<Self> {
12+
match definition {
13+
Definition::Contract(contract) => Some(Self::Contract(Rc::clone(contract))),
14+
Definition::Interface(interface) => Some(Self::Interface(Rc::clone(interface))),
15+
_ => None,
16+
}
17+
}
18+
}
19+
20+
impl ContractDefinitionStruct {
21+
pub fn direct_bases(&self) -> Vec<ContractBase> {
22+
self.inheritance_types()
23+
.iter()
24+
.filter_map(|inheritance_type| {
25+
let base = inheritance_type.type_name().resolve_to_definition()?;
26+
ContractBase::from_definition(&base)
27+
})
28+
.collect()
29+
}
30+
31+
pub fn linearised_bases(&self) -> Vec<ContractBase> {
32+
let Some(base_node_ids) = self
33+
.semantic
34+
.binder()
35+
.get_linearised_bases(self.ir_node.node_id)
36+
else {
37+
// TODO(validation): once we have validation implemented, this
38+
// branch should not be reachable, or we should generate an error
39+
// while building the `SemanticAnalysis`.
40+
return Vec::new();
41+
};
42+
base_node_ids
43+
.iter()
44+
.map(|node_id| {
45+
let base_definition = Rc::new(Definition::create(*node_id, &self.semantic));
46+
ContractBase::from_definition(&base_definition)
47+
.expect("Linearised base is either a contract or interface")
48+
})
49+
.collect()
50+
}
51+
}
Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
use std::rc::Rc;
2+
3+
use paste::paste;
4+
5+
use super::super::nodes::{
6+
ConstantDefinition, ConstantDefinitionStruct, ContractDefinition, ContractDefinitionStruct,
7+
EnumDefinition, EnumDefinitionStruct, ErrorDefinition, ErrorDefinitionStruct, EventDefinition,
8+
EventDefinitionStruct, FunctionDefinition, FunctionDefinitionStruct,
9+
ImportDeconstructionSymbol, ImportDeconstructionSymbolStruct, InterfaceDefinition,
10+
InterfaceDefinitionStruct, LibraryDefinition, LibraryDefinitionStruct, Parameter,
11+
ParameterStruct, PathImport, PathImportStruct, StateVariableDefinition,
12+
StateVariableDefinitionStruct, StructDefinition, StructDefinitionStruct, StructMember,
13+
StructMemberStruct, UserDefinedValueTypeDefinition, UserDefinedValueTypeDefinitionStruct,
14+
VariableDeclarationStatement, VariableDeclarationStatementStruct, YulFunctionDefinition,
15+
YulFunctionDefinitionStruct, YulLabel, YulLabelStruct,
16+
};
17+
use super::{create_identifier, create_yul_identifier, Identifier, Reference, YulIdentifier};
18+
use crate::backend::ir::ast::{
19+
create_constant_definition, create_contract_definition, create_enum_definition,
20+
create_error_definition, create_event_definition, create_function_definition,
21+
create_import_deconstruction_symbol, create_interface_definition, create_library_definition,
22+
create_parameter, create_path_import, create_state_variable_definition,
23+
create_struct_definition, create_struct_member, create_user_defined_value_type_definition,
24+
create_variable_declaration_statement, create_yul_function_definition, create_yul_label,
25+
};
26+
use crate::backend::{binder, SemanticAnalysis};
27+
use crate::cst::NodeId;
28+
29+
// __SLANG_DEFINITION_TYPES__ keep in sync with binder
30+
pub enum Definition {
31+
Constant(ConstantDefinition),
32+
Contract(ContractDefinition),
33+
Enum(EnumDefinition),
34+
EnumMember(Identifier),
35+
Error(ErrorDefinition),
36+
Event(EventDefinition),
37+
Function(FunctionDefinition),
38+
Import(PathImport),
39+
ImportedSymbol(ImportDeconstructionSymbol),
40+
Interface(InterfaceDefinition),
41+
Library(LibraryDefinition),
42+
Modifier(FunctionDefinition),
43+
Parameter(Parameter),
44+
StateVariable(StateVariableDefinition),
45+
Struct(StructDefinition),
46+
StructMember(StructMember),
47+
TypeParameter(Parameter),
48+
UserDefinedValueType(UserDefinedValueTypeDefinition),
49+
Variable(VariableDeclarationStatement),
50+
YulFunction(YulFunctionDefinition),
51+
YulLabel(YulLabel),
52+
YulParameter(YulIdentifier),
53+
YulVariable(YulIdentifier),
54+
}
55+
56+
impl Definition {
57+
pub(crate) fn create(definition_id: NodeId, semantic: &Rc<SemanticAnalysis>) -> Self {
58+
let definition = semantic
59+
.binder()
60+
.find_definition_by_id(definition_id)
61+
.expect("definition_id references a definition node");
62+
63+
match definition {
64+
binder::Definition::Constant(constant_definition) => Self::Constant(
65+
create_constant_definition(&constant_definition.ir_node, semantic),
66+
),
67+
binder::Definition::Contract(contract_definition) => Self::Contract(
68+
create_contract_definition(&contract_definition.ir_node, semantic),
69+
),
70+
binder::Definition::Enum(enum_definition) => {
71+
Self::Enum(create_enum_definition(&enum_definition.ir_node, semantic))
72+
}
73+
binder::Definition::EnumMember(enum_member_definition) => {
74+
Self::EnumMember(create_identifier(&enum_member_definition.ir_node, semantic))
75+
}
76+
binder::Definition::Error(error_definition) => {
77+
Self::Error(create_error_definition(&error_definition.ir_node, semantic))
78+
}
79+
binder::Definition::Event(event_definition) => {
80+
Self::Event(create_event_definition(&event_definition.ir_node, semantic))
81+
}
82+
binder::Definition::Function(function_definition) => Self::Function(
83+
create_function_definition(&function_definition.ir_node, semantic),
84+
),
85+
binder::Definition::Import(import_definition) => {
86+
Self::Import(create_path_import(&import_definition.ir_node, semantic))
87+
}
88+
binder::Definition::ImportedSymbol(imported_symbol_definition) => Self::ImportedSymbol(
89+
create_import_deconstruction_symbol(&imported_symbol_definition.ir_node, semantic),
90+
),
91+
binder::Definition::Interface(interface_definition) => Self::Interface(
92+
create_interface_definition(&interface_definition.ir_node, semantic),
93+
),
94+
binder::Definition::Library(library_definition) => Self::Library(
95+
create_library_definition(&library_definition.ir_node, semantic),
96+
),
97+
binder::Definition::Modifier(modifier_definition) => Self::Modifier(
98+
create_function_definition(&modifier_definition.ir_node, semantic),
99+
),
100+
binder::Definition::Parameter(parameter_definition) => {
101+
Self::Parameter(create_parameter(&parameter_definition.ir_node, semantic))
102+
}
103+
binder::Definition::StateVariable(state_variable_definition) => Self::StateVariable(
104+
create_state_variable_definition(&state_variable_definition.ir_node, semantic),
105+
),
106+
binder::Definition::Struct(struct_definition) => Self::Struct(
107+
create_struct_definition(&struct_definition.ir_node, semantic),
108+
),
109+
binder::Definition::StructMember(struct_member_definition) => Self::StructMember(
110+
create_struct_member(&struct_member_definition.ir_node, semantic),
111+
),
112+
binder::Definition::TypeParameter(type_parameter_definition) => Self::TypeParameter(
113+
create_parameter(&type_parameter_definition.ir_node, semantic),
114+
),
115+
binder::Definition::UserDefinedValueType(user_defined_value_type_definition) => {
116+
Self::UserDefinedValueType(create_user_defined_value_type_definition(
117+
&user_defined_value_type_definition.ir_node,
118+
semantic,
119+
))
120+
}
121+
binder::Definition::Variable(variable_definition) => Self::Variable(
122+
create_variable_declaration_statement(&variable_definition.ir_node, semantic),
123+
),
124+
binder::Definition::YulFunction(yul_function_definition) => Self::YulFunction(
125+
create_yul_function_definition(&yul_function_definition.ir_node, semantic),
126+
),
127+
binder::Definition::YulLabel(yul_label_definition) => {
128+
Self::YulLabel(create_yul_label(&yul_label_definition.ir_node, semantic))
129+
}
130+
binder::Definition::YulParameter(yul_parameter_definition) => Self::YulParameter(
131+
create_yul_identifier(&yul_parameter_definition.ir_node, semantic),
132+
),
133+
binder::Definition::YulVariable(yul_variable_definition) => Self::YulVariable(
134+
create_yul_identifier(&yul_variable_definition.ir_node, semantic),
135+
),
136+
}
137+
}
138+
139+
pub fn references(&self) -> Vec<Reference> {
140+
match self {
141+
Definition::Constant(constant_definition) => constant_definition.references(),
142+
Definition::Contract(contract_definition) => contract_definition.references(),
143+
Definition::Enum(enum_definition) => enum_definition.references(),
144+
Definition::EnumMember(identifier) => identifier.references(),
145+
Definition::Error(error_definition) => error_definition.references(),
146+
Definition::Event(event_definition) => event_definition.references(),
147+
Definition::Function(function_definition) => function_definition.references(),
148+
Definition::Import(path_import) => path_import.references(),
149+
Definition::ImportedSymbol(import_deconstruction_symbol) => {
150+
import_deconstruction_symbol.references()
151+
}
152+
Definition::Interface(interface_definition) => interface_definition.references(),
153+
Definition::Library(library_definition) => library_definition.references(),
154+
Definition::Modifier(function_definition) => function_definition.references(),
155+
Definition::Parameter(parameter) => parameter.references(),
156+
Definition::StateVariable(state_variable_definition) => {
157+
state_variable_definition.references()
158+
}
159+
Definition::Struct(struct_definition) => struct_definition.references(),
160+
Definition::StructMember(struct_member) => struct_member.references(),
161+
Definition::TypeParameter(parameter) => parameter.references(),
162+
Definition::UserDefinedValueType(user_defined_value_type_definition) => {
163+
user_defined_value_type_definition.references()
164+
}
165+
Definition::Variable(variable_declaration_statement) => {
166+
variable_declaration_statement.references()
167+
}
168+
Definition::YulFunction(yul_function_definition) => {
169+
yul_function_definition.references()
170+
}
171+
Definition::YulLabel(yul_label) => yul_label.references(),
172+
Definition::YulParameter(identifier) => identifier.references(),
173+
Definition::YulVariable(identifier) => identifier.references(),
174+
}
175+
}
176+
}
177+
178+
macro_rules! define_references_method {
179+
($type:ident) => {
180+
paste! {
181+
impl [<$type Struct>] {
182+
pub fn references(&self) -> Vec<Reference> {
183+
self.semantic.references_binding_to(self.ir_node.node_id)
184+
}
185+
}
186+
}
187+
};
188+
}
189+
190+
define_references_method!(ConstantDefinition);
191+
define_references_method!(ContractDefinition);
192+
define_references_method!(EnumDefinition);
193+
define_references_method!(ErrorDefinition);
194+
define_references_method!(EventDefinition);
195+
define_references_method!(FunctionDefinition);
196+
define_references_method!(ImportDeconstructionSymbol);
197+
define_references_method!(InterfaceDefinition);
198+
define_references_method!(LibraryDefinition);
199+
define_references_method!(Parameter);
200+
define_references_method!(PathImport);
201+
define_references_method!(StateVariableDefinition);
202+
define_references_method!(StructDefinition);
203+
define_references_method!(StructMember);
204+
define_references_method!(UserDefinedValueTypeDefinition);
205+
define_references_method!(VariableDeclarationStatement);
206+
define_references_method!(YulLabel);
207+
define_references_method!(YulFunctionDefinition);

0 commit comments

Comments
 (0)