Skip to content

Commit

Permalink
Check the bindings assertions
Browse files Browse the repository at this point in the history
  • Loading branch information
ggiraldez committed Jun 17, 2024
1 parent e832e1e commit 07097b9
Show file tree
Hide file tree
Showing 7 changed files with 155 additions and 20 deletions.
12 changes: 12 additions & 0 deletions crates/codegen/runtime/cargo/src/runtime/bindings/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,18 @@ impl Bindings {
handle,
})
}

pub fn cursor_to_handle(&self, cursor: &Cursor) -> Option<Handle<'_>> {
for (handle, handle_cursor) in self.cursors.iter() {
if handle_cursor == cursor {
return Some(Handle {
owner: self,
handle: *handle,
});
}
}
return None;
}
}

pub struct Handle<'a> {
Expand Down
3 changes: 1 addition & 2 deletions crates/solidity/outputs/cargo/slang_solidity/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ required-features = ["cli"]

[features]
default = ["cli"]
cli = ["dep:anyhow", "dep:clap", "dep:serde_json", "__private_ariadne"]
cli = ["dep:clap", "dep:serde_json", "__private_ariadne"]
# This is meant to be used by the CLI or internally only.
__private_ariadne = ["dep:ariadne"]
# For internal development only
Expand All @@ -46,7 +46,6 @@ infra_utils = { workspace = true } # __REMOVE_THIS_LINE_DURING_CAR
solidity_language = { workspace = true } # __REMOVE_THIS_LINE_DURING_CARGO_PUBLISH__

[dependencies]
anyhow = { workspace = true, optional = true }
ariadne = { workspace = true, optional = true }
clap = { workspace = true, optional = true }
metaslang_cst = { workspace = true }
Expand Down
130 changes: 115 additions & 15 deletions crates/solidity/outputs/cargo/slang_solidity/src/assertions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,127 @@ use std::collections::HashMap;
use regex::Regex;
use semver::Version;
use thiserror::Error;
use anyhow::Result;

use slang_solidity::bindings::Bindings;
use slang_solidity::cursor::Cursor;
use slang_solidity::kinds::TerminalKind;
use slang_solidity::query::Query;

use slang_solidity::cli::commands::CommandError;
use slang_solidity::cli::commands;
use slang_solidity::cli::commands::CommandError;

pub fn execute_check_assertions(file_path_string: &str, version: Version) -> Result<()> {
pub fn execute_check_assertions(
file_path_string: &str,
version: Version,
) -> Result<(), CommandError> {
let mut bindings = Bindings::create(version.clone());
let parse_output = commands::parse::parse_source_file(file_path_string, version, |_| ())?;
let tree_cursor = parse_output.create_tree_cursor();

bindings.add_file(file_path_string, tree_cursor.clone())?;

let assertions =
collect_assertions(tree_cursor).map_err(|e| CommandError::Unknown(e.to_string()))?;
for assertion in assertions.iter() {
println!("{}", assertion);
}

check_assertions(&bindings, &assertions)?;

Ok(())
}

fn check_assertions(bindings: &Bindings, assertions: &Assertions) -> Result<(), CommandError> {
let mut count = 0;
let mut success = 0;

for assertion in assertions.definitions.values() {
count += 1;

let Assertion::Definition { id: _, cursor } = assertion else {
unreachable!("{assertion} is not a definition assertion");
};

let Some(handle) = bindings.cursor_to_handle(cursor) else {
eprintln!("{assertion} failed: not found");
continue;
};
if !handle.is_definition() {
eprintln!("{assertion} failed: not a definition");
continue;
}

success += 1;
}

for assertion in assertions.references.iter() {
count += 1;

let Assertion::Reference { id, cursor } = assertion else {
unreachable!("{assertion} is not a reference assertion");
};

let Some(handle) = bindings.cursor_to_handle(cursor) else {
eprintln!("{assertion} failed: not found");
continue;
};
if !handle.is_reference() {
eprintln!("{assertion} failed: not a reference");
continue;
}

let Some(def_handle) = handle.jump_to_definition() else {
// couldn't jump to definition
if id.is_some() {
// but a binding resolution was expected
eprintln!("{assertion} failed: not resolved");
} else {
// and we asserted an unresolved reference -> good
success += 1;
}
continue;
};
let Some(id) = id else {
// expected an unresolved reference
eprintln!(
"{assertion} failed: reference did resolve to {}",
DisplayCursor(&def_handle.get_cursor().unwrap())
);
continue;
};

let Some(Assertion::Definition {
id: _,
cursor: def_cursor,
}) = assertions.definitions.get(id)
else {
eprintln!("{assertion} failed: definition assertion not found");
continue;
};
if let Some(ref_def_cursor) = def_handle.get_cursor() {
if ref_def_cursor != *def_cursor {
eprintln!(
"{assertion} failed: resolved to unexpected {}",
DisplayCursor(&ref_def_cursor)
);
continue;
}
} else {
eprintln!("{assertion} failed: jumped to definition did not resolve to a cursor");
continue;
}

success += 1;
}

if count > success {
eprintln!();
Err(CommandError::Unknown(format!(
"Failed {failed} of {count} bindings assertions",
failed = count - success
)))
} else {
println!("{count} binding assertions OK");
Ok(())
}
}

#[derive(Debug, Error)]
enum AssertionError {
#[error("Invalid assertion at {0}:{1}")]
Expand Down Expand Up @@ -86,10 +182,6 @@ impl Assertions {
}
}
}

fn iter(&self) -> impl Iterator<Item = &Assertion> {
self.definitions.values().chain(self.references.iter())
}
}

#[derive(Clone, Debug, PartialEq)]
Expand Down Expand Up @@ -118,12 +210,20 @@ impl fmt::Display for Assertion {
cursor
}
};
let offset = cursor.text_offset();
let range = cursor.text_range();
write!(f, " {}", DisplayCursor(cursor))
}
}

struct DisplayCursor<'a>(&'a Cursor);

impl<'a> fmt::Display for DisplayCursor<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let offset = self.0.text_offset();
let range = self.0.text_range();
write!(
f,
" `{}` at {}:{} [{}..{}]",
cursor.node().unparse(),
"`{}` at {}:{} [{}..{}]",
self.0.node().unparse(),
offset.line + 1,
offset.column + 1,
range.start,
Expand Down
4 changes: 2 additions & 2 deletions crates/solidity/outputs/cargo/slang_solidity/src/commands.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#[allow(unused_imports)]
use anyhow::Error;
use slang_solidity::cli::commands::CommandError;
use clap::Subcommand;
use std::process::ExitCode;

Expand All @@ -24,7 +24,7 @@ impl CustomCommands {

#[cfg(feature = "__experimental_bindings_api")]
pub fn execute(self) -> ExitCode {
let result: Result<(), Error> = match self {
let result: Result<(), CommandError> = match self {
Self::CheckAssertions { file_path, version } => {
super::assertions::execute_check_assertions(&file_path, version)
}
Expand Down

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

2 changes: 1 addition & 1 deletion crates/solidity/outputs/cargo/slang_solidity/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@ pub use generated::*;
mod supress_cli_dependencies {
#[cfg(feature = "__experimental_bindings_api")]
use regex as _;
use {anyhow as _, ariadne as _, clap as _, serde_json as _};
use {ariadne as _, clap as _, serde_json as _};
}

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

0 comments on commit 07097b9

Please sign in to comment.