From f769edbd0587f0ea2e65bb26ae76c44fbfd7e609 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustavo=20Gir=C3=A1ldez?= Date: Tue, 19 Nov 2024 17:20:45 -0500 Subject: [PATCH 01/16] Use extension scope and extension hooks to graph and customize resolver The ResolverCandidates will insert arbitrary edges to the graph connecting to the extension scopes determined available at the beginning of resolution (these are context dependent) when reaching nodes marked as extension hooks (usually the source unit's lexical scope). --- crates/metaslang/bindings/src/builder/mod.rs | 21 ++- crates/metaslang/bindings/src/lib.rs | 15 ++ crates/metaslang/bindings/src/resolver/mod.rs | 148 +++++++++++++++++- 3 files changed, 178 insertions(+), 6 deletions(-) diff --git a/crates/metaslang/bindings/src/builder/mod.rs b/crates/metaslang/bindings/src/builder/mod.rs index 3a27e9b1cc..28aeab5c6c 100644 --- a/crates/metaslang/bindings/src/builder/mod.rs +++ b/crates/metaslang/bindings/src/builder/mod.rs @@ -282,6 +282,8 @@ static IS_DEFINITION_ATTR: &str = "is_definition"; static IS_ENDPOINT_ATTR: &str = "is_endpoint"; static IS_EXPORTED_ATTR: &str = "is_exported"; static IS_REFERENCE_ATTR: &str = "is_reference"; +static EXTENSION_HOOK_ATTR: &str = "extension_hook"; +static EXTENSION_SCOPE_ATTR: &str = "extension_scope"; static PARENTS_ATTR: &str = "parents"; static SCOPE_ATTR: &str = "scope"; static SOURCE_NODE_ATTR: &str = "source_node"; @@ -302,6 +304,7 @@ static POP_SCOPED_SYMBOL_ATTRS: Lazy> = Lazy::new(|| { EXPORT_NODE_ATTR, IMPORT_NODES_ATTR, SYNTAX_TYPE_ATTR, + EXTENSION_SCOPE_ATTR, ]) }); static POP_SYMBOL_ATTRS: Lazy> = Lazy::new(|| { @@ -315,6 +318,7 @@ static POP_SYMBOL_ATTRS: Lazy> = Lazy::new(|| { EXPORT_NODE_ATTR, IMPORT_NODES_ATTR, SYNTAX_TYPE_ATTR, + EXTENSION_SCOPE_ATTR, ]) }); static PUSH_SCOPED_SYMBOL_ATTRS: Lazy> = Lazy::new(|| { @@ -337,7 +341,7 @@ static PUSH_SYMBOL_ATTRS: Lazy> = Lazy::new(|| { ]) }); static SCOPE_ATTRS: Lazy> = - Lazy::new(|| HashSet::from([TYPE_ATTR, IS_EXPORTED_ATTR, IS_ENDPOINT_ATTR])); + Lazy::new(|| HashSet::from([TYPE_ATTR, IS_EXPORTED_ATTR, IS_ENDPOINT_ATTR, EXTENSION_HOOK_ATTR])); // Edge attribute names static PRECEDENCE_ATTR: &str = "precedence"; @@ -362,6 +366,7 @@ pub(crate) struct Builder<'a, KT: KindTypes + 'static> { cursors: HashMap, Cursor>, definitions_info: HashMap, DefinitionBindingInfo>, references_info: HashMap, ReferenceBindingInfo>, + extension_hooks: HashSet>, } pub(crate) struct BuildResult { @@ -370,6 +375,8 @@ pub(crate) struct BuildResult { pub cursors: HashMap, Cursor>, pub definitions_info: HashMap, DefinitionBindingInfo>, pub references_info: HashMap, ReferenceBindingInfo>, + // Nodes where we want to inject extensions + pub extension_hooks: HashSet>, } impl<'a, KT: KindTypes + 'static> Builder<'a, KT> { @@ -392,6 +399,7 @@ impl<'a, KT: KindTypes + 'static> Builder<'a, KT> { cursors: HashMap::new(), definitions_info: HashMap::new(), references_info: HashMap::new(), + extension_hooks: HashSet::new(), } } @@ -480,6 +488,7 @@ impl<'a, KT: KindTypes + 'static> Builder<'a, KT> { cursors: self.cursors, definitions_info: self.definitions_info, references_info: self.references_info, + extension_hooks: self.extension_hooks, }) } @@ -896,6 +905,11 @@ impl<'a, KT: KindTypes> Builder<'a, KT> { None => Vec::new(), }; + let extension_scope = match node.attributes.get(EXTENSION_SCOPE_ATTR) { + Some(extension_scope) => Some(self.node_handle_for_graph_node(extension_scope.as_graph_node_ref()?)), + None => None, + }; + self.definitions_info.insert( node_handle, DefinitionBindingInfo { @@ -904,6 +918,7 @@ impl<'a, KT: KindTypes> Builder<'a, KT> { parents, export_node, import_nodes, + extension_scope, }, ); } else if stack_graph_node.is_reference() { @@ -911,6 +926,10 @@ impl<'a, KT: KindTypes> Builder<'a, KT> { .insert(node_handle, ReferenceBindingInfo { tag, parents }); } + if Self::load_flag(node, EXTENSION_HOOK_ATTR)? { + self.extension_hooks.insert(node_handle); + } + Ok(()) } diff --git a/crates/metaslang/bindings/src/lib.rs b/crates/metaslang/bindings/src/lib.rs index ccc855fca5..49bc3140e8 100644 --- a/crates/metaslang/bindings/src/lib.rs +++ b/crates/metaslang/bindings/src/lib.rs @@ -36,6 +36,7 @@ pub(crate) struct DefinitionBindingInfo { export_node: Option, #[allow(dead_code)] import_nodes: Vec, + extension_scope: Option, } pub(crate) struct ReferenceBindingInfo { @@ -53,6 +54,7 @@ pub struct Bindings { cursor_to_definitions: HashMap, cursor_to_references: HashMap, context: Option, + extension_hooks: HashSet, } pub enum FileDescriptor { @@ -136,6 +138,7 @@ impl Bindings { cursor_to_definitions: HashMap::new(), cursor_to_references: HashMap::new(), context: None, + extension_hooks: HashSet::new(), } } @@ -187,6 +190,7 @@ impl Bindings { self.definitions_info .extend(result.definitions_info.drain()); self.references_info.extend(result.references_info.drain()); + self.extension_hooks.extend(result.extension_hooks.drain()); result } @@ -338,6 +342,10 @@ impl Bindings { } results } + + pub(crate) fn is_extension_hook(&self, node_handle: GraphHandle) -> bool { + self.extension_hooks.contains(&node_handle) + } } struct DisplayCursor<'a, KT: KindTypes + 'static> { @@ -401,6 +409,13 @@ impl<'a, KT: KindTypes + 'static> Definition<'a, KT> { .unwrap_or_default() } + pub(crate) fn get_extension_scope(&self) -> Option { + self.owner + .definitions_info + .get(&self.handle) + .and_then(|info| info.extension_scope) + } + pub fn to_handle(self) -> DefinitionHandle { DefinitionHandle(self.handle) } diff --git a/crates/metaslang/bindings/src/resolver/mod.rs b/crates/metaslang/bindings/src/resolver/mod.rs index 57c0caf305..f170f83760 100644 --- a/crates/metaslang/bindings/src/resolver/mod.rs +++ b/crates/metaslang/bindings/src/resolver/mod.rs @@ -2,10 +2,14 @@ use std::collections::{HashMap, HashSet}; use std::iter::once; use metaslang_cst::kinds::KindTypes; +use stack_graphs::graph::{Degree, Edge, StackGraph}; use stack_graphs::partial::{PartialPath, PartialPaths}; -use stack_graphs::stitching::{ForwardPartialPathStitcher, GraphEdgeCandidates, StitcherConfig}; +use stack_graphs::stitching::{ + ForwardCandidates, ForwardPartialPathStitcher, GraphEdgeCandidates, GraphEdges, StitcherConfig, +}; +use stack_graphs::{CancellationError, CancellationFlag}; -use crate::{Bindings, Definition, Reference, ResolutionError, Tag}; +use crate::{Bindings, Definition, FileHandle, GraphHandle, Reference, ResolutionError, Tag}; mod c3; @@ -51,6 +55,75 @@ impl<'a, KT: KindTypes + 'static> ResolvedPath<'a, KT> { } } +pub struct NoCancellation; +impl CancellationFlag for NoCancellation { + fn check(&self, _at: &'static str) -> Result<(), CancellationError> { + Ok(()) + } +} + +/// Acts as a database of the edges in the graph. +struct ResolverCandidates<'a, KT: KindTypes + 'static> { + owner: &'a Bindings, + partials: &'a mut PartialPaths, + file: Option, + edges: GraphEdges, + extensions: &'a [GraphHandle], +} + +impl<'a, KT: KindTypes + 'static> ResolverCandidates<'a, KT> { + pub fn new( + owner: &'a Bindings, + partials: &'a mut PartialPaths, + file: Option, + extensions: &'a [GraphHandle], + ) -> Self { + Self { + owner, + partials, + file, + edges: GraphEdges, + extensions, + } + } +} + +impl ForwardCandidates + for ResolverCandidates<'_, KT> +{ + fn get_forward_candidates(&mut self, path: &PartialPath, result: &mut R) + where + R: std::iter::Extend, + { + let node = path.end_node; + result.extend(self.owner.stack_graph.outgoing_edges(node).filter(|e| { + self.file + .map_or(true, |file| self.owner.stack_graph[e.sink].is_in_file(file)) + })); + + if self.owner.is_extension_hook(node) { + // println!("injecting edges to extensions at hook {node:?}"); + let mut extension_edges = Vec::new(); + for extension in self.extensions { + extension_edges.push(Edge { + source: node, + sink: *extension, + precedence: 0, + }); + } + result.extend(extension_edges); + } + } + + fn get_joining_candidate_degree(&self, path: &PartialPath) -> Degree { + self.owner.stack_graph.incoming_edge_degree(path.end_node) + } + + fn get_graph_partials_and_db(&mut self) -> (&StackGraph, &mut PartialPaths, &GraphEdges) { + (&self.owner.stack_graph, self.partials, &self.edges) + } +} + impl<'a, KT: KindTypes + 'static> Resolver<'a, KT> { pub fn build_for(reference: &Reference<'a, KT>) -> Self { let mut resolver = Self { @@ -63,7 +136,8 @@ impl<'a, KT: KindTypes + 'static> Resolver<'a, KT> { resolver } - fn resolve(&mut self) { + #[allow(dead_code)] + fn find_all_complete_partial_paths(&mut self) -> Result, CancellationError> { let mut reference_paths = Vec::new(); ForwardPartialPathStitcher::find_all_complete_partial_paths( &mut GraphEdgeCandidates::new(&self.owner.stack_graph, &mut self.partials, None), @@ -73,8 +147,72 @@ impl<'a, KT: KindTypes + 'static> Resolver<'a, KT> { |_graph, _paths, path| { reference_paths.push(path.clone()); }, - ) - .expect("should never be cancelled"); + )?; + Ok(reference_paths) + } + + fn find_paths_with_extensions( + &mut self, + extensions: &[GraphHandle], + ) -> Result, CancellationError> { + let mut reference_paths = Vec::new(); + let mut candidates = + ResolverCandidates::new(&self.owner, &mut self.partials, None, extensions); + let starting_nodes = once(self.reference.handle); + let cancellation_flag = &NoCancellation; + + let (graph, partials, _) = candidates.get_graph_partials_and_db(); + let initial_paths = starting_nodes + .into_iter() + .filter(|n| graph[*n].is_reference()) + .map(|n| { + let mut p = PartialPath::from_node(graph, partials, n); + p.eliminate_precondition_stack_variables(partials); + p + }) + .collect::>(); + + let mut stitcher = + ForwardPartialPathStitcher::from_partial_paths(graph, partials, initial_paths); + + stitcher.set_similar_path_detection(true); + stitcher.set_collect_stats(false); + stitcher.set_check_only_join_nodes(true); + + while !stitcher.is_complete() { + cancellation_flag.check("finding complete partial paths")?; + for path in stitcher.previous_phase_partial_paths() { + candidates.load_forward_candidates(path, cancellation_flag)?; + } + stitcher.process_next_phase(&mut candidates, |_, _, _| true); + let (graph, _, _) = candidates.get_graph_partials_and_db(); + for path in stitcher.previous_phase_partial_paths() { + if path.is_complete(graph) { + //visit(graph, partials, path); + reference_paths.push(path.clone()); + } + } + } + + Ok(reference_paths) + } + + fn resolve(&mut self) { + let ref_parents = self.reference.resolve_parents(); + let mut extensions = Vec::new(); + for parent in &ref_parents { + if let Some(extension_scope) = parent.get_extension_scope() { + // println!( + // "Parent {parent} of {reference} has extension {extension_scope:?}", + // reference = self.reference + // ); + extensions.push(extension_scope); + } + } + + let reference_paths = self + .find_paths_with_extensions(&extensions) + .expect("Should never be cancelled"); let mut added_nodes = HashSet::new(); for reference_path in &reference_paths { From a61f81681fd139a41ffdfa06558bae31ee44f40a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustavo=20Gir=C3=A1ldez?= Date: Thu, 21 Nov 2024 11:17:18 -0500 Subject: [PATCH 02/16] Add extension hooks and scopes and disable old scope stack rules for extensions --- .../inputs/language/bindings/rules.msgb | 48 ++++++++++++------- .../bindings/generated/binding_rules.rs | 48 ++++++++++++------- 2 files changed, 62 insertions(+), 34 deletions(-) diff --git a/crates/solidity/inputs/language/bindings/rules.msgb b/crates/solidity/inputs/language/bindings/rules.msgb index 0a4b6e4913..50da382ff4 100644 --- a/crates/solidity/inputs/language/bindings/rules.msgb +++ b/crates/solidity/inputs/language/bindings/rules.msgb @@ -74,7 +74,11 @@ inherit .star_extension ; We may jump to scope here to resolve using the extensions scope provided by ; contract/libraries that contain `using` directives - edge @source_unit.lexical_scope -> JUMP_TO_SCOPE_NODE + ; edge @source_unit.lexical_scope -> JUMP_TO_SCOPE_NODE + + ; This is used to indicate the resolution algorithm that here's where it + ; should inject any possible extension scopes + attr (@source_unit.lexical_scope) extension_hook ; Provide a default star extension sink node that gets inherited. This is ; connected to from expressions, and those can potentially happen anywhere. @@ -351,6 +355,7 @@ inherit .star_extension attr (@contract.def) node_definition = @name attr (@contract.def) definiens_node = @contract + attr (@contract.def) extension_scope = @contract.extensions edge @contract.lexical_scope -> @contract.instance @@ -416,7 +421,10 @@ inherit .star_extension attr (push_name) push_symbol = (source-text @name) edge call -> push_typeof edge push_typeof -> push_name - edge push_name -> JUMP_TO_SCOPE_NODE + node hook + attr (hook) extension_hook + edge push_name -> hook + ; edge push_name -> JUMP_TO_SCOPE_NODE if (version-matches "< 0.5.0") { ; For Solidity < 0.5.0 `this` also acts like an `address` @@ -444,7 +452,7 @@ inherit .star_extension ; Since using directives are inherited, we need to *always* connect the push ; extensions to the extended scope, regardless of whether this contract ; contains any `using` directive. - edge @contract.extended_scope -> @contract.push_extensions + ; edge @contract.extended_scope -> @contract.push_extensions ; For Solidity < 0.7.0 using directives are inherited, so we need to connect ; always For newer versions, this connection only happens when there is a @@ -565,7 +573,7 @@ inherit .star_extension ; Connect the extensions push path (this can happen multiple times if there ; are multiple `using` directives in the contract, but that's allowed by the ; graph builder). - edge @contract.extended_scope -> @contract.push_extensions + ; edge @contract.extended_scope -> @contract.push_extensions } @contract [ContractDefinition [ContractMembers @@ -703,7 +711,10 @@ inherit .star_extension attr (push_name) push_symbol = (source-text @name) edge call -> push_typeof edge push_typeof -> push_name - edge push_name -> JUMP_TO_SCOPE_NODE + node hook + attr (hook) extension_hook + edge push_name -> hook + ; edge push_name -> JUMP_TO_SCOPE_NODE ;; "namespace" like access path node ns_member @@ -778,9 +789,12 @@ inherit .star_extension attr (@library.def) node_definition = @name attr (@library.def) definiens_node = @library + attr (@library.def) extension_scope = @library.extensions edge @library.lexical_scope -> @library.ns + let @library.enclosing_def = @library.def + node member attr (member) pop_symbol = "." edge @library.def -> member @@ -845,7 +859,7 @@ inherit .star_extension edge @library.extensions -> @using.def ; Connect the extensions push path - edge @library.extended_scope -> @library.push_extensions + ; edge @library.extended_scope -> @library.push_extensions } @library [LibraryDefinition [LibraryMembers [ContractMember @@ -881,17 +895,17 @@ inherit .star_extension ; Now we define the path to push the .extensions scope into the scope stack. ; We connect this to the extended scope only when there are extensions in the ; contract/library. - node @contract_or_library.push_extensions - attr (@contract_or_library.push_extensions) push_scoped_symbol = "@extend" - attr (@contract_or_library.push_extensions) scope = @contract_or_library.extensions - node drop_scopes - attr (drop_scopes) type = "drop_scopes" - node pop_extensions - attr (pop_extensions) pop_scoped_symbol = "@extend" - - edge @contract_or_library.push_extensions -> drop_scopes - edge drop_scopes -> pop_extensions - edge pop_extensions -> @contract_or_library.lexical_scope + ; node @contract_or_library.push_extensions + ; attr (@contract_or_library.push_extensions) push_scoped_symbol = "@extend" + ; attr (@contract_or_library.push_extensions) scope = @contract_or_library.extensions + ; node drop_scopes + ; attr (drop_scopes) type = "drop_scopes" + ; node pop_extensions + ; attr (pop_extensions) pop_scoped_symbol = "@extend" + + ; edge @contract_or_library.push_extensions -> drop_scopes + ; edge drop_scopes -> pop_extensions + ; edge pop_extensions -> @contract_or_library.lexical_scope } diff --git a/crates/solidity/outputs/cargo/crate/src/generated/bindings/generated/binding_rules.rs b/crates/solidity/outputs/cargo/crate/src/generated/bindings/generated/binding_rules.rs index c47f067e24..9870ff7ee6 100644 --- a/crates/solidity/outputs/cargo/crate/src/generated/bindings/generated/binding_rules.rs +++ b/crates/solidity/outputs/cargo/crate/src/generated/bindings/generated/binding_rules.rs @@ -79,7 +79,11 @@ inherit .star_extension ; We may jump to scope here to resolve using the extensions scope provided by ; contract/libraries that contain `using` directives - edge @source_unit.lexical_scope -> JUMP_TO_SCOPE_NODE + ; edge @source_unit.lexical_scope -> JUMP_TO_SCOPE_NODE + + ; This is used to indicate the resolution algorithm that here's where it + ; should inject any possible extension scopes + attr (@source_unit.lexical_scope) extension_hook ; Provide a default star extension sink node that gets inherited. This is ; connected to from expressions, and those can potentially happen anywhere. @@ -356,6 +360,7 @@ inherit .star_extension attr (@contract.def) node_definition = @name attr (@contract.def) definiens_node = @contract + attr (@contract.def) extension_scope = @contract.extensions edge @contract.lexical_scope -> @contract.instance @@ -421,7 +426,10 @@ inherit .star_extension attr (push_name) push_symbol = (source-text @name) edge call -> push_typeof edge push_typeof -> push_name - edge push_name -> JUMP_TO_SCOPE_NODE + node hook + attr (hook) extension_hook + edge push_name -> hook + ; edge push_name -> JUMP_TO_SCOPE_NODE if (version-matches "< 0.5.0") { ; For Solidity < 0.5.0 `this` also acts like an `address` @@ -449,7 +457,7 @@ inherit .star_extension ; Since using directives are inherited, we need to *always* connect the push ; extensions to the extended scope, regardless of whether this contract ; contains any `using` directive. - edge @contract.extended_scope -> @contract.push_extensions + ; edge @contract.extended_scope -> @contract.push_extensions ; For Solidity < 0.7.0 using directives are inherited, so we need to connect ; always For newer versions, this connection only happens when there is a @@ -570,7 +578,7 @@ inherit .star_extension ; Connect the extensions push path (this can happen multiple times if there ; are multiple `using` directives in the contract, but that's allowed by the ; graph builder). - edge @contract.extended_scope -> @contract.push_extensions + ; edge @contract.extended_scope -> @contract.push_extensions } @contract [ContractDefinition [ContractMembers @@ -708,7 +716,10 @@ inherit .star_extension attr (push_name) push_symbol = (source-text @name) edge call -> push_typeof edge push_typeof -> push_name - edge push_name -> JUMP_TO_SCOPE_NODE + node hook + attr (hook) extension_hook + edge push_name -> hook + ; edge push_name -> JUMP_TO_SCOPE_NODE ;; "namespace" like access path node ns_member @@ -783,9 +794,12 @@ inherit .star_extension attr (@library.def) node_definition = @name attr (@library.def) definiens_node = @library + attr (@library.def) extension_scope = @library.extensions edge @library.lexical_scope -> @library.ns + let @library.enclosing_def = @library.def + node member attr (member) pop_symbol = "." edge @library.def -> member @@ -850,7 +864,7 @@ inherit .star_extension edge @library.extensions -> @using.def ; Connect the extensions push path - edge @library.extended_scope -> @library.push_extensions + ; edge @library.extended_scope -> @library.push_extensions } @library [LibraryDefinition [LibraryMembers [ContractMember @@ -886,17 +900,17 @@ inherit .star_extension ; Now we define the path to push the .extensions scope into the scope stack. ; We connect this to the extended scope only when there are extensions in the ; contract/library. - node @contract_or_library.push_extensions - attr (@contract_or_library.push_extensions) push_scoped_symbol = "@extend" - attr (@contract_or_library.push_extensions) scope = @contract_or_library.extensions - node drop_scopes - attr (drop_scopes) type = "drop_scopes" - node pop_extensions - attr (pop_extensions) pop_scoped_symbol = "@extend" - - edge @contract_or_library.push_extensions -> drop_scopes - edge drop_scopes -> pop_extensions - edge pop_extensions -> @contract_or_library.lexical_scope + ; node @contract_or_library.push_extensions + ; attr (@contract_or_library.push_extensions) push_scoped_symbol = "@extend" + ; attr (@contract_or_library.push_extensions) scope = @contract_or_library.extensions + ; node drop_scopes + ; attr (drop_scopes) type = "drop_scopes" + ; node pop_extensions + ; attr (pop_extensions) pop_scoped_symbol = "@extend" + + ; edge @contract_or_library.push_extensions -> drop_scopes + ; edge drop_scopes -> pop_extensions + ; edge pop_extensions -> @contract_or_library.lexical_scope } From de99cd30130e22603aa585a2fcce084347b2fa6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustavo=20Gir=C3=A1ldez?= Date: Thu, 21 Nov 2024 13:06:39 -0500 Subject: [PATCH 03/16] Add resolver options to disable certain operations There's an initial dual purpose: - make resolution reentrant-safe, as some of the resolution tweaks require resolving references themselves - enable recursive lookup of extension scopes for Solidity < 0.7.0 --- crates/metaslang/bindings/src/lib.rs | 24 +++++-- crates/metaslang/bindings/src/resolver/mod.rs | 72 +++++++++++++++---- .../tests/src/bindings_assertions/runner.rs | 3 + .../cargo/tests/src/bindings_output/runner.rs | 4 ++ 4 files changed, 81 insertions(+), 22 deletions(-) diff --git a/crates/metaslang/bindings/src/lib.rs b/crates/metaslang/bindings/src/lib.rs index 49bc3140e8..98d44dae2f 100644 --- a/crates/metaslang/bindings/src/lib.rs +++ b/crates/metaslang/bindings/src/lib.rs @@ -11,7 +11,7 @@ use metaslang_cst::cursor::Cursor; use metaslang_cst::kinds::KindTypes; use metaslang_graph_builder::ast::File; use metaslang_graph_builder::functions::Functions; -use resolver::Resolver; +use resolver::{ResolveOptions, Resolver}; use semver::Version; use stack_graphs::graph::StackGraph; @@ -55,6 +55,7 @@ pub struct Bindings { cursor_to_references: HashMap, context: Option, extension_hooks: HashSet, + resolve_options: ResolveOptions, } pub enum FileDescriptor { @@ -139,9 +140,14 @@ impl Bindings { cursor_to_references: HashMap::new(), context: None, extension_hooks: HashSet::new(), + resolve_options: ResolveOptions::default(), } } + pub fn use_recursive_extension_scopes(&mut self) { + self.resolve_options.recursive_extension_scopes = true; + } + pub fn add_system_file(&mut self, file_path: &str, tree_cursor: Cursor) { let file_kind = FileDescriptor::System(file_path.into()); let file = self.stack_graph.get_or_create_file(&file_kind.as_string()); @@ -260,10 +266,7 @@ impl Bindings { } else { // TODO: what should we do if the parent reference // cannot be resolved at this point? - self.to_reference(*handle) - .unwrap() - .jump_to_definition() - .ok() + self.to_reference(*handle).unwrap().simple_resolve().ok() } }) .collect() @@ -487,11 +490,18 @@ impl<'a, KT: KindTypes + 'static> Reference<'a, KT> { } pub fn jump_to_definition(&self) -> Result, ResolutionError<'a, KT>> { - Resolver::build_for(self).first() + Resolver::build_for(self, self.owner.resolve_options).first() } pub fn definitions(&self) -> Vec> { - Resolver::build_for(self).all() + Resolver::build_for(self, self.owner.resolve_options).all() + } + + pub(crate) fn simple_resolve(&self) -> Result, ResolutionError<'a, KT>> { + Resolver::build_for( + self, + ResolveOptions::reentrant_safe(), + ).first() } pub(crate) fn has_tag(&self, tag: Tag) -> bool { diff --git a/crates/metaslang/bindings/src/resolver/mod.rs b/crates/metaslang/bindings/src/resolver/mod.rs index f170f83760..ef09ef4cc6 100644 --- a/crates/metaslang/bindings/src/resolver/mod.rs +++ b/crates/metaslang/bindings/src/resolver/mod.rs @@ -41,6 +41,34 @@ pub(crate) struct Resolver<'a, KT: KindTypes + 'static> { reference: Reference<'a, KT>, partials: PartialPaths, results: Vec>, + options: ResolveOptions, +} + +#[derive(Copy, Clone)] +pub(crate) struct ResolveOptions { + pub rank_results: bool, + pub use_extension_hooks: bool, + pub recursive_extension_scopes: bool, +} + +impl ResolveOptions { + pub fn reentrant_safe() -> Self { + Self { + rank_results: false, + use_extension_hooks: false, + recursive_extension_scopes: false, + } + } +} + +impl Default for ResolveOptions { + fn default() -> Self { + Self { + rank_results: true, + use_extension_hooks: true, + recursive_extension_scopes: false, + } + } } struct ResolvedPath<'a, KT: KindTypes + 'static> { @@ -125,12 +153,13 @@ impl ForwardCandidates Resolver<'a, KT> { - pub fn build_for(reference: &Reference<'a, KT>) -> Self { + pub fn build_for(reference: &Reference<'a, KT>, options: ResolveOptions) -> Self { let mut resolver = Self { owner: reference.owner, reference: reference.clone(), partials: PartialPaths::new(), results: Vec::new(), + options, }; resolver.resolve(); resolver @@ -198,21 +227,32 @@ impl<'a, KT: KindTypes + 'static> Resolver<'a, KT> { } fn resolve(&mut self) { - let ref_parents = self.reference.resolve_parents(); - let mut extensions = Vec::new(); - for parent in &ref_parents { - if let Some(extension_scope) = parent.get_extension_scope() { - // println!( - // "Parent {parent} of {reference} has extension {extension_scope:?}", - // reference = self.reference - // ); - extensions.push(extension_scope); + let reference_paths = if self.options.use_extension_hooks { + let ref_parents = self.reference.resolve_parents(); + let mut extensions = HashSet::new(); + for parent in &ref_parents { + if let Some(extension_scope) = parent.get_extension_scope() { + extensions.insert(extension_scope); + } + + if self.options.recursive_extension_scopes { + let grand_parents = Self::resolve_parents_all(parent.clone()); + for grand_parent in grand_parents.values().flatten() { + if let Some(extension_scope) = grand_parent.get_extension_scope() { + extensions.insert(extension_scope); + } + } + } + } - } + let extensions = extensions.drain().collect::>(); - let reference_paths = self - .find_paths_with_extensions(&extensions) - .expect("Should never be cancelled"); + self.find_paths_with_extensions(&extensions) + .expect("Should never be cancelled") + } else { + self.find_all_complete_partial_paths() + .expect("Should never be cancelled") + }; let mut added_nodes = HashSet::new(); for reference_path in &reference_paths { @@ -249,7 +289,9 @@ impl<'a, KT: KindTypes + 'static> Resolver<'a, KT> { pub fn first(&mut self) -> Result, ResolutionError<'a, KT>> { if self.results.len() > 1 { - self.rank_results(); + if self.options.rank_results { + self.rank_results(); + } let top_score = self.results[0].score; let mut results = self diff --git a/crates/solidity/outputs/cargo/tests/src/bindings_assertions/runner.rs b/crates/solidity/outputs/cargo/tests/src/bindings_assertions/runner.rs index fc009f9a68..7b5c1c41a2 100644 --- a/crates/solidity/outputs/cargo/tests/src/bindings_assertions/runner.rs +++ b/crates/solidity/outputs/cargo/tests/src/bindings_assertions/runner.rs @@ -30,6 +30,9 @@ pub fn run(group_name: &str, test_name: &str) -> Result<()> { fn check_assertions_with_version(version: &Version, contents: &str) -> Result<()> { let parser = Parser::create(version.clone())?; let mut bindings = create_bindings(version)?; + if *version < Version::parse("0.7.0").unwrap() { + bindings.use_recursive_extension_scopes(); + } let mut assertions = Assertions::new(); let mut skipped = 0; diff --git a/crates/solidity/outputs/cargo/tests/src/bindings_output/runner.rs b/crates/solidity/outputs/cargo/tests/src/bindings_output/runner.rs index 4d7217163f..ade4fdf9ca 100644 --- a/crates/solidity/outputs/cargo/tests/src/bindings_output/runner.rs +++ b/crates/solidity/outputs/cargo/tests/src/bindings_output/runner.rs @@ -5,6 +5,7 @@ use infra_utils::cargo::CargoWorkspace; use infra_utils::codegen::CodegenFileSystem; use infra_utils::paths::PathExtensions; use metaslang_graph_builder::graph::Graph; +use semver::Version; use slang_solidity::cst::KindTypes; use slang_solidity::parser::{ParseOutput, Parser}; @@ -43,6 +44,9 @@ pub fn run(group_name: &str, test_name: &str) -> Result<()> { for version in &VERSION_BREAKS { let parser = Parser::create(version.clone())?; let mut bindings = create_bindings(version)?; + if *version < Version::parse("0.7.0").unwrap() { + bindings.use_recursive_extension_scopes(); + } let mut parsed_parts: Vec> = Vec::new(); let multi_part = split_multi_file(&contents); From 41352921236a6a88936ca59707dd16ccccfe200c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustavo=20Gir=C3=A1ldez?= Date: Thu, 21 Nov 2024 17:53:23 -0500 Subject: [PATCH 04/16] Disable remaining rules used for extension pushing via rules --- .../solidity/inputs/language/bindings/rules.msgb | 16 ++++++++-------- .../bindings/generated/binding_rules.rs | 16 ++++++++-------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/crates/solidity/inputs/language/bindings/rules.msgb b/crates/solidity/inputs/language/bindings/rules.msgb index 50da382ff4..a29d21820e 100644 --- a/crates/solidity/inputs/language/bindings/rules.msgb +++ b/crates/solidity/inputs/language/bindings/rules.msgb @@ -311,10 +311,10 @@ inherit .star_extension if (version-matches "< 0.7.0") { ; `using` directives are inherited in Solidity < 0.7.0, so connect them to ; our own extensions scope - node extensions_push_guard - attr (extensions_push_guard) push_symbol = "@extensions" - edge heir.extensions -> extensions_push_guard - edge extensions_push_guard -> @type_name.push_begin + ; node extensions_push_guard + ; attr (extensions_push_guard) push_symbol = "@extensions" + ; edge heir.extensions -> extensions_push_guard + ; edge extensions_push_guard -> @type_name.push_begin } } @@ -444,10 +444,10 @@ inherit .star_extension if (version-matches "< 0.7.0") { ; Expose extensions through an `@extensions` guard on Solidity < 0.7.0 so ; that they can be accessed from sub contract extension scopes - node extensions_guard - attr (extensions_guard) pop_symbol = "@extensions" - edge @contract.def -> extensions_guard - edge extensions_guard -> @contract.extensions + ; node extensions_guard + ; attr (extensions_guard) pop_symbol = "@extensions" + ; edge @contract.def -> extensions_guard + ; edge extensions_guard -> @contract.extensions ; Since using directives are inherited, we need to *always* connect the push ; extensions to the extended scope, regardless of whether this contract diff --git a/crates/solidity/outputs/cargo/crate/src/generated/bindings/generated/binding_rules.rs b/crates/solidity/outputs/cargo/crate/src/generated/bindings/generated/binding_rules.rs index 9870ff7ee6..5c7743b323 100644 --- a/crates/solidity/outputs/cargo/crate/src/generated/bindings/generated/binding_rules.rs +++ b/crates/solidity/outputs/cargo/crate/src/generated/bindings/generated/binding_rules.rs @@ -316,10 +316,10 @@ inherit .star_extension if (version-matches "< 0.7.0") { ; `using` directives are inherited in Solidity < 0.7.0, so connect them to ; our own extensions scope - node extensions_push_guard - attr (extensions_push_guard) push_symbol = "@extensions" - edge heir.extensions -> extensions_push_guard - edge extensions_push_guard -> @type_name.push_begin + ; node extensions_push_guard + ; attr (extensions_push_guard) push_symbol = "@extensions" + ; edge heir.extensions -> extensions_push_guard + ; edge extensions_push_guard -> @type_name.push_begin } } @@ -449,10 +449,10 @@ inherit .star_extension if (version-matches "< 0.7.0") { ; Expose extensions through an `@extensions` guard on Solidity < 0.7.0 so ; that they can be accessed from sub contract extension scopes - node extensions_guard - attr (extensions_guard) pop_symbol = "@extensions" - edge @contract.def -> extensions_guard - edge extensions_guard -> @contract.extensions + ; node extensions_guard + ; attr (extensions_guard) pop_symbol = "@extensions" + ; edge @contract.def -> extensions_guard + ; edge extensions_guard -> @contract.extensions ; Since using directives are inherited, we need to *always* connect the push ; extensions to the extended scope, regardless of whether this contract From 5691c735fbd3e1aaf100abb4e4c8976a7fa6bd14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustavo=20Gir=C3=A1ldez?= Date: Thu, 21 Nov 2024 17:53:53 -0500 Subject: [PATCH 05/16] Allow some ranking in resolver when running in reentrant safety --- crates/metaslang/bindings/src/resolver/mod.rs | 57 +++++++++++++++++-- 1 file changed, 53 insertions(+), 4 deletions(-) diff --git a/crates/metaslang/bindings/src/resolver/mod.rs b/crates/metaslang/bindings/src/resolver/mod.rs index ef09ef4cc6..2fd53e9b55 100644 --- a/crates/metaslang/bindings/src/resolver/mod.rs +++ b/crates/metaslang/bindings/src/resolver/mod.rs @@ -47,6 +47,7 @@ pub(crate) struct Resolver<'a, KT: KindTypes + 'static> { #[derive(Copy, Clone)] pub(crate) struct ResolveOptions { pub rank_results: bool, + pub apply_c3_linearisation: bool, pub use_extension_hooks: bool, pub recursive_extension_scopes: bool, } @@ -54,7 +55,8 @@ pub(crate) struct ResolveOptions { impl ResolveOptions { pub fn reentrant_safe() -> Self { Self { - rank_results: false, + rank_results: true, + apply_c3_linearisation: false, use_extension_hooks: false, recursive_extension_scopes: false, } @@ -65,6 +67,7 @@ impl Default for ResolveOptions { fn default() -> Self { Self { rank_results: true, + apply_c3_linearisation: true, use_extension_hooks: true, recursive_extension_scopes: false, } @@ -130,9 +133,16 @@ impl ForwardCandidates Resolver<'a, KT> { fn resolve(&mut self) { let reference_paths = if self.options.use_extension_hooks { + + // let debug = self.reference.get_cursor().unwrap().node().unparse() == "addXXX"; + // if debug { + // println!( + // "Resolving {r} [{idx}] with extensions", + // r = self.reference, + // idx = self.owner.stack_graph[self.reference.handle] + // .id() + // .local_id() + // + 2 + // ); + // } + let ref_parents = self.reference.resolve_parents(); let mut extensions = HashSet::new(); for parent in &ref_parents { + + // if debug { + // println!(" Found parent {parent}"); + // } + if let Some(extension_scope) = parent.get_extension_scope() { + + // if debug { + // println!( + // " - adding extension [{scope}]", + // scope = self.owner.stack_graph[extension_scope].id().local_id() + 2 + // ); + // } + extensions.insert(extension_scope); } if self.options.recursive_extension_scopes { let grand_parents = Self::resolve_parents_all(parent.clone()); for grand_parent in grand_parents.values().flatten() { + + // if debug { + // println!(" Found grandparent {grand_parent}"); + // } + if let Some(extension_scope) = grand_parent.get_extension_scope() { extensions.insert(extension_scope); + + // if debug { + // println!( + // " - adding parent extension [{scope}]", + // scope = self.owner.stack_graph[extension_scope].id().local_id() + 2 + // ); + // } } } } - } let extensions = extensions.drain().collect::>(); @@ -319,7 +366,9 @@ impl<'a, KT: KindTypes + 'static> Resolver<'a, KT> { } self.mark_down_aliases(); self.mark_down_built_ins(); - self.rank_c3_methods(); + if self.options.apply_c3_linearisation { + self.rank_c3_methods(); + } self.results.sort_by(|a, b| b.score.total_cmp(&a.score)); } From ca4096d376b99a0dd779db16888cc4453f738b7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustavo=20Gir=C3=A1ldez?= Date: Wed, 27 Nov 2024 14:01:57 -0500 Subject: [PATCH 06/16] Clean up resolver code, removing commented code and unnecessary code --- crates/metaslang/bindings/src/resolver/mod.rs | 145 ++++-------------- 1 file changed, 28 insertions(+), 117 deletions(-) diff --git a/crates/metaslang/bindings/src/resolver/mod.rs b/crates/metaslang/bindings/src/resolver/mod.rs index 2fd53e9b55..db1b3c53b0 100644 --- a/crates/metaslang/bindings/src/resolver/mod.rs +++ b/crates/metaslang/bindings/src/resolver/mod.rs @@ -93,7 +93,10 @@ impl CancellationFlag for NoCancellation { } } -/// Acts as a database of the edges in the graph. +/// Candidates for the forward stitching resolution process. This will inject +/// edges to the the given extensions scopes at extension hook nodes when asked +/// for forward candidates (ie. `get_forward_candidates`) by the resolution +/// algorithm. Other than that, it's exactly the same as `GraphEdgeCandidates`. struct ResolverCandidates<'a, KT: KindTypes + 'static> { owner: &'a Bindings, partials: &'a mut PartialPaths, @@ -133,16 +136,9 @@ impl ForwardCandidates Resolver<'a, KT> { resolver } - #[allow(dead_code)] - fn find_all_complete_partial_paths(&mut self) -> Result, CancellationError> { - let mut reference_paths = Vec::new(); - ForwardPartialPathStitcher::find_all_complete_partial_paths( - &mut GraphEdgeCandidates::new(&self.owner.stack_graph, &mut self.partials, None), - once(self.reference.handle), - StitcherConfig::default(), - &stack_graphs::NoCancellation, - |_graph, _paths, path| { - reference_paths.push(path.clone()); - }, - )?; - Ok(reference_paths) - } - - fn find_paths_with_extensions( - &mut self, - extensions: &[GraphHandle], - ) -> Result, CancellationError> { - let mut reference_paths = Vec::new(); - let mut candidates = - ResolverCandidates::new(&self.owner, &mut self.partials, None, extensions); - let starting_nodes = once(self.reference.handle); - let cancellation_flag = &NoCancellation; - - let (graph, partials, _) = candidates.get_graph_partials_and_db(); - let initial_paths = starting_nodes - .into_iter() - .filter(|n| graph[*n].is_reference()) - .map(|n| { - let mut p = PartialPath::from_node(graph, partials, n); - p.eliminate_precondition_stack_variables(partials); - p - }) - .collect::>(); - - let mut stitcher = - ForwardPartialPathStitcher::from_partial_paths(graph, partials, initial_paths); - - stitcher.set_similar_path_detection(true); - stitcher.set_collect_stats(false); - stitcher.set_check_only_join_nodes(true); - - while !stitcher.is_complete() { - cancellation_flag.check("finding complete partial paths")?; - for path in stitcher.previous_phase_partial_paths() { - candidates.load_forward_candidates(path, cancellation_flag)?; - } - stitcher.process_next_phase(&mut candidates, |_, _, _| true); - let (graph, _, _) = candidates.get_graph_partials_and_db(); - for path in stitcher.previous_phase_partial_paths() { - if path.is_complete(graph) { - //visit(graph, partials, path); - reference_paths.push(path.clone()); - } - } - } - - Ok(reference_paths) - } - fn resolve(&mut self) { - let reference_paths = if self.options.use_extension_hooks { - - // let debug = self.reference.get_cursor().unwrap().node().unparse() == "addXXX"; - // if debug { - // println!( - // "Resolving {r} [{idx}] with extensions", - // r = self.reference, - // idx = self.owner.stack_graph[self.reference.handle] - // .id() - // .local_id() - // + 2 - // ); - // } - + let mut reference_paths = Vec::new(); + if self.options.use_extension_hooks { let ref_parents = self.reference.resolve_parents(); let mut extensions = HashSet::new(); for parent in &ref_parents { - - // if debug { - // println!(" Found parent {parent}"); - // } - if let Some(extension_scope) = parent.get_extension_scope() { - - // if debug { - // println!( - // " - adding extension [{scope}]", - // scope = self.owner.stack_graph[extension_scope].id().local_id() + 2 - // ); - // } - extensions.insert(extension_scope); } if self.options.recursive_extension_scopes { let grand_parents = Self::resolve_parents_all(parent.clone()); for grand_parent in grand_parents.values().flatten() { - - // if debug { - // println!(" Found grandparent {grand_parent}"); - // } - if let Some(extension_scope) = grand_parent.get_extension_scope() { extensions.insert(extension_scope); - - // if debug { - // println!( - // " - adding parent extension [{scope}]", - // scope = self.owner.stack_graph[extension_scope].id().local_id() + 2 - // ); - // } } } } } let extensions = extensions.drain().collect::>(); - self.find_paths_with_extensions(&extensions) - .expect("Should never be cancelled") + ForwardPartialPathStitcher::find_all_complete_partial_paths( + &mut ResolverCandidates::new(self.owner, &mut self.partials, None, &extensions), + once(self.reference.handle), + StitcherConfig::default(), + &stack_graphs::NoCancellation, + |_graph, _paths, path| { + reference_paths.push(path.clone()); + }, + ).expect("Should never be cancelled"); } else { - self.find_all_complete_partial_paths() - .expect("Should never be cancelled") + ForwardPartialPathStitcher::find_all_complete_partial_paths( + &mut GraphEdgeCandidates::new(&self.owner.stack_graph, &mut self.partials, None), + once(self.reference.handle), + StitcherConfig::default(), + &stack_graphs::NoCancellation, + |_graph, _paths, path| { + reference_paths.push(path.clone()); + }, + ).expect("Should never be cancelled"); }; let mut added_nodes = HashSet::new(); for reference_path in &reference_paths { let end_node = reference_path.end_node; - // Because of how we're using the scope stack to propagate dynamic - // scopes, we may get multiple results with different scope stack - // postconditions but reaching the exact same definition. We only - // care about the definition, so we check for uniqueness. + // There may be duplicate ending nodes with different + // post-conditions in the scope stack, but we only care about the + // definition itself. Hence we need to check for uniqueness. if !added_nodes.contains(&end_node) && reference_paths .iter() From 7dc070f26660b3c24607cafeb2ffd8de61bad1d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustavo=20Gir=C3=A1ldez?= Date: Wed, 27 Nov 2024 14:20:17 -0500 Subject: [PATCH 07/16] Simplify binding rules eliminating extended scopes Since extensions are now injected during the resolution phases, it's no longer needed to have a separate extended scope. This simplifies the existing rules quite a bit. Also removes all previous commented out rules that dealt with pushing the extension scope to the scope stack, since that mechanism is no longer used. --- .../inputs/language/bindings/rules.msgb | 190 ++++-------------- .../bindings/generated/binding_rules.rs | 190 ++++-------------- 2 files changed, 82 insertions(+), 298 deletions(-) diff --git a/crates/solidity/inputs/language/bindings/rules.msgb b/crates/solidity/inputs/language/bindings/rules.msgb index a29d21820e..b2495fa1a8 100644 --- a/crates/solidity/inputs/language/bindings/rules.msgb +++ b/crates/solidity/inputs/language/bindings/rules.msgb @@ -21,7 +21,6 @@ inherit .enclosing_def inherit .parent_scope inherit .lexical_scope -inherit .extended_scope ; Used to resolve extension methods for `using for *` directives ; This is used as a minor optimization to avoid introducing new possible paths @@ -68,13 +67,6 @@ inherit .star_extension ;; inherited) for contracts to resolve bases (both in inheritance lists and ;; override specifiers) let @source_unit.parent_scope = @source_unit.lexical_scope - ; FIXME: we probably need to make extended scope different than lexical scope - ; and push an extension scope on that path - let @source_unit.extended_scope = @source_unit.lexical_scope - - ; We may jump to scope here to resolve using the extensions scope provided by - ; contract/libraries that contain `using` directives - ; edge @source_unit.lexical_scope -> JUMP_TO_SCOPE_NODE ; This is used to indicate the resolution algorithm that here's where it ; should inject any possible extension scopes @@ -108,17 +100,6 @@ inherit .star_extension edge @unit_member.lexical_scope -> @source_unit.lexical_scope } -;; Definitions that need to resolve expressions do it through an extended_scope -@source_unit [SourceUnit [SourceUnitMembers - [SourceUnitMember @unit_member ( - [FunctionDefinition] - | [ConstantDefinition] - )] -]] { - edge @unit_member.extended_scope -> @source_unit.extended_scope -} - - ;; Special case for built-ins: we want to export all symbols in the contract: ;; functions, types and state variables. All built-in symbols are defined in an ;; internal contract named '%BuiltIns%' (renamed from '$BuiltIns$') so we need @@ -307,15 +288,6 @@ inherit .star_extension attr (ns_member) push_symbol = "." edge heir.ns -> ns_member edge ns_member -> @type_name.push_begin - - if (version-matches "< 0.7.0") { - ; `using` directives are inherited in Solidity < 0.7.0, so connect them to - ; our own extensions scope - ; node extensions_push_guard - ; attr (extensions_push_guard) push_symbol = "@extensions" - ; edge heir.extensions -> extensions_push_guard - ; edge extensions_push_guard -> @type_name.push_begin - } } ;; The next couple of rules setup a `.parent_refs` attribute to use in the @@ -345,7 +317,6 @@ inherit .star_extension @contract [ContractDefinition @name name: [Identifier]] { node @contract.lexical_scope - node @contract.extended_scope node @contract.extensions node @contract.def node @contract.members @@ -355,6 +326,7 @@ inherit .star_extension attr (@contract.def) node_definition = @name attr (@contract.def) definiens_node = @contract + ; The .extensions node is where `using` directives will hook the definitions attr (@contract.def) extension_scope = @contract.extensions edge @contract.lexical_scope -> @contract.instance @@ -419,12 +391,12 @@ inherit .star_extension attr (push_typeof) push_symbol = "@typeof" node push_name attr (push_name) push_symbol = (source-text @name) - edge call -> push_typeof - edge push_typeof -> push_name node hook attr (hook) extension_hook + + edge call -> push_typeof + edge push_typeof -> push_name edge push_name -> hook - ; edge push_name -> JUMP_TO_SCOPE_NODE if (version-matches "< 0.5.0") { ; For Solidity < 0.5.0 `this` also acts like an `address` @@ -442,22 +414,10 @@ inherit .star_extension attr (@contract.star_extension) push_symbol = "@*" if (version-matches "< 0.7.0") { - ; Expose extensions through an `@extensions` guard on Solidity < 0.7.0 so - ; that they can be accessed from sub contract extension scopes - ; node extensions_guard - ; attr (extensions_guard) pop_symbol = "@extensions" - ; edge @contract.def -> extensions_guard - ; edge extensions_guard -> @contract.extensions - - ; Since using directives are inherited, we need to *always* connect the push - ; extensions to the extended scope, regardless of whether this contract - ; contains any `using` directive. - ; edge @contract.extended_scope -> @contract.push_extensions - ; For Solidity < 0.7.0 using directives are inherited, so we need to connect ; always For newer versions, this connection only happens when there is a ; `using for *` directive in the contract (see rule below) - edge @contract.star_extension -> @contract.extended_scope + edge @contract.star_extension -> @contract.lexical_scope } ; Path to resolve the built-in type for type() expressions @@ -561,7 +521,6 @@ inherit .star_extension )] ]] { edge @member.lexical_scope -> @contract.lexical_scope - edge @member.extended_scope -> @contract.extended_scope } @contract [ContractDefinition [ContractMembers @@ -569,11 +528,6 @@ inherit .star_extension ]] { ; Hook the using definition in the extensions scope edge @contract.extensions -> @using.def - - ; Connect the extensions push path (this can happen multiple times if there - ; are multiple `using` directives in the contract, but that's allowed by the - ; graph builder). - ; edge @contract.extended_scope -> @contract.push_extensions } @contract [ContractDefinition [ContractMembers @@ -649,7 +603,7 @@ inherit .star_extension ]]] { ; Connect the star extension node to the resolution extended scope if there is ; a `using for *` directive in the contract - edge @contract.star_extension -> @contract.extended_scope + edge @contract.star_extension -> @contract.lexical_scope } ; This applies to both state variables and function definitions @@ -677,9 +631,6 @@ inherit .star_extension edge @interface.lexical_scope -> @interface.instance - ; Interfaces don't contain expressions (or `using` directives), so the - ; extended scope is the same as the lexical scope - let @interface.extended_scope = @interface.lexical_scope ; The extensions node is required for the inheritance rules, but not used in interfaces let @interface.extensions = (node) @@ -764,7 +715,6 @@ inherit .star_extension item: [ContractMember @function variant: [FunctionDefinition]] ]] { edge @interface.members -> @function.def - edge @function.extended_scope -> @interface.extended_scope } [InterfaceDefinition [InterfaceMembers [ContractMember @using [UsingDirective]]]] { @@ -781,7 +731,6 @@ inherit .star_extension @library [LibraryDefinition @name name: [Identifier]] { node @library.lexical_scope - node @library.extended_scope node @library.extensions node @library.def node @library.ns @@ -789,6 +738,7 @@ inherit .star_extension attr (@library.def) node_definition = @name attr (@library.def) definiens_node = @library + ; The .extensions node is where `using` directives will hook the definitions attr (@library.def) extension_scope = @library.extensions edge @library.lexical_scope -> @library.ns @@ -840,7 +790,6 @@ inherit .star_extension )] ]] { edge @member.lexical_scope -> @library.lexical_scope - edge @member.extended_scope -> @library.extended_scope edge @library.ns -> @member.def } @@ -849,7 +798,6 @@ inherit .star_extension ]] { edge @library.modifiers -> @modifier.def edge @modifier.lexical_scope -> @library.lexical_scope - edge @modifier.extended_scope -> @library.extended_scope } @library [LibraryDefinition [LibraryMembers @@ -857,9 +805,6 @@ inherit .star_extension ]] { ; Expose the using directive from the extensions scope edge @library.extensions -> @using.def - - ; Connect the extensions push path - ; edge @library.extended_scope -> @library.push_extensions } @library [LibraryDefinition [LibraryMembers [ContractMember @@ -867,58 +812,13 @@ inherit .star_extension ]]] { ; Connect the star extension node to the resolution extended scope if there is ; a `using for *` directive in the library - edge @library.star_extension -> @library.extended_scope + edge @library.star_extension -> @library.lexical_scope } -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; Extensions scope rules -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -;; For contracts (and libraries) navigating to the source unit lexical scope -;; *also* needs to (optionally) propagate an extensions scope to be able to -;; correctly bind `using` attached functions. -@contract_or_library ([ContractDefinition] | [LibraryDefinition]) { - ; The `.extended_scope` resolution scope used by function bodies and other - ; expressions. From there we need to (optionally) push the extensions scope - ; (ie. `using` directives definitions) to the scope stack. - ; We will connect the path to push the extensions *if* the contract/library - ; has a `using` directive. Also, the extended scope links to the lexical scope - ; of the contract/library directly, regardless of whether there is a `using` - ; directive or not. - ; TODO: if we had a query negation operator to detect when there is no `using` - ; directive, could we avoid connecting directly when there are extensions? - edge @contract_or_library.extended_scope -> @contract_or_library.lexical_scope - - ; The .extensions node is where `using` directives will hook the definitions - attr (@contract_or_library.extensions) is_exported - - ; Now we define the path to push the .extensions scope into the scope stack. - ; We connect this to the extended scope only when there are extensions in the - ; contract/library. - ; node @contract_or_library.push_extensions - ; attr (@contract_or_library.push_extensions) push_scoped_symbol = "@extend" - ; attr (@contract_or_library.push_extensions) scope = @contract_or_library.extensions - ; node drop_scopes - ; attr (drop_scopes) type = "drop_scopes" - ; node pop_extensions - ; attr (pop_extensions) pop_scoped_symbol = "@extend" - - ; edge @contract_or_library.push_extensions -> drop_scopes - ; edge drop_scopes -> pop_extensions - ; edge pop_extensions -> @contract_or_library.lexical_scope -} - - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Using directives ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; The UsingDirective node requires the enclosing context to setup an -;; .extended_scope scoped variable for it to resolve both targets and subjects. -;; The resolution connects to the extended scope in order to (potentially) push -;; the same extension scope again, to resolve chained calls that all make use of -;; attached functions. - @using [UsingDirective] { ; This node acts as a definition in the sense that provides an entry point ; that pops the target type and pushes the library/functions to attach to the @@ -933,7 +833,7 @@ inherit .star_extension @using [UsingDirective [UsingClause @id_path [IdentifierPath]]] { ; resolve the library to be used in the directive - edge @id_path.push_end -> @using.extended_scope + edge @id_path.push_end -> @using.lexical_scope ; because we're using the whole library, we don't need to "consume" the ; attached function (as when using the deconstruction syntax), but we still @@ -955,7 +855,7 @@ inherit .star_extension ]] ]]] { ; resolve the function to be used in the directive - edge @id_path.push_end -> @using.extended_scope + edge @id_path.push_end -> @using.lexical_scope node dot attr (dot) pop_symbol = "." @@ -982,9 +882,8 @@ inherit .star_extension edge @type_name.pop_end -> cast edge cast -> @using.clause - ; resolve the target type of the directive on the extended scope so the - ; extension scope gets re-pushed - edge @type_name.type_ref -> @using.extended_scope + ; resolve the target type of the directive on the lexical scope + edge @type_name.type_ref -> @using.lexical_scope } [ContractMember @using [UsingDirective [UsingTarget [Asterisk]]]] { @@ -1448,7 +1347,6 @@ inherit .star_extension } node @function.lexical_scope - node @function.extended_scope node @function.def ; this path from the function definition to the scope allows attaching @@ -1468,12 +1366,12 @@ inherit .star_extension } @function [FunctionDefinition @params parameters: [ParametersDeclaration]] { - edge @params.lexical_scope -> @function.extended_scope + edge @params.lexical_scope -> @function.lexical_scope ;; Input parameters are available in the function scope - edge @function.extended_scope -> @params.defs + edge @function.lexical_scope -> @params.defs ;; ... and shadow other declarations - attr (@function.extended_scope -> @params.defs) precedence = 1 + attr (@function.lexical_scope -> @params.defs) precedence = 1 ;; Connect to paramaters for named argument resolution edge @function.def -> @params.names @@ -1482,12 +1380,12 @@ inherit .star_extension @function [FunctionDefinition returns: [ReturnsDeclaration @return_params [ParametersDeclaration] ]] { - edge @return_params.lexical_scope -> @function.extended_scope + edge @return_params.lexical_scope -> @function.lexical_scope ;; Return parameters are available in the function scope - edge @function.extended_scope -> @return_params.defs + edge @function.lexical_scope -> @return_params.defs ;; ... and shadow other declarations - attr (@function.extended_scope -> @return_params.defs) precedence = 1 + attr (@function.lexical_scope -> @return_params.defs) precedence = 1 } ;; Only functions that return a single value have an actual return type @@ -1504,13 +1402,13 @@ inherit .star_extension ;; Connect the function body's block lexical scope to the function @function [FunctionDefinition [FunctionBody @block [Block]]] { - edge @block.lexical_scope -> @function.extended_scope + edge @block.lexical_scope -> @function.lexical_scope } @function [FunctionDefinition [FunctionAttributes item: [FunctionAttribute @modifier [ModifierInvocation] ]]] { - edge @modifier.lexical_scope -> @function.extended_scope + edge @modifier.lexical_scope -> @function.lexical_scope } @modifier [ModifierInvocation @name [IdentifierPath]] { @@ -1534,18 +1432,17 @@ inherit .star_extension ;;; Unnamed functions (deprecated) @unnamed_function [UnnamedFunctionDefinition] { node @unnamed_function.lexical_scope - node @unnamed_function.extended_scope } @unnamed_function [UnnamedFunctionDefinition @params parameters: [ParametersDeclaration]] { - edge @params.lexical_scope -> @unnamed_function.extended_scope + edge @params.lexical_scope -> @unnamed_function.lexical_scope - edge @unnamed_function.extended_scope -> @params.defs - attr (@unnamed_function.extended_scope -> @params.defs) precedence = 1 + edge @unnamed_function.lexical_scope -> @params.defs + attr (@unnamed_function.lexical_scope -> @params.defs) precedence = 1 } @unnamed_function [UnnamedFunctionDefinition [FunctionBody @block [Block]]] { - edge @block.lexical_scope -> @unnamed_function.extended_scope + edge @block.lexical_scope -> @unnamed_function.lexical_scope } @unnamed_function [UnnamedFunctionDefinition @@ -1562,17 +1459,16 @@ inherit .star_extension @constructor [ConstructorDefinition] { node @constructor.lexical_scope - node @constructor.extended_scope node @constructor.def } @constructor [ConstructorDefinition @params parameters: [ParametersDeclaration]] { - edge @params.lexical_scope -> @constructor.extended_scope + edge @params.lexical_scope -> @constructor.lexical_scope ;; Input parameters are available in the constructor scope - edge @constructor.extended_scope -> @params.defs + edge @constructor.lexical_scope -> @params.defs ;; ... and shadow other declarations - attr (@constructor.extended_scope -> @params.defs) precedence = 1 + attr (@constructor.lexical_scope -> @params.defs) precedence = 1 ;; Connect to paramaters for named argument resolution edge @constructor.def -> @params.names @@ -1580,13 +1476,13 @@ inherit .star_extension ;; Connect the constructor body's block lexical scope to the constructor @constructor [ConstructorDefinition @block [Block]] { - edge @block.lexical_scope -> @constructor.extended_scope + edge @block.lexical_scope -> @constructor.lexical_scope } @constructor [ConstructorDefinition [ConstructorAttributes item: [ConstructorAttribute @modifier [ModifierInvocation] ]]] { - edge @modifier.lexical_scope -> @constructor.extended_scope + edge @modifier.lexical_scope -> @constructor.lexical_scope edge @modifier.identifier -> @constructor.lexical_scope } @@ -1637,15 +1533,14 @@ inherit .star_extension @fallback [FallbackFunctionDefinition] { node @fallback.lexical_scope - node @fallback.extended_scope } @fallback [FallbackFunctionDefinition @params parameters: [ParametersDeclaration]] { - edge @params.lexical_scope -> @fallback.extended_scope + edge @params.lexical_scope -> @fallback.lexical_scope ;; Input parameters are available in the fallback function scope - edge @fallback.extended_scope -> @params.defs - attr (@fallback.extended_scope -> @params.defs) precedence = 1 + edge @fallback.lexical_scope -> @params.defs + attr (@fallback.lexical_scope -> @params.defs) precedence = 1 } @fallback [FallbackFunctionDefinition returns: [ReturnsDeclaration @@ -1654,12 +1549,12 @@ inherit .star_extension edge @return_params.lexical_scope -> @fallback.lexical_scope ;; Return parameters are available in the fallback function scope - edge @fallback.extended_scope -> @return_params.defs - attr (@fallback.extended_scope -> @return_params.defs) precedence = 1 + edge @fallback.lexical_scope -> @return_params.defs + attr (@fallback.lexical_scope -> @return_params.defs) precedence = 1 } @fallback [FallbackFunctionDefinition [FunctionBody @block [Block]]] { - edge @block.lexical_scope -> @fallback.extended_scope + edge @block.lexical_scope -> @fallback.lexical_scope } @fallback [FallbackFunctionDefinition [FallbackFunctionAttributes @@ -1670,11 +1565,10 @@ inherit .star_extension @receive [ReceiveFunctionDefinition] { node @receive.lexical_scope - node @receive.extended_scope } @receive [ReceiveFunctionDefinition [FunctionBody @block [Block]]] { - edge @block.lexical_scope -> @receive.extended_scope + edge @block.lexical_scope -> @receive.lexical_scope } @receive [ReceiveFunctionDefinition [ReceiveFunctionAttributes @@ -1691,7 +1585,6 @@ inherit .star_extension @modifier [ModifierDefinition] { node @modifier.def node @modifier.lexical_scope - node @modifier.extended_scope } @modifier [ModifierDefinition @@ -1701,7 +1594,7 @@ inherit .star_extension attr (@modifier.def) node_definition = @name attr (@modifier.def) definiens_node = @modifier - edge @body.lexical_scope -> @modifier.extended_scope + edge @body.lexical_scope -> @modifier.lexical_scope ; Special case: bind the place holder statement `_` to the built-in ; `%placeholder`. This only happens in the body of a modifier. @@ -1716,11 +1609,11 @@ inherit .star_extension } @modifier [ModifierDefinition @params [ParametersDeclaration]] { - edge @params.lexical_scope -> @modifier.extended_scope + edge @params.lexical_scope -> @modifier.lexical_scope ;; Input parameters are available in the modifier scope - edge @modifier.extended_scope -> @params.defs - attr (@modifier.extended_scope -> @params.defs) precedence = 1 + edge @modifier.lexical_scope -> @params.defs + attr (@modifier.lexical_scope -> @params.defs) precedence = 1 } @@ -2079,7 +1972,6 @@ inherit .star_extension @state_var [StateVariableDefinition] { node @state_var.lexical_scope - node @state_var.extended_scope node @state_var.def } @@ -2121,7 +2013,7 @@ inherit .star_extension @state_var [StateVariableDefinition [StateVariableDefinitionValue @value [Expression]] ] { - let @value.lexical_scope = @state_var.extended_scope + let @value.lexical_scope = @state_var.lexical_scope } diff --git a/crates/solidity/outputs/cargo/crate/src/generated/bindings/generated/binding_rules.rs b/crates/solidity/outputs/cargo/crate/src/generated/bindings/generated/binding_rules.rs index 5c7743b323..5eb83819c6 100644 --- a/crates/solidity/outputs/cargo/crate/src/generated/bindings/generated/binding_rules.rs +++ b/crates/solidity/outputs/cargo/crate/src/generated/bindings/generated/binding_rules.rs @@ -26,7 +26,6 @@ inherit .enclosing_def inherit .parent_scope inherit .lexical_scope -inherit .extended_scope ; Used to resolve extension methods for `using for *` directives ; This is used as a minor optimization to avoid introducing new possible paths @@ -73,13 +72,6 @@ inherit .star_extension ;; inherited) for contracts to resolve bases (both in inheritance lists and ;; override specifiers) let @source_unit.parent_scope = @source_unit.lexical_scope - ; FIXME: we probably need to make extended scope different than lexical scope - ; and push an extension scope on that path - let @source_unit.extended_scope = @source_unit.lexical_scope - - ; We may jump to scope here to resolve using the extensions scope provided by - ; contract/libraries that contain `using` directives - ; edge @source_unit.lexical_scope -> JUMP_TO_SCOPE_NODE ; This is used to indicate the resolution algorithm that here's where it ; should inject any possible extension scopes @@ -113,17 +105,6 @@ inherit .star_extension edge @unit_member.lexical_scope -> @source_unit.lexical_scope } -;; Definitions that need to resolve expressions do it through an extended_scope -@source_unit [SourceUnit [SourceUnitMembers - [SourceUnitMember @unit_member ( - [FunctionDefinition] - | [ConstantDefinition] - )] -]] { - edge @unit_member.extended_scope -> @source_unit.extended_scope -} - - ;; Special case for built-ins: we want to export all symbols in the contract: ;; functions, types and state variables. All built-in symbols are defined in an ;; internal contract named '%BuiltIns%' (renamed from '$BuiltIns$') so we need @@ -312,15 +293,6 @@ inherit .star_extension attr (ns_member) push_symbol = "." edge heir.ns -> ns_member edge ns_member -> @type_name.push_begin - - if (version-matches "< 0.7.0") { - ; `using` directives are inherited in Solidity < 0.7.0, so connect them to - ; our own extensions scope - ; node extensions_push_guard - ; attr (extensions_push_guard) push_symbol = "@extensions" - ; edge heir.extensions -> extensions_push_guard - ; edge extensions_push_guard -> @type_name.push_begin - } } ;; The next couple of rules setup a `.parent_refs` attribute to use in the @@ -350,7 +322,6 @@ inherit .star_extension @contract [ContractDefinition @name name: [Identifier]] { node @contract.lexical_scope - node @contract.extended_scope node @contract.extensions node @contract.def node @contract.members @@ -360,6 +331,7 @@ inherit .star_extension attr (@contract.def) node_definition = @name attr (@contract.def) definiens_node = @contract + ; The .extensions node is where `using` directives will hook the definitions attr (@contract.def) extension_scope = @contract.extensions edge @contract.lexical_scope -> @contract.instance @@ -424,12 +396,12 @@ inherit .star_extension attr (push_typeof) push_symbol = "@typeof" node push_name attr (push_name) push_symbol = (source-text @name) - edge call -> push_typeof - edge push_typeof -> push_name node hook attr (hook) extension_hook + + edge call -> push_typeof + edge push_typeof -> push_name edge push_name -> hook - ; edge push_name -> JUMP_TO_SCOPE_NODE if (version-matches "< 0.5.0") { ; For Solidity < 0.5.0 `this` also acts like an `address` @@ -447,22 +419,10 @@ inherit .star_extension attr (@contract.star_extension) push_symbol = "@*" if (version-matches "< 0.7.0") { - ; Expose extensions through an `@extensions` guard on Solidity < 0.7.0 so - ; that they can be accessed from sub contract extension scopes - ; node extensions_guard - ; attr (extensions_guard) pop_symbol = "@extensions" - ; edge @contract.def -> extensions_guard - ; edge extensions_guard -> @contract.extensions - - ; Since using directives are inherited, we need to *always* connect the push - ; extensions to the extended scope, regardless of whether this contract - ; contains any `using` directive. - ; edge @contract.extended_scope -> @contract.push_extensions - ; For Solidity < 0.7.0 using directives are inherited, so we need to connect ; always For newer versions, this connection only happens when there is a ; `using for *` directive in the contract (see rule below) - edge @contract.star_extension -> @contract.extended_scope + edge @contract.star_extension -> @contract.lexical_scope } ; Path to resolve the built-in type for type() expressions @@ -566,7 +526,6 @@ inherit .star_extension )] ]] { edge @member.lexical_scope -> @contract.lexical_scope - edge @member.extended_scope -> @contract.extended_scope } @contract [ContractDefinition [ContractMembers @@ -574,11 +533,6 @@ inherit .star_extension ]] { ; Hook the using definition in the extensions scope edge @contract.extensions -> @using.def - - ; Connect the extensions push path (this can happen multiple times if there - ; are multiple `using` directives in the contract, but that's allowed by the - ; graph builder). - ; edge @contract.extended_scope -> @contract.push_extensions } @contract [ContractDefinition [ContractMembers @@ -654,7 +608,7 @@ inherit .star_extension ]]] { ; Connect the star extension node to the resolution extended scope if there is ; a `using for *` directive in the contract - edge @contract.star_extension -> @contract.extended_scope + edge @contract.star_extension -> @contract.lexical_scope } ; This applies to both state variables and function definitions @@ -682,9 +636,6 @@ inherit .star_extension edge @interface.lexical_scope -> @interface.instance - ; Interfaces don't contain expressions (or `using` directives), so the - ; extended scope is the same as the lexical scope - let @interface.extended_scope = @interface.lexical_scope ; The extensions node is required for the inheritance rules, but not used in interfaces let @interface.extensions = (node) @@ -769,7 +720,6 @@ inherit .star_extension item: [ContractMember @function variant: [FunctionDefinition]] ]] { edge @interface.members -> @function.def - edge @function.extended_scope -> @interface.extended_scope } [InterfaceDefinition [InterfaceMembers [ContractMember @using [UsingDirective]]]] { @@ -786,7 +736,6 @@ inherit .star_extension @library [LibraryDefinition @name name: [Identifier]] { node @library.lexical_scope - node @library.extended_scope node @library.extensions node @library.def node @library.ns @@ -794,6 +743,7 @@ inherit .star_extension attr (@library.def) node_definition = @name attr (@library.def) definiens_node = @library + ; The .extensions node is where `using` directives will hook the definitions attr (@library.def) extension_scope = @library.extensions edge @library.lexical_scope -> @library.ns @@ -845,7 +795,6 @@ inherit .star_extension )] ]] { edge @member.lexical_scope -> @library.lexical_scope - edge @member.extended_scope -> @library.extended_scope edge @library.ns -> @member.def } @@ -854,7 +803,6 @@ inherit .star_extension ]] { edge @library.modifiers -> @modifier.def edge @modifier.lexical_scope -> @library.lexical_scope - edge @modifier.extended_scope -> @library.extended_scope } @library [LibraryDefinition [LibraryMembers @@ -862,9 +810,6 @@ inherit .star_extension ]] { ; Expose the using directive from the extensions scope edge @library.extensions -> @using.def - - ; Connect the extensions push path - ; edge @library.extended_scope -> @library.push_extensions } @library [LibraryDefinition [LibraryMembers [ContractMember @@ -872,58 +817,13 @@ inherit .star_extension ]]] { ; Connect the star extension node to the resolution extended scope if there is ; a `using for *` directive in the library - edge @library.star_extension -> @library.extended_scope + edge @library.star_extension -> @library.lexical_scope } -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; Extensions scope rules -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -;; For contracts (and libraries) navigating to the source unit lexical scope -;; *also* needs to (optionally) propagate an extensions scope to be able to -;; correctly bind `using` attached functions. -@contract_or_library ([ContractDefinition] | [LibraryDefinition]) { - ; The `.extended_scope` resolution scope used by function bodies and other - ; expressions. From there we need to (optionally) push the extensions scope - ; (ie. `using` directives definitions) to the scope stack. - ; We will connect the path to push the extensions *if* the contract/library - ; has a `using` directive. Also, the extended scope links to the lexical scope - ; of the contract/library directly, regardless of whether there is a `using` - ; directive or not. - ; TODO: if we had a query negation operator to detect when there is no `using` - ; directive, could we avoid connecting directly when there are extensions? - edge @contract_or_library.extended_scope -> @contract_or_library.lexical_scope - - ; The .extensions node is where `using` directives will hook the definitions - attr (@contract_or_library.extensions) is_exported - - ; Now we define the path to push the .extensions scope into the scope stack. - ; We connect this to the extended scope only when there are extensions in the - ; contract/library. - ; node @contract_or_library.push_extensions - ; attr (@contract_or_library.push_extensions) push_scoped_symbol = "@extend" - ; attr (@contract_or_library.push_extensions) scope = @contract_or_library.extensions - ; node drop_scopes - ; attr (drop_scopes) type = "drop_scopes" - ; node pop_extensions - ; attr (pop_extensions) pop_scoped_symbol = "@extend" - - ; edge @contract_or_library.push_extensions -> drop_scopes - ; edge drop_scopes -> pop_extensions - ; edge pop_extensions -> @contract_or_library.lexical_scope -} - - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Using directives ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; The UsingDirective node requires the enclosing context to setup an -;; .extended_scope scoped variable for it to resolve both targets and subjects. -;; The resolution connects to the extended scope in order to (potentially) push -;; the same extension scope again, to resolve chained calls that all make use of -;; attached functions. - @using [UsingDirective] { ; This node acts as a definition in the sense that provides an entry point ; that pops the target type and pushes the library/functions to attach to the @@ -938,7 +838,7 @@ inherit .star_extension @using [UsingDirective [UsingClause @id_path [IdentifierPath]]] { ; resolve the library to be used in the directive - edge @id_path.push_end -> @using.extended_scope + edge @id_path.push_end -> @using.lexical_scope ; because we're using the whole library, we don't need to "consume" the ; attached function (as when using the deconstruction syntax), but we still @@ -960,7 +860,7 @@ inherit .star_extension ]] ]]] { ; resolve the function to be used in the directive - edge @id_path.push_end -> @using.extended_scope + edge @id_path.push_end -> @using.lexical_scope node dot attr (dot) pop_symbol = "." @@ -987,9 +887,8 @@ inherit .star_extension edge @type_name.pop_end -> cast edge cast -> @using.clause - ; resolve the target type of the directive on the extended scope so the - ; extension scope gets re-pushed - edge @type_name.type_ref -> @using.extended_scope + ; resolve the target type of the directive on the lexical scope + edge @type_name.type_ref -> @using.lexical_scope } [ContractMember @using [UsingDirective [UsingTarget [Asterisk]]]] { @@ -1453,7 +1352,6 @@ inherit .star_extension } node @function.lexical_scope - node @function.extended_scope node @function.def ; this path from the function definition to the scope allows attaching @@ -1473,12 +1371,12 @@ inherit .star_extension } @function [FunctionDefinition @params parameters: [ParametersDeclaration]] { - edge @params.lexical_scope -> @function.extended_scope + edge @params.lexical_scope -> @function.lexical_scope ;; Input parameters are available in the function scope - edge @function.extended_scope -> @params.defs + edge @function.lexical_scope -> @params.defs ;; ... and shadow other declarations - attr (@function.extended_scope -> @params.defs) precedence = 1 + attr (@function.lexical_scope -> @params.defs) precedence = 1 ;; Connect to paramaters for named argument resolution edge @function.def -> @params.names @@ -1487,12 +1385,12 @@ inherit .star_extension @function [FunctionDefinition returns: [ReturnsDeclaration @return_params [ParametersDeclaration] ]] { - edge @return_params.lexical_scope -> @function.extended_scope + edge @return_params.lexical_scope -> @function.lexical_scope ;; Return parameters are available in the function scope - edge @function.extended_scope -> @return_params.defs + edge @function.lexical_scope -> @return_params.defs ;; ... and shadow other declarations - attr (@function.extended_scope -> @return_params.defs) precedence = 1 + attr (@function.lexical_scope -> @return_params.defs) precedence = 1 } ;; Only functions that return a single value have an actual return type @@ -1509,13 +1407,13 @@ inherit .star_extension ;; Connect the function body's block lexical scope to the function @function [FunctionDefinition [FunctionBody @block [Block]]] { - edge @block.lexical_scope -> @function.extended_scope + edge @block.lexical_scope -> @function.lexical_scope } @function [FunctionDefinition [FunctionAttributes item: [FunctionAttribute @modifier [ModifierInvocation] ]]] { - edge @modifier.lexical_scope -> @function.extended_scope + edge @modifier.lexical_scope -> @function.lexical_scope } @modifier [ModifierInvocation @name [IdentifierPath]] { @@ -1539,18 +1437,17 @@ inherit .star_extension ;;; Unnamed functions (deprecated) @unnamed_function [UnnamedFunctionDefinition] { node @unnamed_function.lexical_scope - node @unnamed_function.extended_scope } @unnamed_function [UnnamedFunctionDefinition @params parameters: [ParametersDeclaration]] { - edge @params.lexical_scope -> @unnamed_function.extended_scope + edge @params.lexical_scope -> @unnamed_function.lexical_scope - edge @unnamed_function.extended_scope -> @params.defs - attr (@unnamed_function.extended_scope -> @params.defs) precedence = 1 + edge @unnamed_function.lexical_scope -> @params.defs + attr (@unnamed_function.lexical_scope -> @params.defs) precedence = 1 } @unnamed_function [UnnamedFunctionDefinition [FunctionBody @block [Block]]] { - edge @block.lexical_scope -> @unnamed_function.extended_scope + edge @block.lexical_scope -> @unnamed_function.lexical_scope } @unnamed_function [UnnamedFunctionDefinition @@ -1567,17 +1464,16 @@ inherit .star_extension @constructor [ConstructorDefinition] { node @constructor.lexical_scope - node @constructor.extended_scope node @constructor.def } @constructor [ConstructorDefinition @params parameters: [ParametersDeclaration]] { - edge @params.lexical_scope -> @constructor.extended_scope + edge @params.lexical_scope -> @constructor.lexical_scope ;; Input parameters are available in the constructor scope - edge @constructor.extended_scope -> @params.defs + edge @constructor.lexical_scope -> @params.defs ;; ... and shadow other declarations - attr (@constructor.extended_scope -> @params.defs) precedence = 1 + attr (@constructor.lexical_scope -> @params.defs) precedence = 1 ;; Connect to paramaters for named argument resolution edge @constructor.def -> @params.names @@ -1585,13 +1481,13 @@ inherit .star_extension ;; Connect the constructor body's block lexical scope to the constructor @constructor [ConstructorDefinition @block [Block]] { - edge @block.lexical_scope -> @constructor.extended_scope + edge @block.lexical_scope -> @constructor.lexical_scope } @constructor [ConstructorDefinition [ConstructorAttributes item: [ConstructorAttribute @modifier [ModifierInvocation] ]]] { - edge @modifier.lexical_scope -> @constructor.extended_scope + edge @modifier.lexical_scope -> @constructor.lexical_scope edge @modifier.identifier -> @constructor.lexical_scope } @@ -1642,15 +1538,14 @@ inherit .star_extension @fallback [FallbackFunctionDefinition] { node @fallback.lexical_scope - node @fallback.extended_scope } @fallback [FallbackFunctionDefinition @params parameters: [ParametersDeclaration]] { - edge @params.lexical_scope -> @fallback.extended_scope + edge @params.lexical_scope -> @fallback.lexical_scope ;; Input parameters are available in the fallback function scope - edge @fallback.extended_scope -> @params.defs - attr (@fallback.extended_scope -> @params.defs) precedence = 1 + edge @fallback.lexical_scope -> @params.defs + attr (@fallback.lexical_scope -> @params.defs) precedence = 1 } @fallback [FallbackFunctionDefinition returns: [ReturnsDeclaration @@ -1659,12 +1554,12 @@ inherit .star_extension edge @return_params.lexical_scope -> @fallback.lexical_scope ;; Return parameters are available in the fallback function scope - edge @fallback.extended_scope -> @return_params.defs - attr (@fallback.extended_scope -> @return_params.defs) precedence = 1 + edge @fallback.lexical_scope -> @return_params.defs + attr (@fallback.lexical_scope -> @return_params.defs) precedence = 1 } @fallback [FallbackFunctionDefinition [FunctionBody @block [Block]]] { - edge @block.lexical_scope -> @fallback.extended_scope + edge @block.lexical_scope -> @fallback.lexical_scope } @fallback [FallbackFunctionDefinition [FallbackFunctionAttributes @@ -1675,11 +1570,10 @@ inherit .star_extension @receive [ReceiveFunctionDefinition] { node @receive.lexical_scope - node @receive.extended_scope } @receive [ReceiveFunctionDefinition [FunctionBody @block [Block]]] { - edge @block.lexical_scope -> @receive.extended_scope + edge @block.lexical_scope -> @receive.lexical_scope } @receive [ReceiveFunctionDefinition [ReceiveFunctionAttributes @@ -1696,7 +1590,6 @@ inherit .star_extension @modifier [ModifierDefinition] { node @modifier.def node @modifier.lexical_scope - node @modifier.extended_scope } @modifier [ModifierDefinition @@ -1706,7 +1599,7 @@ inherit .star_extension attr (@modifier.def) node_definition = @name attr (@modifier.def) definiens_node = @modifier - edge @body.lexical_scope -> @modifier.extended_scope + edge @body.lexical_scope -> @modifier.lexical_scope ; Special case: bind the place holder statement `_` to the built-in ; `%placeholder`. This only happens in the body of a modifier. @@ -1721,11 +1614,11 @@ inherit .star_extension } @modifier [ModifierDefinition @params [ParametersDeclaration]] { - edge @params.lexical_scope -> @modifier.extended_scope + edge @params.lexical_scope -> @modifier.lexical_scope ;; Input parameters are available in the modifier scope - edge @modifier.extended_scope -> @params.defs - attr (@modifier.extended_scope -> @params.defs) precedence = 1 + edge @modifier.lexical_scope -> @params.defs + attr (@modifier.lexical_scope -> @params.defs) precedence = 1 } @@ -2084,7 +1977,6 @@ inherit .star_extension @state_var [StateVariableDefinition] { node @state_var.lexical_scope - node @state_var.extended_scope node @state_var.def } @@ -2126,7 +2018,7 @@ inherit .star_extension @state_var [StateVariableDefinition [StateVariableDefinitionValue @value [Expression]] ] { - let @value.lexical_scope = @state_var.extended_scope + let @value.lexical_scope = @state_var.lexical_scope } From bbfb744116ac979e747d1ce06ae557c35db9dce2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustavo=20Gir=C3=A1ldez?= Date: Wed, 27 Nov 2024 15:23:40 -0500 Subject: [PATCH 08/16] Inherited extensions are now indicated from the rules themselves Instead of having to manually customize the resolver. Also, simplify `ResolveOptions` to an enum with two possible values: `Full` and `NonRecursive`. `NonRecursive` is used internally from `simple_resolve` to disable code paths that could lead to infinite recursions when attempting to resolve a reference. --- crates/metaslang/bindings/src/builder/mod.rs | 20 ++++++-- crates/metaslang/bindings/src/lib.rs | 27 +++++----- crates/metaslang/bindings/src/resolver/mod.rs | 49 +++++-------------- .../inputs/language/bindings/rules.msgb | 3 ++ .../bindings/generated/binding_rules.rs | 3 ++ .../tests/src/bindings_assertions/runner.rs | 3 -- .../cargo/tests/src/bindings_output/runner.rs | 4 -- 7 files changed, 49 insertions(+), 60 deletions(-) diff --git a/crates/metaslang/bindings/src/builder/mod.rs b/crates/metaslang/bindings/src/builder/mod.rs index 28aeab5c6c..f4a9d68be8 100644 --- a/crates/metaslang/bindings/src/builder/mod.rs +++ b/crates/metaslang/bindings/src/builder/mod.rs @@ -284,6 +284,7 @@ static IS_EXPORTED_ATTR: &str = "is_exported"; static IS_REFERENCE_ATTR: &str = "is_reference"; static EXTENSION_HOOK_ATTR: &str = "extension_hook"; static EXTENSION_SCOPE_ATTR: &str = "extension_scope"; +static INHERIT_EXTENSIONS_ATTR: &str = "inherit_extensions"; static PARENTS_ATTR: &str = "parents"; static SCOPE_ATTR: &str = "scope"; static SOURCE_NODE_ATTR: &str = "source_node"; @@ -305,6 +306,7 @@ static POP_SCOPED_SYMBOL_ATTRS: Lazy> = Lazy::new(|| { IMPORT_NODES_ATTR, SYNTAX_TYPE_ATTR, EXTENSION_SCOPE_ATTR, + INHERIT_EXTENSIONS_ATTR, ]) }); static POP_SYMBOL_ATTRS: Lazy> = Lazy::new(|| { @@ -319,6 +321,7 @@ static POP_SYMBOL_ATTRS: Lazy> = Lazy::new(|| { IMPORT_NODES_ATTR, SYNTAX_TYPE_ATTR, EXTENSION_SCOPE_ATTR, + INHERIT_EXTENSIONS_ATTR, ]) }); static PUSH_SCOPED_SYMBOL_ATTRS: Lazy> = Lazy::new(|| { @@ -340,8 +343,14 @@ static PUSH_SYMBOL_ATTRS: Lazy> = Lazy::new(|| { PARENTS_ATTR, ]) }); -static SCOPE_ATTRS: Lazy> = - Lazy::new(|| HashSet::from([TYPE_ATTR, IS_EXPORTED_ATTR, IS_ENDPOINT_ATTR, EXTENSION_HOOK_ATTR])); +static SCOPE_ATTRS: Lazy> = Lazy::new(|| { + HashSet::from([ + TYPE_ATTR, + IS_EXPORTED_ATTR, + IS_ENDPOINT_ATTR, + EXTENSION_HOOK_ATTR, + ]) +}); // Edge attribute names static PRECEDENCE_ATTR: &str = "precedence"; @@ -906,10 +915,14 @@ impl<'a, KT: KindTypes> Builder<'a, KT> { }; let extension_scope = match node.attributes.get(EXTENSION_SCOPE_ATTR) { - Some(extension_scope) => Some(self.node_handle_for_graph_node(extension_scope.as_graph_node_ref()?)), + Some(extension_scope) => { + Some(self.node_handle_for_graph_node(extension_scope.as_graph_node_ref()?)) + } None => None, }; + let inherit_extensions = Self::load_flag(node, INHERIT_EXTENSIONS_ATTR)?; + self.definitions_info.insert( node_handle, DefinitionBindingInfo { @@ -919,6 +932,7 @@ impl<'a, KT: KindTypes> Builder<'a, KT> { export_node, import_nodes, extension_scope, + inherit_extensions, }, ); } else if stack_graph_node.is_reference() { diff --git a/crates/metaslang/bindings/src/lib.rs b/crates/metaslang/bindings/src/lib.rs index 98d44dae2f..d89b500e4c 100644 --- a/crates/metaslang/bindings/src/lib.rs +++ b/crates/metaslang/bindings/src/lib.rs @@ -32,11 +32,10 @@ pub(crate) struct DefinitionBindingInfo { definiens: Option>, tag: Option, parents: Vec, - #[allow(dead_code)] export_node: Option, - #[allow(dead_code)] import_nodes: Vec, extension_scope: Option, + inherit_extensions: bool, } pub(crate) struct ReferenceBindingInfo { @@ -55,7 +54,6 @@ pub struct Bindings { cursor_to_references: HashMap, context: Option, extension_hooks: HashSet, - resolve_options: ResolveOptions, } pub enum FileDescriptor { @@ -140,14 +138,9 @@ impl Bindings { cursor_to_references: HashMap::new(), context: None, extension_hooks: HashSet::new(), - resolve_options: ResolveOptions::default(), } } - pub fn use_recursive_extension_scopes(&mut self) { - self.resolve_options.recursive_extension_scopes = true; - } - pub fn add_system_file(&mut self, file_path: &str, tree_cursor: Cursor) { let file_kind = FileDescriptor::System(file_path.into()); let file = self.stack_graph.get_or_create_file(&file_kind.as_string()); @@ -419,6 +412,13 @@ impl<'a, KT: KindTypes + 'static> Definition<'a, KT> { .and_then(|info| info.extension_scope) } + pub(crate) fn inherit_extensions(&self) -> bool { + self.owner + .definitions_info + .get(&self.handle) + .map_or(false, |info| info.inherit_extensions) + } + pub fn to_handle(self) -> DefinitionHandle { DefinitionHandle(self.handle) } @@ -490,18 +490,17 @@ impl<'a, KT: KindTypes + 'static> Reference<'a, KT> { } pub fn jump_to_definition(&self) -> Result, ResolutionError<'a, KT>> { - Resolver::build_for(self, self.owner.resolve_options).first() + Resolver::build_for(self, ResolveOptions::Full).first() } pub fn definitions(&self) -> Vec> { - Resolver::build_for(self, self.owner.resolve_options).all() + Resolver::build_for(self, ResolveOptions::Full).all() } pub(crate) fn simple_resolve(&self) -> Result, ResolutionError<'a, KT>> { - Resolver::build_for( - self, - ResolveOptions::reentrant_safe(), - ).first() + // This was likely originated from a full resolution call, so cut + // recursion here by restricting the resolution algorithm. + Resolver::build_for(self, ResolveOptions::NonRecursive).first() } pub(crate) fn has_tag(&self, tag: Tag) -> bool { diff --git a/crates/metaslang/bindings/src/resolver/mod.rs b/crates/metaslang/bindings/src/resolver/mod.rs index db1b3c53b0..549cf2a75b 100644 --- a/crates/metaslang/bindings/src/resolver/mod.rs +++ b/crates/metaslang/bindings/src/resolver/mod.rs @@ -44,34 +44,10 @@ pub(crate) struct Resolver<'a, KT: KindTypes + 'static> { options: ResolveOptions, } -#[derive(Copy, Clone)] -pub(crate) struct ResolveOptions { - pub rank_results: bool, - pub apply_c3_linearisation: bool, - pub use_extension_hooks: bool, - pub recursive_extension_scopes: bool, -} - -impl ResolveOptions { - pub fn reentrant_safe() -> Self { - Self { - rank_results: true, - apply_c3_linearisation: false, - use_extension_hooks: false, - recursive_extension_scopes: false, - } - } -} - -impl Default for ResolveOptions { - fn default() -> Self { - Self { - rank_results: true, - apply_c3_linearisation: true, - use_extension_hooks: true, - recursive_extension_scopes: false, - } - } +#[derive(Copy, Clone, Eq, PartialEq)] +pub(crate) enum ResolveOptions { + Full, + NonRecursive, } struct ResolvedPath<'a, KT: KindTypes + 'static> { @@ -173,7 +149,7 @@ impl<'a, KT: KindTypes + 'static> Resolver<'a, KT> { fn resolve(&mut self) { let mut reference_paths = Vec::new(); - if self.options.use_extension_hooks { + if self.options == ResolveOptions::Full { let ref_parents = self.reference.resolve_parents(); let mut extensions = HashSet::new(); for parent in &ref_parents { @@ -181,7 +157,8 @@ impl<'a, KT: KindTypes + 'static> Resolver<'a, KT> { extensions.insert(extension_scope); } - if self.options.recursive_extension_scopes { + if parent.inherit_extensions() { + #[allow(clippy::mutable_key_type)] let grand_parents = Self::resolve_parents_all(parent.clone()); for grand_parent in grand_parents.values().flatten() { if let Some(extension_scope) = grand_parent.get_extension_scope() { @@ -200,7 +177,8 @@ impl<'a, KT: KindTypes + 'static> Resolver<'a, KT> { |_graph, _paths, path| { reference_paths.push(path.clone()); }, - ).expect("Should never be cancelled"); + ) + .expect("Should never be cancelled"); } else { ForwardPartialPathStitcher::find_all_complete_partial_paths( &mut GraphEdgeCandidates::new(&self.owner.stack_graph, &mut self.partials, None), @@ -210,7 +188,8 @@ impl<'a, KT: KindTypes + 'static> Resolver<'a, KT> { |_graph, _paths, path| { reference_paths.push(path.clone()); }, - ).expect("Should never be cancelled"); + ) + .expect("Should never be cancelled"); }; let mut added_nodes = HashSet::new(); @@ -247,9 +226,7 @@ impl<'a, KT: KindTypes + 'static> Resolver<'a, KT> { pub fn first(&mut self) -> Result, ResolutionError<'a, KT>> { if self.results.len() > 1 { - if self.options.rank_results { - self.rank_results(); - } + self.rank_results(); let top_score = self.results[0].score; let mut results = self @@ -277,7 +254,7 @@ impl<'a, KT: KindTypes + 'static> Resolver<'a, KT> { } self.mark_down_aliases(); self.mark_down_built_ins(); - if self.options.apply_c3_linearisation { + if self.options == ResolveOptions::Full { self.rank_c3_methods(); } self.results.sort_by(|a, b| b.score.total_cmp(&a.score)); diff --git a/crates/solidity/inputs/language/bindings/rules.msgb b/crates/solidity/inputs/language/bindings/rules.msgb index b2495fa1a8..68c8c2708c 100644 --- a/crates/solidity/inputs/language/bindings/rules.msgb +++ b/crates/solidity/inputs/language/bindings/rules.msgb @@ -460,6 +460,9 @@ inherit .star_extension ; above to connect the instance scope of this contract to the parents. let @specifier.heir = @contract attr (@contract.def) parents = @specifier.parent_refs + if (version-matches "< 0.7.0") { + attr (@contract.def) inherit_extensions + } ; The rest of these statements deal with defining and connecting the `super` ; keyword path. diff --git a/crates/solidity/outputs/cargo/crate/src/generated/bindings/generated/binding_rules.rs b/crates/solidity/outputs/cargo/crate/src/generated/bindings/generated/binding_rules.rs index 5eb83819c6..579c433711 100644 --- a/crates/solidity/outputs/cargo/crate/src/generated/bindings/generated/binding_rules.rs +++ b/crates/solidity/outputs/cargo/crate/src/generated/bindings/generated/binding_rules.rs @@ -465,6 +465,9 @@ inherit .star_extension ; above to connect the instance scope of this contract to the parents. let @specifier.heir = @contract attr (@contract.def) parents = @specifier.parent_refs + if (version-matches "< 0.7.0") { + attr (@contract.def) inherit_extensions + } ; The rest of these statements deal with defining and connecting the `super` ; keyword path. diff --git a/crates/solidity/outputs/cargo/tests/src/bindings_assertions/runner.rs b/crates/solidity/outputs/cargo/tests/src/bindings_assertions/runner.rs index 7b5c1c41a2..fc009f9a68 100644 --- a/crates/solidity/outputs/cargo/tests/src/bindings_assertions/runner.rs +++ b/crates/solidity/outputs/cargo/tests/src/bindings_assertions/runner.rs @@ -30,9 +30,6 @@ pub fn run(group_name: &str, test_name: &str) -> Result<()> { fn check_assertions_with_version(version: &Version, contents: &str) -> Result<()> { let parser = Parser::create(version.clone())?; let mut bindings = create_bindings(version)?; - if *version < Version::parse("0.7.0").unwrap() { - bindings.use_recursive_extension_scopes(); - } let mut assertions = Assertions::new(); let mut skipped = 0; diff --git a/crates/solidity/outputs/cargo/tests/src/bindings_output/runner.rs b/crates/solidity/outputs/cargo/tests/src/bindings_output/runner.rs index ade4fdf9ca..4d7217163f 100644 --- a/crates/solidity/outputs/cargo/tests/src/bindings_output/runner.rs +++ b/crates/solidity/outputs/cargo/tests/src/bindings_output/runner.rs @@ -5,7 +5,6 @@ use infra_utils::cargo::CargoWorkspace; use infra_utils::codegen::CodegenFileSystem; use infra_utils::paths::PathExtensions; use metaslang_graph_builder::graph::Graph; -use semver::Version; use slang_solidity::cst::KindTypes; use slang_solidity::parser::{ParseOutput, Parser}; @@ -44,9 +43,6 @@ pub fn run(group_name: &str, test_name: &str) -> Result<()> { for version in &VERSION_BREAKS { let parser = Parser::create(version.clone())?; let mut bindings = create_bindings(version)?; - if *version < Version::parse("0.7.0").unwrap() { - bindings.use_recursive_extension_scopes(); - } let mut parsed_parts: Vec> = Vec::new(); let multi_part = split_multi_file(&contents); From 74a53b141f35f5b315fc237f07bf2b51582ebce0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustavo=20Gir=C3=A1ldez?= Date: Wed, 27 Nov 2024 15:32:52 -0500 Subject: [PATCH 09/16] Rename `simple_resolve` to `non_recursive_resolve` --- crates/metaslang/bindings/src/lib.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/crates/metaslang/bindings/src/lib.rs b/crates/metaslang/bindings/src/lib.rs index d89b500e4c..6d39e99d3e 100644 --- a/crates/metaslang/bindings/src/lib.rs +++ b/crates/metaslang/bindings/src/lib.rs @@ -259,7 +259,10 @@ impl Bindings { } else { // TODO: what should we do if the parent reference // cannot be resolved at this point? - self.to_reference(*handle).unwrap().simple_resolve().ok() + self.to_reference(*handle) + .unwrap() + .non_recursive_resolve() + .ok() } }) .collect() @@ -497,7 +500,9 @@ impl<'a, KT: KindTypes + 'static> Reference<'a, KT> { Resolver::build_for(self, ResolveOptions::Full).all() } - pub(crate) fn simple_resolve(&self) -> Result, ResolutionError<'a, KT>> { + pub(crate) fn non_recursive_resolve( + &self, + ) -> Result, ResolutionError<'a, KT>> { // This was likely originated from a full resolution call, so cut // recursion here by restricting the resolution algorithm. Resolver::build_for(self, ResolveOptions::NonRecursive).first() From 67ae3056cee3ad530acde30d5c8fd566d5faf998 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustavo=20Gir=C3=A1ldez?= Date: Wed, 27 Nov 2024 15:34:32 -0500 Subject: [PATCH 10/16] Refactor: rename `jump_to_definition` to `resolve_definition` --- crates/metaslang/bindings/src/lib.rs | 2 +- .../outputs/cargo/tests/src/bindings_assertions/assertions.rs | 2 +- .../outputs/cargo/tests/src/bindings_output/renderer.rs | 2 +- crates/solidity/testing/perf/src/tests/references.rs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/metaslang/bindings/src/lib.rs b/crates/metaslang/bindings/src/lib.rs index 6d39e99d3e..fc60664582 100644 --- a/crates/metaslang/bindings/src/lib.rs +++ b/crates/metaslang/bindings/src/lib.rs @@ -492,7 +492,7 @@ impl<'a, KT: KindTypes + 'static> Reference<'a, KT> { .expect("Reference does not have a valid file descriptor") } - pub fn jump_to_definition(&self) -> Result, ResolutionError<'a, KT>> { + pub fn resolve_definition(&self) -> Result, ResolutionError<'a, KT>> { Resolver::build_for(self, ResolveOptions::Full).first() } diff --git a/crates/solidity/outputs/cargo/tests/src/bindings_assertions/assertions.rs b/crates/solidity/outputs/cargo/tests/src/bindings_assertions/assertions.rs index 42d533ed5d..52e242cda8 100644 --- a/crates/solidity/outputs/cargo/tests/src/bindings_assertions/assertions.rs +++ b/crates/solidity/outputs/cargo/tests/src/bindings_assertions/assertions.rs @@ -475,7 +475,7 @@ fn find_and_resolve_reference<'a>( // For the purpose of binding assertions, any failure to resolve to a single // definition will be treated as if it was unresolved - Ok(reference.jump_to_definition().ok()) + Ok(reference.resolve_definition().ok()) } fn lookup_referenced_definition<'a>( diff --git a/crates/solidity/outputs/cargo/tests/src/bindings_output/renderer.rs b/crates/solidity/outputs/cargo/tests/src/bindings_output/renderer.rs index 7298d965d5..13c7035452 100644 --- a/crates/solidity/outputs/cargo/tests/src/bindings_output/renderer.rs +++ b/crates/solidity/outputs/cargo/tests/src/bindings_output/renderer.rs @@ -118,7 +118,7 @@ fn build_report_for_part<'a>( start..end }; - let definition = reference.jump_to_definition(); + let definition = reference.resolve_definition(); let message = match definition { Ok(definition) => { if definition.get_file().is_system() { diff --git a/crates/solidity/testing/perf/src/tests/references.rs b/crates/solidity/testing/perf/src/tests/references.rs index 070f357b4e..98fb8bbf9d 100644 --- a/crates/solidity/testing/perf/src/tests/references.rs +++ b/crates/solidity/testing/perf/src/tests/references.rs @@ -17,7 +17,7 @@ pub fn run(bindings: Bindings) { } reference_count += 1; - let resolution = reference.jump_to_definition(); + let resolution = reference.resolve_definition(); if resolution.is_ok() { resolved_references += 1; } From 05afc72fd095c916e9a8f10919552c2ab0a4fdbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustavo=20Gir=C3=A1ldez?= Date: Wed, 27 Nov 2024 15:45:15 -0500 Subject: [PATCH 11/16] Move `lookup_definition_by_name` to tests crate --- crates/metaslang/bindings/src/lib.rs | 5 ----- crates/solidity/outputs/cargo/tests/src/bindings.rs | 8 +++++++- .../outputs/cargo/tests/src/bindings_assertions/runner.rs | 5 ++--- .../outputs/cargo/tests/src/bindings_output/runner.rs | 5 ++--- 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/crates/metaslang/bindings/src/lib.rs b/crates/metaslang/bindings/src/lib.rs index fc60664582..af852723b5 100644 --- a/crates/metaslang/bindings/src/lib.rs +++ b/crates/metaslang/bindings/src/lib.rs @@ -268,11 +268,6 @@ impl Bindings { .collect() } - pub fn lookup_definition_by_name(&self, name: &str) -> Option> { - self.all_definitions() - .find(|definition| definition.get_cursor().unwrap().node().unparse() == name) - } - pub fn get_context(&self) -> Option> { self.context.and_then(|handle| self.to_definition(handle)) } diff --git a/crates/solidity/outputs/cargo/tests/src/bindings.rs b/crates/solidity/outputs/cargo/tests/src/bindings.rs index c346ab6afb..40c354f01e 100644 --- a/crates/solidity/outputs/cargo/tests/src/bindings.rs +++ b/crates/solidity/outputs/cargo/tests/src/bindings.rs @@ -2,7 +2,7 @@ use std::sync::Arc; use anyhow::Result; use semver::Version; -use slang_solidity::bindings::{self, Bindings}; +use slang_solidity::bindings::{self, Bindings, Definition}; use slang_solidity::cst::TextIndex; use slang_solidity::parser::Parser; use slang_solidity::transform_built_ins_node; @@ -26,3 +26,9 @@ pub fn create_bindings(version: &Version) -> Result { bindings.add_system_file("built_ins.sol", built_ins_cursor); Ok(bindings) } + +pub fn lookup_definition_by_name<'a>(bindings: &'a Bindings, name: &str) -> Option> { + bindings + .all_definitions() + .find(|definition| definition.get_cursor().unwrap().node().unparse() == name) +} diff --git a/crates/solidity/outputs/cargo/tests/src/bindings_assertions/runner.rs b/crates/solidity/outputs/cargo/tests/src/bindings_assertions/runner.rs index fc009f9a68..1c92493c10 100644 --- a/crates/solidity/outputs/cargo/tests/src/bindings_assertions/runner.rs +++ b/crates/solidity/outputs/cargo/tests/src/bindings_assertions/runner.rs @@ -6,7 +6,7 @@ use semver::Version; use slang_solidity::diagnostic; use slang_solidity::parser::Parser; -use crate::bindings::create_bindings; +use crate::bindings::{create_bindings, lookup_definition_by_name}; use crate::bindings_assertions::assertions::{ check_assertions, collect_assertions_into, Assertions, }; @@ -63,8 +63,7 @@ fn check_assertions_with_version(version: &Version, contents: &str) -> Result<() } if let Some(context) = multi_part.context { - let context_definition = bindings - .lookup_definition_by_name(context) + let context_definition = lookup_definition_by_name(&bindings, context) .expect("context definition to be found") .to_handle(); bindings.set_context(&context_definition); diff --git a/crates/solidity/outputs/cargo/tests/src/bindings_output/runner.rs b/crates/solidity/outputs/cargo/tests/src/bindings_output/runner.rs index 4d7217163f..2870d5ae7c 100644 --- a/crates/solidity/outputs/cargo/tests/src/bindings_output/runner.rs +++ b/crates/solidity/outputs/cargo/tests/src/bindings_output/runner.rs @@ -11,7 +11,7 @@ use slang_solidity::parser::{ParseOutput, Parser}; use super::graph::graphviz::render as render_graphviz_graph; use super::graph::mermaid::render as render_mermaid_graph; use super::renderer::render_bindings; -use crate::bindings::create_bindings; +use crate::bindings::{create_bindings, lookup_definition_by_name}; use crate::generated::VERSION_BREAKS; use crate::multi_part_file::{split_multi_file, Part}; @@ -63,8 +63,7 @@ pub fn run(group_name: &str, test_name: &str) -> Result<()> { } if let Some(context) = multi_part.context { - let context_definition = bindings - .lookup_definition_by_name(context) + let context_definition = lookup_definition_by_name(&bindings, context) .expect("context definition to be found") .to_handle(); bindings.set_context(&context_definition); From 3d1f44788738db586642b69444b4a2f9ce829875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustavo=20Gir=C3=A1ldez?= Date: Wed, 27 Nov 2024 16:02:00 -0500 Subject: [PATCH 12/16] Remove unused `NoCancellation` struct --- crates/metaslang/bindings/src/resolver/mod.rs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/crates/metaslang/bindings/src/resolver/mod.rs b/crates/metaslang/bindings/src/resolver/mod.rs index 549cf2a75b..f9b425a5c6 100644 --- a/crates/metaslang/bindings/src/resolver/mod.rs +++ b/crates/metaslang/bindings/src/resolver/mod.rs @@ -7,7 +7,7 @@ use stack_graphs::partial::{PartialPath, PartialPaths}; use stack_graphs::stitching::{ ForwardCandidates, ForwardPartialPathStitcher, GraphEdgeCandidates, GraphEdges, StitcherConfig, }; -use stack_graphs::{CancellationError, CancellationFlag}; +use stack_graphs::CancellationError; use crate::{Bindings, Definition, FileHandle, GraphHandle, Reference, ResolutionError, Tag}; @@ -62,13 +62,6 @@ impl<'a, KT: KindTypes + 'static> ResolvedPath<'a, KT> { } } -pub struct NoCancellation; -impl CancellationFlag for NoCancellation { - fn check(&self, _at: &'static str) -> Result<(), CancellationError> { - Ok(()) - } -} - /// Candidates for the forward stitching resolution process. This will inject /// edges to the the given extensions scopes at extension hook nodes when asked /// for forward candidates (ie. `get_forward_candidates`) by the resolution From f387bba60f96ae680765e1a47e4a6f32f7395f1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustavo=20Gir=C3=A1ldez?= Date: Wed, 27 Nov 2024 16:03:59 -0500 Subject: [PATCH 13/16] Update public_api.txt --- crates/metaslang/bindings/generated/public_api.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/metaslang/bindings/generated/public_api.txt b/crates/metaslang/bindings/generated/public_api.txt index f98a7e4582..59b63d67ae 100644 --- a/crates/metaslang/bindings/generated/public_api.txt +++ b/crates/metaslang/bindings/generated/public_api.txt @@ -22,7 +22,6 @@ pub fn metaslang_bindings::Bindings::all_references(&self) -> impl core::ite pub fn metaslang_bindings::Bindings::create(version: semver::Version, binding_rules: &str, path_resolver: alloc::sync::Arc<(dyn metaslang_bindings::PathResolver + core::marker::Sync + core::marker::Send)>) -> Self pub fn metaslang_bindings::Bindings::definition_at(&self, cursor: &metaslang_cst::cursor::Cursor) -> core::option::Option> pub fn metaslang_bindings::Bindings::get_context(&self) -> core::option::Option> -pub fn metaslang_bindings::Bindings::lookup_definition_by_name(&self, name: &str) -> core::option::Option> pub fn metaslang_bindings::Bindings::reference_at(&self, cursor: &metaslang_cst::cursor::Cursor) -> core::option::Option> pub fn metaslang_bindings::Bindings::set_context(&mut self, context: &metaslang_bindings::DefinitionHandle) pub struct metaslang_bindings::Definition<'a, KT: metaslang_cst::kinds::KindTypes + 'static> @@ -48,7 +47,7 @@ impl<'a, KT: metaslang_cst::kinds::KindTypes + 'static> metaslang_bindings::Refe pub fn metaslang_bindings::Reference<'a, KT>::definitions(&self) -> alloc::vec::Vec> pub fn metaslang_bindings::Reference<'a, KT>::get_cursor(&self) -> core::option::Option> pub fn metaslang_bindings::Reference<'a, KT>::get_file(&self) -> metaslang_bindings::FileDescriptor -pub fn metaslang_bindings::Reference<'a, KT>::jump_to_definition(&self) -> core::result::Result, metaslang_bindings::ResolutionError<'a, KT>> +pub fn metaslang_bindings::Reference<'a, KT>::resolve_definition(&self) -> core::result::Result, metaslang_bindings::ResolutionError<'a, KT>> impl<'a, KT: core::clone::Clone + metaslang_cst::kinds::KindTypes + 'static> core::clone::Clone for metaslang_bindings::Reference<'a, KT> pub fn metaslang_bindings::Reference<'a, KT>::clone(&self) -> metaslang_bindings::Reference<'a, KT> impl core::cmp::PartialEq for metaslang_bindings::Reference<'_, KT> From 24abef602da59c1247f820de64e65e6c66a11c5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustavo=20Gir=C3=A1ldez?= Date: Wed, 27 Nov 2024 16:42:38 -0500 Subject: [PATCH 14/16] Move built-ins parsing and ingestion to `slang_solidity` crate Since the built-ins file needs to be pre-processed to transform the symbols as to ensure no conflicts can occur with user code, adding the built-ins requires a couple of manual steps that were replicated in every construction of Solidity bindings API. By encapsulating this functionality in the `slang_solidity` crate we remove a source of user error and make it easier to make changes to the built-ins ingestion code. --- .../solidity/outputs/cargo/crate/src/lib.rs | 98 ++++++++++++------- .../outputs/cargo/tests/src/bindings.rs | 17 +--- .../solidity/testing/perf/benches/iai/main.rs | 9 +- crates/solidity/testing/perf/src/lib.rs | 8 +- .../testing/perf/src/tests/definitions.rs | 2 +- .../testing/perf/src/tests/init_bindings.rs | 23 +---- .../solidity/testing/sanctuary/src/tests.rs | 16 +-- 7 files changed, 84 insertions(+), 89 deletions(-) diff --git a/crates/solidity/outputs/cargo/crate/src/lib.rs b/crates/solidity/outputs/cargo/crate/src/lib.rs index 515f523f68..7463e16525 100644 --- a/crates/solidity/outputs/cargo/crate/src/lib.rs +++ b/crates/solidity/outputs/cargo/crate/src/lib.rs @@ -3,44 +3,68 @@ mod generated; pub use generated::*; #[cfg(feature = "__experimental_bindings_api")] -pub fn transform_built_ins_node(node: &generated::cst::Node) -> generated::cst::Node { - use std::rc::Rc; - - use generated::cst::{Edge, Node, NonterminalNode, TerminalNode}; - - use crate::cst::TerminalKind; - - match node { - Node::Nonterminal(nonterminal) => { - let NonterminalNode { - kind, - text_len, - children, - } = nonterminal.as_ref(); - let children = children - .iter() - .map(|edge| Edge { - label: edge.label, - node: transform_built_ins_node(&edge.node), - }) - .collect(); - let nonterminal = Rc::new(NonterminalNode { - kind: *kind, - text_len: *text_len, - children, - }); - Node::Nonterminal(nonterminal) - } - Node::Terminal(terminal) => { - let TerminalNode { kind, text } = terminal.as_ref(); - let terminal = match terminal.as_ref().kind { - TerminalKind::Identifier => Rc::new(TerminalNode { +pub mod bindings { + use semver::Version; + + pub use super::generated::bindings::*; + use crate::cst::TextIndex; + use crate::parser::{Parser, ParserInitializationError}; + + pub fn add_built_ins( + bindings: &mut Bindings, + version: &Version, + ) -> Result<(), ParserInitializationError> { + let parser = Parser::create(version.clone())?; + let built_ins_parse_output = parser.parse(Parser::ROOT_KIND, get_built_ins(version)); + assert!( + built_ins_parse_output.is_valid(), + "built-ins parse without errors" + ); + + let built_ins_cursor = transform_built_ins_node(&built_ins_parse_output.tree()) + .cursor_with_offset(TextIndex::ZERO); + + bindings.add_system_file("built_ins.sol", built_ins_cursor); + Ok(()) + } + + fn transform_built_ins_node(node: &crate::cst::Node) -> crate::cst::Node { + use std::rc::Rc; + + use crate::cst::{Edge, Node, NonterminalNode, TerminalKind, TerminalNode}; + + match node { + Node::Nonterminal(nonterminal) => { + let NonterminalNode { + kind, + text_len, + children, + } = nonterminal.as_ref(); + let children = children + .iter() + .map(|edge| Edge { + label: edge.label, + node: transform_built_ins_node(&edge.node), + }) + .collect(); + let nonterminal = Rc::new(NonterminalNode { kind: *kind, - text: text.replace('$', "%"), - }), - _ => Rc::clone(terminal), - }; - Node::Terminal(terminal) + text_len: *text_len, + children, + }); + Node::Nonterminal(nonterminal) + } + Node::Terminal(terminal) => { + let TerminalNode { kind, text } = terminal.as_ref(); + let terminal = match terminal.as_ref().kind { + TerminalKind::Identifier => Rc::new(TerminalNode { + kind: *kind, + text: text.replace('$', "%"), + }), + _ => Rc::clone(terminal), + }; + Node::Terminal(terminal) + } } } } diff --git a/crates/solidity/outputs/cargo/tests/src/bindings.rs b/crates/solidity/outputs/cargo/tests/src/bindings.rs index 40c354f01e..aab85d9bad 100644 --- a/crates/solidity/outputs/cargo/tests/src/bindings.rs +++ b/crates/solidity/outputs/cargo/tests/src/bindings.rs @@ -2,28 +2,15 @@ use std::sync::Arc; use anyhow::Result; use semver::Version; -use slang_solidity::bindings::{self, Bindings, Definition}; -use slang_solidity::cst::TextIndex; -use slang_solidity::parser::Parser; -use slang_solidity::transform_built_ins_node; +use slang_solidity::bindings::{self, add_built_ins, Bindings, Definition}; use crate::resolver::TestsPathResolver; pub fn create_bindings(version: &Version) -> Result { - let parser = Parser::create(version.clone())?; let mut bindings = bindings::create_with_resolver(version.clone(), Arc::new(TestsPathResolver {})); + add_built_ins(&mut bindings, version)?; - let built_ins_parse_output = parser.parse(Parser::ROOT_KIND, bindings::get_built_ins(version)); - assert!( - built_ins_parse_output.is_valid(), - "built-ins parse without errors" - ); - - let built_ins_cursor = transform_built_ins_node(&built_ins_parse_output.tree()) - .cursor_with_offset(TextIndex::ZERO); - - bindings.add_system_file("built_ins.sol", built_ins_cursor); Ok(bindings) } diff --git a/crates/solidity/testing/perf/benches/iai/main.rs b/crates/solidity/testing/perf/benches/iai/main.rs index 1957df012e..0db0eae29d 100644 --- a/crates/solidity/testing/perf/benches/iai/main.rs +++ b/crates/solidity/testing/perf/benches/iai/main.rs @@ -8,7 +8,6 @@ use iai_callgrind::{ LibraryBenchmarkConfig, Tool, ValgrindTool, }; use slang_solidity::bindings::Bindings; -use slang_solidity::parser::ParseOutput; use solidity_testing_perf::dataset::SourceFile; use solidity_testing_perf::tests::definitions::Dependencies; use solidity_testing_perf::tests::parser::ParsedFile; @@ -18,6 +17,12 @@ mod __dependencies_used_in_lib__ { } macro_rules! define_benchmark { + ($name:ident) => { + #[library_benchmark()] + fn $name() { + black_box(solidity_testing_perf::tests::$name::run()); + } + }; ($name:ident, $payload:ty) => { #[library_benchmark(setup = solidity_testing_perf::tests::$name::setup)] fn $name(payload: $payload) { @@ -36,7 +41,7 @@ macro_rules! define_benchmark { define_benchmark!(parser, Vec); define_benchmark!(cursor, Vec); define_benchmark!(query, Vec); -define_benchmark!(init_bindings, ParseOutput); +define_benchmark!(init_bindings); define_benchmark!(definitions, Dependencies); define_benchmark!(references, Bindings); diff --git a/crates/solidity/testing/perf/src/lib.rs b/crates/solidity/testing/perf/src/lib.rs index 7819ab9ea0..2ed1851441 100644 --- a/crates/solidity/testing/perf/src/lib.rs +++ b/crates/solidity/testing/perf/src/lib.rs @@ -19,6 +19,12 @@ mod unit_tests { crate::tests::$name::run(payload); } }; + ($name:ident, empty_payload) => { + #[test] + fn $name() { + crate::tests::$name::run(); + } + }; } /* @@ -27,7 +33,7 @@ mod unit_tests { define_test!(parser); define_test!(cursor); define_test!(query); - define_test!(init_bindings); + define_test!(init_bindings, empty_payload); define_test!(definitions); define_test!(references); } diff --git a/crates/solidity/testing/perf/src/tests/definitions.rs b/crates/solidity/testing/perf/src/tests/definitions.rs index ecf6f92ccc..9c488bf48d 100644 --- a/crates/solidity/testing/perf/src/tests/definitions.rs +++ b/crates/solidity/testing/perf/src/tests/definitions.rs @@ -8,7 +8,7 @@ pub struct Dependencies { } pub fn setup() -> Dependencies { - let bindings = super::init_bindings::run(super::init_bindings::setup()); + let bindings = super::init_bindings::run(); let files = super::parser::run(super::parser::setup()); Dependencies { bindings, files } diff --git a/crates/solidity/testing/perf/src/tests/init_bindings.rs b/crates/solidity/testing/perf/src/tests/init_bindings.rs index 4dc9af819a..f14b539fc4 100644 --- a/crates/solidity/testing/perf/src/tests/init_bindings.rs +++ b/crates/solidity/testing/perf/src/tests/init_bindings.rs @@ -1,30 +1,13 @@ use std::sync::Arc; use metaslang_bindings::PathResolver; -use slang_solidity::bindings::{create_with_resolver, get_built_ins, Bindings}; -use slang_solidity::cst::TextIndex; -use slang_solidity::parser::{ParseOutput, Parser}; -use slang_solidity::transform_built_ins_node; +use slang_solidity::bindings::{add_built_ins, create_with_resolver, Bindings}; use crate::dataset::SOLC_VERSION; -pub fn setup() -> ParseOutput { - let parser = Parser::create(SOLC_VERSION).unwrap(); - - let built_ins = parser.parse(Parser::ROOT_KIND, get_built_ins(&SOLC_VERSION)); - - assert!(built_ins.is_valid(), "built-ins parse without errors"); - - built_ins -} - -pub fn run(built_ins: ParseOutput) -> Bindings { +pub fn run() -> Bindings { let mut bindings = create_with_resolver(SOLC_VERSION, Arc::new(NoOpResolver {})); - - let built_ins_cursor = - transform_built_ins_node(&built_ins.tree()).cursor_with_offset(TextIndex::ZERO); - - bindings.add_system_file("built_ins.sol", built_ins_cursor); + add_built_ins(&mut bindings, &SOLC_VERSION).unwrap(); bindings } diff --git a/crates/solidity/testing/sanctuary/src/tests.rs b/crates/solidity/testing/sanctuary/src/tests.rs index 6571da0b11..37a2972f02 100644 --- a/crates/solidity/testing/sanctuary/src/tests.rs +++ b/crates/solidity/testing/sanctuary/src/tests.rs @@ -7,11 +7,11 @@ use infra_utils::paths::PathExtensions; use itertools::Itertools; use metaslang_bindings::PathResolver; use semver::Version; +use slang_solidity::bindings; use slang_solidity::bindings::Bindings; -use slang_solidity::cst::{Cursor, NonterminalKind, TextIndex, TextRange}; +use slang_solidity::cst::{Cursor, NonterminalKind, TextRange}; use slang_solidity::diagnostic::{Diagnostic, Severity}; use slang_solidity::parser::{ParseOutput, Parser}; -use slang_solidity::{bindings, transform_built_ins_node}; use crate::datasets::{DataSet, SourceFile}; use crate::events::{Events, TestOutcome}; @@ -211,17 +211,7 @@ fn create_bindings(version: &Version, source_id: &str, output: &ParseOutput) -> source_id: source_id.into(), }), ); - let parser = Parser::create(version.clone())?; - let built_ins_tree = parser - .parse( - NonterminalKind::SourceUnit, - bindings::get_built_ins(version), - ) - .tree(); - let built_ins_cursor = - transform_built_ins_node(&built_ins_tree).cursor_with_offset(TextIndex::ZERO); - - bindings.add_system_file("built_ins.sol", built_ins_cursor); + bindings::add_built_ins(&mut bindings, version)?; bindings.add_user_file(source_id, output.create_tree_cursor()); Ok(bindings) } From 900120c5a727fd487422d3be9516846bf472c56a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustavo=20Gir=C3=A1ldez?= Date: Wed, 27 Nov 2024 16:57:26 -0500 Subject: [PATCH 15/16] Update public_api.txt --- crates/solidity/outputs/cargo/crate/generated/public_api.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/solidity/outputs/cargo/crate/generated/public_api.txt b/crates/solidity/outputs/cargo/crate/generated/public_api.txt index 5fa1c4d0eb..160b08e248 100644 --- a/crates/solidity/outputs/cargo/crate/generated/public_api.txt +++ b/crates/solidity/outputs/cargo/crate/generated/public_api.txt @@ -2,6 +2,7 @@ pub mod slang_solidity pub mod slang_solidity::bindings +pub fn slang_solidity::bindings::add_built_ins(bindings: &mut slang_solidity::bindings::Bindings, version: &semver::Version) -> core::result::Result<(), slang_solidity::parser::ParserInitializationError> pub fn slang_solidity::bindings::create_with_resolver(version: semver::Version, resolver: alloc::sync::Arc<(dyn metaslang_bindings::PathResolver + core::marker::Sync + core::marker::Send)>) -> slang_solidity::bindings::Bindings pub fn slang_solidity::bindings::get_binding_rules() -> &'static str pub fn slang_solidity::bindings::get_built_ins(version: &semver::Version) -> &'static str @@ -930,4 +931,3 @@ pub fn slang_solidity::parser::Parser::parse(&self, kind: slang_solidity::cst::N pub fn slang_solidity::parser::Parser::version(&self) -> &semver::Version impl core::fmt::Debug for slang_solidity::parser::Parser pub fn slang_solidity::parser::Parser::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result -pub fn slang_solidity::transform_built_ins_node(node: &slang_solidity::cst::Node) -> slang_solidity::cst::Node From 5a93a4e5894c052b508b8b2e248e0ba8c3b3b558 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustavo=20Gir=C3=A1ldez?= Date: Wed, 27 Nov 2024 17:32:44 -0500 Subject: [PATCH 16/16] Added documentation on the added graph attributes --- crates/metaslang/bindings/src/builder/mod.rs | 80 ++++++++++++++++++-- 1 file changed, 73 insertions(+), 7 deletions(-) diff --git a/crates/metaslang/bindings/src/builder/mod.rs b/crates/metaslang/bindings/src/builder/mod.rs index f4a9d68be8..f768302d52 100644 --- a/crates/metaslang/bindings/src/builder/mod.rs +++ b/crates/metaslang/bindings/src/builder/mod.rs @@ -130,7 +130,7 @@ //! To do this, add a `source_node` attribute, whose value is a syntax node capture: //! //! ``` skip -//! @func [FunctionDefinition ... [FunctionName @id [Identifier]] ...] { +//! @func [FunctionDefinition [FunctionName @id [Identifier]]] { //! node def //! attr (def) type = "pop_symbol", symbol = (source-text @id), source_node = @func, is_definition //! } @@ -161,7 +161,7 @@ //! `syntax_type` attribute, whose value is a string indicating the syntax type. //! //! ``` skip -//! @func [FunctionDefinition ... [FunctionName @id [Identifier]] ...] { +//! @func [FunctionDefinition [FunctionName @id [Identifier]]] { //! node def //! ; ... //! attr (def) syntax_type = "function" @@ -175,7 +175,7 @@ //! `definiens_node` attribute, whose value is a syntax node that spans the definiens. //! //! ``` skip -//! @func [FunctionDefinition ... [FunctionName @id [Identifier]] ... @body [FunctionBody] ...] { +//! @func [FunctionDefinition [FunctionName @id [Identifier]] @body [FunctionBody]] { //! node def //! ; ... //! attr (def) definiens_node = @body @@ -189,7 +189,7 @@ //! To connect two stack graph nodes, use the `edge` statement to add an edge between them: //! //! ``` skip -//! @func [FunctionDefinition ... [FunctionName @id [Identifier]] ...] { +//! @func [FunctionDefinition [FunctionName @id [Identifier]]] { //! node def //! attr (def) type = "pop_symbol", symbol = (source-text @id), source_node = @func, is_definition //! node body @@ -201,7 +201,7 @@ //! you can add a `precedence` attribute to each edge to indicate which paths are prioritized: //! //! ``` skip -//! @func [FunctionDefinition ... [FunctionName @id [Identifier]] ...] { +//! @func [FunctionDefinition [FunctionName @id [Identifier]]] { //! node def //! attr (def) type = "pop_symbol", symbol = (source-text @id), source_node = @func, is_definition //! node body @@ -220,7 +220,7 @@ //! ``` skip //! global ROOT_NODE //! -//! @func [FunctionDefinition ... [FunctionName @id [Identifier]] ...] { +//! @func [FunctionDefinition [FunctionName @id [Identifier]]] { //! node def //! attr (def) type = "pop_symbol", symbol = (source-text @id), source_node = @func, is_definition //! edge ROOT_NODE -> def @@ -235,7 +235,7 @@ //! a scope node with a kind as follows: //! //! ``` skip -//! @func [FunctionDefinition ... [FunctionName @id [Identifier]] ...] { +//! @func [FunctionDefinition [FunctionName @id [Identifier]]] { //! ; ... //! node param_scope //! attr (param_scope) debug_kind = "param_scope" @@ -243,6 +243,72 @@ //! } //! ``` //! +//! ### Other node attributes introduced in Slang's usage of stack-graphs +//! +//! #### `tag` attribute +//! +//! This is used to attach a specific meaning to the node, to alter the ranking +//! algorithm used when attempting to disambiguate between multiple definitions +//! found for a reference. This is an optional string attribute. +//! +//! Possible values: +//! +//! - "alias": marks a definition node as a semantic alias of another definition +//! (eg. an import alias) +//! +//! - "c3": used to mark a function/method definition to be a candidate in +//! disambiguation using the C3 linearisation algorithm. In order for C3 +//! linearisation to be possible, type hierarchy attributes need to be provided +//! as well (see `parents` attribute below). +//! +//! - "super": marks a reference as a call to super virtual call. This modifies +//! the C3 linearisation algorithm by eliminating the candidates that are at +//! or further down the hierarchy of where the reference occurs. To determine +//! where the reference occurs, we also use the `parents` attribute. +//! +//! #### `parents` attribute +//! +//! Is used to convey semantic hierarchy. Can be applied to both definitions and +//! references. It's an optional, list of graph nodes attribute. +//! +//! For references it can indicate in which language context the reference +//! occurs (eg. in which method or class). For definitions it can indicate the +//! enclosing type of the definition, or parent classes in a class hierarchy. +//! The parent handles themselves can refer to definitions or references. In the +//! later case, generally speaking they will need to be resolved at resolution +//! time in order to be useful. +//! +//! #### `export_node` and `import_nodes` +//! +//! These are used to define static fixed edges to add via `set_context()`. +//! Using `set_context()` will modify the underlying stack graph by inserting +//! edges from the `import_nodes` of all parents (resolved recursively) of the +//! given context, to the `export_node` associated with the context. +//! +//! This can be used to inject virtual method implementations defined in +//! subclasses in the scope of their parent classes, which are otherwise +//! lexically inaccessible. +//! +//! `export_node` is an optional graph node attribute, and `import_nodes` is an +//! optional list of graph nodes. Both apply only to definition nodes. +//! +//! #### `extension_hook`, `extension_scope` and `inherit_extensions` +//! +//! These attributes enable the bindings API to resolve extension methods by +//! injecting specific scopes at potentially unrelated (lexically speaking) +//! nodes in the stack graph. Availability and application of extension scopes +//! depend on the call site (ie. the reference node). Thus, the extension scope +//! to (potentially) apply when resolving a reference is computed by looking up +//! the `parents` of the reference and then querying those parent nodes for +//! their `extension_scope` (an optional scope node). Any extension providing +//! node can also have the `inherit_extensions` attribute (a boolean) which +//! indicates that the algorithm should recurse and resolve its parents to +//! further look for other extensions scopes. +//! +//! Finally, the attribute `extension_hook` defines where in the graph should +//! these extension scopes be injected. This is typically the root lexical +//! scope. This attribute applies to any scope node and is boolean. +//! mod cancellation; mod functions;