Преглед изворни кода

sema::expression() should not modify namespace

We want to be able to call sema::expression without side effects. This is
needed for a later commit, where we want to resolve an expression dependant
on context.

Note that Namespace is still a &mut argument, this is because the resolving
constructors involves adding an entry to the creates member. Ideally we
wouldn't be doing this, however for now I don't know what the best solution
is.

Signed-off-by: Sean Young <sean@mess.org>
Sean Young пре 4 година
родитељ
комит
5a870ba4b2

+ 20 - 8
src/codegen/expression.rs

@@ -3,7 +3,7 @@ use super::storage::{array_offset, array_pop, array_push, bytes_pop, bytes_push}
 use crate::parser::pt;
 use crate::sema::ast::{Builtin, CallTy, Expression, Namespace, Parameter, StringLocation, Type};
 use crate::sema::eval::eval_const_number;
-use crate::sema::expression::{cast_shift_arg, try_bigint_to_expression, try_cast};
+use crate::sema::expression::{bigint_to_expression, cast, cast_shift_arg};
 use crate::Target;
 use num_bigint::BigInt;
 use num_traits::FromPrimitive;
@@ -933,7 +933,7 @@ pub fn expression(
                 vec![args_iter.next().unwrap().clone()],
             );
             let hash = expression(&hash, cfg, contract_no, ns, vartab);
-            let selector = try_cast(loc, hash, &Type::Bytes(4), false, ns).unwrap();
+            let selector = cast(loc, hash, &Type::Bytes(4), false, ns, &mut Vec::new()).unwrap();
             let args = args_iter
                 .map(|v| expression(&v, cfg, contract_no, ns, vartab))
                 .collect();
@@ -1414,7 +1414,9 @@ fn array_subscript(
     let index_width = index_ty.bits(ns);
 
     let array_length = match array_ty.deref_any() {
-        Type::Bytes(n) => try_bigint_to_expression(&array.loc(), &BigInt::from(*n)).unwrap(),
+        Type::Bytes(n) => {
+            bigint_to_expression(&array.loc(), &BigInt::from(*n), &mut Vec::new()).unwrap()
+        }
         Type::Array(_, _) => match array_ty.array_length() {
             None => {
                 if let Type::StorageRef(_) = array_ty {
@@ -1428,7 +1430,7 @@ fn array_subscript(
                     Expression::DynamicArrayLength(*loc, Box::new(array.clone()))
                 }
             }
-            Some(l) => try_bigint_to_expression(loc, l).unwrap(),
+            Some(l) => bigint_to_expression(loc, l, &mut Vec::new()).unwrap(),
         },
         Type::DynamicBytes => Expression::DynamicArrayLength(*loc, Box::new(array.clone())),
         _ => {
@@ -1453,7 +1455,7 @@ fn array_subscript(
         Instr::Set {
             loc: pt::Loc(0, 0, 0),
             res: pos,
-            expr: try_cast(&index.loc(), index, &coerced_ty, false, ns).unwrap(),
+            expr: cast(&index.loc(), index, &coerced_ty, false, ns, &mut Vec::new()).unwrap(),
         },
     );
 
@@ -1469,7 +1471,15 @@ fn array_subscript(
                 *loc,
                 Box::new(Expression::Variable(index_loc, coerced_ty.clone(), pos)),
                 Box::new(
-                    try_cast(&array.loc(), array_length.clone(), &coerced_ty, false, ns).unwrap(),
+                    cast(
+                        &array.loc(),
+                        array_length.clone(),
+                        &coerced_ty,
+                        false,
+                        ns,
+                        &mut Vec::new(),
+                    )
+                    .unwrap(),
                 ),
             ),
             true_block: out_of_bounds,
@@ -1502,12 +1512,13 @@ fn array_subscript(
                             *loc,
                             Type::Uint(64),
                             Box::new(
-                                try_cast(
+                                cast(
                                     &index_loc,
                                     Expression::Variable(index_loc, coerced_ty, pos),
                                     &Type::Uint(64),
                                     false,
                                     ns,
+                                    &mut Vec::new(),
                                 )
                                 .unwrap(),
                             ),
@@ -1521,12 +1532,13 @@ fn array_subscript(
         array_offset(
             loc,
             array,
-            try_cast(
+            cast(
                 &index_loc,
                 Expression::Variable(index_loc, coerced_ty, pos),
                 &ns.storage_type(),
                 false,
                 ns,
+                &mut Vec::new(),
             )
             .unwrap(),
             elem_ty,

+ 11 - 4
src/codegen/statements.rs

@@ -7,7 +7,7 @@ use crate::parser::pt;
 use crate::sema::ast::{
     CallTy, DestructureField, Expression, Function, Namespace, Parameter, Statement, Type,
 };
-use crate::sema::expression::try_cast;
+use crate::sema::expression::cast;
 
 /// Resolve a statement, which might be a block of statements or an entire body of a function
 pub fn statement(
@@ -452,7 +452,7 @@ pub fn statement(
                     }
                     DestructureField::VariableDecl(res, param) => {
                         // the resolver did not cast the expression
-                        let expr = try_cast(&param.loc, right, &param.ty, true, ns)
+                        let expr = cast(&param.loc, right, &param.ty, true, ns, &mut Vec::new())
                             .expect("sema should have checked cast");
 
                         cfg.add(
@@ -468,8 +468,15 @@ pub fn statement(
                         // the resolver did not cast the expression
                         let loc = left.loc();
 
-                        let expr = try_cast(&loc, right, left.ty().deref_any(), true, ns)
-                            .expect("sema should have checked cast");
+                        let expr = cast(
+                            &loc,
+                            right,
+                            left.ty().deref_any(),
+                            true,
+                            ns,
+                            &mut Vec::new(),
+                        )
+                        .expect("sema should have checked cast");
 
                         assign_single(left, &expr, cfg, contract_no, ns, vartab);
                     }

+ 81 - 30
src/sema/builtin.rs

@@ -372,7 +372,8 @@ pub fn builtin_var(
     loc: &pt::Loc,
     namespace: Option<&str>,
     fname: &str,
-    ns: &mut Namespace,
+    ns: &Namespace,
+    diagnostics: &mut Vec<Diagnostic>,
 ) -> Option<(Builtin, Type)> {
     if let Some(p) = BUILTIN_VARIABLE
         .iter()
@@ -380,7 +381,7 @@ pub fn builtin_var(
     {
         if p.target.is_none() || p.target == Some(ns.target) {
             if ns.target == Target::Substrate && p.builtin == Builtin::Gasprice {
-                ns.diagnostics.push(Diagnostic::error(
+                diagnostics.push(Diagnostic::error(
                     *loc,
                     String::from(
                         "use the function ‘tx.gasprice(gas)’ in stead, as ‘tx.gasprice’ may round down to zero. See https://solang.readthedocs.io/en/latest/language.html#gasprice",
@@ -422,6 +423,7 @@ pub fn resolve_call(
     contract_no: Option<usize>,
     ns: &mut Namespace,
     symtable: &Symtable,
+    diagnostics: &mut Vec<Diagnostic>,
 ) -> Result<Expression, ()> {
     let matches = BUILTIN_FUNCTIONS
         .iter()
@@ -431,15 +433,16 @@ pub fn resolve_call(
     let mut resolved_args = Vec::new();
 
     for arg in args {
-        let expr = expression(arg, file_no, contract_no, ns, symtable, false)?;
+        let expr = expression(arg, file_no, contract_no, ns, symtable, false, diagnostics)?;
 
         resolved_args.push(expr);
     }
 
-    let marker = ns.diagnostics.len();
+    let marker = diagnostics.len();
+
     for func in &matches {
         if func.args.len() != args.len() {
-            ns.diagnostics.push(Diagnostic::error(
+            diagnostics.push(Diagnostic::error(
                 *loc,
                 format!(
                     "builtin function ‘{}’ expects {} arguments, {} provided",
@@ -456,7 +459,14 @@ pub fn resolve_call(
 
         // check if arguments can be implicitly casted
         for (i, arg) in resolved_args.iter().enumerate() {
-            match cast(&pt::Loc(0, 0, 0), arg.clone(), &func.args[i], true, ns) {
+            match cast(
+                &pt::Loc(0, 0, 0),
+                arg.clone(),
+                &func.args[i],
+                true,
+                ns,
+                diagnostics,
+            ) {
                 Ok(expr) => cast_args.push(expr),
                 Err(()) => {
                     matches = false;
@@ -466,13 +476,13 @@ pub fn resolve_call(
         }
 
         if matches {
-            ns.diagnostics.truncate(marker);
+            diagnostics.truncate(marker);
 
             // tx.gasprice(1) is a bad idea, just like tx.gasprice. Warn about this
             if ns.target == Target::Substrate && func.builtin == Builtin::Gasprice {
                 if let Ok((_, val)) = eval_const_number(&cast_args[0], contract_no, ns) {
                     if val == BigInt::one() {
-                        ns.diagnostics.push(Diagnostic::warning(
+                        diagnostics.push(Diagnostic::warning(
                             *loc,
                             String::from(
                                 "the function call ‘tx.gasprice(1)’ may round down to zero. See https://solang.readthedocs.io/en/latest/language.html#gasprice",
@@ -492,8 +502,8 @@ pub fn resolve_call(
     }
 
     if matches.len() != 1 {
-        ns.diagnostics.truncate(marker);
-        ns.diagnostics.push(Diagnostic::error(
+        diagnostics.truncate(marker);
+        diagnostics.push(Diagnostic::error(
             *loc,
             "cannot find overloaded function which matches signature".to_string(),
         ));
@@ -515,6 +525,7 @@ pub fn resolve_method_call(
     contract_no: Option<usize>,
     ns: &mut Namespace,
     symtable: &Symtable,
+    diagnostics: &mut Vec<Diagnostic>,
 ) -> Result<Expression, ()> {
     // The abi.* functions need special handling, others do not
     if namespace != "abi" {
@@ -527,6 +538,7 @@ pub fn resolve_method_call(
             contract_no,
             ns,
             symtable,
+            diagnostics,
         );
     }
 
@@ -541,7 +553,7 @@ pub fn resolve_method_call(
 
     if builtin == Builtin::AbiDecode {
         if args.len() != 2 {
-            ns.diagnostics.push(Diagnostic::error(
+            diagnostics.push(Diagnostic::error(
                 *loc,
                 format!("function expects {} arguments, {} provided", 2, args.len()),
             ));
@@ -552,10 +564,19 @@ pub fn resolve_method_call(
         // first args
         let data = cast(
             &args[0].loc(),
-            expression(&args[0], file_no, contract_no, ns, symtable, false)?,
+            expression(
+                &args[0],
+                file_no,
+                contract_no,
+                ns,
+                symtable,
+                false,
+                diagnostics,
+            )?,
             &Type::DynamicBytes,
             true,
             ns,
+            diagnostics,
         )?;
 
         let mut tys = Vec::new();
@@ -565,10 +586,11 @@ pub fn resolve_method_call(
             pt::Expression::List(_, list) => {
                 for (loc, param) in list {
                     if let Some(param) = param {
-                        let ty = ns.resolve_type(file_no, contract_no, false, &param.ty)?;
+                        let ty =
+                            ns.resolve_type(file_no, contract_no, false, &param.ty, diagnostics)?;
 
                         if let Some(storage) = &param.storage {
-                            ns.diagnostics.push(Diagnostic::error(
+                            diagnostics.push(Diagnostic::error(
                                 *storage.loc(),
                                 format!("storage modifier ‘{}’ not allowed", storage),
                             ));
@@ -576,7 +598,7 @@ pub fn resolve_method_call(
                         }
 
                         if let Some(name) = &param.name {
-                            ns.diagnostics.push(Diagnostic::error(
+                            diagnostics.push(Diagnostic::error(
                                 name.loc,
                                 format!("unexpected identifier ‘{}’ in type", name.name),
                             ));
@@ -584,7 +606,7 @@ pub fn resolve_method_call(
                         }
 
                         if ty.is_mapping() {
-                            ns.diagnostics.push(Diagnostic::error(
+                            diagnostics.push(Diagnostic::error(
                                 *loc,
                                 "mapping cannot be abi decoded or encoded".to_string(),
                             ));
@@ -593,18 +615,17 @@ pub fn resolve_method_call(
 
                         tys.push(ty);
                     } else {
-                        ns.diagnostics
-                            .push(Diagnostic::error(*loc, "missing type".to_string()));
+                        diagnostics.push(Diagnostic::error(*loc, "missing type".to_string()));
 
                         broken = true;
                     }
                 }
             }
             _ => {
-                let ty = ns.resolve_type(file_no, contract_no, false, &args[1])?;
+                let ty = ns.resolve_type(file_no, contract_no, false, &args[1], diagnostics)?;
 
                 if ty.is_mapping() {
-                    ns.diagnostics.push(Diagnostic::error(
+                    diagnostics.push(Diagnostic::error(
                         *loc,
                         "mapping cannot be abi decoded or encoded".to_string(),
                     ));
@@ -634,14 +655,29 @@ pub fn resolve_method_call(
         Builtin::AbiEncodeWithSelector => {
             // first argument is selector
             if let Some(selector) = args_iter.next() {
-                let selector = expression(selector, file_no, contract_no, ns, symtable, false)?;
+                let selector = expression(
+                    selector,
+                    file_no,
+                    contract_no,
+                    ns,
+                    symtable,
+                    false,
+                    diagnostics,
+                )?;
 
                 resolved_args.insert(
                     0,
-                    cast(&selector.loc(), selector, &Type::Bytes(4), true, ns)?,
+                    cast(
+                        &selector.loc(),
+                        selector,
+                        &Type::Bytes(4),
+                        true,
+                        ns,
+                        diagnostics,
+                    )?,
                 );
             } else {
-                ns.diagnostics.push(Diagnostic::error(
+                diagnostics.push(Diagnostic::error(
                     *loc,
                     "function requires one ‘bytes4’ selector argument".to_string(),
                 ));
@@ -652,14 +688,29 @@ pub fn resolve_method_call(
         Builtin::AbiEncodeWithSignature => {
             // first argument is signature
             if let Some(signature) = args_iter.next() {
-                let signature = expression(signature, file_no, contract_no, ns, symtable, false)?;
+                let signature = expression(
+                    signature,
+                    file_no,
+                    contract_no,
+                    ns,
+                    symtable,
+                    false,
+                    diagnostics,
+                )?;
 
                 resolved_args.insert(
                     0,
-                    cast(&signature.loc(), signature, &Type::String, true, ns)?,
+                    cast(
+                        &signature.loc(),
+                        signature,
+                        &Type::String,
+                        true,
+                        ns,
+                        diagnostics,
+                    )?,
                 );
             } else {
-                ns.diagnostics.push(Diagnostic::error(
+                diagnostics.push(Diagnostic::error(
                     *loc,
                     "function requires one ‘string’ signature argument".to_string(),
                 ));
@@ -671,11 +722,11 @@ pub fn resolve_method_call(
     }
 
     for arg in args_iter {
-        let mut expr = expression(arg, file_no, contract_no, ns, symtable, false)?;
+        let mut expr = expression(arg, file_no, contract_no, ns, symtable, false, diagnostics)?;
         let ty = expr.ty();
 
         if ty.is_mapping() {
-            ns.diagnostics.push(Diagnostic::error(
+            diagnostics.push(Diagnostic::error(
                 arg.loc(),
                 "mapping type not permitted".to_string(),
             ));
@@ -683,11 +734,11 @@ pub fn resolve_method_call(
             return Err(());
         }
 
-        expr = cast(&arg.loc(), expr, ty.deref_any(), true, ns)?;
+        expr = cast(&arg.loc(), expr, ty.deref_any(), true, ns, diagnostics)?;
 
         // A string or hex literal should be encoded as a string
         if let Expression::BytesLiteral(_, _, _) = &expr {
-            expr = cast(&arg.loc(), expr, &Type::String, true, ns)?;
+            expr = cast(&arg.loc(), expr, &Type::String, true, ns, diagnostics)?;
         }
 
         resolved_args.push(expr);

+ 33 - 8
src/sema/contracts.rs

@@ -220,6 +220,8 @@ fn resolve_base_args(
     file_no: usize,
     ns: &mut ast::Namespace,
 ) {
+    let mut diagnostics = Vec::new();
+
     // for every contract, if we have a base which resolved successfully, resolve any constructor args
     for (contract_no, def) in contracts {
         for base in &def.base {
@@ -235,17 +237,27 @@ fn resolve_base_args(
                         let symtable = Symtable::new();
 
                         for arg in args {
-                            if let Ok(e) =
-                                expression(&arg, file_no, Some(*contract_no), ns, &symtable, true)
-                            {
+                            if let Ok(e) = expression(
+                                &arg,
+                                file_no,
+                                Some(*contract_no),
+                                ns,
+                                &symtable,
+                                true,
+                                &mut diagnostics,
+                            ) {
                                 resolved_args.push(e);
                             }
                         }
 
                         // find constructor which matches this
-                        if let Ok((Some(constructor_no), args)) =
-                            match_constructor_to_args(&base.loc, resolved_args, base_no, ns)
-                        {
+                        if let Ok((Some(constructor_no), args)) = match_constructor_to_args(
+                            &base.loc,
+                            resolved_args,
+                            base_no,
+                            ns,
+                            &mut diagnostics,
+                        ) {
                             ns.contracts[*contract_no].bases[pos].constructor =
                                 Some((constructor_no, args));
                         }
@@ -254,6 +266,8 @@ fn resolve_base_args(
             }
         }
     }
+
+    ns.diagnostics.extend(diagnostics);
 }
 
 /// Visit base contracts in depth-first post-order
@@ -770,7 +784,15 @@ fn resolve_using(
                     }
 
                     let ty = if let Some(expr) = &using.ty {
-                        match ns.resolve_type(file_no, Some(*contract_no), false, expr) {
+                        let mut diagnostics = Vec::new();
+
+                        match ns.resolve_type(
+                            file_no,
+                            Some(*contract_no),
+                            false,
+                            expr,
+                            &mut diagnostics,
+                        ) {
                             Ok(ast::Type::Contract(contract_no)) => {
                                 ns.diagnostics.push(ast::Diagnostic::error(
                                     using.library.loc,
@@ -782,7 +804,10 @@ fn resolve_using(
                                 continue;
                             }
                             Ok(ty) => Some(ty),
-                            Err(_) => continue,
+                            Err(_) => {
+                                ns.diagnostics.extend(diagnostics);
+                                continue;
+                            }
                         }
                     } else {
                         None

Разлика између датотеке није приказан због своје велике величине
+ 186 - 137
src/sema/expression.rs


+ 17 - 17
src/sema/format.rs

@@ -1,5 +1,5 @@
 use super::ast::{Diagnostic, Expression, FormatArg, Namespace, Type};
-use super::expression::{expression, try_cast};
+use super::expression::{cast, expression};
 use super::symtable::Symtable;
 use crate::parser::pt;
 
@@ -19,15 +19,16 @@ pub fn string_format(
     contract_no: Option<usize>,
     ns: &mut Namespace,
     symtable: &Symtable,
+    diagnostics: &mut Vec<Diagnostic>,
 ) -> Result<Expression, ()> {
     // first resolve the arguments. We can't say anything about the format string if the args are broken
     let mut resolved_args = Vec::new();
 
     for arg in args {
-        let expr = expression(arg, file_no, contract_no, ns, symtable, false)?;
+        let expr = expression(arg, file_no, contract_no, ns, symtable, false, diagnostics)?;
         let ty = expr.ty();
 
-        resolved_args.push(try_cast(&arg.loc(), expr, ty.deref_any(), true, ns).unwrap());
+        resolved_args.push(cast(&arg.loc(), expr, ty.deref_any(), true, ns, diagnostics).unwrap());
     }
 
     let mut format_iterator = FormatIterator::new(literals).peekable();
@@ -41,8 +42,7 @@ pub fn string_format(
                 // ok, let's skip over it
                 format_iterator.next();
             } else {
-                ns.diagnostics
-                    .push(Diagnostic::error(loc, String::from("unmatched ‘}’")));
+                diagnostics.push(Diagnostic::error(loc, String::from("unmatched ‘}’")));
                 return Err(());
             }
         }
@@ -60,10 +60,10 @@ pub fn string_format(
                     string_literal = String::new();
                 }
 
-                let specifier = parse_format_specifier(loc, &mut format_iterator, ns)?;
+                let specifier = parse_format_specifier(loc, &mut format_iterator, diagnostics)?;
 
                 if resolved_args.is_empty() {
-                    ns.diagnostics.push(Diagnostic::error(
+                    diagnostics.push(Diagnostic::error(
                         loc,
                         String::from("missing argument to format"),
                     ));
@@ -76,7 +76,7 @@ pub fn string_format(
 
                 if matches!(specifier, FormatArg::Binary | FormatArg::Hex) {
                     if !matches!(arg_ty, Type::Uint(_) | Type::Int(_)) {
-                        ns.diagnostics.push(Diagnostic::error(
+                        diagnostics.push(Diagnostic::error(
                             arg.loc(),
                             String::from("argument must be signed or unsigned integer type"),
                         ));
@@ -84,7 +84,7 @@ pub fn string_format(
                     }
                 } else if !matches!(arg_ty, Type::Uint(_) | Type::Int(_) | Type::Bytes(_) | Type::Enum(_) | Type::Address(_) | Type::Contract(_) | Type::String | Type::DynamicBytes | Type::Bool)
                 {
-                    ns.diagnostics.push(Diagnostic::error(
+                    diagnostics.push(Diagnostic::error(
                         arg.loc(),
                         String::from(
                             "argument must be a bool, enum, address, contract, string, or bytes",
@@ -101,7 +101,7 @@ pub fn string_format(
     }
 
     if !resolved_args.is_empty() {
-        ns.diagnostics.push(Diagnostic::error(
+        diagnostics.push(Diagnostic::error(
             *loc,
             String::from("too many argument for format string"),
         ));
@@ -121,7 +121,7 @@ pub fn string_format(
 fn parse_format_specifier(
     loc: pt::Loc,
     format_iterator: &mut Peekable<FormatIterator>,
-    ns: &mut Namespace,
+    diagnostics: &mut Vec<Diagnostic>,
 ) -> Result<FormatArg, ()> {
     let mut last_loc = loc;
     let arg;
@@ -143,14 +143,14 @@ fn parse_format_specifier(
                     return Ok(FormatArg::Default);
                 }
                 Some((loc, ch)) => {
-                    ns.diagnostics.push(Diagnostic::error(
+                    diagnostics.push(Diagnostic::error(
                         loc,
                         format!("unexpected format char ‘{}’", ch),
                     ));
                     return Err(());
                 }
                 None => {
-                    ns.diagnostics.push(Diagnostic::error(
+                    diagnostics.push(Diagnostic::error(
                         last_loc,
                         String::from("missing format specifier"),
                     ));
@@ -161,14 +161,14 @@ fn parse_format_specifier(
             match format_iterator.next() {
                 Some((_, '}')) => Ok(arg),
                 Some((loc, ch)) => {
-                    ns.diagnostics.push(Diagnostic::error(
+                    diagnostics.push(Diagnostic::error(
                         loc,
                         format!("unexpected format char ‘{:}’, expected closing ‘}}’", ch),
                     ));
                     Err(())
                 }
                 None => {
-                    ns.diagnostics.push(Diagnostic::error(
+                    diagnostics.push(Diagnostic::error(
                         last_loc,
                         String::from("missing closing ‘}’"),
                     ));
@@ -177,14 +177,14 @@ fn parse_format_specifier(
             }
         }
         Some((loc, ch)) => {
-            ns.diagnostics.push(Diagnostic::error(
+            diagnostics.push(Diagnostic::error(
                 loc,
                 format!("unexpected format char ‘{}’", ch),
             ));
             Err(())
         }
         None => {
-            ns.diagnostics.push(Diagnostic::error(
+            diagnostics.push(Diagnostic::error(
                 last_loc,
                 String::from("missing closing ‘}’"),
             ));

+ 30 - 17
src/sema/functions.rs

@@ -281,12 +281,15 @@ pub fn contract_function(
         }
     };
 
+    let mut diagnostics = Vec::new();
+
     let (params, params_success) = resolve_params(
         &func.params,
         storage_allowed,
         file_no,
         Some(contract_no),
         ns,
+        &mut diagnostics,
     );
 
     let (returns, returns_success) = resolve_returns(
@@ -295,8 +298,11 @@ pub fn contract_function(
         file_no,
         Some(contract_no),
         ns,
+        &mut diagnostics,
     );
 
+    ns.diagnostics.extend(diagnostics);
+
     if ns.contracts[contract_no].is_interface() {
         if func.ty == pt::FunctionTy::Constructor {
             ns.diagnostics.push(Diagnostic::error(
@@ -679,9 +685,16 @@ pub fn function(
             }
         }
     }
-    let (params, params_success) = resolve_params(&func.params, true, file_no, None, ns);
 
-    let (returns, returns_success) = resolve_returns(&func.returns, true, file_no, None, ns);
+    let mut diagnostics = Vec::new();
+
+    let (params, params_success) =
+        resolve_params(&func.params, true, file_no, None, ns, &mut diagnostics);
+
+    let (returns, returns_success) =
+        resolve_returns(&func.returns, true, file_no, None, ns, &mut diagnostics);
+
+    ns.diagnostics.extend(diagnostics);
 
     if func.body.is_none() {
         ns.diagnostics.push(Diagnostic::error(
@@ -764,6 +777,7 @@ pub fn resolve_params(
     file_no: usize,
     contract_no: Option<usize>,
     ns: &mut Namespace,
+    diagnostics: &mut Vec<Diagnostic>,
 ) -> (Vec<Parameter>, bool) {
     let mut params = Vec::new();
     let mut success = true;
@@ -772,8 +786,7 @@ pub fn resolve_params(
         let p = match p {
             Some(p) => p,
             None => {
-                ns.diagnostics
-                    .push(Diagnostic::error(*loc, "missing parameter type".to_owned()));
+                diagnostics.push(Diagnostic::error(*loc, "missing parameter type".to_owned()));
                 success = false;
                 continue;
             }
@@ -781,10 +794,10 @@ pub fn resolve_params(
 
         let mut ty_loc = p.ty.loc();
 
-        match ns.resolve_type(file_no, contract_no, false, &p.ty) {
+        match ns.resolve_type(file_no, contract_no, false, &p.ty, diagnostics) {
             Ok(ty) => {
                 if !is_internal && ty.contains_internal_function(ns) {
-                    ns.diagnostics.push(Diagnostic::error(
+                    diagnostics.push(Diagnostic::error(
                         p.ty.loc(),
                         "parameter of type ‘function internal’ not allowed public or external functions".to_string(),
                     ));
@@ -793,7 +806,7 @@ pub fn resolve_params(
 
                 let ty = if !ty.can_have_data_location() {
                     if let Some(storage) = &p.storage {
-                        ns.diagnostics.push(Diagnostic::error(
+                        diagnostics.push(Diagnostic::error(
                             *storage.loc(),
                                 format!("data location ‘{}’ can only be specified for array, struct or mapping",
                                 storage)
@@ -804,7 +817,7 @@ pub fn resolve_params(
                     ty
                 } else if let Some(pt::StorageLocation::Storage(loc)) = p.storage {
                     if !is_internal {
-                        ns.diagnostics.push(Diagnostic::error(
+                        diagnostics.push(Diagnostic::error(
                             loc,
                             "parameter of type ‘storage’ not allowed public or external functions"
                                 .to_string(),
@@ -817,7 +830,7 @@ pub fn resolve_params(
                     Type::StorageRef(Box::new(ty))
                 } else {
                     if ty.contains_mapping(ns) {
-                        ns.diagnostics.push(Diagnostic::error(
+                        diagnostics.push(Diagnostic::error(
                             p.ty.loc(),
                             "parameter with mapping type must be of type ‘storage’".to_string(),
                         ));
@@ -853,6 +866,7 @@ pub fn resolve_returns(
     file_no: usize,
     contract_no: Option<usize>,
     ns: &mut Namespace,
+    diagnostics: &mut Vec<Diagnostic>,
 ) -> (Vec<Parameter>, bool) {
     let mut resolved_returns = Vec::new();
     let mut success = true;
@@ -861,8 +875,7 @@ pub fn resolve_returns(
         let r = match r {
             Some(r) => r,
             None => {
-                ns.diagnostics
-                    .push(Diagnostic::error(*loc, "missing return type".to_owned()));
+                diagnostics.push(Diagnostic::error(*loc, "missing return type".to_owned()));
                 success = false;
                 continue;
             }
@@ -870,10 +883,10 @@ pub fn resolve_returns(
 
         let mut ty_loc = r.ty.loc();
 
-        match ns.resolve_type(file_no, contract_no, false, &r.ty) {
+        match ns.resolve_type(file_no, contract_no, false, &r.ty, diagnostics) {
             Ok(ty) => {
                 if !is_internal && ty.contains_internal_function(ns) {
-                    ns.diagnostics.push(Diagnostic::error(
+                    diagnostics.push(Diagnostic::error(
                         r.ty.loc(),
                         "return type ‘function internal’ not allowed public or external functions"
                             .to_string(),
@@ -883,7 +896,7 @@ pub fn resolve_returns(
 
                 let ty = if !ty.can_have_data_location() {
                     if let Some(storage) = &r.storage {
-                        ns.diagnostics.push(Diagnostic::error(
+                        diagnostics.push(Diagnostic::error(
                             *storage.loc(),
                                 format!("data location ‘{}’ can only be specified for array, struct or mapping",
                                 storage)
@@ -895,7 +908,7 @@ pub fn resolve_returns(
                 } else {
                     match r.storage {
                         Some(pt::StorageLocation::Calldata(loc)) => {
-                            ns.diagnostics.push(Diagnostic::error(
+                            diagnostics.push(Diagnostic::error(
                                 loc,
                                 "data location ‘calldata’ can not be used for return types"
                                     .to_string(),
@@ -908,7 +921,7 @@ pub fn resolve_returns(
                         }
                         Some(pt::StorageLocation::Storage(loc)) => {
                             if !is_internal {
-                                ns.diagnostics.push(Diagnostic::error(
+                                diagnostics.push(Diagnostic::error(
                                     loc,
                                     "return type of type ‘storage’ not allowed public or external functions"
                                         .to_string(),
@@ -922,7 +935,7 @@ pub fn resolve_returns(
                         }
                         _ => {
                             if ty.contains_mapping(ns) {
-                                ns.diagnostics.push(Diagnostic::error(
+                                diagnostics.push(Diagnostic::error(
                                     r.ty.loc(),
                                     "return type containing mapping must be of type ‘storage’"
                                         .to_string(),

+ 89 - 56
src/sema/mod.rs

@@ -1,5 +1,6 @@
 use crate::parser::{parse, pt};
 use crate::Target;
+use ast::Diagnostic;
 use num_bigint::BigInt;
 use num_traits::Signed;
 use num_traits::Zero;
@@ -532,11 +533,13 @@ impl ast::Namespace {
         file_no: usize,
         contract_no: Option<usize>,
         expr: &pt::Expression,
+        diagnostics: &mut Vec<ast::Diagnostic>,
     ) -> Result<Vec<usize>, ()> {
-        let (namespace, id, dimensions) = self.expr_to_type(file_no, contract_no, expr)?;
+        let (namespace, id, dimensions) =
+            self.expr_to_type(file_no, contract_no, expr, diagnostics)?;
 
         if !dimensions.is_empty() {
-            self.diagnostics.push(ast::Diagnostic::decl_error(
+            diagnostics.push(ast::Diagnostic::decl_error(
                 expr.loc(),
                 "array type found where event type expected".to_string(),
             ));
@@ -546,7 +549,7 @@ impl ast::Namespace {
         let id = match id {
             pt::Expression::Variable(id) => id,
             _ => {
-                self.diagnostics.push(ast::Diagnostic::decl_error(
+                diagnostics.push(ast::Diagnostic::decl_error(
                     expr.loc(),
                     "expression found where event type expected".to_string(),
                 ));
@@ -575,7 +578,7 @@ impl ast::Namespace {
                         sym => {
                             let error = ast::Namespace::wrong_symbol(sym, &id);
 
-                            self.diagnostics.push(error);
+                            diagnostics.push(error);
 
                             return Err(());
                         }
@@ -585,7 +588,7 @@ impl ast::Namespace {
 
             return match self.symbols.get(&(file_no, None, id.name.to_owned())) {
                 None if events.is_empty() => {
-                    self.diagnostics.push(ast::Diagnostic::decl_error(
+                    diagnostics.push(ast::Diagnostic::decl_error(
                         id.loc,
                         format!("event ‘{}’ not found", id.name),
                     ));
@@ -601,21 +604,21 @@ impl ast::Namespace {
                 sym => {
                     let error = ast::Namespace::wrong_symbol(sym, &id);
 
-                    self.diagnostics.push(error);
+                    diagnostics.push(error);
 
                     Err(())
                 }
             };
         }
 
-        let s = self.resolve_namespace(namespace, file_no, contract_no, &id)?;
+        let s = self.resolve_namespace(namespace, file_no, contract_no, &id, diagnostics)?;
 
         if let Some(ast::Symbol::Event(events)) = s {
             Ok(events.iter().map(|(_, event_no)| *event_no).collect())
         } else {
             let error = ast::Namespace::wrong_symbol(s, &id);
 
-            self.diagnostics.push(error);
+            diagnostics.push(error);
 
             Err(())
         }
@@ -689,7 +692,7 @@ impl ast::Namespace {
 
     /// Resolve contract variable
     pub fn resolve_var(
-        &mut self,
+        &self,
         file_no: usize,
         contract_no: Option<usize>,
         id: &pt::Identifier,
@@ -819,23 +822,24 @@ impl ast::Namespace {
         contract_no: Option<usize>,
         casting: bool,
         id: &pt::Expression,
+        diagnostics: &mut Vec<ast::Diagnostic>,
     ) -> Result<ast::Type, ()> {
         fn resolve_dimensions(
             ast_dimensions: &[Option<(pt::Loc, BigInt)>],
-            ns: &mut ast::Namespace,
+            diagnostics: &mut Vec<Diagnostic>,
         ) -> Result<Vec<Option<BigInt>>, ()> {
             let mut dimensions = Vec::new();
 
             for d in ast_dimensions.iter().rev() {
                 if let Some((loc, n)) = d {
                     if n.is_zero() {
-                        ns.diagnostics.push(ast::Diagnostic::decl_error(
+                        diagnostics.push(ast::Diagnostic::decl_error(
                             *loc,
                             "zero size array not permitted".to_string(),
                         ));
                         return Err(());
                     } else if n.is_negative() {
-                        ns.diagnostics.push(ast::Diagnostic::decl_error(
+                        diagnostics.push(ast::Diagnostic::decl_error(
                             *loc,
                             "negative size of array declared".to_string(),
                         ));
@@ -850,33 +854,34 @@ impl ast::Namespace {
             Ok(dimensions)
         }
 
-        let (namespace, id, dimensions) = self.expr_to_type(file_no, contract_no, &id)?;
+        let (namespace, id, dimensions) =
+            self.expr_to_type(file_no, contract_no, &id, diagnostics)?;
 
         if let pt::Expression::Type(_, ty) = &id {
             assert!(namespace.is_empty());
 
             let ty = match ty {
                 pt::Type::Mapping(_, k, v) => {
-                    let key = self.resolve_type(file_no, contract_no, false, k)?;
-                    let value = self.resolve_type(file_no, contract_no, false, v)?;
+                    let key = self.resolve_type(file_no, contract_no, false, k, diagnostics)?;
+                    let value = self.resolve_type(file_no, contract_no, false, v, diagnostics)?;
 
                     match key {
                         ast::Type::Mapping(_, _) => {
-                            self.diagnostics.push(ast::Diagnostic::decl_error(
+                            diagnostics.push(ast::Diagnostic::decl_error(
                                 k.loc(),
                                 "key of mapping cannot be another mapping type".to_string(),
                             ));
                             return Err(());
                         }
                         ast::Type::Struct(_) => {
-                            self.diagnostics.push(ast::Diagnostic::decl_error(
+                            diagnostics.push(ast::Diagnostic::decl_error(
                                 k.loc(),
                                 "key of mapping cannot be struct type".to_string(),
                             ));
                             return Err(());
                         }
                         ast::Type::Array(_, _) => {
-                            self.diagnostics.push(ast::Diagnostic::decl_error(
+                            diagnostics.push(ast::Diagnostic::decl_error(
                                 k.loc(),
                                 "key of mapping cannot be array type".to_string(),
                             ));
@@ -900,7 +905,7 @@ impl ast::Namespace {
                         match a {
                             pt::FunctionAttribute::StateMutability(m) => {
                                 if let Some(e) = &mutability {
-                                    self.diagnostics.push(ast::Diagnostic::error_with_note(
+                                    diagnostics.push(ast::Diagnostic::error_with_note(
                                         m.loc(),
                                         format!(
                                             "function type mutability redeclared `{}'",
@@ -917,7 +922,7 @@ impl ast::Namespace {
                                 }
 
                                 if let pt::StateMutability::Constant(loc) = m {
-                                    self.diagnostics.push(ast::Diagnostic::warning(
+                                    diagnostics.push(ast::Diagnostic::warning(
                                         *loc,
                                         "‘constant’ is deprecated. Use ‘view’ instead".to_string(),
                                     ));
@@ -929,7 +934,7 @@ impl ast::Namespace {
                             }
                             pt::FunctionAttribute::Visibility(v) => {
                                 if let Some(e) = &visibility {
-                                    self.diagnostics.push(ast::Diagnostic::error_with_note(
+                                    diagnostics.push(ast::Diagnostic::error_with_note(
                                         v.loc(),
                                         format!(
                                             "function type visibility redeclared `{}'",
@@ -955,7 +960,7 @@ impl ast::Namespace {
                         None | Some(pt::Visibility::Internal(_)) => false,
                         Some(pt::Visibility::External(_)) => true,
                         Some(v) => {
-                            self.diagnostics.push(ast::Diagnostic::error(
+                            diagnostics.push(ast::Diagnostic::error(
                                 v.loc(),
                                 format!("function type cannot have visibility attribute `{}'", v),
                             ));
@@ -964,18 +969,30 @@ impl ast::Namespace {
                         }
                     };
 
-                    let (params, params_success) =
-                        resolve_params(params, is_external, file_no, contract_no, self);
-
-                    let (returns, returns_success) =
-                        resolve_returns(returns, is_external, file_no, contract_no, self);
+                    let (params, params_success) = resolve_params(
+                        params,
+                        is_external,
+                        file_no,
+                        contract_no,
+                        self,
+                        diagnostics,
+                    );
+
+                    let (returns, returns_success) = resolve_returns(
+                        returns,
+                        is_external,
+                        file_no,
+                        contract_no,
+                        self,
+                        diagnostics,
+                    );
 
                     // trailing attribute should not be there
                     // trailing visibility for contract variables should be removed already
                     for a in trailing_attributes {
                         match a {
                             pt::FunctionAttribute::StateMutability(m) => {
-                                self.diagnostics.push(ast::Diagnostic::error(
+                                diagnostics.push(ast::Diagnostic::error(
                                     m.loc(),
                                     format!(
                                         "mutability `{}' cannot be declared after returns",
@@ -985,7 +1002,7 @@ impl ast::Namespace {
                                 success = false;
                             }
                             pt::FunctionAttribute::Visibility(v) => {
-                                self.diagnostics.push(ast::Diagnostic::error(
+                                diagnostics.push(ast::Diagnostic::error(
                                     v.loc(),
                                     format!(
                                         "visibility `{}' cannot be declared after returns",
@@ -1006,7 +1023,7 @@ impl ast::Namespace {
                         .into_iter()
                         .map(|p| {
                             if !p.name.is_empty() {
-                                self.diagnostics.push(ast::Diagnostic::error(
+                                diagnostics.push(ast::Diagnostic::error(
                                     p.name_loc.unwrap(),
                                     "function type parameters cannot be named".to_string(),
                                 ));
@@ -1020,7 +1037,7 @@ impl ast::Namespace {
                         .into_iter()
                         .map(|p| {
                             if !p.name.is_empty() {
-                                self.diagnostics.push(ast::Diagnostic::error(
+                                diagnostics.push(ast::Diagnostic::error(
                                     p.name_loc.unwrap(),
                                     "function type returns cannot be named".to_string(),
                                 ));
@@ -1046,7 +1063,7 @@ impl ast::Namespace {
                 }
                 pt::Type::Payable => {
                     if !casting {
-                        self.diagnostics.push(ast::Diagnostic::decl_error(
+                        diagnostics.push(ast::Diagnostic::decl_error(
                             id.loc(),
                             "‘payable’ cannot be used for type declarations, only casting. use ‘address payable’"
                                 .to_string(),
@@ -1064,7 +1081,7 @@ impl ast::Namespace {
             } else {
                 Ok(ast::Type::Array(
                     Box::new(ty),
-                    resolve_dimensions(&dimensions, self)?,
+                    resolve_dimensions(&dimensions, diagnostics)?,
                 ))
             };
         }
@@ -1074,11 +1091,11 @@ impl ast::Namespace {
             _ => unreachable!(),
         };
 
-        let s = self.resolve_namespace(namespace, file_no, contract_no, &id)?;
+        let s = self.resolve_namespace(namespace, file_no, contract_no, &id, diagnostics)?;
 
         match s {
             None => {
-                self.diagnostics.push(ast::Diagnostic::decl_error(
+                diagnostics.push(ast::Diagnostic::decl_error(
                     id.loc,
                     format!("type ‘{}’ not found", id.name),
                 ));
@@ -1087,43 +1104,43 @@ impl ast::Namespace {
             Some(ast::Symbol::Enum(_, n)) if dimensions.is_empty() => Ok(ast::Type::Enum(*n)),
             Some(ast::Symbol::Enum(_, n)) => Ok(ast::Type::Array(
                 Box::new(ast::Type::Enum(*n)),
-                resolve_dimensions(&dimensions, self)?,
+                resolve_dimensions(&dimensions, diagnostics)?,
             )),
             Some(ast::Symbol::Struct(_, n)) if dimensions.is_empty() => Ok(ast::Type::Struct(*n)),
             Some(ast::Symbol::Struct(_, n)) => Ok(ast::Type::Array(
                 Box::new(ast::Type::Struct(*n)),
-                resolve_dimensions(&dimensions, self)?,
+                resolve_dimensions(&dimensions, diagnostics)?,
             )),
             Some(ast::Symbol::Contract(_, n)) if dimensions.is_empty() => {
                 Ok(ast::Type::Contract(*n))
             }
             Some(ast::Symbol::Contract(_, n)) => Ok(ast::Type::Array(
                 Box::new(ast::Type::Contract(*n)),
-                resolve_dimensions(&dimensions, self)?,
+                resolve_dimensions(&dimensions, diagnostics)?,
             )),
             Some(ast::Symbol::Event(_)) => {
-                self.diagnostics.push(ast::Diagnostic::decl_error(
+                diagnostics.push(ast::Diagnostic::decl_error(
                     id.loc,
                     format!("‘{}’ is an event", id.name),
                 ));
                 Err(())
             }
             Some(ast::Symbol::Function(_)) => {
-                self.diagnostics.push(ast::Diagnostic::decl_error(
+                diagnostics.push(ast::Diagnostic::decl_error(
                     id.loc,
                     format!("‘{}’ is a function", id.name),
                 ));
                 Err(())
             }
             Some(ast::Symbol::Variable(_, _, _)) => {
-                self.diagnostics.push(ast::Diagnostic::decl_error(
+                diagnostics.push(ast::Diagnostic::decl_error(
                     id.loc,
                     format!("‘{}’ is a contract variable", id.name),
                 ));
                 Err(())
             }
             Some(ast::Symbol::Import(_, _)) => {
-                self.diagnostics.push(ast::Diagnostic::decl_error(
+                diagnostics.push(ast::Diagnostic::decl_error(
                     id.loc,
                     format!("‘{}’ is an import variable", id.name),
                 ));
@@ -1134,11 +1151,12 @@ impl ast::Namespace {
 
     /// Resolve the type name with the namespace to a symbol
     fn resolve_namespace(
-        &mut self,
+        &self,
         mut namespace: Vec<&pt::Identifier>,
         file_no: usize,
         mut contract_no: Option<usize>,
         id: &pt::Identifier,
+        diagnostics: &mut Vec<Diagnostic>,
     ) -> Result<Option<&ast::Symbol>, ()> {
         // The leading part of the namespace can be import variables
         let mut import_file_no = file_no;
@@ -1163,7 +1181,7 @@ impl ast::Namespace {
                     .get(&(import_file_no, None, contract_name.name.clone()))
                 {
                     None => {
-                        self.diagnostics.push(ast::Diagnostic::decl_error(
+                        diagnostics.push(ast::Diagnostic::decl_error(
                             id.loc,
                             format!("contract type ‘{}’ not found", id.name),
                         ));
@@ -1171,7 +1189,7 @@ impl ast::Namespace {
                     }
                     Some(ast::Symbol::Contract(_, n)) => {
                         if namespace.len() > 1 {
-                            self.diagnostics.push(ast::Diagnostic::decl_error(
+                            diagnostics.push(ast::Diagnostic::decl_error(
                                 id.loc,
                                 format!("‘{}’ not found", namespace[1].name),
                             ));
@@ -1180,35 +1198,35 @@ impl ast::Namespace {
                         Some(*n)
                     }
                     Some(ast::Symbol::Function(_)) => {
-                        self.diagnostics.push(ast::Diagnostic::decl_error(
+                        diagnostics.push(ast::Diagnostic::decl_error(
                             id.loc,
                             format!("‘{}’ is a function", id.name),
                         ));
                         return Err(());
                     }
                     Some(ast::Symbol::Variable(_, _, _)) => {
-                        self.diagnostics.push(ast::Diagnostic::decl_error(
+                        diagnostics.push(ast::Diagnostic::decl_error(
                             id.loc,
                             format!("‘{}’ is a contract variable", id.name),
                         ));
                         return Err(());
                     }
                     Some(ast::Symbol::Event(_)) => {
-                        self.diagnostics.push(ast::Diagnostic::decl_error(
+                        diagnostics.push(ast::Diagnostic::decl_error(
                             id.loc,
                             format!("‘{}’ is an event", id.name),
                         ));
                         return Err(());
                     }
                     Some(ast::Symbol::Struct(_, _)) => {
-                        self.diagnostics.push(ast::Diagnostic::decl_error(
+                        diagnostics.push(ast::Diagnostic::decl_error(
                             id.loc,
                             format!("‘{}’ is a struct", id.name),
                         ));
                         return Err(());
                     }
                     Some(ast::Symbol::Enum(_, _)) => {
-                        self.diagnostics.push(ast::Diagnostic::decl_error(
+                        diagnostics.push(ast::Diagnostic::decl_error(
                             id.loc,
                             format!("‘{}’ is an enum variable", id.name),
                         ));
@@ -1249,6 +1267,7 @@ impl ast::Namespace {
         file_no: usize,
         contract_no: Option<usize>,
         expr: &'a pt::Expression,
+        diagnostics: &mut Vec<ast::Diagnostic>,
     ) -> Result<(Vec<&'a pt::Identifier>, pt::Expression, Vec<ArrayDimension>), ()> {
         let mut expr = expr;
         let mut dimensions = Vec::new();
@@ -1261,7 +1280,12 @@ impl ast::Namespace {
                     r.as_ref()
                 }
                 pt::Expression::ArraySubscript(_, r, Some(index)) => {
-                    dimensions.push(self.resolve_array_dimension(file_no, contract_no, index)?);
+                    dimensions.push(self.resolve_array_dimension(
+                        file_no,
+                        contract_no,
+                        index,
+                        diagnostics,
+                    )?);
 
                     r.as_ref()
                 }
@@ -1284,7 +1308,7 @@ impl ast::Namespace {
 
                         return Ok((names, pt::Expression::Variable(id.clone()), dimensions));
                     } else {
-                        self.diagnostics.push(ast::Diagnostic::decl_error(
+                        diagnostics.push(ast::Diagnostic::decl_error(
                             namespace.loc(),
                             "expression found where type expected".to_string(),
                         ));
@@ -1292,7 +1316,7 @@ impl ast::Namespace {
                     }
                 }
                 _ => {
-                    self.diagnostics.push(ast::Diagnostic::decl_error(
+                    diagnostics.push(ast::Diagnostic::decl_error(
                         expr.loc(),
                         "expression found where type expected".to_string(),
                     ));
@@ -1308,15 +1332,24 @@ impl ast::Namespace {
         file_no: usize,
         contract_no: Option<usize>,
         expr: &pt::Expression,
+        diagnostics: &mut Vec<ast::Diagnostic>,
     ) -> Result<ArrayDimension, ()> {
         let symtable = Symtable::new();
 
-        let size_expr = expression(&expr, file_no, contract_no, self, &symtable, true)?;
+        let size_expr = expression(
+            &expr,
+            file_no,
+            contract_no,
+            self,
+            &symtable,
+            true,
+            diagnostics,
+        )?;
 
         match size_expr.ty() {
             ast::Type::Uint(_) | ast::Type::Int(_) => {}
             _ => {
-                self.diagnostics.push(ast::Diagnostic::decl_error(
+                diagnostics.push(ast::Diagnostic::decl_error(
                     expr.loc(),
                     "expression is not a number".to_string(),
                 ));
@@ -1327,7 +1360,7 @@ impl ast::Namespace {
         match eval_const_number(&size_expr, contract_no, self) {
             Ok(n) => Ok(Some(n)),
             Err(d) => {
-                self.diagnostics.push(d);
+                diagnostics.push(d);
 
                 Err(())
             }

+ 256 - 99
src/sema/statements.rs

@@ -2,7 +2,7 @@ use super::ast::*;
 use super::contracts::is_base;
 use super::expression::{
     available_functions, call_position_args, cast, constructor_named_args, expression,
-    function_call_expr, match_constructor_to_args, named_function_call_expr, new, try_cast,
+    function_call_expr, match_constructor_to_args, named_function_call_expr, new,
 };
 use super::symtable::{LoopScopes, Symtable};
 use crate::parser::pt;
@@ -66,6 +66,7 @@ pub fn resolve_function_body(
                             all_ok = false;
                         } else if let Some(args) = &base.args {
                             let mut resolved_args = Vec::new();
+                            let mut diagnostics = Vec::new();
                             let mut ok = true;
 
                             for arg in args {
@@ -76,6 +77,7 @@ pub fn resolve_function_body(
                                     ns,
                                     &symtable,
                                     false,
+                                    &mut diagnostics,
                                 ) {
                                     resolved_args.push(e);
                                 } else {
@@ -86,9 +88,13 @@ pub fn resolve_function_body(
 
                             // find constructor which matches this
                             if ok {
-                                if let Ok((Some(constructor_no), args)) =
-                                    match_constructor_to_args(&base.loc, resolved_args, base_no, ns)
-                                {
+                                if let Ok((Some(constructor_no), args)) = match_constructor_to_args(
+                                    &base.loc,
+                                    resolved_args,
+                                    base_no,
+                                    ns,
+                                    &mut diagnostics,
+                                ) {
                                     ns.functions[function_no]
                                         .bases
                                         .insert(base_no, (base.loc, constructor_no, args));
@@ -96,6 +102,8 @@ pub fn resolve_function_body(
                                     resolve_bases.insert(base_no, base.loc);
                                 }
                             }
+
+                            ns.diagnostics.extend(diagnostics);
                         } else {
                             ns.diagnostics.push(Diagnostic::error(
                                 *loc,
@@ -149,6 +157,7 @@ pub fn resolve_function_body(
     // resolve modifiers on functions
     if def.ty == pt::FunctionTy::Function {
         let mut modifiers = Vec::new();
+        let mut diagnostics = Vec::new();
 
         for attr in &def.attributes {
             if let pt::FunctionAttribute::BaseOrModifier(_, modifier) = attr {
@@ -163,12 +172,14 @@ pub fn resolve_function_body(
                     contract_no,
                     ns,
                     &symtable,
+                    &mut diagnostics,
                 ) {
                     modifiers.push(e);
                 }
             }
         }
 
+        ns.diagnostics.extend(diagnostics);
         ns.functions[function_no].modifiers = modifiers;
     }
 
@@ -208,6 +219,8 @@ pub fn resolve_function_body(
 
     let body = def.body.as_ref().unwrap();
 
+    let mut diagnostics = Vec::new();
+
     let reachable = statement(
         body,
         &mut res,
@@ -217,10 +230,13 @@ pub fn resolve_function_body(
         &mut symtable,
         &mut loops,
         ns,
-    )?;
+        &mut diagnostics,
+    );
+
+    ns.diagnostics.extend(diagnostics);
 
-    // ensure we have a return instruction
-    if reachable {
+    if reachable? {
+        // ensure we have a return instruction
         if let Some(Statement::Return(_, _)) = res.last() {
             // ok
         } else if return_required {
@@ -240,6 +256,7 @@ pub fn resolve_function_body(
                 &mut symtable,
                 &mut loops,
                 ns,
+                &mut Vec::new(),
             )?;
         }
     }
@@ -286,16 +303,24 @@ fn statement(
     symtable: &mut Symtable,
     loops: &mut LoopScopes,
     ns: &mut Namespace,
+    diagnostics: &mut Vec<Diagnostic>,
 ) -> Result<bool, ()> {
     match stmt {
         pt::Statement::VariableDefinition(loc, decl, initializer) => {
-            let (var_ty, ty_loc) =
-                resolve_var_decl_ty(&decl.ty, &decl.storage, file_no, contract_no, ns)?;
+            let (var_ty, ty_loc) = resolve_var_decl_ty(
+                &decl.ty,
+                &decl.storage,
+                file_no,
+                contract_no,
+                ns,
+                diagnostics,
+            )?;
 
             let initializer = if let Some(init) = initializer {
-                let expr = expression(init, file_no, contract_no, ns, symtable, false)?;
+                let expr =
+                    expression(init, file_no, contract_no, ns, symtable, false, diagnostics)?;
 
-                Some(cast(&expr.loc(), expr, &var_ty, true, ns)?)
+                Some(cast(&expr.loc(), expr, &var_ty, true, ns, diagnostics)?)
             } else {
                 None
             };
@@ -341,6 +366,7 @@ fn statement(
                     symtable,
                     loops,
                     ns,
+                    diagnostics,
                 )?;
             }
 
@@ -353,7 +379,7 @@ fn statement(
                 res.push(Statement::Break(*loc));
                 Ok(false)
             } else {
-                ns.diagnostics.push(Diagnostic::error(
+                diagnostics.push(Diagnostic::error(
                     stmt.loc(),
                     "break statement not in loop".to_string(),
                 ));
@@ -365,7 +391,7 @@ fn statement(
                 res.push(Statement::Continue(*loc));
                 Ok(false)
             } else {
-                ns.diagnostics.push(Diagnostic::error(
+                diagnostics.push(Diagnostic::error(
                     stmt.loc(),
                     "continue statement not in loop".to_string(),
                 ));
@@ -373,9 +399,17 @@ fn statement(
             }
         }
         pt::Statement::While(loc, cond_expr, body) => {
-            let expr = expression(cond_expr, file_no, contract_no, ns, symtable, false)?;
+            let expr = expression(
+                cond_expr,
+                file_no,
+                contract_no,
+                ns,
+                symtable,
+                false,
+                diagnostics,
+            )?;
 
-            let cond = cast(&expr.loc(), expr, &Type::Bool, true, ns)?;
+            let cond = cast(&expr.loc(), expr, &Type::Bool, true, ns, diagnostics)?;
 
             symtable.new_scope();
             let mut body_stmts = Vec::new();
@@ -389,6 +423,7 @@ fn statement(
                 symtable,
                 loops,
                 ns,
+                diagnostics,
             )?;
             symtable.leave_scope();
             loops.leave_scope();
@@ -398,9 +433,17 @@ fn statement(
             Ok(true)
         }
         pt::Statement::DoWhile(loc, body, cond_expr) => {
-            let expr = expression(cond_expr, file_no, contract_no, ns, symtable, false)?;
+            let expr = expression(
+                cond_expr,
+                file_no,
+                contract_no,
+                ns,
+                symtable,
+                false,
+                diagnostics,
+            )?;
 
-            let cond = cast(&expr.loc(), expr, &Type::Bool, true, ns)?;
+            let cond = cast(&expr.loc(), expr, &Type::Bool, true, ns, diagnostics)?;
 
             symtable.new_scope();
             let mut body_stmts = Vec::new();
@@ -414,6 +457,7 @@ fn statement(
                 symtable,
                 loops,
                 ns,
+                diagnostics,
             )?;
             symtable.leave_scope();
             loops.leave_scope();
@@ -422,9 +466,17 @@ fn statement(
             Ok(true)
         }
         pt::Statement::If(loc, cond_expr, then, else_) => {
-            let expr = expression(cond_expr, file_no, contract_no, ns, symtable, false)?;
+            let expr = expression(
+                cond_expr,
+                file_no,
+                contract_no,
+                ns,
+                symtable,
+                false,
+                diagnostics,
+            )?;
 
-            let cond = cast(&expr.loc(), expr, &Type::Bool, true, ns)?;
+            let cond = cast(&expr.loc(), expr, &Type::Bool, true, ns, diagnostics)?;
 
             symtable.new_scope();
             let mut then_stmts = Vec::new();
@@ -437,6 +489,7 @@ fn statement(
                 symtable,
                 loops,
                 ns,
+                diagnostics,
             )?;
             symtable.leave_scope();
 
@@ -452,6 +505,7 @@ fn statement(
                     symtable,
                     loops,
                     ns,
+                    diagnostics,
                 )?;
 
                 symtable.leave_scope();
@@ -485,6 +539,7 @@ fn statement(
                     symtable,
                     loops,
                     ns,
+                    diagnostics,
                 )?;
             }
 
@@ -502,6 +557,7 @@ fn statement(
                     symtable,
                     loops,
                     ns,
+                    diagnostics,
                 )?;
             }
 
@@ -519,6 +575,7 @@ fn statement(
                     symtable,
                     loops,
                     ns,
+                    diagnostics,
                 )?;
             }
 
@@ -552,12 +609,21 @@ fn statement(
                     symtable,
                     loops,
                     ns,
+                    diagnostics,
                 )?;
             }
 
-            let expr = expression(cond_expr, file_no, contract_no, ns, symtable, false)?;
+            let expr = expression(
+                cond_expr,
+                file_no,
+                contract_no,
+                ns,
+                symtable,
+                false,
+                diagnostics,
+            )?;
 
-            let cond = cast(&cond_expr.loc(), expr, &Type::Bool, true, ns)?;
+            let cond = cast(&cond_expr.loc(), expr, &Type::Bool, true, ns, diagnostics)?;
 
             // continue goes to next, and if that does exist, cond
             loops.new_scope();
@@ -572,6 +638,7 @@ fn statement(
                     symtable,
                     loops,
                     ns,
+                    diagnostics,
                 )?,
                 None => true,
             };
@@ -593,6 +660,7 @@ fn statement(
                         symtable,
                         loops,
                         ns,
+                        diagnostics,
                     )?;
                 }
             }
@@ -646,6 +714,7 @@ fn statement(
                 function_no,
                 symtable,
                 ns,
+                diagnostics,
             )?;
 
             res.push(Statement::Return(*loc, vals));
@@ -655,7 +724,8 @@ fn statement(
         pt::Statement::Expression(loc, expr) => {
             // delete statement
             if let pt::Expression::Delete(_, expr) = expr {
-                let expr = expression(expr, file_no, contract_no, ns, symtable, false)?;
+                let expr =
+                    expression(expr, file_no, contract_no, ns, symtable, false, diagnostics)?;
 
                 return if let Type::StorageRef(ty) = expr.ty() {
                     if expr.ty().is_mapping() {
@@ -707,6 +777,7 @@ fn statement(
                         contract_no,
                         symtable,
                         ns,
+                        diagnostics,
                     )?);
 
                     // if a noreturn function was called, then the destructure would not resolve
@@ -715,7 +786,7 @@ fn statement(
             }
 
             // the rest
-            let expr = expression(expr, file_no, contract_no, ns, symtable, false)?;
+            let expr = expression(expr, file_no, contract_no, ns, symtable, false, diagnostics)?;
 
             let reachable = expr.ty() != Type::Unreachable;
 
@@ -736,13 +807,23 @@ fn statement(
                 symtable,
                 loops,
                 ns,
+                diagnostics,
             )?;
             res.push(stmt);
 
             Ok(reachable)
         }
         pt::Statement::Emit(loc, ty) => {
-            if let Ok(emit) = emit_event(loc, ty, file_no, contract_no, function_no, symtable, ns) {
+            if let Ok(emit) = emit_event(
+                loc,
+                ty,
+                file_no,
+                contract_no,
+                function_no,
+                symtable,
+                ns,
+                diagnostics,
+            ) {
                 res.push(emit);
             }
 
@@ -760,25 +841,26 @@ fn emit_event(
     function_no: usize,
     symtable: &mut Symtable,
     ns: &mut Namespace,
+    diagnostics: &mut Vec<Diagnostic>,
 ) -> Result<Statement, ()> {
     match ty {
         pt::Expression::FunctionCall(_, ty, args) => {
             let event_loc = ty.loc();
-            let event_nos = ns.resolve_event(file_no, contract_no, ty)?;
+            let event_nos = ns.resolve_event(file_no, contract_no, ty, diagnostics)?;
 
             let mut resolved_args = Vec::new();
 
             for arg in args {
-                let expr = expression(arg, file_no, contract_no, ns, symtable, false)?;
+                let expr = expression(arg, file_no, contract_no, ns, symtable, false, diagnostics)?;
                 resolved_args.push(expr);
             }
 
-            let mut diagnostics = Vec::new();
+            let mut temp_diagnostics = Vec::new();
 
             for event_no in &event_nos {
                 let event = &ns.events[*event_no];
                 if resolved_args.len() != event.fields.len() {
-                    diagnostics.push(Diagnostic::error(
+                    temp_diagnostics.push(Diagnostic::error(
                         *loc,
                         format!(
                             "event type ‘{}’ has {} fields, {} provided",
@@ -794,10 +876,16 @@ fn emit_event(
                 let mut matches = true;
                 // check if arguments can be implicitly casted
                 for (i, arg) in resolved_args.iter().enumerate() {
-                    match try_cast(&arg.loc(), arg.clone(), &event.fields[i].ty, true, ns) {
+                    match cast(
+                        &arg.loc(),
+                        arg.clone(),
+                        &event.fields[i].ty,
+                        true,
+                        ns,
+                        &mut temp_diagnostics,
+                    ) {
                         Ok(expr) => cast_args.push(expr),
-                        Err(e) => {
-                            diagnostics.push(e);
+                        Err(_) => {
                             matches = false;
                         }
                     }
@@ -818,9 +906,9 @@ fn emit_event(
             }
 
             if event_nos.len() == 1 {
-                ns.diagnostics.extend(diagnostics);
+                diagnostics.extend(temp_diagnostics);
             } else {
-                ns.diagnostics.push(Diagnostic::error(
+                diagnostics.push(Diagnostic::error(
                     *loc,
                     "cannot find event which matches signature".to_string(),
                 ));
@@ -828,7 +916,7 @@ fn emit_event(
         }
         pt::Expression::NamedFunctionCall(_, ty, args) => {
             let event_loc = ty.loc();
-            let event_nos = ns.resolve_event(file_no, contract_no, ty)?;
+            let event_nos = ns.resolve_event(file_no, contract_no, ty, diagnostics)?;
 
             let mut arguments = HashMap::new();
 
@@ -842,18 +930,26 @@ fn emit_event(
                 }
                 arguments.insert(
                     arg.name.name.to_string(),
-                    expression(&arg.expr, file_no, contract_no, ns, symtable, false)?,
+                    expression(
+                        &arg.expr,
+                        file_no,
+                        contract_no,
+                        ns,
+                        symtable,
+                        false,
+                        diagnostics,
+                    )?,
                 );
             }
 
-            let mut diagnostics = Vec::new();
+            let mut temp_diagnostics = Vec::new();
 
             for event_no in &event_nos {
                 let event = &ns.events[*event_no];
                 let params_len = event.fields.len();
 
                 if params_len != arguments.len() {
-                    diagnostics.push(Diagnostic::error(
+                    temp_diagnostics.push(Diagnostic::error(
                         *loc,
                         format!(
                             "event expects {} arguments, {} provided",
@@ -872,7 +968,7 @@ fn emit_event(
                     let param = event.fields[i].clone();
 
                     if param.name.is_empty() {
-                        ns.diagnostics.push(Diagnostic::error(
+                        temp_diagnostics.push(Diagnostic::error(
                         *loc,
                         format!(
                             "event ‘{}’ cannot emitted by argument name since argument {} has no name",
@@ -887,7 +983,7 @@ fn emit_event(
                         Some(a) => a,
                         None => {
                             matches = false;
-                            ns.diagnostics.push(Diagnostic::error(
+                            temp_diagnostics.push(Diagnostic::error(
                                 *loc,
                                 format!(
                                     "missing argument ‘{}’ to event ‘{}’",
@@ -897,10 +993,16 @@ fn emit_event(
                             break;
                         }
                     };
-                    match try_cast(&arg.loc(), arg.clone(), &param.ty, true, ns) {
+                    match cast(
+                        &arg.loc(),
+                        arg.clone(),
+                        &param.ty,
+                        true,
+                        ns,
+                        &mut temp_diagnostics,
+                    ) {
                         Ok(expr) => cast_args.push(expr),
-                        Err(e) => {
-                            diagnostics.push(e);
+                        Err(_) => {
                             matches = false;
                         }
                     }
@@ -921,18 +1023,18 @@ fn emit_event(
             }
 
             if event_nos.len() == 1 {
-                ns.diagnostics.extend(diagnostics);
+                diagnostics.extend(temp_diagnostics);
             } else {
-                ns.diagnostics.push(Diagnostic::error(
+                diagnostics.push(Diagnostic::error(
                     *loc,
                     "cannot find event which matches signature".to_string(),
                 ));
             }
         }
         pt::Expression::FunctionCallBlock(_, ty, block) => {
-            let _ = ns.resolve_event(file_no, contract_no, ty);
+            let _ = ns.resolve_event(file_no, contract_no, ty, diagnostics);
 
-            ns.diagnostics.push(Diagnostic::error(
+            diagnostics.push(Diagnostic::error(
                 block.loc(),
                 "expected event arguments, found code block".to_string(),
             ));
@@ -952,23 +1054,38 @@ fn destructure(
     contract_no: Option<usize>,
     symtable: &mut Symtable,
     ns: &mut Namespace,
+    diagnostics: &mut Vec<Diagnostic>,
 ) -> Result<Statement, ()> {
     let expr = match expr {
-        pt::Expression::FunctionCall(loc, ty, args) => {
-            function_call_expr(loc, ty, args, file_no, contract_no, ns, symtable)?
-        }
-        pt::Expression::NamedFunctionCall(loc, ty, args) => {
-            named_function_call_expr(loc, ty, args, file_no, contract_no, ns, symtable)?
-        }
+        pt::Expression::FunctionCall(loc, ty, args) => function_call_expr(
+            loc,
+            ty,
+            args,
+            file_no,
+            contract_no,
+            ns,
+            symtable,
+            diagnostics,
+        )?,
+        pt::Expression::NamedFunctionCall(loc, ty, args) => named_function_call_expr(
+            loc,
+            ty,
+            args,
+            file_no,
+            contract_no,
+            ns,
+            symtable,
+            diagnostics,
+        )?,
         _ => {
             let mut list = Vec::new();
 
-            for e in parameter_list_to_expr_list(expr, ns)? {
-                let e = expression(e, file_no, contract_no, ns, symtable, false)?;
+            for e in parameter_list_to_expr_list(expr, diagnostics)? {
+                let e = expression(e, file_no, contract_no, ns, symtable, false, diagnostics)?;
 
                 match e.ty() {
                     Type::Void | Type::Unreachable => {
-                        ns.diagnostics.push(Diagnostic::error(
+                        diagnostics.push(Diagnostic::error(
                             e.loc(),
                             "function does not return a value".to_string(),
                         ));
@@ -992,7 +1109,7 @@ fn destructure(
     }
 
     if vars.len() != tys.len() {
-        ns.diagnostics.push(Diagnostic::error(
+        diagnostics.push(Diagnostic::error(
             *loc,
             format!(
                 "destructuring assignment has {} elements on the left and {} on the right",
@@ -1018,7 +1135,7 @@ fn destructure(
                 name: None,
             }) => {
                 if let Some(storage) = storage {
-                    ns.diagnostics.push(Diagnostic::error(
+                    diagnostics.push(Diagnostic::error(
                         *storage.loc(),
                         format!("storage modifier ‘{}’ not permitted on assignment", storage),
                     ));
@@ -1026,11 +1143,11 @@ fn destructure(
                 }
 
                 // ty will just be a normal expression, not a type
-                let e = expression(ty, file_no, contract_no, ns, symtable, false)?;
+                let e = expression(ty, file_no, contract_no, ns, symtable, false, diagnostics)?;
 
                 match &e {
                     Expression::ConstantVariable(_, _, Some(contract_no), var_no) => {
-                        ns.diagnostics.push(Diagnostic::error(
+                        diagnostics.push(Diagnostic::error(
                             *loc,
                             format!(
                                 "cannot assign to constant ‘{}’",
@@ -1040,7 +1157,7 @@ fn destructure(
                         return Err(());
                     }
                     Expression::ConstantVariable(_, _, None, var_no) => {
-                        ns.diagnostics.push(Diagnostic::error(
+                        diagnostics.push(Diagnostic::error(
                             *loc,
                             format!("cannot assign to constant ‘{}’", ns.constants[*var_no].name),
                         ));
@@ -1050,7 +1167,7 @@ fn destructure(
                     _ => match e.ty() {
                         Type::Ref(_) | Type::StorageRef(_) => (),
                         _ => {
-                            ns.diagnostics.push(Diagnostic::error(
+                            diagnostics.push(Diagnostic::error(
                                 *loc,
                                 "expression is not assignable".to_string(),
                             ));
@@ -1066,6 +1183,7 @@ fn destructure(
                     e.ty().deref_any(),
                     true,
                     ns,
+                    diagnostics,
                 )?;
 
                 fields.push(DestructureField::Expression(e));
@@ -1076,7 +1194,8 @@ fn destructure(
                 storage,
                 name: Some(name),
             }) => {
-                let (ty, ty_loc) = resolve_var_decl_ty(&ty, &storage, file_no, contract_no, ns)?;
+                let (ty, ty_loc) =
+                    resolve_var_decl_ty(&ty, &storage, file_no, contract_no, ns, diagnostics)?;
 
                 // here we only CHECK if we can cast the type
                 let _ = cast(
@@ -1085,6 +1204,7 @@ fn destructure(
                     ty.deref_any(),
                     true,
                     ns,
+                    diagnostics,
                 )?;
 
                 if let Some(pos) = symtable.add(&name, ty.clone(), ns) {
@@ -1116,13 +1236,14 @@ fn resolve_var_decl_ty(
     file_no: usize,
     contract_no: Option<usize>,
     ns: &mut Namespace,
+    diagnostics: &mut Vec<Diagnostic>,
 ) -> Result<(Type, pt::Loc), ()> {
     let mut loc_ty = ty.loc();
-    let mut var_ty = ns.resolve_type(file_no, contract_no, false, &ty)?;
+    let mut var_ty = ns.resolve_type(file_no, contract_no, false, &ty, diagnostics)?;
 
     if let Some(storage) = storage {
         if !var_ty.can_have_data_location() {
-            ns.diagnostics.push(Diagnostic::error(
+            diagnostics.push(Diagnostic::error(
                 *storage.loc(),
                 format!(
                     "data location ‘{}’ only allowed for array, struct or mapping type",
@@ -1142,7 +1263,7 @@ fn resolve_var_decl_ty(
     }
 
     if var_ty.contains_mapping(ns) && !var_ty.is_contract_storage() {
-        ns.diagnostics.push(Diagnostic::error(
+        diagnostics.push(Diagnostic::error(
             ty.loc(),
             "mapping only allowed in storage".to_string(),
         ));
@@ -1150,7 +1271,7 @@ fn resolve_var_decl_ty(
     }
 
     if !var_ty.is_contract_storage() && var_ty.size_of(ns) > BigInt::from(1024 * 1024) {
-        ns.diagnostics.push(Diagnostic::error(
+        diagnostics.push(Diagnostic::error(
             ty.loc(),
             "type to large to fit into memory".to_string(),
         ));
@@ -1169,13 +1290,14 @@ fn return_with_values(
     function_no: usize,
     symtable: &mut Symtable,
     ns: &mut Namespace,
+    diagnostics: &mut Vec<Diagnostic>,
 ) -> Result<Vec<Expression>, ()> {
-    let returns = parameter_list_to_expr_list(returns, ns)?;
+    let returns = parameter_list_to_expr_list(returns, diagnostics)?;
 
     let no_returns = ns.functions[function_no].returns.len();
 
     if no_returns > 0 && returns.is_empty() {
-        ns.diagnostics.push(Diagnostic::error(
+        diagnostics.push(Diagnostic::error(
             *loc,
             format!(
                 "missing return value, {} return values expected",
@@ -1186,7 +1308,7 @@ fn return_with_values(
     }
 
     if no_returns == 0 && !returns.is_empty() {
-        ns.diagnostics.push(Diagnostic::error(
+        diagnostics.push(Diagnostic::error(
             *loc,
             "function has no return values".to_string(),
         ));
@@ -1194,7 +1316,7 @@ fn return_with_values(
     }
 
     if no_returns != returns.len() {
-        ns.diagnostics.push(Diagnostic::error(
+        diagnostics.push(Diagnostic::error(
             *loc,
             format!(
                 "incorrect number of return values, expected {} but got {}",
@@ -1208,7 +1330,7 @@ fn return_with_values(
     let mut exprs = Vec::new();
 
     for (i, r) in returns.iter().enumerate() {
-        let e = expression(r, file_no, contract_no, ns, symtable, false)?;
+        let e = expression(r, file_no, contract_no, ns, symtable, false, diagnostics)?;
 
         exprs.push(cast(
             &r.loc(),
@@ -1216,6 +1338,7 @@ fn return_with_values(
             &ns.functions[function_no].returns[i].ty.clone(),
             true,
             ns,
+            diagnostics,
         )?);
     }
 
@@ -1226,7 +1349,7 @@ fn return_with_values(
 /// simple expression list.
 pub fn parameter_list_to_expr_list<'a>(
     e: &'a pt::Expression,
-    ns: &mut Namespace,
+    diagnostics: &mut Vec<Diagnostic>,
 ) -> Result<Vec<&'a pt::Expression>, ()> {
     if let pt::Expression::List(_, v) = &e {
         let mut list = Vec::new();
@@ -1235,14 +1358,13 @@ pub fn parameter_list_to_expr_list<'a>(
         for e in v {
             match &e.1 {
                 None => {
-                    ns.diagnostics
-                        .push(Diagnostic::error(e.0, "stray comma".to_string()));
+                    diagnostics.push(Diagnostic::error(e.0, "stray comma".to_string()));
                     broken = true;
                 }
                 Some(pt::Parameter {
                     name: Some(name), ..
                 }) => {
-                    ns.diagnostics.push(Diagnostic::error(
+                    diagnostics.push(Diagnostic::error(
                         name.loc,
                         "single value expected".to_string(),
                     ));
@@ -1252,7 +1374,7 @@ pub fn parameter_list_to_expr_list<'a>(
                     storage: Some(storage),
                     ..
                 }) => {
-                    ns.diagnostics.push(Diagnostic::error(
+                    diagnostics.push(Diagnostic::error(
                         *storage.loc(),
                         "storage specified not permitted here".to_string(),
                     ));
@@ -1276,6 +1398,7 @@ pub fn parameter_list_to_expr_list<'a>(
 
 /// Parse try catch
 #[allow(clippy::type_complexity)]
+#[allow(clippy::too_many_arguments)]
 fn try_catch(
     loc: &pt::Loc,
     expr: &pt::Expression,
@@ -1288,13 +1411,14 @@ fn try_catch(
     symtable: &mut Symtable,
     loops: &mut LoopScopes,
     ns: &mut Namespace,
+    diagnostics: &mut Vec<Diagnostic>,
 ) -> Result<(Statement, bool), ()> {
     let mut expr = expr;
     let mut ok = None;
 
     while let pt::Expression::FunctionCallBlock(_, e, block) = expr {
         if ok.is_some() {
-            ns.diagnostics.push(Diagnostic::error(
+            diagnostics.push(Diagnostic::error(
                 block.loc(),
                 "unexpected code block".to_string(),
             ));
@@ -1307,12 +1431,26 @@ fn try_catch(
     }
 
     let fcall = match expr {
-        pt::Expression::FunctionCall(loc, ty, args) => {
-            function_call_expr(loc, ty, args, file_no, contract_no, ns, symtable)?
-        }
-        pt::Expression::NamedFunctionCall(loc, ty, args) => {
-            named_function_call_expr(loc, ty, args, file_no, contract_no, ns, symtable)?
-        }
+        pt::Expression::FunctionCall(loc, ty, args) => function_call_expr(
+            loc,
+            ty,
+            args,
+            file_no,
+            contract_no,
+            ns,
+            symtable,
+            diagnostics,
+        )?,
+        pt::Expression::NamedFunctionCall(loc, ty, args) => named_function_call_expr(
+            loc,
+            ty,
+            args,
+            file_no,
+            contract_no,
+            ns,
+            symtable,
+            diagnostics,
+        )?,
         pt::Expression::New(loc, call) => {
             let mut call = call.as_ref();
 
@@ -1331,17 +1469,31 @@ fn try_catch(
             }
 
             match call {
-                pt::Expression::FunctionCall(_, ty, args) => {
-                    new(loc, ty, args, file_no, contract_no, ns, symtable)?
-                }
-                pt::Expression::NamedFunctionCall(_, ty, args) => {
-                    constructor_named_args(loc, ty, args, file_no, contract_no, ns, symtable)?
-                }
+                pt::Expression::FunctionCall(_, ty, args) => new(
+                    loc,
+                    ty,
+                    args,
+                    file_no,
+                    contract_no,
+                    ns,
+                    symtable,
+                    diagnostics,
+                )?,
+                pt::Expression::NamedFunctionCall(_, ty, args) => constructor_named_args(
+                    loc,
+                    ty,
+                    args,
+                    file_no,
+                    contract_no,
+                    ns,
+                    symtable,
+                    diagnostics,
+                )?,
                 _ => unreachable!(),
             }
         }
         _ => {
-            ns.diagnostics.push(Diagnostic::error(
+            diagnostics.push(Diagnostic::error(
                 expr.loc(),
                 "try only supports external calls or constructor calls".to_string(),
             ));
@@ -1353,7 +1505,7 @@ fn try_catch(
 
     if let Some((rets, block)) = returns_and_ok {
         if ok.is_some() {
-            ns.diagnostics.push(Diagnostic::error(
+            diagnostics.push(Diagnostic::error(
                 block.loc(),
                 "unexpected code block".to_string(),
             ));
@@ -1371,7 +1523,7 @@ fn try_catch(
             // position after the expression
             let pos = expr.loc().1;
 
-            ns.diagnostics.push(Diagnostic::error(
+            diagnostics.push(Diagnostic::error(
                 pt::Loc(file_no, pos, pos),
                 "code block missing for no catch".to_string(),
             ));
@@ -1393,7 +1545,7 @@ fn try_catch(
             }
 
             if returns.len() != func_returns.len() {
-                ns.diagnostics.push(Diagnostic::error(
+                diagnostics.push(Diagnostic::error(
                     expr.loc(),
                     format!(
                         "try returns list has {} entries while function returns {} values",
@@ -1410,7 +1562,7 @@ fn try_catch(
             0 => Vec::new(),
             1 => vec![Type::Contract(*contract_no)],
             _ => {
-                ns.diagnostics.push(Diagnostic::error(
+                diagnostics.push(Diagnostic::error(
                     expr.loc(),
                     format!(
                         "constructor returns single contract, not {} values",
@@ -1421,7 +1573,7 @@ fn try_catch(
             }
         },
         _ => {
-            ns.diagnostics.push(Diagnostic::error(
+            diagnostics.push(Diagnostic::error(
                 expr.loc(),
                 "try only supports external calls or constructor calls".to_string(),
             ));
@@ -1441,10 +1593,10 @@ fn try_catch(
                 ty, storage, name, ..
             }) => {
                 let (ret_ty, ty_loc) =
-                    resolve_var_decl_ty(&ty, &storage, file_no, contract_no, ns)?;
+                    resolve_var_decl_ty(&ty, &storage, file_no, contract_no, ns, diagnostics)?;
 
                 if arg_ty != ret_ty {
-                    ns.diagnostics.push(Diagnostic::error(
+                    diagnostics.push(Diagnostic::error(
                         ty.loc(),
                         format!(
                             "type ‘{}’ does not match return value of function ‘{}’",
@@ -1485,7 +1637,7 @@ fn try_catch(
                 }
             }
             None => {
-                ns.diagnostics.push(Diagnostic::error(
+                diagnostics.push(Diagnostic::error(
                     param.0,
                     "missing return type".to_string(),
                 ));
@@ -1509,6 +1661,7 @@ fn try_catch(
         symtable,
         loops,
         ns,
+        diagnostics,
     )?;
 
     symtable.leave_scope();
@@ -1531,6 +1684,7 @@ fn try_catch(
             file_no,
             contract_no,
             ns,
+            diagnostics,
         )?;
 
         if error_ty != Type::String {
@@ -1575,6 +1729,7 @@ fn try_catch(
             symtable,
             loops,
             ns,
+            diagnostics,
         )?;
 
         finally_reachable &= reachable;
@@ -1592,10 +1747,11 @@ fn try_catch(
         file_no,
         contract_no,
         ns,
+        diagnostics,
     )?;
 
     if catch_ty != Type::DynamicBytes {
-        ns.diagnostics.push(Diagnostic::error(
+        diagnostics.push(Diagnostic::error(
             catch_stmt.0.ty.loc(),
             format!(
                 "catch can only take ‘bytes memory’, not ‘{}’",
@@ -1636,6 +1792,7 @@ fn try_catch(
         symtable,
         loops,
         ns,
+        diagnostics,
     )?;
 
     finally_reachable &= reachable;

+ 8 - 2
src/sema/types.rs

@@ -306,9 +306,12 @@ pub fn struct_decl(
     let mut fields: Vec<Parameter> = Vec::new();
 
     for field in &def.fields {
-        let ty = match ns.resolve_type(file_no, contract_no, false, &field.ty) {
+        let mut diagnostics = Vec::new();
+
+        let ty = match ns.resolve_type(file_no, contract_no, false, &field.ty, &mut diagnostics) {
             Ok(s) => s,
             Err(()) => {
+                ns.diagnostics.extend(diagnostics);
                 valid = false;
                 continue;
             }
@@ -396,9 +399,12 @@ pub fn event_decl(
     let mut indexed_fields = 0;
 
     for field in &def.fields {
-        let ty = match ns.resolve_type(file_no, contract_no, false, &field.ty) {
+        let mut diagnostics = Vec::new();
+
+        let ty = match ns.resolve_type(file_no, contract_no, false, &field.ty, &mut diagnostics) {
             Ok(s) => s,
             Err(()) => {
+                ns.diagnostics.extend(diagnostics);
                 valid = false;
                 continue;
             }

+ 16 - 4
src/sema/variables.rs

@@ -84,9 +84,12 @@ pub fn var_decl(
         }
     }
 
-    let ty = match ns.resolve_type(file_no, contract_no, false, &ty) {
+    let mut diagnostics = Vec::new();
+
+    let ty = match ns.resolve_type(file_no, contract_no, false, &ty, &mut diagnostics) {
         Ok(s) => s,
         Err(()) => {
+            ns.diagnostics.extend(diagnostics);
             return None;
         }
     };
@@ -169,6 +172,8 @@ pub fn var_decl(
     }
 
     let initializer = if let Some(initializer) = &s.initializer {
+        let mut diagnostics = Vec::new();
+
         let res = match expression(
             &initializer,
             file_no,
@@ -176,15 +181,22 @@ pub fn var_decl(
             ns,
             &symtable,
             is_constant,
+            &mut diagnostics,
         ) {
             Ok(res) => res,
-            Err(()) => return None,
+            Err(()) => {
+                ns.diagnostics.extend(diagnostics);
+                return None;
+            }
         };
 
         // implicitly conversion to correct ty
-        let res = match cast(&s.loc, res, &ty, true, ns) {
+        let res = match cast(&s.loc, res, &ty, true, ns, &mut diagnostics) {
             Ok(res) => res,
-            Err(_) => return None,
+            Err(_) => {
+                ns.diagnostics.extend(diagnostics);
+                return None;
+            }
         };
 
         Some(res)

Неке датотеке нису приказане због велике количине промена