Преглед на файлове

Parse file level using declarations

Signed-off-by: Sean Young <sean@mess.org>
Sean Young преди 3 години
родител
ревизия
2ef5651781

+ 10 - 2
src/sema/ast.rs

@@ -450,6 +450,8 @@ pub struct Namespace {
     pub structs: Vec<StructDecl>,
     pub events: Vec<EventDecl>,
     pub contracts: Vec<Contract>,
+    /// Global using declarations
+    pub using: Vec<Using>,
     /// All type declarations
     pub user_types: Vec<UserTypeDecl>,
     /// All functions
@@ -489,7 +491,13 @@ pub struct Base {
     pub constructor: Option<(usize, Vec<Expression>)>,
 }
 
-pub enum Using {
+pub struct Using {
+    pub list: UsingList,
+    pub ty: Option<Type>,
+    pub file_no: Option<usize>,
+}
+
+pub enum UsingList {
     Library(usize),
     Functions(Vec<usize>),
 }
@@ -500,7 +508,7 @@ pub struct Contract {
     pub ty: pt::ContractTy,
     pub name: String,
     pub bases: Vec<Base>,
-    pub using: Vec<(Using, Option<Type>)>,
+    pub using: Vec<Using>,
     pub layout: Vec<Layout>,
     pub fixed_layout_size: BigInt,
     pub functions: Vec<usize>,

+ 3 - 132
src/sema/contracts.rs

@@ -12,7 +12,7 @@ use super::{
     functions, statements,
     symtable::Symtable,
     tags::parse_doccomments,
-    variables,
+    using, variables,
 };
 #[cfg(feature = "llvm")]
 use crate::emit;
@@ -829,144 +829,15 @@ fn resolve_using(
     file_no: usize,
     ns: &mut ast::Namespace,
 ) {
-    let mut diagnostics = Vec::new();
-
     for (contract_no, def) in contracts {
         for part in &def.parts {
             if let pt::ContractPart::Using(using) = part {
-                let ty = if let Some(expr) = &using.ty {
-                    let mut diagnostics = Vec::new();
-
-                    match ns.resolve_type(
-                        file_no,
-                        Some(*contract_no),
-                        false,
-                        expr,
-                        &mut diagnostics,
-                    ) {
-                        Ok(ast::Type::Contract(contract_no))
-                            if ns.contracts[contract_no].is_library() =>
-                        {
-                            ns.diagnostics.push(ast::Diagnostic::error(
-                                expr.loc(),
-                                format!("using for library '{}' type not permitted", expr),
-                            ));
-                            continue;
-                        }
-                        Ok(ty) => Some(ty),
-                        Err(_) => {
-                            ns.diagnostics.extend(diagnostics);
-                            continue;
-                        }
-                    }
-                } else {
-                    None
-                };
-
-                let list = match &using.list {
-                    pt::UsingList::Library(library) => {
-                        if let Ok(library_no) =
-                            ns.resolve_contract_with_namespace(file_no, library, &mut diagnostics)
-                        {
-                            if ns.contracts[library_no].is_library() {
-                                ast::Using::Library(library_no)
-                            } else {
-                                ns.diagnostics.push(ast::Diagnostic::error(
-                                    library.loc(),
-                                    format!(
-                                        "library expected but {} '{}' found",
-                                        ns.contracts[library_no].ty, library
-                                    ),
-                                ));
-                                continue;
-                            }
-                        } else {
-                            continue;
-                        }
-                    }
-                    pt::UsingList::Functions(functions) => {
-                        let mut res = Vec::new();
-
-                        for function_name in functions {
-                            if let Ok(list) = ns.resolve_free_function_with_namespace(
-                                file_no,
-                                function_name,
-                                &mut diagnostics,
-                            ) {
-                                if list.len() > 1 {
-                                    let notes = list
-                                        .iter()
-                                        .map(|(loc, _)| ast::Note {
-                                            pos: *loc,
-                                            message: format!("definition of '{}'", function_name),
-                                        })
-                                        .collect();
-
-                                    diagnostics.push(ast::Diagnostic::error_with_notes(
-                                        function_name.loc(),
-                                        format!("'{}' is an overloaded function", function_name),
-                                        notes,
-                                    ));
-                                    continue;
-                                }
-
-                                let (loc, func_no) = list[0];
-
-                                let func = &ns.functions[func_no];
-
-                                if func.params.is_empty() {
-                                    diagnostics.push(ast::Diagnostic::error_with_note(
-                                        function_name.loc(),
-                                        format!(
-                                            "'{}' has no arguments, at least one argument required",
-                                            function_name
-                                        ),
-                                        loc,
-                                        format!("definition of '{}'", function_name),
-                                    ));
-                                    continue;
-                                }
-
-                                if let Some(ty) = &ty {
-                                    if *ty != func.params[0].ty {
-                                        diagnostics.push(ast::Diagnostic::error_with_note(
-                                            function_name.loc(),
-                                            format!("function cannot be used since first argument is '{}' rather than the required '{}'", func.params[0].ty.to_string(ns), ty.to_string(ns)),
-                                            loc,
-                                            format!("definition of '{}'", function_name),
-                                        ));
-                                        continue;
-                                    }
-                                }
-
-                                res.push(func_no);
-                            }
-                        }
-
-                        ast::Using::Functions(res)
-                    }
-                };
-
-                if let Some(global) = &using.global {
-                    if global.name == "global" {
-                        ns.diagnostics.push(ast::Diagnostic::error(
-                            global.loc,
-                            format!("'{}' on using within contract not permitted", global.name),
-                        ));
-                    } else {
-                        ns.diagnostics.push(ast::Diagnostic::error(
-                            global.loc,
-                            format!("'{}' not expected", global.name),
-                        ));
-                    }
+                if let Ok(using) = using::using_decl(using, file_no, Some(*contract_no), ns) {
+                    ns.contracts[*contract_no].using.push(using);
                 }
-
-                ns.contracts[*contract_no].using.push((list, ty));
             }
         }
     }
-
-    ns.diagnostics.extend(diagnostics);
 }
 
 /// Resolve contract functions bodies

+ 5 - 5
src/sema/dotgraphviz.rs

@@ -2229,9 +2229,9 @@ impl Namespace {
                 dot.add_tags(&var.tags, node);
             }
 
-            for (libraries, ty) in &c.using {
-                let mut labels = match libraries {
-                    Using::Functions(functions) => functions
+            for using in &c.using {
+                let mut labels = match &using.list {
+                    UsingList::Functions(functions) => functions
                         .iter()
                         .map(|func_no| {
                             let func = &self.functions[*func_no];
@@ -2239,14 +2239,14 @@ impl Namespace {
                             format!("function {} {}", func.name, self.loc_to_string(&func.loc))
                         })
                         .collect(),
-                    Using::Library(library_no) => {
+                    UsingList::Library(library_no) => {
                         let library = &self.contracts[*library_no];
 
                         vec![format!("library {}", library.name)]
                     }
                 };
 
-                if let Some(ty) = ty {
+                if let Some(ty) = &using.ty {
                     labels.insert(0, format!("using for {}", ty.to_string(self)));
                 }
 

+ 24 - 182
src/sema/expression.rs

@@ -8,21 +8,20 @@ use num_traits::ToPrimitive;
 use num_traits::Zero;
 use std::cmp;
 use std::cmp::Ordering;
-use std::collections::HashSet;
 use std::collections::{BTreeMap, HashMap};
 use std::ops::{Add, Shl, Sub};
 
 use super::address::to_hexstr_eip55;
 use super::ast::{
     Builtin, BuiltinStruct, CallArgs, CallTy, Diagnostic, Expression, Function, Mutability,
-    Namespace, StringLocation, Symbol, Type, Using,
+    Namespace, StringLocation, Symbol, Type,
 };
 use super::builtin;
 use super::contracts::{is_base, visit_bases};
 use super::eval::eval_const_number;
 use super::eval::eval_const_rational;
 use super::format::string_format;
-use super::symtable::Symtable;
+use super::{symtable::Symtable, using};
 use crate::ast::RetrieveType;
 use crate::parser::pt;
 use crate::parser::pt::CodeLocation;
@@ -6101,7 +6100,7 @@ fn method_call_pos_args(
         let self_ty = var_ty.deref_any();
 
         // what about call args
-        match resolve_using(
+        match using::try_resolve_using_call(
             loc,
             func,
             &var_expr,
@@ -6281,28 +6280,26 @@ fn method_call_pos_args(
     }
 
     // resolve it using library extension
-    if context.contract_no.is_some() {
-        let self_ty = var_ty.deref_any();
+    let self_ty = var_ty.deref_any();
 
-        match resolve_using(
-            loc,
-            func,
-            &var_expr,
-            self_ty,
-            context,
-            args,
-            symtable,
-            diagnostics,
-            ns,
-            resolve_to,
-        ) {
-            Ok(Some(expr)) => {
-                return Ok(expr);
-            }
-            Ok(None) => (),
-            Err(_) => {
-                return Err(());
-            }
+    match using::try_resolve_using_call(
+        loc,
+        func,
+        &var_expr,
+        self_ty,
+        context,
+        args,
+        symtable,
+        diagnostics,
+        ns,
+        resolve_to,
+    ) {
+        Ok(Some(expr)) => {
+            return Ok(expr);
+        }
+        Ok(None) => (),
+        Err(_) => {
+            return Err(());
         }
     }
 
@@ -6314,161 +6311,6 @@ fn method_call_pos_args(
     Err(())
 }
 
-fn resolve_using(
-    loc: &pt::Loc,
-    func: &pt::Identifier,
-    self_expr: &Expression,
-    self_ty: &Type,
-    context: &ExprContext,
-    args: &[pt::Expression],
-    symtable: &mut Symtable,
-    diagnostics: &mut Vec<Diagnostic>,
-    ns: &mut Namespace,
-    resolve_to: ResolveTo,
-) -> Result<Option<Expression>, ()> {
-    // first collect all possible functions that could be used for using
-    // Use HashSet for deduplication.
-    // If the using directive specifies a type, the type must match the type of
-    // the method call object exactly.
-    let possible_functions: HashSet<usize> = ns.contracts[context.contract_no.unwrap()]
-        .using
-        .iter()
-        .filter(|(_, ty)| {
-            if let Some(ty) = ty {
-                self_ty == ty
-            } else {
-                true
-            }
-        })
-        .flat_map(|(using, _)| {
-            match using {
-                Using::Library(library_no) => ns.contracts[*library_no].functions.iter(),
-                Using::Functions(functions) => functions.iter(),
-            }
-            .filter(|func_no| {
-                let libfunc = &ns.functions[**func_no];
-
-                libfunc.name == func.name && libfunc.ty == pt::FunctionTy::Function
-            })
-            .cloned()
-        })
-        .collect();
-
-    let mut name_matches = 0;
-    let mut errors = Vec::new();
-
-    for function_no in possible_functions {
-        let libfunc = &ns.functions[function_no];
-        if libfunc.name != func.name || libfunc.ty != pt::FunctionTy::Function {
-            continue;
-        }
-
-        name_matches += 1;
-
-        let params_len = libfunc.params.len();
-
-        if params_len != args.len() + 1 {
-            errors.push(Diagnostic::error(
-                *loc,
-                format!(
-                    "using function expects {} arguments, {} provided (including self)",
-                    params_len,
-                    args.len() + 1
-                ),
-            ));
-            continue;
-        }
-        let mut matches = true;
-        let mut cast_args = Vec::new();
-
-        match self_expr.cast(
-            &self_expr.loc(),
-            &libfunc.params[0].ty,
-            true,
-            ns,
-            &mut errors,
-        ) {
-            Ok(e) => cast_args.push(e),
-            Err(()) => continue,
-        }
-
-        // check if arguments can be implicitly casted
-        for (i, arg) in args.iter().enumerate() {
-            let ty = ns.functions[function_no].params[i + 1].ty.clone();
-
-            let arg = match expression(
-                arg,
-                context,
-                ns,
-                symtable,
-                &mut errors,
-                ResolveTo::Type(&ty),
-            ) {
-                Ok(e) => e,
-                Err(()) => {
-                    matches = false;
-                    continue;
-                }
-            };
-
-            match arg.cast(&arg.loc(), &ty, true, ns, &mut errors) {
-                Ok(expr) => cast_args.push(expr),
-                Err(_) => {
-                    matches = false;
-                    break;
-                }
-            }
-        }
-        if !matches {
-            continue;
-        }
-
-        let libfunc = &ns.functions[function_no];
-
-        if libfunc.is_private() {
-            errors.push(Diagnostic::error_with_note(
-                *loc,
-                "cannot call private library function".to_string(),
-                libfunc.loc,
-                format!("declaration of function '{}'", libfunc.name),
-            ));
-
-            continue;
-        }
-
-        let returns = function_returns(libfunc, resolve_to);
-        let ty = function_type(libfunc, false, resolve_to);
-
-        return Ok(Some(Expression::InternalFunctionCall {
-            loc: *loc,
-            returns,
-            function: Box::new(Expression::InternalFunction {
-                loc: *loc,
-                ty,
-                function_no,
-                signature: None,
-            }),
-            args: cast_args,
-        }));
-    }
-
-    match name_matches {
-        0 => Ok(None),
-        1 => {
-            diagnostics.extend(errors);
-
-            Err(())
-        }
-        _ => {
-            diagnostics.push(Diagnostic::error(
-                *loc,
-                "cannot find overloaded function which matches signature".to_string(),
-            ));
-            Err(())
-        }
-    }
-}
-
 fn method_call_named_args(
     loc: &pt::Loc,
     var: &pt::Expression,
@@ -7540,7 +7382,7 @@ pub fn named_function_call_expr(
 }
 
 /// Get the return values for a function call
-fn function_returns(ftype: &Function, resolve_to: ResolveTo) -> Vec<Type> {
+pub(crate) fn function_returns(ftype: &Function, resolve_to: ResolveTo) -> Vec<Type> {
     if !ftype.returns.is_empty() && !matches!(resolve_to, ResolveTo::Discard) {
         ftype.returns.iter().map(|p| p.ty.clone()).collect()
     } else {
@@ -7549,7 +7391,7 @@ fn function_returns(ftype: &Function, resolve_to: ResolveTo) -> Vec<Type> {
 }
 
 /// Get the function type for an internal.external function call
-fn function_type(func: &Function, external: bool, resolve_to: ResolveTo) -> Type {
+pub(crate) fn function_type(func: &Function, external: bool, resolve_to: ResolveTo) -> Type {
     let params = func.params.iter().map(|p| p.ty.clone()).collect();
     let mutability = func.mutability.clone();
     let returns = function_returns(func, resolve_to);

+ 10 - 0
src/sema/mod.rs

@@ -30,6 +30,7 @@ pub mod tags;
 mod tests;
 mod types;
 mod unused_variable;
+mod using;
 mod variables;
 pub mod yul;
 
@@ -139,6 +140,15 @@ fn sema_file(file: &ResolvedFile, resolver: &mut FileResolver, ns: &mut ast::Nam
         }
     }
 
+    // Now we can resolve the global using directives
+    for part in &pt.0 {
+        if let pt::SourceUnitPart::Using(using) = part {
+            if let Ok(using) = using::using_decl(using, file_no, None, ns) {
+                ns.using.push(using);
+            }
+        }
+    }
+
     // now resolve the contracts
     contracts::resolve(&contracts_to_resolve, file_no, ns);
 

+ 1 - 0
src/sema/namespace.rs

@@ -36,6 +36,7 @@ impl Namespace {
             enums: Vec::new(),
             structs: Vec::new(),
             events: Vec::new(),
+            using: Vec::new(),
             contracts: Vec::new(),
             user_types: Vec::new(),
             functions: Vec::new(),

+ 345 - 0
src/sema/using.rs

@@ -0,0 +1,345 @@
+use super::{
+    ast::{Diagnostic, Expression, Namespace, Note, Type, Using, UsingList},
+    expression::{expression, function_returns, function_type, ExprContext, ResolveTo},
+    symtable::Symtable,
+};
+use crate::parser::pt;
+use crate::parser::pt::CodeLocation;
+use std::collections::HashSet;
+
+/// Resolve a using declaration in either file scope or contract scope
+pub(crate) fn using_decl(
+    using: &pt::Using,
+    file_no: usize,
+    contract_no: Option<usize>,
+    ns: &mut Namespace,
+) -> Result<Using, ()> {
+    let mut diagnostics = Vec::new();
+
+    let ty = if let Some(expr) = &using.ty {
+        match ns.resolve_type(file_no, contract_no, false, expr, &mut diagnostics) {
+            Ok(Type::Contract(contract_no)) if ns.contracts[contract_no].is_library() => {
+                ns.diagnostics.push(Diagnostic::error(
+                    expr.loc(),
+                    format!("using for library '{}' type not permitted", expr),
+                ));
+                return Err(());
+            }
+            Ok(ty) => Some(ty),
+            Err(_) => {
+                ns.diagnostics.extend(diagnostics);
+                return Err(());
+            }
+        }
+    } else {
+        if contract_no.is_none() {
+            ns.diagnostics.push(Diagnostic::error(
+                using.loc,
+                "using must be bound to specific type, '*' cannot be used on file scope"
+                    .to_string(),
+            ));
+            return Err(());
+        }
+        None
+    };
+
+    let list = match &using.list {
+        pt::UsingList::Library(library) => {
+            if let Ok(library_no) =
+                ns.resolve_contract_with_namespace(file_no, library, &mut diagnostics)
+            {
+                if ns.contracts[library_no].is_library() {
+                    UsingList::Library(library_no)
+                } else {
+                    ns.diagnostics.push(Diagnostic::error(
+                        library.loc(),
+                        format!(
+                            "library expected but {} '{}' found",
+                            ns.contracts[library_no].ty, library
+                        ),
+                    ));
+                    return Err(());
+                }
+            } else {
+                ns.diagnostics.extend(diagnostics);
+                return Err(());
+            }
+        }
+        pt::UsingList::Functions(functions) => {
+            let mut res = Vec::new();
+
+            for function_name in functions {
+                if let Ok(list) = ns.resolve_free_function_with_namespace(
+                    file_no,
+                    function_name,
+                    &mut diagnostics,
+                ) {
+                    if list.len() > 1 {
+                        let notes = list
+                            .iter()
+                            .map(|(loc, _)| Note {
+                                pos: *loc,
+                                message: format!("definition of '{}'", function_name),
+                            })
+                            .collect();
+
+                        diagnostics.push(Diagnostic::error_with_notes(
+                            function_name.loc(),
+                            format!("'{}' is an overloaded function", function_name),
+                            notes,
+                        ));
+                        continue;
+                    }
+
+                    let (loc, func_no) = list[0];
+
+                    let func = &ns.functions[func_no];
+
+                    if func.params.is_empty() {
+                        diagnostics.push(Diagnostic::error_with_note(
+                            function_name.loc(),
+                            format!(
+                                "'{}' has no arguments, at least one argument required",
+                                function_name
+                            ),
+                            loc,
+                            format!("definition of '{}'", function_name),
+                        ));
+                        continue;
+                    }
+
+                    if let Some(ty) = &ty {
+                        if *ty != func.params[0].ty {
+                            diagnostics.push(Diagnostic::error_with_note(
+                                function_name.loc(),
+                                format!("function cannot be used since first argument is '{}' rather than the required '{}'", func.params[0].ty.to_string(ns), ty.to_string(ns)),
+                                loc,
+                                format!("definition of '{}'", function_name),
+                            ));
+                            continue;
+                        }
+                    }
+
+                    res.push(func_no);
+                }
+            }
+
+            UsingList::Functions(res)
+        }
+    };
+
+    let mut file_no = Some(file_no);
+
+    if let Some(global) = &using.global {
+        if global.name == "global" {
+            if contract_no.is_some() {
+                ns.diagnostics.push(Diagnostic::error(
+                    global.loc,
+                    format!("'{}' on using within contract not permitted", global.name),
+                ));
+            } else {
+                match &ty {
+                    Some(Type::Struct(_)) | Some(Type::UserType(_)) | Some(Type::Enum(_)) => {
+                        file_no = None;
+                    }
+                    _ => {
+                        ns.diagnostics.push(Diagnostic::error(
+                            global.loc,
+                            format!("'{}' only permitted on user defined types", global.name),
+                        ));
+                    }
+                }
+            }
+        } else {
+            ns.diagnostics.push(Diagnostic::error(
+                global.loc,
+                format!("'{}' not expected, did you mean 'global'?", global.name),
+            ));
+        }
+    }
+
+    ns.diagnostics.extend(diagnostics);
+
+    Ok(Using { list, ty, file_no })
+}
+
+/// Given the using declarations, find all the possible functions that could be called via using
+/// for the given name, type and file scope
+fn possible_functions(
+    using: &[Using],
+    file_no: usize,
+    function_name: &str,
+    self_ty: &Type,
+    ns: &Namespace,
+) -> HashSet<usize> {
+    using
+        .iter()
+        .filter(|using| {
+            if let Some(ty) = &using.ty {
+                self_ty == ty
+            } else {
+                true
+            }
+        })
+        .filter(|using| {
+            if let Some(no) = using.file_no {
+                no == file_no
+            } else {
+                true
+            }
+        })
+        .flat_map(|using| match &using.list {
+            UsingList::Library(library_no) => ns.contracts[*library_no].functions.iter(),
+            UsingList::Functions(functions) => functions.iter(),
+        })
+        .filter(|func_no| {
+            let func = &ns.functions[**func_no];
+
+            func.name == function_name && func.ty == pt::FunctionTy::Function
+        })
+        .cloned()
+        .collect()
+}
+
+pub(crate) fn try_resolve_using_call(
+    loc: &pt::Loc,
+    func: &pt::Identifier,
+    self_expr: &Expression,
+    self_ty: &Type,
+    context: &ExprContext,
+    args: &[pt::Expression],
+    symtable: &mut Symtable,
+    diagnostics: &mut Vec<Diagnostic>,
+    ns: &mut Namespace,
+    resolve_to: ResolveTo,
+) -> Result<Option<Expression>, ()> {
+    // first collect all possible functions that could be used for using
+    // Use HashSet for deduplication.
+    // If the using directive specifies a type, the type must match the type of
+    // the method call object exactly.
+    let mut functions = possible_functions(&ns.using, context.file_no, &func.name, self_ty, ns);
+
+    if let Some(contract_no) = context.contract_no {
+        functions.extend(possible_functions(
+            &ns.contracts[contract_no].using,
+            context.file_no,
+            &func.name,
+            self_ty,
+            ns,
+        ));
+    }
+
+    let mut name_matches = 0;
+    let mut errors = Vec::new();
+
+    for function_no in functions {
+        let libfunc = &ns.functions[function_no];
+        if libfunc.name != func.name || libfunc.ty != pt::FunctionTy::Function {
+            continue;
+        }
+
+        name_matches += 1;
+
+        let params_len = libfunc.params.len();
+
+        if params_len != args.len() + 1 {
+            errors.push(Diagnostic::error(
+                *loc,
+                format!(
+                    "using function expects {} arguments, {} provided (including self)",
+                    params_len,
+                    args.len() + 1
+                ),
+            ));
+            continue;
+        }
+        let mut matches = true;
+        let mut cast_args = Vec::new();
+
+        match self_expr.cast(
+            &self_expr.loc(),
+            &libfunc.params[0].ty,
+            true,
+            ns,
+            &mut errors,
+        ) {
+            Ok(e) => cast_args.push(e),
+            Err(()) => continue,
+        }
+
+        // check if arguments can be implicitly casted
+        for (i, arg) in args.iter().enumerate() {
+            let ty = ns.functions[function_no].params[i + 1].ty.clone();
+
+            let arg = match expression(
+                arg,
+                context,
+                ns,
+                symtable,
+                &mut errors,
+                ResolveTo::Type(&ty),
+            ) {
+                Ok(e) => e,
+                Err(()) => {
+                    matches = false;
+                    continue;
+                }
+            };
+
+            match arg.cast(&arg.loc(), &ty, true, ns, &mut errors) {
+                Ok(expr) => cast_args.push(expr),
+                Err(_) => {
+                    matches = false;
+                    break;
+                }
+            }
+        }
+        if !matches {
+            continue;
+        }
+
+        let libfunc = &ns.functions[function_no];
+
+        if libfunc.is_private() {
+            errors.push(Diagnostic::error_with_note(
+                *loc,
+                "cannot call private library function".to_string(),
+                libfunc.loc,
+                format!("declaration of function '{}'", libfunc.name),
+            ));
+
+            continue;
+        }
+
+        let returns = function_returns(libfunc, resolve_to);
+        let ty = function_type(libfunc, false, resolve_to);
+
+        return Ok(Some(Expression::InternalFunctionCall {
+            loc: *loc,
+            returns,
+            function: Box::new(Expression::InternalFunction {
+                loc: *loc,
+                ty,
+                function_no,
+                signature: None,
+            }),
+            args: cast_args,
+        }));
+    }
+
+    match name_matches {
+        0 => Ok(None),
+        1 => {
+            diagnostics.extend(errors);
+
+            Err(())
+        }
+        _ => {
+            diagnostics.push(Diagnostic::error(
+                *loc,
+                "cannot find overloaded function which matches signature".to_string(),
+            ));
+            Err(())
+        }
+    }
+}

+ 12 - 10
tests/contract_testcases/solana/import_contracts_via_object.dot

@@ -1,22 +1,24 @@
 strict digraph "tests/contract_testcases/solana/import_contracts_via_object.sol" {
+	S [label="name:S\ntests/contract_testcases/solana/simple.sol:3:8-9\nfield name:f1 ty:int64\nfield name:f2 ty:bool"]
 	contract [label="contract C\ntests/contract_testcases/solana/import_contracts_via_object.sol:2:1-3:20"]
 	base [label="base A\ntests/contract_testcases/solana/import_contracts_via_object.sol:3:15-20"]
 	using [label="library L"]
-	node_5 [label="constructor \ncontract: C\ntests/contract_testcases/solana/import_contracts_via_object.sol:5:2-23\nsignature ()\nvisibility public\nmutability nonpayable"]
-	contract_6 [label="contract A\ntests/contract_testcases/solana/simple.sol:1:1-12"]
-	contract_7 [label="contract L\ntests/contract_testcases/solana/simple.sol:1:14-2:11"]
+	node_6 [label="constructor \ncontract: C\ntests/contract_testcases/solana/import_contracts_via_object.sol:5:2-23\nsignature ()\nvisibility public\nmutability nonpayable"]
+	contract_7 [label="contract A\ntests/contract_testcases/solana/simple.sol:1:1-12"]
+	contract_8 [label="contract L\ntests/contract_testcases/solana/simple.sol:1:14-2:11"]
 	diagnostic [label="found contract 'A'\nlevel Debug\ntests/contract_testcases/solana/simple.sol:1:1-12"]
-	diagnostic_10 [label="found library 'L'\nlevel Debug\ntests/contract_testcases/solana/simple.sol:1:14-2:11"]
-	diagnostic_11 [label="found contract 'C'\nlevel Debug\ntests/contract_testcases/solana/import_contracts_via_object.sol:2:1-3:20"]
-	diagnostic_12 [label="revert with custom error 'IMP.E' not supported yet\nlevel Error\ntests/contract_testcases/solana/import_contracts_via_object.sol:6:10-15"]
+	diagnostic_11 [label="found library 'L'\nlevel Debug\ntests/contract_testcases/solana/simple.sol:1:14-2:11"]
+	diagnostic_12 [label="found contract 'C'\nlevel Debug\ntests/contract_testcases/solana/import_contracts_via_object.sol:2:1-3:20"]
+	diagnostic_13 [label="revert with custom error 'IMP.E' not supported yet\nlevel Error\ntests/contract_testcases/solana/import_contracts_via_object.sol:6:10-15"]
+	structs -> S
 	contracts -> contract
 	contract -> base [label="base"]
 	contract -> using [label="base"]
-	contract -> node_5 [label="constructor"]
-	contracts -> contract_6
+	contract -> node_6 [label="constructor"]
 	contracts -> contract_7
+	contracts -> contract_8
 	diagnostics -> diagnostic [label="Debug"]
-	diagnostics -> diagnostic_10 [label="Debug"]
 	diagnostics -> diagnostic_11 [label="Debug"]
-	diagnostics -> diagnostic_12 [label="Error"]
+	diagnostics -> diagnostic_12 [label="Debug"]
+	diagnostics -> diagnostic_13 [label="Error"]
 }

+ 28 - 4
tests/contract_testcases/solana/simple.dot

@@ -1,10 +1,34 @@
 strict digraph "tests/contract_testcases/solana/simple.sol" {
+	S [label="name:S\ntests/contract_testcases/solana/simple.sol:3:8-9\nfield name:f1 ty:int64\nfield name:f2 ty:bool"]
+	inc [label="function inc\ntests/contract_testcases/solana/simple.sol:6:2-7:23\nsignature inc((int64,bool))\nvisibility internal\nmutability pure"]
+	parameters [label="parameters\nstruct S s"]
+	expr [label="expression\ntests/contract_testcases/solana/simple.sol:7:26-35"]
+	assign [label="assign\nvoid\ntests/contract_testcases/solana/simple.sol:7:31-33"]
+	structmember [label="struct member #0 int64\ntests/contract_testcases/solana/simple.sol:7:28-30"]
+	variable [label="variable: s\nstruct S\ntests/contract_testcases/solana/simple.sol:7:26-27"]
+	add [label="add\nint64\ntests/contract_testcases/solana/simple.sol:7:31-33"]
+	load [label="load int64\ntests/contract_testcases/solana/simple.sol:7:31-33"]
+	structmember_11 [label="struct member #0 int64\ntests/contract_testcases/solana/simple.sol:7:28-30"]
+	variable_12 [label="variable: s\nstruct S\ntests/contract_testcases/solana/simple.sol:7:26-27"]
+	number_literal [label="int64 literal: 1\ntests/contract_testcases/solana/simple.sol:7:34-35"]
 	contract [label="contract A\ntests/contract_testcases/solana/simple.sol:1:1-12"]
-	contract_4 [label="contract L\ntests/contract_testcases/solana/simple.sol:1:14-2:11"]
+	contract_16 [label="contract L\ntests/contract_testcases/solana/simple.sol:1:14-2:11"]
 	diagnostic [label="found contract 'A'\nlevel Debug\ntests/contract_testcases/solana/simple.sol:1:1-12"]
-	diagnostic_7 [label="found library 'L'\nlevel Debug\ntests/contract_testcases/solana/simple.sol:1:14-2:11"]
+	diagnostic_19 [label="found library 'L'\nlevel Debug\ntests/contract_testcases/solana/simple.sol:1:14-2:11"]
+	structs -> S
+	free_functions -> inc [label="function"]
+	inc -> parameters [label="parameters"]
+	inc -> expr [label="body"]
+	expr -> assign [label="expr"]
+	assign -> structmember [label="left"]
+	structmember -> variable [label="var"]
+	assign -> add [label="right"]
+	add -> load [label="left"]
+	load -> structmember_11 [label="expr"]
+	structmember_11 -> variable_12 [label="var"]
+	add -> number_literal [label="right"]
 	contracts -> contract
-	contracts -> contract_4
+	contracts -> contract_16
 	diagnostics -> diagnostic [label="Debug"]
-	diagnostics -> diagnostic_7 [label="Debug"]
+	diagnostics -> diagnostic_19 [label="Debug"]
 }

+ 6 - 0
tests/contract_testcases/solana/simple.sol

@@ -1,2 +1,8 @@
 contract A {}
 library L {}
+struct S {
+	int64 f1;
+	bool f2;
+}
+function inc(S s) pure { s.f1 += 1; }
+using {inc} for S global;

+ 38 - 0
tests/contract_testcases/solana/using_import.dot

@@ -0,0 +1,38 @@
+strict digraph "tests/contract_testcases/solana/using_import.sol" {
+	S [label="name:S\ntests/contract_testcases/solana/simple.sol:3:8-9\nfield name:f1 ty:int64\nfield name:f2 ty:bool"]
+	contract [label="contract c\ntests/contract_testcases/solana/using_import.sol:5:1-6:12"]
+	test [label="function test\ncontract: c\ntests/contract_testcases/solana/using_import.sol:7:2-35\nsignature test((int64,bool))\nvisibility public\nmutability nonpayable"]
+	parameters [label="parameters\nstruct S s"]
+	expr [label="expression\ntests/contract_testcases/solana/using_import.sol:8:3-10"]
+	call_internal_function [label="call internal function\ntests/contract_testcases/solana/using_import.sol:8:3-10"]
+	internal_function [label="function(struct S) internal pure returns (void)\nfree function inc\ntests/contract_testcases/solana/using_import.sol:8:3-10"]
+	variable [label="variable: s\nstruct S\ntests/contract_testcases/solana/using_import.sol:8:3-4"]
+	expr_10 [label="expression\ntests/contract_testcases/solana/using_import.sol:9:3-10"]
+	call_internal_function_11 [label="call internal function\ntests/contract_testcases/solana/using_import.sol:9:3-10"]
+	internal_function_12 [label="function(struct S) internal pure returns (void)\nfree function dec\ntests/contract_testcases/solana/using_import.sol:9:3-10"]
+	variable_13 [label="variable: s\nstruct S\ntests/contract_testcases/solana/using_import.sol:9:3-4"]
+	contract_14 [label="contract A\ntests/contract_testcases/solana/simple.sol:1:1-12"]
+	contract_15 [label="contract L\ntests/contract_testcases/solana/simple.sol:1:14-2:11"]
+	diagnostic [label="found contract 'A'\nlevel Debug\ntests/contract_testcases/solana/simple.sol:1:1-12"]
+	diagnostic_18 [label="found library 'L'\nlevel Debug\ntests/contract_testcases/solana/simple.sol:1:14-2:11"]
+	diagnostic_19 [label="found contract 'c'\nlevel Debug\ntests/contract_testcases/solana/using_import.sol:5:1-6:12"]
+	diagnostic_20 [label="function can be declared 'pure'\nlevel Warning\ntests/contract_testcases/solana/using_import.sol:7:2-35"]
+	structs -> S
+	contracts -> contract
+	contract -> test [label="function"]
+	test -> parameters [label="parameters"]
+	test -> expr [label="body"]
+	expr -> call_internal_function [label="expr"]
+	call_internal_function -> internal_function [label="function"]
+	call_internal_function -> variable [label="arg #0"]
+	expr -> expr_10 [label="next"]
+	expr_10 -> call_internal_function_11 [label="expr"]
+	call_internal_function_11 -> internal_function_12 [label="function"]
+	call_internal_function_11 -> variable_13 [label="arg #0"]
+	contracts -> contract_14
+	contracts -> contract_15
+	diagnostics -> diagnostic [label="Debug"]
+	diagnostics -> diagnostic_18 [label="Debug"]
+	diagnostics -> diagnostic_19 [label="Debug"]
+	diagnostics -> diagnostic_20 [label="Warning"]
+}

+ 11 - 0
tests/contract_testcases/solana/using_import.sol

@@ -0,0 +1,11 @@
+import "simple.sol" as simpels;
+
+function dec(simpels.S s) pure { s.f1 -= 1; }
+using {dec} for simpels.S;
+
+contract c {
+	function test(simpels.S s) public {
+		s.inc();
+		s.dec();
+	}
+}

+ 4 - 4
tests/contract_testcases/substrate/libraries/using_07.dot

@@ -15,9 +15,9 @@ strict digraph "tests/contract_testcases/substrate/libraries/using_07.sol" {
 	variable_15 [label="variable: a\nint256\ntests/contract_testcases/substrate/libraries/using_07.sol:20:3-4"]
 	number_literal [label="int256 literal: 1\ntests/contract_testcases/substrate/libraries/using_07.sol:20:10-11"]
 	diagnostic [label="'global' on using within contract not permitted\nlevel Error\ntests/contract_testcases/substrate/libraries/using_07.sol:12:23-29"]
-	diagnostic_19 [label="'feh' not expected\nlevel Error\ntests/contract_testcases/substrate/libraries/using_07.sol:13:21-24"]
-	diagnostic_20 [label="'foo1' has no arguments, at least one argument required\nlevel Error\ntests/contract_testcases/substrate/libraries/using_07.sol:12:9-13"]
+	diagnostic_19 [label="'foo1' has no arguments, at least one argument required\nlevel Error\ntests/contract_testcases/substrate/libraries/using_07.sol:12:9-13"]
 	note [label="definition of 'foo1'\ntests/contract_testcases/substrate/libraries/using_07.sol:1:10-14"]
+	diagnostic_21 [label="'feh' not expected, did you mean 'global'?\nlevel Error\ntests/contract_testcases/substrate/libraries/using_07.sol:13:21-24"]
 	diagnostic_22 [label="'foo2' is an overloaded function\nlevel Error\ntests/contract_testcases/substrate/libraries/using_07.sol:13:9-13"]
 	note_23 [label="definition of 'foo2'\ntests/contract_testcases/substrate/libraries/using_07.sol:2:10-14"]
 	note_24 [label="definition of 'foo2'\ntests/contract_testcases/substrate/libraries/using_07.sol:2:22-3:21"]
@@ -42,8 +42,8 @@ strict digraph "tests/contract_testcases/substrate/libraries/using_07.sol" {
 	call_internal_function_13 -> number_literal [label="arg #1"]
 	diagnostics -> diagnostic [label="Error"]
 	diagnostics -> diagnostic_19 [label="Error"]
-	diagnostics -> diagnostic_20 [label="Error"]
-	diagnostic_20 -> note [label="note"]
+	diagnostic_19 -> note [label="note"]
+	diagnostics -> diagnostic_21 [label="Error"]
 	diagnostics -> diagnostic_22 [label="Error"]
 	diagnostic_22 -> note_23 [label="note"]
 	diagnostic_22 -> note_24 [label="note"]

+ 26 - 0
tests/contract_testcases/substrate/libraries/using_08.dot

@@ -0,0 +1,26 @@
+strict digraph "tests/contract_testcases/substrate/libraries/using_08.sol" {
+	S [label="name:S\ntests/contract_testcases/substrate/libraries/using_08.sol:2:8-9\nfield name:f1 ty:int256"]
+	contract [label="contract c\ntests/contract_testcases/substrate/libraries/using_08.sol:13:1-14:12"]
+	f [label="function f\ncontract: c\ntests/contract_testcases/substrate/libraries/using_08.sol:15:2-31\nsignature f((int256))\nvisibility public\nmutability nonpayable"]
+	parameters [label="parameters\nstruct S s"]
+	expr [label="expression\ntests/contract_testcases/substrate/libraries/using_08.sol:16:3-10"]
+	call_internal_function [label="call internal function\ntests/contract_testcases/substrate/libraries/using_08.sol:16:3-10"]
+	internal_function [label="function(struct S) internal returns (void)\nfree function bar\ntests/contract_testcases/substrate/libraries/using_08.sol:16:3-10"]
+	variable [label="variable: s\nstruct S\ntests/contract_testcases/substrate/libraries/using_08.sol:16:3-4"]
+	diagnostic [label="using must be bound to specific type, '*' cannot be used on file scope\nlevel Error\ntests/contract_testcases/substrate/libraries/using_08.sol:4:1-5:18"]
+	diagnostic_12 [label="'global' only permitted on user defined types\nlevel Error\ntests/contract_testcases/substrate/libraries/using_08.sol:6:21-27"]
+	diagnostic_13 [label="'meh' not expected, did you mean 'global'?\nlevel Error\ntests/contract_testcases/substrate/libraries/using_08.sol:8:19-22"]
+	diagnostic_14 [label="found contract 'c'\nlevel Debug\ntests/contract_testcases/substrate/libraries/using_08.sol:13:1-14:12"]
+	structs -> S
+	contracts -> contract
+	contract -> f [label="function"]
+	f -> parameters [label="parameters"]
+	f -> expr [label="body"]
+	expr -> call_internal_function [label="expr"]
+	call_internal_function -> internal_function [label="function"]
+	call_internal_function -> variable [label="arg #0"]
+	diagnostics -> diagnostic [label="Error"]
+	diagnostics -> diagnostic_12 [label="Error"]
+	diagnostics -> diagnostic_13 [label="Error"]
+	diagnostics -> diagnostic_14 [label="Debug"]
+}

+ 19 - 0
tests/contract_testcases/substrate/libraries/using_08.sol

@@ -0,0 +1,19 @@
+function foo(int) {}
+struct S { int f1; }
+function bar(S memory) {}
+
+using {foo} for *;
+using {foo} for int global;
+using {foo} for int;
+using {bar} for S meh;
+
+function test(int a) {
+	a.foo();
+}
+
+contract c {
+	function f(S memory s) public {
+		s.bar();
+	}
+}
+

+ 24 - 0
tests/contract_testcases/substrate/libraries/using_09.dot

@@ -0,0 +1,24 @@
+strict digraph "tests/contract_testcases/substrate/libraries/using_09.sol" {
+	S [label="name:S\ntests/contract_testcases/substrate/libraries/using_09.sol:2:8-9\nfield name:f1 ty:int256"]
+	contract [label="contract c\ntests/contract_testcases/substrate/libraries/using_09.sol:11:1-12:12"]
+	f [label="function f\ncontract: c\ntests/contract_testcases/substrate/libraries/using_09.sol:13:2-31\nsignature f((int256))\nvisibility public\nmutability nonpayable"]
+	parameters [label="parameters\nstruct S s"]
+	expr [label="expression\ntests/contract_testcases/substrate/libraries/using_09.sol:14:3-10"]
+	call_internal_function [label="call internal function\ntests/contract_testcases/substrate/libraries/using_09.sol:14:3-10"]
+	internal_function [label="function(struct S) internal returns (void)\nfree function bar\ntests/contract_testcases/substrate/libraries/using_09.sol:14:3-10"]
+	variable [label="variable: s\nstruct S\ntests/contract_testcases/substrate/libraries/using_09.sol:14:3-4"]
+	diagnostic [label="found contract 'c'\nlevel Debug\ntests/contract_testcases/substrate/libraries/using_09.sol:11:1-12:12"]
+	diagnostic_12 [label="function can be declared 'pure'\nlevel Warning\ntests/contract_testcases/substrate/libraries/using_09.sol:1:1-19"]
+	diagnostic_13 [label="function can be declared 'pure'\nlevel Warning\ntests/contract_testcases/substrate/libraries/using_09.sol:2:21-3:24"]
+	structs -> S
+	contracts -> contract
+	contract -> f [label="function"]
+	f -> parameters [label="parameters"]
+	f -> expr [label="body"]
+	expr -> call_internal_function [label="expr"]
+	call_internal_function -> internal_function [label="function"]
+	call_internal_function -> variable [label="arg #0"]
+	diagnostics -> diagnostic [label="Debug"]
+	diagnostics -> diagnostic_12 [label="Warning"]
+	diagnostics -> diagnostic_13 [label="Warning"]
+}

+ 17 - 0
tests/contract_testcases/substrate/libraries/using_09.sol

@@ -0,0 +1,17 @@
+function foo(int) {}
+struct S { int f1; }
+function bar(S memory) {}
+
+using {foo} for int;
+using {bar} for S global;
+
+function test(int a) {
+	a.foo();
+}
+
+contract c {
+	function f(S memory s) public {
+		s.bar();
+	}
+}
+