Skip to content

Commit

Permalink
Add bindings API function to retrieve *all* definitions from a reference
Browse files Browse the repository at this point in the history
  • Loading branch information
ggiraldez committed Aug 12, 2024
1 parent d4e1d57 commit 1e31da4
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 27 deletions.
104 changes: 85 additions & 19 deletions crates/metaslang/bindings/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
pub mod builder;

use std::collections::HashMap;
use std::fmt;
use std::fmt::Debug;
use std::fmt::{self, Display};
use std::iter::once;
use std::sync::Arc;

Expand All @@ -13,7 +12,7 @@ use metaslang_graph_builder::ast::File;
use metaslang_graph_builder::functions::Functions;
use semver::Version;
use stack_graphs::graph::StackGraph;
use stack_graphs::partial::PartialPaths;
use stack_graphs::partial::{PartialPath, PartialPaths};
use stack_graphs::stitching::{
Appendable, ForwardPartialPathStitcher, GraphEdgeCandidates, StitcherConfig,
};
Expand Down Expand Up @@ -141,6 +140,25 @@ impl<KT: KindTypes + 'static> Bindings<KT> {
}
}

struct DisplayCursor<'a, KT: KindTypes + 'static> {
cursor: &'a Cursor<KT>,
file: Option<&'a str>,
}

impl<'a, KT: KindTypes + 'static> fmt::Display for DisplayCursor<'a, KT> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let offset = self.cursor.text_offset();
write!(
f,
"`{}` at {}:{}:{}",
self.cursor.node().unparse(),
self.file.unwrap_or("<unknown_file>"),
offset.line + 1,
offset.column + 1,
)
}
}

#[derive(Clone)]
pub struct Definition<'a, KT: KindTypes + 'static> {
owner: &'a Bindings<KT>,
Expand All @@ -163,9 +181,20 @@ impl<'a, KT: KindTypes + 'static> Definition<'a, KT> {
}
}

impl<KT: KindTypes + 'static> Debug for Definition<'_, KT> {
impl<KT: KindTypes + 'static> Display for Definition<'_, KT> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("BindingsHandle").field(&self.handle).finish()
if let Some(cursor) = self.get_cursor() {
write!(
f,
"definition {}",
DisplayCursor {
cursor: &cursor,
file: self.get_file()
}
)
} else {
write!(f, "{}", self.handle.display(&self.owner.stack_graph))
}
}
}

Expand Down Expand Up @@ -213,7 +242,42 @@ impl<'a, KT: KindTypes + 'static> Reference<'a, KT> {
/// path to the actual definition; hence why this function will return
/// the longest path available.
///
/// 3. Overriden virtual methods: a reference will find valid paths to all
/// available definitions in the class hierarchy.
///
pub fn jump_to_definition(&self) -> Option<Definition<'a, KT>> {
let definitions = self.resolve();
if definitions.len() > 1 {
println!(
"WARN: More than one definition found for {self}, will return the longest path",
);

for (index, result) in definitions.iter().enumerate() {
let definition = Definition {
owner: self.owner,
handle: result.end_node(),
};
println!(
" {index}. {definition} (length {length})",
index = index + 1,
length = result.edges.len(),
);
}
}
definitions.first().map(|path| Definition {
owner: self.owner,
handle: path.end_node(),
})
}

pub fn definitions(&self) -> Vec<Definition<'a, KT>> {
self.resolve().into_iter().map(|path| Definition {
owner: self.owner,
handle: path.end_node(),
}).collect()
}

fn resolve(&self) -> Vec<PartialPath> {
let mut partials = PartialPaths::new();
let mut reference_paths = Vec::new();
ForwardPartialPathStitcher::find_all_complete_partial_paths(
Expand All @@ -233,26 +297,28 @@ impl<'a, KT: KindTypes + 'static> Reference<'a, KT> {
.iter()
.all(|other| !other.shadows(&mut partials, reference_path))
{
results.push(reference_path);
results.push(reference_path.clone());
}
}
if results.len() > 1 {
println!(
"WARN: More than one definition found for {}, will return the longest path",
self.handle.display(&self.owner.stack_graph)
);
results.sort_by(|a, b| b.edges.len().cmp(&a.edges.len()));
}
results.first().map(|path| Definition {
owner: self.owner,
handle: path.end_node(),
})
results.sort_by(|a, b| b.edges.len().cmp(&a.edges.len()));
results
}
}

impl<KT: KindTypes + 'static> Debug for Reference<'_, KT> {
impl<KT: KindTypes + 'static> Display for Reference<'_, KT> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("BindingsHandle").field(&self.handle).finish()
if let Some(cursor) = self.get_cursor() {
write!(
f,
"reference {}",
DisplayCursor {
cursor: &cursor,
file: self.get_file()
}
)
} else {
write!(f, "{}", self.handle.display(&self.owner.stack_graph))
}
}
}

Expand Down
8 changes: 5 additions & 3 deletions crates/solidity/inputs/language/bindings/rules.msgb
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ attribute symbol_reference = symbol => type = "push_symbol", symbol = symbol, i
edge @contract.lexical_scope -> super
}

@contract [ContractDefinition [InheritanceSpecifier [InheritanceTypes @type [InheritanceType
@contract [ContractDefinition [InheritanceSpecifier [InheritanceTypes @_type [InheritanceType
@type_name [IdentifierPath]
]]]] {
;; Resolve contract bases names through the parent scope of the contract (aka
Expand All @@ -327,9 +327,11 @@ attribute symbol_reference = symbol => type = "push_symbol", symbol = symbol, i

;; The base contract defs are directly accesible through our special super scope
edge @contract.super_scope -> @type_name.right

;; Precedence order for bases defined from right to left (ie. rightmost base has higher precedence)
let p = (plus 1 (named-child-index @type))
attr (@contract.super_scope -> @type_name.right) precedence = p
;; NOTE: this will overriden implementations of other bases than the first
; let p = (named-child-index @type)
; attr (@contract.super_scope -> @type_name.right) precedence = p
}

@interface [InterfaceDefinition @name name: [Identifier]] {
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,14 @@ contract D is B, C {
// D.foo() returns "C"
function foo() public pure override(B, C) returns (string memory) {
return super.foo();
// ^ref:3 (>=0.6.0)
// ^FIXME ref:3 (>=0.6.0)
}
}

contract E is C, B {
// E.foo() returns "B"
function foo() public pure override(C, B) returns (string memory) {
return super.foo();
// ^ref:2 (>=0.6.0)
// ^FIXME ref:2 (>=0.6.0)
}
}

0 comments on commit 1e31da4

Please sign in to comment.