Skip to content

Commit

Permalink
move rust_check to cdk
Browse files Browse the repository at this point in the history
  • Loading branch information
chenyan-dfinity committed May 7, 2024
1 parent 221422f commit 511629b
Show file tree
Hide file tree
Showing 4 changed files with 2 additions and 213 deletions.
2 changes: 0 additions & 2 deletions Cargo.lock

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

3 changes: 0 additions & 3 deletions rust/candid_parser/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,6 @@ convert_case = "0.6"
handlebars = "5.1"
toml = { version = "0.8", default-features = false, features = ["parse"] }

syn = { version = "2.0", features = ["full", "visit", "extra-traits"] }
proc-macro2 = { version = "1.0", features = ["span-locations"] }

arbitrary = { workspace = true, optional = true }
fake = { version = "2.4", optional = true }
rand = { version = "0.8", optional = true }
Expand Down
197 changes: 0 additions & 197 deletions rust/candid_parser/src/bindings/rust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -772,203 +772,6 @@ fn nominalize_all(env: &TypeEnv, actor: &Option<Type>) -> (TypeEnv, Option<Type>
.map(|ty| nominalize(&mut res, &mut vec![], ty));
(res, actor)
}
pub fn check_rust(source: &str, candid: &Output) {
use codespan_reporting::{
files::SimpleFile,
term::{self, termcolor::StandardStream},
};
let rust = get_endpoint_from_rust_source(source);
let diags = diff_did_and_rust(candid, &rust);
let writer = StandardStream::stderr(term::termcolor::ColorChoice::Auto);
let config = term::Config::default();
let file = SimpleFile::new("rust code", source);
for diag in diags {
term::emit(&mut writer.lock(), &config, &file, &diag).unwrap();
}
}
fn get_endpoint_from_rust_source(source: &str) -> Vec<CDKMethod> {
use syn::visit::{self, Visit};
use syn::{ImplItemFn, ItemFn};
struct FnVisitor(Vec<CDKMethod>);
impl<'ast> Visit<'ast> for FnVisitor {
fn visit_item_fn(&mut self, node: &'ast ItemFn) {
if let Some(m) = get_cdk_function(&node.attrs, &node.sig) {
self.0.push(m);
}
// handle nested functions
visit::visit_item_fn(self, node);
}
fn visit_impl_item_fn(&mut self, node: &'ast ImplItemFn) {
if let Some(m) = get_cdk_function(&node.attrs, &node.sig) {
self.0.push(m);
}
// handle nested functions
visit::visit_impl_item_fn(self, node);
}
}
let ast = syn::parse_file(source).unwrap();
let mut visitor = FnVisitor(Vec::new());
visitor.visit_file(&ast);
for m in &visitor.0 {
m.debug_print(source);
}
visitor.0
}
struct CDKMethod {
func_name: syn::Ident,
export_name: Option<(String, syn::Meta)>,
composite: Option<syn::Meta>,
mode: syn::Ident,
args: Vec<syn::Type>,
rets: Vec<syn::Type>,
}
impl CDKMethod {
fn debug_print(&self, source: &str) {
use syn::spanned::Spanned;
println!("{} {}", self.func_name, self.mode);
if let Some((_, meta)) = &self.export_name {
let range = meta.span().byte_range();
println!(" export {}", &source[range]);
}
if let Some(composite) = &self.composite {
let range = composite.span().byte_range();
println!(" composite {}", &source[range]);
}
for arg in &self.args {
let range = arg.span().byte_range();
println!(" arg {}", &source[range]);
}
for ret in &self.rets {
let range = ret.span().byte_range();
println!(" ret {}", &source[range]);
}
}
}
use codespan_reporting::diagnostic::Diagnostic;
fn diff_did_and_rust(candid: &Output, rust: &[CDKMethod]) -> Vec<Diagnostic<()>> {
use codespan_reporting::diagnostic::Label;
use syn::spanned::Spanned;
let mut res = Vec::new();
let rust: BTreeMap<_, _> = rust
.iter()
.map(|m| {
let name = m
.export_name
.as_ref()
.map(|x| x.0.clone())
.unwrap_or(m.func_name.to_string());
(name, m)
})
.collect();
for m in &candid.methods {
let diag = Diagnostic::error()
.with_message(format!("Error with Candid method {}", m.original_name));
let mut labels = Vec::new();
let mut notes = Vec::new();
if let Some(func) = rust.get(&m.original_name) {
if m.original_name == m.name {
} else {
if let Some((name, meta)) = &func.export_name {
if *name != m.original_name {
labels
.push(Label::primary((), meta.span().byte_range()).with_message(
format!("expect {}", m.original_name.escape_debug()),
));
}
} else {
labels.push(
Label::primary((), func.mode.span().byte_range()).with_message(format!(
"missing (name = \"{}\")",
m.original_name.escape_debug()
)),
);
}
}
let args = func.args.iter().zip(m.args.iter().map(|x| &x.1));
for (rust_arg, candid_arg) in args {
let parsed_candid_arg: syn::Type = syn::parse_str(candid_arg).unwrap();
if parsed_candid_arg != *rust_arg {
labels.push(
Label::primary((), rust_arg.span().byte_range())
.with_message(format!("expect type: {}", candid_arg)),
);
}
}
} else {
notes.push(format!(
"method \"{}\" missing from Rust code",
m.original_name
));
}
res.push(diag.with_labels(labels).with_notes(notes));
}
res
}
fn get_cdk_function(attrs: &[syn::Attribute], sig: &syn::Signature) -> Option<CDKMethod> {
use syn::parse::Parser;
use syn::{Expr, ExprLit, FnArg, Lit, Meta, ReturnType};
let func_name = sig.ident.clone();
let mut mode = None;
let mut export_name = None;
let mut composite = None;
for attr in attrs {
let attr_name = &attr.meta.path().segments.last().unwrap().ident;
if attr_name != "update" && attr_name != "query" && attr_name != "init" {
continue;
}
mode = Some(attr_name.clone());
if let Meta::List(list) = &attr.meta {
let nested = syn::punctuated::Punctuated::<Meta, syn::Token![,]>::parse_terminated
.parse2(list.tokens.clone())
.unwrap();
for meta in nested {
if let Meta::NameValue(ref m) = meta {
if m.path.is_ident("name") {
if let Expr::Lit(ExprLit {
lit: Lit::Str(name),
..
}) = &m.value
{
export_name = Some((name.value(), meta));
}
} else if m.path.is_ident("composite") {
if let Expr::Lit(ExprLit {
lit: Lit::Bool(b), ..
}) = &m.value
{
if b.value {
composite = Some(meta);
}
}
}
}
}
}
}
let args = sig
.inputs
.iter()
.filter_map(|arg| match arg {
FnArg::Receiver(_) => None,
FnArg::Typed(pat) => Some(*pat.ty.clone()),
})
.collect();
let rets = match &sig.output {
ReturnType::Default => Vec::new(),
ReturnType::Type(_, ty) => match ty.as_ref() {
syn::Type::Tuple(ty) => ty.elems.iter().map(|t| (*t).clone()).collect(),
_ => vec![*ty.clone()],
},
};
mode.map(|mode| CDKMethod {
func_name,
export_name,
composite,
args,
rets,
mode,
})
}
fn get_hbs() -> handlebars::Handlebars<'static> {
use handlebars::*;
let mut hbs = Handlebars::new();
Expand Down
13 changes: 2 additions & 11 deletions tools/didc/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,22 +216,13 @@ fn main() -> Result<()> {
"did" => candid_parser::pretty::candid::compile(&env, &actor),
"mo" => candid_parser::bindings::motoko::compile(&env, &actor),
"rs" => {
use candid_parser::bindings::rust::{
check_rust, compile, emit_bindgen, Config, ExternalConfig,
};
use candid_parser::bindings::rust::{compile, Config, ExternalConfig};
let external = configs
.get_subtable(&["external".to_string(), "rust".to_string()])
.map(|x| x.clone().try_into().unwrap())
.unwrap_or(ExternalConfig::default());
let config = Config::new(configs);
if let Some(file) = external.0.get("output_file") {
let (output, _) = emit_bindgen(&config, &env, &actor);
let file = std::fs::read_to_string(file).unwrap();
check_rust(&file, &output);
String::new()
} else {
compile(&config, &env, &actor, external)
}
compile(&config, &env, &actor, external)
}
"rs-agent" | "rs-stub" => {
use candid_parser::bindings::rust::{compile, Config, ExternalConfig};
Expand Down

0 comments on commit 511629b

Please sign in to comment.