diff --git a/crates/metaslang/bindings/generated/public_api.txt b/crates/metaslang/bindings/generated/public_api.txt index 0e7a4e3bb8..e0fe3e3212 100644 --- a/crates/metaslang/bindings/generated/public_api.txt +++ b/crates/metaslang/bindings/generated/public_api.txt @@ -32,7 +32,6 @@ pub fn metaslang_bindings::BindingGraph::all_references(&self) -> impl core: pub fn metaslang_bindings::BindingGraph::create(version: semver::Version, binding_rules: &str, path_resolver: alloc::rc::Rc>) -> Self pub fn metaslang_bindings::BindingGraph::definition_at(&self, cursor: &metaslang_cst::cursor::Cursor) -> core::option::Option> pub fn metaslang_bindings::BindingGraph::get_context(&self) -> core::option::Option> -pub fn metaslang_bindings::BindingGraph::lookup_definition_by_name(&self, name: &str) -> core::option::Option> pub fn metaslang_bindings::BindingGraph::reference_at(&self, cursor: &metaslang_cst::cursor::Cursor) -> core::option::Option> pub fn metaslang_bindings::BindingGraph::set_context(&mut self, context: &metaslang_bindings::DefinitionHandle) pub struct metaslang_bindings::BuiltInLocation @@ -67,8 +66,8 @@ pub fn metaslang_bindings::Reference<'a, KT>::definitions(&self) -> alloc::vec:: pub fn metaslang_bindings::Reference<'a, KT>::get_cursor(&self) -> &metaslang_cst::cursor::Cursor pub fn metaslang_bindings::Reference<'a, KT>::get_file(&self) -> metaslang_bindings::FileDescriptor pub fn metaslang_bindings::Reference<'a, KT>::id(&self) -> usize -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>::location(&self) -> metaslang_bindings::BindingLocation +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> diff --git a/crates/metaslang/bindings/src/builder/mod.rs b/crates/metaslang/bindings/src/builder/mod.rs index 3a27e9b1cc..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; @@ -282,6 +348,9 @@ 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 INHERIT_EXTENSIONS_ATTR: &str = "inherit_extensions"; static PARENTS_ATTR: &str = "parents"; static SCOPE_ATTR: &str = "scope"; static SOURCE_NODE_ATTR: &str = "source_node"; @@ -302,6 +371,8 @@ static POP_SCOPED_SYMBOL_ATTRS: Lazy> = Lazy::new(|| { EXPORT_NODE_ATTR, IMPORT_NODES_ATTR, SYNTAX_TYPE_ATTR, + EXTENSION_SCOPE_ATTR, + INHERIT_EXTENSIONS_ATTR, ]) }); static POP_SYMBOL_ATTRS: Lazy> = Lazy::new(|| { @@ -315,6 +386,8 @@ static POP_SYMBOL_ATTRS: Lazy> = Lazy::new(|| { EXPORT_NODE_ATTR, IMPORT_NODES_ATTR, SYNTAX_TYPE_ATTR, + EXTENSION_SCOPE_ATTR, + INHERIT_EXTENSIONS_ATTR, ]) }); static PUSH_SCOPED_SYMBOL_ATTRS: Lazy> = Lazy::new(|| { @@ -336,8 +409,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])); +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"; @@ -362,6 +441,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 +450,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 +474,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 +563,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 +980,15 @@ 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, + }; + + let inherit_extensions = Self::load_flag(node, INHERIT_EXTENSIONS_ATTR)?; + self.definitions_info.insert( node_handle, DefinitionBindingInfo { @@ -904,6 +997,8 @@ impl<'a, KT: KindTypes> Builder<'a, KT> { parents, export_node, import_nodes, + extension_scope, + inherit_extensions, }, ); } else if stack_graph_node.is_reference() { @@ -911,6 +1006,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 6b655f2a33..e830f97004 100644 --- a/crates/metaslang/bindings/src/lib.rs +++ b/crates/metaslang/bindings/src/lib.rs @@ -12,7 +12,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; @@ -36,10 +36,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 { @@ -57,6 +57,7 @@ pub struct BindingGraph { cursor_to_definitions: HashMap, cursor_to_references: HashMap, context: Option, + extension_hooks: HashSet, } pub enum FileDescriptor { @@ -140,6 +141,7 @@ impl BindingGraph { cursor_to_definitions: HashMap::new(), cursor_to_references: HashMap::new(), context: None, + extension_hooks: HashSet::new(), } } @@ -191,6 +193,7 @@ impl BindingGraph { 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 } @@ -258,17 +261,12 @@ impl BindingGraph { if self.stack_graph[*handle].is_definition() { self.to_definition(*handle) } else { - self.to_reference(*handle)?.jump_to_definition().ok() + self.to_reference(*handle)?.non_recursive_resolve().ok() } }) .collect() } - pub fn lookup_definition_by_name(&self, name: &str) -> Option> { - self.all_definitions() - .find(|definition| definition.get_cursor().node().unparse() == name) - } - pub fn get_context(&self) -> Option> { self.context.and_then(|handle| self.to_definition(handle)) } @@ -337,6 +335,10 @@ impl BindingGraph { } results } + + pub(crate) fn is_extension_hook(&self, node_handle: GraphHandle) -> bool { + self.extension_hooks.contains(&node_handle) + } } struct DisplayCursor<'a, KT: KindTypes + 'static> { @@ -428,6 +430,20 @@ 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(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) } @@ -509,12 +525,20 @@ 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>> { - Resolver::build_for(self).first() + pub fn resolve_definition(&self) -> Result, ResolutionError<'a, KT>> { + Resolver::build_for(self, ResolveOptions::Full).first() } pub fn definitions(&self) -> Vec> { - Resolver::build_for(self).all() + Resolver::build_for(self, ResolveOptions::Full).all() + } + + 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() } 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 7019a73425..bcd01dc3b7 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; -use crate::{BindingGraph, Definition, Reference, ResolutionError, Tag}; +use crate::{BindingGraph, Definition, FileHandle, GraphHandle, Reference, ResolutionError, Tag}; mod c3; @@ -37,6 +41,13 @@ pub(crate) struct Resolver<'a, KT: KindTypes + 'static> { reference: Reference<'a, KT>, partials: PartialPaths, results: Vec>, + options: ResolveOptions, +} + +#[derive(Copy, Clone, Eq, PartialEq)] +pub(crate) enum ResolveOptions { + Full, + NonRecursive, } struct ResolvedPath<'a, KT: KindTypes + 'static> { @@ -51,13 +62,79 @@ impl<'a, KT: KindTypes + 'static> ResolvedPath<'a, KT> { } } +/// 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 BindingGraph, + partials: &'a mut PartialPaths, + file: Option, + edges: GraphEdges, + extensions: &'a [GraphHandle], +} + +impl<'a, KT: KindTypes + 'static> ResolverCandidates<'a, KT> { + pub fn new( + owner: &'a BindingGraph, + 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) { + // Inject edges from the extension hook node to each extension scope + 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 { + 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 @@ -65,25 +142,56 @@ impl<'a, KT: KindTypes + 'static> Resolver<'a, KT> { fn resolve(&mut self) { 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()); - }, - ) - .expect("should never be cancelled"); + if self.options == ResolveOptions::Full { + 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 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() { + extensions.insert(extension_scope); + } + } + } + } + let extensions = extensions.drain().collect::>(); + + 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 { + 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() @@ -139,7 +247,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 == 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 4bab41894f..0033e7f90e 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,10 @@ 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 + 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. @@ -104,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 @@ -317,15 +302,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 @@ -355,7 +331,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 @@ -365,6 +340,8 @@ 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 @@ -428,9 +405,12 @@ inherit .star_extension attr (push_typeof) push_symbol = "@typeof" node push_name attr (push_name) push_symbol = (source-text @name) + node hook + attr (hook) extension_hook + edge call -> push_typeof edge push_typeof -> push_name - edge push_name -> JUMP_TO_SCOPE_NODE + edge push_name -> hook if (version-matches "< 0.5.0") { ; For Solidity < 0.5.0 `this` also acts like an `address` @@ -448,22 +428,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 @@ -506,6 +474,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. @@ -567,7 +538,6 @@ inherit .star_extension )] ]] { edge @member.lexical_scope -> @contract.lexical_scope - edge @member.extended_scope -> @contract.extended_scope } @contract [ContractDefinition [ContractMembers @@ -575,11 +545,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 @@ -655,7 +620,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 @@ -683,9 +648,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) @@ -717,7 +679,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 @@ -767,7 +732,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]]]] { @@ -784,7 +748,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 @@ -792,9 +755,13 @@ 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 + let @library.enclosing_def = @library.def + node member attr (member) pop_symbol = "." edge @library.def -> member @@ -840,7 +807,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 +815,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 +822,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 +829,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 -} - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; 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 + edge @library.star_extension -> @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 +850,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 +872,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 +899,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 +1364,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 +1383,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 +1397,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 +1419,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 +1449,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 +1476,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 +1493,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 +1550,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 +1566,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 +1582,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 +1602,6 @@ inherit .star_extension @modifier [ModifierDefinition] { node @modifier.def node @modifier.lexical_scope - node @modifier.extended_scope } @modifier [ModifierDefinition @@ -1701,7 +1611,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 +1626,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 +1989,6 @@ inherit .star_extension @state_var [StateVariableDefinition] { node @state_var.lexical_scope - node @state_var.extended_scope node @state_var.def } @@ -2121,7 +2030,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 59e4f7f07a..4817a55eb7 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,10 @@ 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 + 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. @@ -109,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 @@ -322,15 +307,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 @@ -360,7 +336,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 @@ -370,6 +345,8 @@ 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 @@ -433,9 +410,12 @@ inherit .star_extension attr (push_typeof) push_symbol = "@typeof" node push_name attr (push_name) push_symbol = (source-text @name) + node hook + attr (hook) extension_hook + edge call -> push_typeof edge push_typeof -> push_name - edge push_name -> JUMP_TO_SCOPE_NODE + edge push_name -> hook if (version-matches "< 0.5.0") { ; For Solidity < 0.5.0 `this` also acts like an `address` @@ -453,22 +433,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 @@ -511,6 +479,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. @@ -572,7 +543,6 @@ inherit .star_extension )] ]] { edge @member.lexical_scope -> @contract.lexical_scope - edge @member.extended_scope -> @contract.extended_scope } @contract [ContractDefinition [ContractMembers @@ -580,11 +550,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 @@ -660,7 +625,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 @@ -688,9 +653,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) @@ -722,7 +684,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 @@ -772,7 +737,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]]]] { @@ -789,7 +753,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 @@ -797,9 +760,13 @@ 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 + let @library.enclosing_def = @library.def + node member attr (member) pop_symbol = "." edge @library.def -> member @@ -845,7 +812,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 +820,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 +827,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 +834,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 -} - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; 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 + edge @library.star_extension -> @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 +855,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 +877,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 +904,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 +1369,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 +1388,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 +1402,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 +1424,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 +1454,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 +1481,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 +1498,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 +1555,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 +1571,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 +1587,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 +1607,6 @@ inherit .star_extension @modifier [ModifierDefinition] { node @modifier.def node @modifier.lexical_scope - node @modifier.extended_scope } @modifier [ModifierDefinition @@ -1706,7 +1616,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 +1631,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 +1994,6 @@ inherit .star_extension @state_var [StateVariableDefinition] { node @state_var.lexical_scope - node @state_var.extended_scope node @state_var.def } @@ -2126,7 +2035,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/tests/src/bindings.rs b/crates/solidity/outputs/cargo/tests/src/bindings.rs new file mode 100644 index 0000000000..c5a9cf60dc --- /dev/null +++ b/crates/solidity/outputs/cargo/tests/src/bindings.rs @@ -0,0 +1,10 @@ +use slang_solidity::bindings::{BindingGraph, Definition}; + +pub fn lookup_definition_by_name<'a>( + binding_graph: &'a BindingGraph, + name: &str, +) -> Option> { + binding_graph + .all_definitions() + .find(|definition| definition.get_cursor().node().unparse() == name) +} 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 94aeca6f2d..fcc4d19547 100644 --- a/crates/solidity/outputs/cargo/tests/src/bindings_assertions/assertions.rs +++ b/crates/solidity/outputs/cargo/tests/src/bindings_assertions/assertions.rs @@ -482,7 +482,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_assertions/runner.rs b/crates/solidity/outputs/cargo/tests/src/bindings_assertions/runner.rs index 2da4fe4aff..f593e1937d 100644 --- a/crates/solidity/outputs/cargo/tests/src/bindings_assertions/runner.rs +++ b/crates/solidity/outputs/cargo/tests/src/bindings_assertions/runner.rs @@ -7,6 +7,7 @@ use semver::Version; use slang_solidity::parser::Parser; use slang_solidity::{bindings, diagnostic}; +use crate::bindings::lookup_definition_by_name; use crate::bindings_assertions::assertions::{ check_assertions, collect_assertions_into, Assertions, }; @@ -65,8 +66,7 @@ fn check_assertions_with_version(version: &Version, contents: &str) -> Result<() } if let Some(context) = multi_part.context { - let context_definition = binding_graph - .lookup_definition_by_name(context) + let context_definition = lookup_definition_by_name(&binding_graph, context) .expect("context definition to be found") .to_handle(); binding_graph.set_context(&context_definition); 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 423b7c9910..b347b541d7 100644 --- a/crates/solidity/outputs/cargo/tests/src/bindings_output/renderer.rs +++ b/crates/solidity/outputs/cargo/tests/src/bindings_output/renderer.rs @@ -111,7 +111,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/outputs/cargo/tests/src/bindings_output/runner.rs b/crates/solidity/outputs/cargo/tests/src/bindings_output/runner.rs index 35ee7d233b..5daaf93288 100644 --- a/crates/solidity/outputs/cargo/tests/src/bindings_output/runner.rs +++ b/crates/solidity/outputs/cargo/tests/src/bindings_output/runner.rs @@ -13,6 +13,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::lookup_definition_by_name; use crate::generated::VERSION_BREAKS; use crate::multi_part_file::{split_multi_file, Part}; use crate::resolver::TestsPathResolver; @@ -66,8 +67,7 @@ pub fn run(group_name: &str, test_name: &str) -> Result<()> { } if let Some(context) = multi_part.context { - let context_definition = binding_graph - .lookup_definition_by_name(context) + let context_definition = lookup_definition_by_name(&binding_graph, context) .expect("context definition to be found") .to_handle(); binding_graph.set_context(&context_definition); diff --git a/crates/solidity/outputs/cargo/tests/src/lib.rs b/crates/solidity/outputs/cargo/tests/src/lib.rs index 519c795d76..b62d53c1a2 100644 --- a/crates/solidity/outputs/cargo/tests/src/lib.rs +++ b/crates/solidity/outputs/cargo/tests/src/lib.rs @@ -3,6 +3,7 @@ use metaslang_bindings as _; mod binding_rules; +mod bindings; mod bindings_assertions; mod bindings_output; mod built_ins; diff --git a/crates/solidity/testing/perf/src/tests/references.rs b/crates/solidity/testing/perf/src/tests/references.rs index fed8fb3bec..c039149a6d 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(binding_graph: BindingGraph) { } reference_count += 1; - let resolution = reference.jump_to_definition(); + let resolution = reference.resolve_definition(); if resolution.is_ok() { resolved_references += 1; }