소스 검색

Improve overloaded function call diagnostics (#1592)

From Solidity 0.6 onwards, when an overloaded function/event resolves to
multiple candidates, this is an error. In earlier versions, the first
result is used. So, use the `pragma solidity` version to decide whether
to error or not.

Fixes: https://github.com/hyperledger/solang/issues/1534
Fixes: https://github.com/hyperledger/solang/issues/707

Signed-off-by: Sean Young <sean@mess.org>
Sean Young 1 년 전
부모
커밋
68b59a4111
50개의 변경된 파일2058개의 추가작업 그리고 926개의 파일을 삭제
  1. 10 10
      docs/examples/base_contract_function_call.sol
  2. 11 11
      docs/examples/contract_multiple_inheritance.sol
  3. 10 12
      docs/examples/function_overloading.sol
  4. 0 1
      solang-parser/src/lib.rs
  5. 1 1
      src/abi/tests.rs
  6. 4 4
      src/sema/ast.rs
  7. 76 110
      src/sema/builtin.rs
  8. 6 20
      src/sema/diagnostics.rs
  9. 2 29
      src/sema/expression/arithmetic.rs
  10. 195 122
      src/sema/expression/constructor.rs
  11. 436 269
      src/sema/expression/function_call.rs
  12. 2 3
      src/sema/expression/literals.rs
  13. 2 0
      src/sema/expression/mod.rs
  14. 2 134
      src/sema/mod.rs
  15. 276 0
      src/sema/pragma.rs
  16. 263 121
      src/sema/statements.rs
  17. 17 0
      tests/contract_testcases/evm/builtin_overloaded.sol
  18. 15 0
      tests/contract_testcases/evm/contract-shadows-global.sol
  19. 47 0
      tests/contract_testcases/evm/dup1.sol
  20. 17 0
      tests/contract_testcases/evm/ev.sol
  21. 25 0
      tests/contract_testcases/evm/events_v0.5.sol
  22. 25 0
      tests/contract_testcases/evm/events_v0.6.sol
  23. 22 0
      tests/contract_testcases/evm/issue1534.sol
  24. 112 0
      tests/contract_testcases/evm/overloaded.sol
  25. 45 29
      tests/contract_testcases/evm/pragmas.dot
  26. 9 6
      tests/contract_testcases/evm/pragmas.sol
  27. 30 0
      tests/contract_testcases/evm/super-resolves-to-multiple.sol
  28. 3 2
      tests/contract_testcases/evm/unresolved_constants.sol
  29. 2 0
      tests/contract_testcases/polkadot/contracts/external_call_02.sol
  30. 2 1
      tests/contract_testcases/polkadot/contracts/external_call_03.sol
  31. 59 0
      tests/contract_testcases/polkadot/contracts/overloaded_constructor.sol
  32. 53 0
      tests/contract_testcases/polkadot/contracts/overloaded_constructor2.sol
  33. 2 2
      tests/contract_testcases/polkadot/doccomments_everywhere.sol
  34. 1 0
      tests/contract_testcases/polkadot/events/emit_02.sol
  35. 0 2
      tests/contract_testcases/polkadot/events/emit_04.sol
  36. 2 0
      tests/contract_testcases/polkadot/events/signatures.sol
  37. 2 0
      tests/contract_testcases/polkadot/events/signatures_01.sol
  38. 13 8
      tests/contract_testcases/polkadot/events/signatures_02.sol
  39. 4 1
      tests/contract_testcases/polkadot/events/signatures_03.sol
  40. 3 0
      tests/contract_testcases/polkadot/inheritance/call_inherited_function_02.sol
  41. 26 0
      tests/contract_testcases/solana/dup1.sol
  42. 6 0
      tests/contract_testcases/solana/event.sol
  43. 2 2
      tests/contract_testcases/solana/now.sol
  44. 3 1
      tests/evm.rs
  45. 3 0
      tests/evm_tests/mod.rs
  46. 192 0
      tests/evm_tests/pragma.rs
  47. 0 1
      tests/solana.rs
  48. 10 12
      tests/unused_variable_detection.rs
  49. 8 8
      vscode/src/test/suite/extension.test.ts
  50. 2 4
      vscode/src/testFixture/four.sol

+ 10 - 10
docs/examples/base_contract_function_call.sol

@@ -1,13 +1,3 @@
-contract b is a {
-    function baz() public pure returns (uint64) {
-        return foo();
-    }
-
-    function foo() internal pure override returns (uint64) {
-        return 2;
-    }
-}
-
 abstract contract a {
     function foo() internal virtual returns (uint64) {
         return 1;
@@ -27,3 +17,13 @@ abstract contract a {
         return a.foo();
     }
 }
+
+contract b is a {
+    function baz() public pure returns (uint64) {
+        return foo();
+    }
+
+    function foo() internal pure override returns (uint64) {
+        return 2;
+    }
+}

+ 11 - 11
docs/examples/contract_multiple_inheritance.sol

@@ -1,14 +1,3 @@
-contract a is b1, b2 {
-    function baz() public returns (uint64) {
-        // this will return 100
-        return super.foo();
-    }
-
-    function foo() internal override(b1, b2) returns (uint64) {
-        return 2;
-    }
-}
-
 abstract contract b1 {
     function foo() internal virtual returns (uint64) {
         return 100;
@@ -20,3 +9,14 @@ abstract contract b2 {
         return 200;
     }
 }
+
+contract a is b1, b2 {
+    function baz() public returns (uint64) {
+        // this will return 100
+        return super.foo();
+    }
+
+    function foo() internal override(b1, b2) returns (uint64) {
+        return 2;
+    }
+}

+ 10 - 12
docs/examples/function_overloading.sol

@@ -1,23 +1,21 @@
 contract shape {
     int64 bar;
 
-    function abs(int256 val) public returns (int256) {
-        if (val >= 0) {
-            return val;
-        } else {
-            return -val;
-        }
+    function max(int64 val1, int64 val2, int64 val3) public pure returns (int64) {
+	int64 val = max(val1, val2);
+
+	return max(val, val3);
     }
 
-    function abs(int64 val) public returns (int64) {
-        if (val >= 0) {
-            return val;
+    function max(int64 val1, int64 val2) public pure returns (int64) {
+        if (val1 >= val2) {
+            return val2;
         } else {
-            return -val;
+            return val1;
         }
     }
 
-    function foo(int64 x) public {
-        bar = int64(abs(x));
+    function foo(int64 x, int64 y) public {
+        bar = max(bar, x, y);
     }
 }

+ 0 - 1
solang-parser/src/lib.rs

@@ -23,7 +23,6 @@ mod tests;
     clippy::needless_lifetimes,
     clippy::type_complexity,
     clippy::ptr_arg,
-    clippy::redundant_clone,
     clippy::just_underscores_and_digits
 )]
 mod solidity {

+ 1 - 1
src/abi/tests.rs

@@ -169,7 +169,7 @@ fn instructions_and_types() {
     }
 
     modifier doSomething() {
-        require(msg.value >= 50);
+        require(block.number >= 50);
         _;
     }
 

+ 4 - 4
src/sema/ast.rs

@@ -742,11 +742,11 @@ pub enum VersionReq {
     },
 }
 
-#[derive(Debug)]
+#[derive(Debug, Clone, PartialEq)]
 pub struct Version {
-    pub major: u64,
-    pub minor: Option<u64>,
-    pub patch: Option<u64>,
+    pub major: u32,
+    pub minor: Option<u32>,
+    pub patch: Option<u32>,
 }
 
 impl fmt::Display for Version {

+ 76 - 110
src/sema/builtin.rs

@@ -9,7 +9,7 @@ use super::eval::eval_const_number;
 use super::expression::{ExprContext, ResolveTo};
 use super::symtable::Symtable;
 use crate::sema::ast::{RetrieveType, Tag, UserTypeDecl};
-use crate::sema::expression::resolve_expression::expression;
+use crate::sema::expression::{function_call::evaluate_argument, resolve_expression::expression};
 use crate::sema::namespace::ResolveTypeContext;
 use crate::Target;
 use num_bigint::BigInt;
@@ -952,24 +952,30 @@ pub(super) fn resolve_call(
         .iter()
         .filter(|p| p.name == id && p.namespace == namespace && p.method.is_empty())
         .collect::<Vec<&Prototype>>();
-    let mut errors: Diagnostics = Diagnostics::default();
+
+    // try to resolve the arguments, give up if there are any errors
+    if args.iter().fold(false, |acc, arg| {
+        acc | expression(arg, context, ns, symtable, diagnostics, ResolveTo::Unknown).is_err()
+    }) {
+        return Err(());
+    }
+
+    let mut call_diagnostics = Diagnostics::default();
 
     for func in &funcs {
-        let mut matches = true;
+        let mut candidate_diagnostics = Diagnostics::default();
+        let mut cast_args = Vec::new();
 
         if context.constant && !func.constant {
-            errors.push(Diagnostic::cast_error(
+            candidate_diagnostics.push(Diagnostic::cast_error(
                 *loc,
                 format!(
                     "cannot call function '{}' in constant expression",
                     func.name
                 ),
             ));
-            matches = false;
-        }
-
-        if func.params.len() != args.len() {
-            errors.push(Diagnostic::cast_error(
+        } else if func.params.len() != args.len() {
+            candidate_diagnostics.push(Diagnostic::cast_error(
                 *loc,
                 format!(
                     "builtin function '{}' expects {} arguments, {} provided",
@@ -978,44 +984,31 @@ pub(super) fn resolve_call(
                     args.len()
                 ),
             ));
-            matches = false;
-        }
-
-        let mut cast_args = Vec::new();
-
-        // check if arguments can be implicitly casted
-        for (i, arg) in args.iter().enumerate() {
-            let ty = func.params.get(i);
-
-            let arg = match expression(
-                arg,
-                context,
-                ns,
-                symtable,
-                &mut errors,
-                ty.map(ResolveTo::Type).unwrap_or(ResolveTo::Unknown),
-            ) {
-                Ok(e) => e,
-                Err(()) => {
-                    matches = false;
-                    continue;
-                }
-            };
+        } else {
+            // check if arguments can be implicitly casted
+            for (i, arg) in args.iter().enumerate() {
+                let ty = func.params[i].clone();
 
-            if let Some(ty) = ty {
-                match arg.cast(&arg.loc(), ty, true, ns, &mut errors) {
-                    Ok(expr) => cast_args.push(expr),
-                    Err(()) => {
-                        matches = false;
-                    }
-                }
+                evaluate_argument(
+                    arg,
+                    context,
+                    ns,
+                    symtable,
+                    &ty,
+                    &mut candidate_diagnostics,
+                    &mut cast_args,
+                );
             }
         }
 
-        if !matches {
-            if funcs.len() > 1 && diagnostics.extend_non_casting(&errors) {
-                return Err(());
+        if candidate_diagnostics.any_errors() {
+            if funcs.len() != 1 {
+                candidate_diagnostics.push(Diagnostic::error(
+                    *loc,
+                    "cannot find overloaded builtin which matches signature".into(),
+                ));
             }
+            call_diagnostics.extend(candidate_diagnostics);
         } else {
             // tx.gasprice(1) is a bad idea, just like tx.gasprice. Warn about this
             if ns.target.is_polkadot() && func.builtin == Builtin::Gasprice {
@@ -1031,6 +1024,8 @@ pub(super) fn resolve_call(
                 }
             }
 
+            diagnostics.extend(candidate_diagnostics);
+
             return Ok(Expression::Builtin {
                 loc: *loc,
                 tys: func.ret.to_vec(),
@@ -1040,14 +1035,7 @@ pub(super) fn resolve_call(
         }
     }
 
-    if funcs.len() != 1 {
-        diagnostics.push(Diagnostic::error(
-            *loc,
-            "cannot find overloaded function which matches signature".to_string(),
-        ));
-    } else {
-        diagnostics.extend(errors);
-    }
+    diagnostics.extend(call_diagnostics);
 
     Err(())
 }
@@ -1413,24 +1401,30 @@ pub(super) fn resolve_method_call(
         .iter()
         .filter(|func| func.name == id.name && func.method.contains(deref_ty))
         .collect();
-    let mut errors = Diagnostics::default();
+
+    // try to resolve the arguments, give up if there are any errors
+    if args.iter().fold(false, |acc, arg| {
+        acc | expression(arg, context, ns, symtable, diagnostics, ResolveTo::Unknown).is_err()
+    }) {
+        return Err(());
+    }
+
+    let mut call_diagnostics = Diagnostics::default();
 
     for func in &funcs {
-        let mut matches = true;
+        let mut candidate_diagnostics = Diagnostics::default();
+        let mut cast_args = Vec::new();
 
         if context.constant && !func.constant {
-            diagnostics.push(Diagnostic::cast_error(
+            candidate_diagnostics.push(Diagnostic::cast_error(
                 id.loc,
                 format!(
                     "cannot call function '{}' in constant expression",
                     func.name
                 ),
             ));
-            matches = false;
-        }
-
-        if func.params.len() != args.len() {
-            errors.push(Diagnostic::cast_error(
+        } else if func.params.len() != args.len() {
+            candidate_diagnostics.push(Diagnostic::cast_error(
                 id.loc,
                 format!(
                     "builtin function '{}' expects {} arguments, {} provided",
@@ -1439,47 +1433,25 @@ pub(super) fn resolve_method_call(
                     args.len()
                 ),
             ));
-            matches = false;
-        }
-
-        let mut cast_args = Vec::new();
-
-        // check if arguments can be implicitly casted
-        for (i, arg) in args.iter().enumerate() {
-            // we may have arguments that parameters
-            let ty = func.params.get(i);
-
-            let arg = match expression(
-                arg,
-                context,
-                ns,
-                symtable,
-                &mut errors,
-                ty.map(ResolveTo::Type).unwrap_or(ResolveTo::Unknown),
-            ) {
-                Ok(e) => e,
-                Err(()) => {
-                    matches = false;
-                    continue;
-                }
-            };
+        } else {
+            // check if arguments can be implicitly casted
+            for (i, arg) in args.iter().enumerate() {
+                // we may have arguments that parameters
+                let ty = func.params[i].clone();
 
-            if let Some(ty) = ty {
-                match arg.cast(&arg.loc(), ty, true, ns, &mut errors) {
-                    Ok(expr) => cast_args.push(expr),
-                    Err(()) => {
-                        matches = false;
-                        continue;
-                    }
-                }
+                evaluate_argument(
+                    arg,
+                    context,
+                    ns,
+                    symtable,
+                    &ty,
+                    &mut candidate_diagnostics,
+                    &mut cast_args,
+                );
             }
         }
 
-        if !matches {
-            if funcs.len() > 1 && diagnostics.extend_non_casting(&errors) {
-                return Err(());
-            }
-        } else {
+        if !candidate_diagnostics.any_errors() {
             cast_args.insert(
                 0,
                 expr.cast(&id.loc, deref_ty, true, ns, diagnostics).unwrap(),
@@ -1491,6 +1463,8 @@ pub(super) fn resolve_method_call(
                 func.ret.to_vec()
             };
 
+            diagnostics.extend(candidate_diagnostics);
+
             return Ok(Some(Expression::Builtin {
                 loc: id.loc,
                 tys: returns,
@@ -1498,23 +1472,15 @@ pub(super) fn resolve_method_call(
                 args: cast_args,
             }));
         }
-    }
 
-    match funcs.len() {
-        0 => Ok(None),
-        1 => {
-            diagnostics.extend(errors);
-
-            Err(())
-        }
-        _ => {
-            diagnostics.push(Diagnostic::error(
-                id.loc,
-                "cannot find overloaded function which matches signature".to_string(),
-            ));
+        call_diagnostics.extend(candidate_diagnostics);
+    }
 
-            Err(())
-        }
+    if funcs.is_empty() {
+        Ok(None)
+    } else {
+        diagnostics.extend(call_diagnostics);
+        Err(())
     }
 }
 

+ 6 - 20
src/sema/diagnostics.rs

@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: Apache-2.0
 
-use super::ast::{Diagnostic, ErrorType, Level, Namespace};
+use super::ast::{Diagnostic, Level, Namespace};
 use crate::file_resolver::FileResolver;
 use crate::standard_json::{LocJson, OutputJson};
 use codespan_reporting::{diagnostic, files, term};
@@ -8,7 +8,7 @@ use itertools::Itertools;
 use solang_parser::pt::Loc;
 use std::{
     collections::HashMap,
-    slice::Iter,
+    slice::{Iter, IterMut},
     {io, sync::Arc},
 };
 
@@ -31,6 +31,10 @@ impl Diagnostics {
         self.contents.iter()
     }
 
+    pub fn iter_mut(&mut self) -> IterMut<Diagnostic> {
+        self.contents.iter_mut()
+    }
+
     pub fn is_empty(&self) -> bool {
         self.contents.is_empty()
     }
@@ -47,24 +51,6 @@ impl Diagnostics {
         self.contents.extend(diagnostics.contents);
     }
 
-    /// Filter out all the diagnostics which are not the result of casting problems
-    pub fn extend_non_casting(&mut self, other: &Diagnostics) -> bool {
-        let others: Vec<_> = other
-            .iter()
-            .filter(|diag| diag.ty != ErrorType::CastError)
-            .cloned()
-            .collect();
-        if others.is_empty() {
-            false
-        } else {
-            if !self.has_error {
-                self.has_error = others.iter().any(|m| m.level == Level::Error);
-            }
-            self.contents.extend(others);
-            true
-        }
-    }
-
     pub fn append(&mut self, diagnostics: &mut Vec<Diagnostic>) {
         if !self.has_error {
             self.has_error = diagnostics.iter().any(|m| m.level == Level::Error);

+ 2 - 29
src/sema/expression/arithmetic.rs

@@ -732,8 +732,8 @@ pub(super) fn addition(
     diagnostics: &mut Diagnostics,
     resolve_to: ResolveTo,
 ) -> Result<Expression, ()> {
-    let mut left = expression(l, context, ns, symtable, diagnostics, resolve_to)?;
-    let mut right = expression(r, context, ns, symtable, diagnostics, resolve_to)?;
+    let left = expression(l, context, ns, symtable, diagnostics, resolve_to)?;
+    let right = expression(r, context, ns, symtable, diagnostics, resolve_to)?;
 
     check_var_usage_expression(ns, &left, &right, symtable);
 
@@ -799,33 +799,6 @@ pub(super) fn addition(
         };
     }
 
-    // If we don't know what type the result is going to be
-    if resolve_to == ResolveTo::Unknown {
-        let bits = std::cmp::min(256, ty.bits(ns) * 2);
-        let resolve_to = if ty.is_signed_int(ns) {
-            Type::Int(bits)
-        } else {
-            Type::Uint(bits)
-        };
-
-        left = expression(
-            l,
-            context,
-            ns,
-            symtable,
-            diagnostics,
-            ResolveTo::Type(&resolve_to),
-        )?;
-        right = expression(
-            r,
-            context,
-            ns,
-            symtable,
-            diagnostics,
-            ResolveTo::Type(&resolve_to),
-        )?;
-    }
-
     Ok(Expression::Add {
         loc: *loc,
         ty: ty.clone(),

+ 195 - 122
src/sema/expression/constructor.rs

@@ -1,8 +1,10 @@
 // SPDX-License-Identifier: Apache-2.0
 
-use crate::sema::ast::{ArrayLength, CallArgs, Expression, Namespace, RetrieveType, Type};
+use crate::sema::ast::{ArrayLength, CallArgs, Expression, Namespace, Note, RetrieveType, Type};
 use crate::sema::diagnostics::Diagnostics;
-use crate::sema::expression::function_call::{collect_call_args, parse_call_args};
+use crate::sema::expression::function_call::{
+    collect_call_args, evaluate_argument, parse_call_args,
+};
 use crate::sema::expression::resolve_expression::expression;
 use crate::sema::expression::{ExprContext, ResolveTo};
 use crate::sema::namespace::ResolveTypeContext;
@@ -98,8 +100,6 @@ pub fn match_constructor_to_args(
     symtable: &mut Symtable,
     diagnostics: &mut Diagnostics,
 ) -> Result<(Option<usize>, Vec<Expression>), ()> {
-    let mut errors = Diagnostics::default();
-
     // constructor call
     let function_nos: Vec<usize> = ns.contracts[contract_no]
         .functions
@@ -108,13 +108,23 @@ pub fn match_constructor_to_args(
         .copied()
         .collect();
 
-    for function_no in &function_nos {
-        let mut matches = true;
+    // try to resolve the arguments, give up if there are any errors
+    if args.iter().fold(false, |acc, arg| {
+        acc | expression(arg, context, ns, symtable, diagnostics, ResolveTo::Unknown).is_err()
+    }) {
+        return Err(());
+    }
+
+    let mut call_diagnostics = Diagnostics::default();
+    let mut resolved_calls = Vec::new();
 
+    for function_no in &function_nos {
         let params_len = ns.functions[*function_no].params.len();
+        let mut candidate_diagnostics = Diagnostics::default();
+        let mut cast_args = Vec::new();
 
         if params_len != args.len() {
-            errors.push(Diagnostic::cast_error(
+            candidate_diagnostics.push(Diagnostic::cast_error(
                 *loc,
                 format!(
                     "constructor expects {} arguments, {} provided",
@@ -122,70 +132,85 @@ pub fn match_constructor_to_args(
                     args.len()
                 ),
             ));
-            matches = false;
+        } else {
+            // resolve arguments for this constructor
+            for (i, arg) in args.iter().enumerate() {
+                let ty = ns.functions[*function_no].params[i].ty.clone();
+
+                evaluate_argument(
+                    arg,
+                    context,
+                    ns,
+                    symtable,
+                    &ty,
+                    &mut candidate_diagnostics,
+                    &mut cast_args,
+                );
+            }
         }
 
-        let mut cast_args = Vec::new();
+        if candidate_diagnostics.any_errors() {
+            if function_nos.len() != 1 {
+                let func = &ns.functions[*function_no];
 
-        // resolve arguments for this constructor
-        for (i, arg) in args.iter().enumerate() {
-            let ty = ns.functions[*function_no]
-                .params
-                .get(i)
-                .map(|p| p.ty.clone());
+                candidate_diagnostics.iter_mut().for_each(|diagnostic| {
+                    diagnostic.notes.push(Note {
+                        loc: func.loc,
+                        message: "candidate constructor".into(),
+                    })
+                });
 
-            let arg = match expression(
-                arg,
-                context,
-                ns,
-                symtable,
-                &mut errors,
-                if let Some(ty) = &ty {
-                    ResolveTo::Type(ty)
-                } else {
-                    ResolveTo::Unknown
-                },
-            ) {
-                Ok(v) => v,
-                Err(()) => {
-                    matches = false;
-                    continue;
-                }
-            };
-
-            if let Some(ty) = &ty {
-                match arg.cast(&arg.loc(), ty, true, ns, &mut errors) {
-                    Ok(expr) => cast_args.push(expr),
-                    Err(()) => {
-                        matches = false;
-                    }
-                }
+                // will be de-duped
+                candidate_diagnostics.push(Diagnostic::error(
+                    *loc,
+                    "cannot find overloaded constructor which matches signature".into(),
+                ));
             }
+        } else {
+            resolved_calls.push((Some(*function_no), cast_args));
+            continue;
         }
 
-        if matches {
-            return Ok((Some(*function_no), cast_args));
-        } else if function_nos.len() > 1 && diagnostics.extend_non_casting(&errors) {
-            return Err(());
-        }
+        call_diagnostics.extend(candidate_diagnostics);
     }
 
-    match function_nos.len() {
-        0 if args.is_empty() => {
-            return Ok((None, Vec::new()));
+    match resolved_calls.len() {
+        0 if function_nos.is_empty() => {
+            if args.is_empty() {
+                Ok((None, Vec::new()))
+            } else {
+                diagnostics.push(Diagnostic::error(
+                    *loc,
+                    "default constructor does not take arguments".into(),
+                ));
+                Err(())
+            }
         }
-        0 | 1 => {
-            diagnostics.extend(errors);
+        0 => {
+            diagnostics.extend(call_diagnostics);
+
+            Err(())
         }
+        1 => Ok(resolved_calls.remove(0)),
         _ => {
-            diagnostics.push(Diagnostic::error(
+            diagnostics.push(Diagnostic::error_with_notes(
                 *loc,
-                "cannot find overloaded constructor which matches signature".to_string(),
+                "constructor can be resolved to multiple functions".into(),
+                resolved_calls
+                    .iter()
+                    .map(|(func_no, _)| {
+                        let func = &ns.functions[func_no.unwrap()];
+
+                        Note {
+                            loc: func.loc,
+                            message: "candidate constructor".into(),
+                        }
+                    })
+                    .collect(),
             ));
+            Err(())
         }
     }
-
-    Err(())
 }
 
 /// check if from creates to, recursively
@@ -291,20 +316,42 @@ pub fn constructor_named_args(
 
     let mut arguments: BTreeMap<&str, &pt::Expression> = BTreeMap::new();
 
-    for arg in args {
+    if args.iter().fold(false, |mut acc, arg| {
         if let Some(prev) = arguments.get(arg.name.name.as_str()) {
             diagnostics.push(Diagnostic::error_with_note(
-                *loc,
-                format!("duplicate argument name '{}'", arg.name.name),
+                arg.name.loc,
+                format!("duplicate argument with name '{}'", arg.name.name),
                 prev.loc(),
-                String::from("location of previous argument"),
+                "location of previous argument".into(),
             ));
-            return Err(());
+
+            let _ = expression(
+                &arg.expr,
+                context,
+                ns,
+                symtable,
+                diagnostics,
+                ResolveTo::Unknown,
+            );
+            acc = true;
+        } else {
+            acc |= expression(
+                &arg.expr,
+                context,
+                ns,
+                symtable,
+                diagnostics,
+                ResolveTo::Unknown,
+            )
+            .is_err()
         }
-        arguments.insert(&arg.name.name, &arg.expr);
-    }
 
-    let mut errors = Diagnostics::default();
+        arguments.insert(arg.name.name.as_str(), &arg.expr);
+
+        acc
+    }) {
+        return Err(());
+    }
 
     // constructor call
     let function_nos: Vec<usize> = ns.contracts[no]
@@ -314,17 +361,23 @@ pub fn constructor_named_args(
         .copied()
         .collect();
 
+    let mut call_diagnostics = Diagnostics::default();
+    let mut resolved_calls = Vec::new();
+
     // constructor call
     for function_no in &function_nos {
         let func = &ns.functions[*function_no];
         let params_len = func.params.len();
 
-        let mut matches = true;
+        let mut candidate_diagnostics = Diagnostics::default();
 
         let unnamed_params = func.params.iter().filter(|p| p.id.is_none()).count();
+        let func_loc = ns.functions[*function_no].loc_prototype;
+
+        let mut cast_args = Vec::new();
 
         if unnamed_params > 0 {
-            errors.push(Diagnostic::cast_error_with_note(
+            candidate_diagnostics.push(Diagnostic::cast_error_with_note(
                 *loc,
                 format!(
                     "constructor cannot be called with named arguments as {unnamed_params} of its parameters do not have names"
@@ -332,9 +385,8 @@ pub fn constructor_named_args(
                 func.loc_prototype,
                 format!("definition of {}", func.ty),
             ));
-            matches = false;
         } else if params_len != args.len() {
-            errors.push(Diagnostic::cast_error_with_note(
+            candidate_diagnostics.push(Diagnostic::cast_error_with_note(
                 *loc,
                 format!(
                     "constructor expects {} arguments, {} provided",
@@ -344,86 +396,107 @@ pub fn constructor_named_args(
                 func.loc_prototype,
                 "definition of constructor".to_owned(),
             ));
-            matches = false;
+        } else {
+            // check if arguments can be implicitly casted
+            for i in 0..params_len {
+                let param = ns.functions[*function_no].params[i].clone();
+
+                let arg = match arguments.get(param.name_as_str()) {
+                    Some(a) => a,
+                    None => {
+                        candidate_diagnostics.push(Diagnostic::cast_error_with_note(
+                            *loc,
+                            format!("missing argument '{}' to constructor", param.name_as_str()),
+                            func_loc,
+                            "definition of constructor".to_owned(),
+                        ));
+                        continue;
+                    }
+                };
+
+                evaluate_argument(
+                    arg,
+                    context,
+                    ns,
+                    symtable,
+                    &param.ty,
+                    &mut candidate_diagnostics,
+                    &mut cast_args,
+                );
+            }
         }
 
-        let mut cast_args = Vec::new();
+        if candidate_diagnostics.any_errors() {
+            if function_nos.len() != 1 {
+                let func = &ns.functions[*function_no];
 
-        let func_loc = ns.functions[*function_no].loc_prototype;
+                candidate_diagnostics.iter_mut().for_each(|diagnostic| {
+                    diagnostic.notes.push(Note {
+                        loc: func.loc,
+                        message: "candidate constructor".into(),
+                    })
+                });
 
-        // check if arguments can be implicitly casted
-        for i in 0..params_len {
-            let param = ns.functions[*function_no].params[i].clone();
-
-            let arg = match arguments.get(param.name_as_str()) {
-                Some(a) => a,
-                None => {
-                    matches = false;
-                    errors.push(Diagnostic::cast_error_with_note(
-                        *loc,
-                        format!("missing argument '{}' to constructor", param.name_as_str()),
-                        func_loc,
-                        "definition of constructor".to_owned(),
-                    ));
-                    break;
-                }
-            };
-
-            let arg = match expression(
-                arg,
-                context,
-                ns,
-                symtable,
-                &mut errors,
-                ResolveTo::Type(&param.ty),
-            ) {
-                Ok(e) => e,
-                Err(()) => {
-                    matches = false;
-                    continue;
-                }
-            };
-
-            match arg.cast(&arg.loc(), &param.ty, true, ns, &mut errors) {
-                Ok(expr) => cast_args.push(expr),
-                Err(()) => {
-                    matches = false;
-                }
+                // will be de-duped
+                candidate_diagnostics.push(Diagnostic::error(
+                    *loc,
+                    "cannot find overloaded constructor which matches signature".into(),
+                ));
             }
-        }
-
-        if matches {
-            return Ok(Expression::Constructor {
+        } else {
+            resolved_calls.push(Expression::Constructor {
                 loc: *loc,
                 contract_no: no,
                 constructor_no: Some(*function_no),
                 args: cast_args,
-                call_args,
+                call_args: call_args.clone(),
             });
-        } else if function_nos.len() > 1 && diagnostics.extend_non_casting(&errors) {
-            return Err(());
+            continue;
         }
+
+        call_diagnostics.extend(candidate_diagnostics);
     }
 
-    match function_nos.len() {
-        0 if args.is_empty() => Ok(Expression::Constructor {
+    match resolved_calls.len() {
+        0 if function_nos.is_empty() && args.is_empty() => Ok(Expression::Constructor {
             loc: *loc,
             contract_no: no,
             constructor_no: None,
             args: Vec::new(),
             call_args,
         }),
-        0 | 1 => {
-            diagnostics.extend(errors);
+        0 => {
+            diagnostics.extend(call_diagnostics);
+
+            if function_nos.is_empty() {
+                diagnostics.push(Diagnostic::error(
+                    *loc,
+                    "cannot find matching constructor".into(),
+                ));
+            }
 
             Err(())
         }
+        1 => Ok(resolved_calls.remove(0)),
         _ => {
-            diagnostics.push(Diagnostic::error(
+            diagnostics.push(Diagnostic::error_with_notes(
                 *loc,
-                "cannot find overloaded constructor which matches signature".to_string(),
+                "can be resolved to multiple constructors".into(),
+                resolved_calls
+                    .iter()
+                    .map(|expr| {
+                        let Expression::Constructor { constructor_no, .. } = expr else {
+                            unreachable!()
+                        };
+                        let func = &ns.functions[constructor_no.unwrap()];
+
+                        Note {
+                            loc: func.loc,
+                            message: "candidate constructor".into(),
+                        }
+                    })
+                    .collect(),
             ));
-
             Err(())
         }
     }

+ 436 - 269
src/sema/expression/function_call.rs

@@ -19,10 +19,10 @@ use crate::sema::unused_variable::check_function_call;
 use crate::sema::{builtin, using};
 use crate::Target;
 use num_bigint::{BigInt, Sign};
-use solang_parser::diagnostics::Diagnostic;
+use solang_parser::diagnostics::{Diagnostic, Note};
 use solang_parser::pt;
 use solang_parser::pt::{CodeLocation, Loc, Visibility};
-use std::collections::HashMap;
+use std::collections::{HashMap, HashSet};
 
 /// Resolve a function call via function type
 /// Function types do not have names so call cannot be using named parameters
@@ -38,6 +38,14 @@ pub(super) fn call_function_type(
     diagnostics: &mut Diagnostics,
     resolve_to: ResolveTo,
 ) -> Result<Expression, ()> {
+    if context.constant {
+        diagnostics.push(Diagnostic::error(
+            *loc,
+            "cannot call function in constant expression".to_string(),
+        ));
+        return Err(());
+    }
+
     let mut function = expression(expr, context, ns, symtable, diagnostics, ResolveTo::Unknown)?;
 
     let mut ty = function.ty();
@@ -190,14 +198,6 @@ pub fn available_functions(
 ) -> Vec<usize> {
     let mut list = Vec::new();
 
-    if global {
-        if let Some(Symbol::Function(v)) =
-            ns.function_symbols.get(&(file_no, None, name.to_owned()))
-        {
-            list.extend(v.iter().map(|(_, func_no)| *func_no));
-        }
-    }
-
     if let Some(contract_no) = contract_no {
         list.extend(
             ns.contracts[contract_no]
@@ -205,9 +205,19 @@ pub fn available_functions(
                 .keys()
                 .filter(|func_no| ns.functions[**func_no].id.name == name)
                 .filter_map(|func_no| {
-                    let is_abstract = ns.functions[*func_no].is_virtual
-                        && !ns.contracts[contract_no].is_concrete();
-                    if ns.functions[*func_no].has_body || is_abstract {
+                    let func = &ns.functions[*func_no];
+
+                    // For a virtual function, only the most-overriden is available
+                    if func.is_virtual
+                        && ns.contracts[contract_no].virtual_functions[&func.signature].last()
+                            != Some(func_no)
+                    {
+                        return None;
+                    }
+
+                    let is_abstract = func.is_virtual && !ns.contracts[contract_no].is_concrete();
+
+                    if func.has_body || is_abstract {
                         return Some(*func_no);
                     }
                     None
@@ -215,12 +225,22 @@ pub fn available_functions(
         );
     }
 
+    // global functions may shadowed by contract-defined functions
+    if list.is_empty() && global {
+        if let Some(Symbol::Function(v)) =
+            ns.function_symbols.get(&(file_no, None, name.to_owned()))
+        {
+            list.extend(v.iter().map(|(_, func_no)| *func_no));
+        }
+    }
+
     list
 }
 
 /// Create a list of functions that can be called via super
 pub fn available_super_functions(name: &str, contract_no: usize, ns: &Namespace) -> Vec<usize> {
     let mut list = Vec::new();
+    let mut signatures = HashSet::new();
 
     for base_contract_no in ns.contract_bases(contract_no).into_iter().rev() {
         if base_contract_no == contract_no {
@@ -235,7 +255,11 @@ pub fn available_super_functions(name: &str, contract_no: usize, ns: &Namespace)
                     let func = &ns.functions[*func_no];
 
                     if func.id.name == name && func.has_body {
-                        Some(*func_no)
+                        if func.is_virtual && !signatures.insert(&func.signature) {
+                            None
+                        } else {
+                            Some(*func_no)
+                        }
                     } else {
                         None
                     }
@@ -252,7 +276,7 @@ pub fn function_call_pos_args(
     id: &pt::IdentifierPath,
     func_ty: pt::FunctionTy,
     args: &[pt::Expression],
-    function_nos: Vec<usize>,
+    mut function_nos: Vec<usize>,
     virtual_call: bool,
     context: &mut ExprContext,
     ns: &mut Namespace,
@@ -260,9 +284,6 @@ pub fn function_call_pos_args(
     symtable: &mut Symtable,
     diagnostics: &mut Diagnostics,
 ) -> Result<Expression, ()> {
-    let mut name_matches = 0;
-    let mut errors = Diagnostics::default();
-
     if context.constant {
         diagnostics.push(Diagnostic::error(
             *loc,
@@ -271,51 +292,70 @@ pub fn function_call_pos_args(
         return Err(());
     }
 
+    // try to resolve the arguments, give up if there are any errors
+    if args.iter().fold(false, |acc, arg| {
+        acc | expression(arg, context, ns, symtable, diagnostics, ResolveTo::Unknown).is_err()
+    }) {
+        return Err(());
+    }
+
+    function_nos.retain(|function_no| ns.functions[*function_no].ty == func_ty);
+
+    let mut call_diagnostics = Diagnostics::default();
+    let mut resolved_calls = Vec::new();
+
     // Try to resolve as a function call
     for function_no in &function_nos {
         let func = &ns.functions[*function_no];
 
-        if func.ty != func_ty {
-            continue;
-        }
-
-        name_matches += 1;
-
-        let params_len = func.params.len();
+        let mut candidate_diagnostics = Diagnostics::default();
+        let mut cast_args = Vec::new();
 
-        if params_len != args.len() {
-            errors.push(Diagnostic::error(
+        if func.params.len() != args.len() {
+            candidate_diagnostics.push(Diagnostic::error(
                 *loc,
                 format!(
                     "{} expects {} arguments, {} provided",
                     func.ty,
-                    params_len,
+                    func.params.len(),
                     args.len()
                 ),
             ));
-            continue;
-        }
-
-        let mut matches = true;
-        let mut cast_args = Vec::new();
-
-        // check if arguments can be implicitly casted
-        for (i, arg) in args.iter().enumerate() {
-            let ty = ns.functions[*function_no].params[i].ty.clone();
+        } else {
+            // check if arguments can be implicitly casted
+            for (i, arg) in args.iter().enumerate() {
+                let ty = ns.functions[*function_no].params[i].ty.clone();
 
-            matches &=
-                evaluate_argument(arg, context, ns, symtable, &ty, &mut errors, &mut cast_args);
+                evaluate_argument(
+                    arg,
+                    context,
+                    ns,
+                    symtable,
+                    &ty,
+                    &mut candidate_diagnostics,
+                    &mut cast_args,
+                );
+            }
         }
 
-        if !matches {
-            if function_nos.len() > 1 && diagnostics.extend_non_casting(&errors) {
-                return Err(());
-            }
+        if candidate_diagnostics.any_errors() {
+            if function_nos.len() != 1 {
+                // will be de-duped
+                candidate_diagnostics.push(Diagnostic::error(
+                    *loc,
+                    format!("cannot find overloaded {func_ty} which matches signature"),
+                ));
 
-            continue;
-        }
+                let func = &ns.functions[*function_no];
 
-        match resolve_internal_call(
+                candidate_diagnostics.iter_mut().for_each(|diagnostic| {
+                    diagnostic.notes.push(Note {
+                        loc: func.loc,
+                        message: "candidate function".into(),
+                    })
+                });
+            }
+        } else if let Some(resolved_call) = resolve_internal_call(
             loc,
             id,
             *function_no,
@@ -324,38 +364,56 @@ pub fn function_call_pos_args(
             virtual_call,
             cast_args,
             ns,
-            &mut errors,
+            &mut candidate_diagnostics,
         ) {
-            Some(resolved_call) => return Ok(resolved_call),
-            None => continue,
+            resolved_calls.push((*function_no, resolved_call));
+            continue;
         }
+
+        call_diagnostics.extend(candidate_diagnostics);
     }
 
     let id = id.identifiers.last().unwrap();
-    match name_matches {
+    match resolved_calls.len() {
         0 => {
-            if func_ty == pt::FunctionTy::Modifier {
-                diagnostics.push(Diagnostic::error(
-                    id.loc,
-                    format!("unknown modifier '{}'", id.name),
-                ));
-            } else {
-                diagnostics.push(Diagnostic::error(
-                    id.loc,
-                    format!("unknown {} or type '{}'", func_ty, id.name),
-                ));
+            diagnostics.extend(call_diagnostics);
+
+            if function_nos.is_empty() {
+                if func_ty == pt::FunctionTy::Modifier {
+                    diagnostics.push(Diagnostic::error(
+                        id.loc,
+                        format!("unknown modifier '{}'", id.name),
+                    ));
+                } else {
+                    diagnostics.push(Diagnostic::error(
+                        id.loc,
+                        format!("unknown {} or type '{}'", func_ty, id.name),
+                    ));
+                }
             }
+
+            Err(())
         }
-        1 => diagnostics.extend(errors),
+        1 => Ok(resolved_calls[0].1.clone()),
         _ => {
-            diagnostics.push(Diagnostic::error(
+            diagnostics.push(Diagnostic::error_with_notes(
                 *loc,
-                format!("cannot find overloaded {func_ty} which matches signature"),
+                "function call can be resolved to multiple functions".into(),
+                resolved_calls
+                    .iter()
+                    .map(|(func_no, _)| {
+                        let func = &ns.functions[*func_no];
+
+                        Note {
+                            loc: func.loc,
+                            message: "candidate function".into(),
+                        }
+                    })
+                    .collect(),
             ));
+            Err(())
         }
     }
-
-    Err(())
 }
 
 /// Resolve a function call with named arguments
@@ -363,7 +421,7 @@ pub(super) fn function_call_named_args(
     loc: &pt::Loc,
     id: &pt::IdentifierPath,
     args: &[pt::NamedArgument],
-    function_nos: Vec<usize>,
+    mut function_nos: Vec<usize>,
     virtual_call: bool,
     context: &mut ExprContext,
     resolve_to: ResolveTo,
@@ -373,7 +431,7 @@ pub(super) fn function_call_named_args(
 ) -> Result<Expression, ()> {
     let mut arguments = HashMap::new();
 
-    for arg in args {
+    if args.iter().fold(false, |mut acc, arg| {
         if arguments.contains_key(arg.name.name.as_str()) {
             diagnostics.push(Diagnostic::error(
                 arg.name.loc,
@@ -388,27 +446,43 @@ pub(super) fn function_call_named_args(
                 diagnostics,
                 ResolveTo::Unknown,
             );
+        } else {
+            acc |= expression(
+                &arg.expr,
+                context,
+                ns,
+                symtable,
+                diagnostics,
+                ResolveTo::Unknown,
+            )
+            .is_err()
         }
 
         arguments.insert(arg.name.name.as_str(), &arg.expr);
+
+        acc
+    }) {
+        return Err(());
     }
+
+    function_nos.retain(|function_no| ns.functions[*function_no].ty == pt::FunctionTy::Function);
+
     // Try to resolve as a function call
-    let mut errors = Diagnostics::default();
+    let mut call_diagnostics = Diagnostics::default();
+    let mut resolved_calls = Vec::new();
 
     // Try to resolve as a function call
     for function_no in &function_nos {
         let func = &ns.functions[*function_no];
 
-        if func.ty != pt::FunctionTy::Function {
-            continue;
-        }
+        let mut candidate_diagnostics = Diagnostics::default();
 
         let unnamed_params = func.params.iter().filter(|p| p.id.is_none()).count();
         let params_len = func.params.len();
-        let mut matches = true;
+        let mut cast_args = Vec::new();
 
         if unnamed_params > 0 {
-            errors.push(Diagnostic::cast_error_with_note(
+            candidate_diagnostics.push(Diagnostic::cast_error_with_note(
                 *loc,
                 format!(
                     "function cannot be called with named arguments as {unnamed_params} of its parameters do not have names"
@@ -416,57 +490,71 @@ pub(super) fn function_call_named_args(
                 func.loc_prototype,
                 format!("definition of {}", func.id),
             ));
-            matches = false;
-        } else if params_len != args.len() {
-            errors.push(Diagnostic::cast_error(
-                *loc,
-                format!(
-                    "function expects {} arguments, {} provided",
-                    params_len,
-                    args.len()
-                ),
-            ));
-            matches = false;
-        }
-
-        let mut cast_args = Vec::new();
-
-        // check if arguments can be implicitly casted
-        for i in 0..params_len {
-            let param = &ns.functions[*function_no].params[i];
-            if param.id.is_none() {
-                continue;
+        } else {
+            if params_len != args.len() {
+                candidate_diagnostics.push(Diagnostic::cast_error(
+                    *loc,
+                    format!(
+                        "function expects {} arguments, {} provided",
+                        params_len,
+                        args.len()
+                    ),
+                ));
             }
-            let arg = match arguments.get(param.name_as_str()) {
-                Some(a) => a,
-                None => {
-                    matches = false;
-                    diagnostics.push(Diagnostic::cast_error(
-                        *loc,
-                        format!(
-                            "missing argument '{}' to function '{}'",
-                            param.name_as_str(),
-                            id.identifiers.last().unwrap().name,
-                        ),
-                    ));
+
+            // check if arguments can be implicitly casted
+            for i in 0..params_len {
+                let param = &ns.functions[*function_no].params[i];
+                if param.id.is_none() {
                     continue;
                 }
-            };
-
-            let ty = param.ty.clone();
+                let arg = match arguments.get(param.name_as_str()) {
+                    Some(a) => a,
+                    None => {
+                        candidate_diagnostics.push(Diagnostic::cast_error(
+                            *loc,
+                            format!(
+                                "missing argument '{}' to function '{}'",
+                                param.name_as_str(),
+                                id,
+                            ),
+                        ));
+                        continue;
+                    }
+                };
 
-            matches &=
-                evaluate_argument(arg, context, ns, symtable, &ty, &mut errors, &mut cast_args);
-        }
+                let ty = param.ty.clone();
 
-        if !matches {
-            if diagnostics.extend_non_casting(&errors) {
-                return Err(());
+                evaluate_argument(
+                    arg,
+                    context,
+                    ns,
+                    symtable,
+                    &ty,
+                    &mut candidate_diagnostics,
+                    &mut cast_args,
+                );
             }
-            continue;
         }
 
-        match resolve_internal_call(
+        if candidate_diagnostics.any_errors() {
+            if function_nos.len() != 1 {
+                // will be de-duped
+                candidate_diagnostics.push(Diagnostic::error(
+                    *loc,
+                    "cannot find overloaded function which matches signature".into(),
+                ));
+
+                let func = &ns.functions[*function_no];
+
+                candidate_diagnostics.iter_mut().for_each(|diagnostic| {
+                    diagnostic.notes.push(Note {
+                        loc: func.loc,
+                        message: "candidate function".into(),
+                    })
+                });
+            }
+        } else if let Some(resolved_call) = resolve_internal_call(
             loc,
             id,
             *function_no,
@@ -475,31 +563,49 @@ pub(super) fn function_call_named_args(
             virtual_call,
             cast_args,
             ns,
-            &mut errors,
+            &mut candidate_diagnostics,
         ) {
-            Some(resolved_call) => return Ok(resolved_call),
-            None => continue,
+            resolved_calls.push((*function_no, resolved_call));
+            continue;
         }
+
+        call_diagnostics.extend(candidate_diagnostics);
     }
 
-    match function_nos.len() {
+    match resolved_calls.len() {
         0 => {
-            let id = id.identifiers.last().unwrap();
-            diagnostics.push(Diagnostic::error(
-                id.loc,
-                format!("unknown function or type '{}'", id.name),
-            ));
+            diagnostics.extend(call_diagnostics);
+
+            if function_nos.is_empty() {
+                let id = id.identifiers.last().unwrap();
+                diagnostics.push(Diagnostic::error(
+                    id.loc,
+                    format!("unknown function or type '{}'", id.name),
+                ));
+            }
+
+            Err(())
         }
-        1 => diagnostics.extend(errors),
+        1 => Ok(resolved_calls[0].1.clone()),
         _ => {
-            diagnostics.push(Diagnostic::error(
+            diagnostics.push(Diagnostic::error_with_notes(
                 *loc,
-                "cannot find overloaded function which matches signature".to_string(),
+                "function call can be resolved to multiple functions".into(),
+                resolved_calls
+                    .iter()
+                    .map(|(func_no, _)| {
+                        let func = &ns.functions[*func_no];
+
+                        Note {
+                            loc: func.loc,
+                            message: "candidate function".into(),
+                        }
+                    })
+                    .collect(),
             ));
+            Err(())
         }
     }
-
-    Err(())
 }
 
 /// Check if the function is a method of a variable
@@ -2340,14 +2446,6 @@ pub fn function_call_expr(
                 };
             }
 
-            if context.constant {
-                diagnostics.push(Diagnostic::error(
-                    *loc,
-                    "cannot call function in constant expression".to_string(),
-                ));
-                return Err(());
-            }
-
             // is there a local variable or contract variable with this name
             if symtable.find(context, &id.name).is_some()
                 || matches!(
@@ -2514,7 +2612,7 @@ pub(crate) fn function_type(func: &Function, external: bool, resolve_to: Resolve
 
 /// This function evaluates the arguments of a function call with either positional arguments or
 /// named arguments.
-fn evaluate_argument(
+pub(crate) fn evaluate_argument(
     arg: &pt::Expression,
     context: &mut ExprContext,
     ns: &mut Namespace,
@@ -2613,7 +2711,7 @@ fn contract_call_named_args(
     let mut arguments = HashMap::new();
 
     // check if the arguments are not garbage
-    for arg in args {
+    if args.iter().fold(false, |mut acc, arg| {
         if arguments.contains_key(arg.name.name.as_str()) {
             diagnostics.push(Diagnostic::error(
                 arg.name.loc,
@@ -2628,11 +2726,23 @@ fn contract_call_named_args(
                 diagnostics,
                 ResolveTo::Unknown,
             );
-
-            continue;
+        } else {
+            acc |= expression(
+                &arg.expr,
+                context,
+                ns,
+                symtable,
+                diagnostics,
+                ResolveTo::Unknown,
+            )
+            .is_err()
         }
 
         arguments.insert(arg.name.name.as_str(), &arg.expr);
+
+        acc
+    }) {
+        return Err(());
     }
 
     let (call_args, name_matches) = match preprocess_contract_call(
@@ -2654,18 +2764,20 @@ fn contract_call_named_args(
         PreProcessedCall::Error => return Err(()),
     };
 
-    let mut errors = Diagnostics::default();
+    let mut call_diagnostics = Diagnostics::default();
+    let mut resolved_calls = Vec::new();
 
     for function_no in &name_matches {
         let func = &ns.functions[*function_no];
 
+        let mut candidate_diagnostics = Diagnostics::default();
+
         let unnamed_params = func.params.iter().filter(|p| p.id.is_none()).count();
         let params_len = func.params.len();
-
-        let mut matches = true;
+        let mut cast_args = Vec::new();
 
         if unnamed_params > 0 {
-            errors.push(Diagnostic::cast_error_with_note(
+            candidate_diagnostics.push(Diagnostic::cast_error_with_note(
                 *loc,
                 format!(
                     "function cannot be called with named arguments as {unnamed_params} of its parameters do not have names"
@@ -2673,9 +2785,8 @@ fn contract_call_named_args(
                 func.loc_prototype,
                 format!("definition of {}", func.id),
             ));
-            matches = false;
         } else if params_len != args.len() {
-            errors.push(Diagnostic::cast_error(
+            candidate_diagnostics.push(Diagnostic::cast_error(
                 *loc,
                 format!(
                     "function expects {} arguments, {} provided",
@@ -2683,9 +2794,7 @@ fn contract_call_named_args(
                     args.len()
                 ),
             ));
-            matches = false;
         }
-        let mut cast_args = Vec::new();
 
         for i in 0..params_len {
             let param = ns.functions[*function_no].params[i].clone();
@@ -2696,8 +2805,7 @@ fn contract_call_named_args(
             let arg = match arguments.get(param.name_as_str()) {
                 Some(a) => a,
                 None => {
-                    matches = false;
-                    diagnostics.push(Diagnostic::cast_error(
+                    candidate_diagnostics.push(Diagnostic::cast_error(
                         *loc,
                         format!(
                             "missing argument '{}' to function '{}'",
@@ -2709,67 +2817,91 @@ fn contract_call_named_args(
                 }
             };
 
-            let arg = match expression(
+            let ty = param.ty.clone();
+
+            evaluate_argument(
                 arg,
                 context,
                 ns,
                 symtable,
-                &mut errors,
-                ResolveTo::Type(&param.ty),
-            ) {
-                Ok(e) => e,
-                Err(()) => {
-                    matches = false;
-                    continue;
-                }
-            };
+                &ty,
+                &mut candidate_diagnostics,
+                &mut cast_args,
+            );
+        }
 
-            match arg.cast(&arg.loc(), &param.ty, true, ns, &mut errors) {
-                Ok(expr) => cast_args.push(expr),
-                Err(()) => {
-                    matches = false;
-                    break;
-                }
+        if candidate_diagnostics.any_errors() {
+            if name_matches.len() != 1 {
+                // will be de-duped
+                candidate_diagnostics.push(Diagnostic::error(
+                    *loc,
+                    "cannot find overloaded function which matches signature".into(),
+                ));
+
+                let func = &ns.functions[*function_no];
+
+                candidate_diagnostics.iter_mut().for_each(|diagnostic| {
+                    diagnostic.notes.push(Note {
+                        loc: func.loc,
+                        message: "candidate function".into(),
+                    })
+                });
             }
+        } else if let Ok(resolved_call) = contract_call_match(
+            loc,
+            func_name,
+            *function_no,
+            external_contract_no,
+            call_args.clone(),
+            cast_args,
+            var_expr.as_ref(),
+            ns,
+            &mut candidate_diagnostics,
+            resolve_to,
+        ) {
+            resolved_calls.push((*function_no, resolved_call));
+            continue;
         }
 
-        if matches {
-            return contract_call_match(
-                loc,
-                func_name,
-                *function_no,
-                external_contract_no,
-                call_args,
-                cast_args,
-                var_expr.as_ref(),
-                ns,
-                diagnostics,
-                resolve_to,
-            );
-        } else if name_matches.len() > 1 && diagnostics.extend_non_casting(&errors) {
-            return Err(());
-        }
+        call_diagnostics.extend(candidate_diagnostics);
     }
 
-    match name_matches.len() {
+    match resolved_calls.len() {
         0 => {
-            diagnostics.push(Diagnostic::error(
-                *loc,
-                format!(
-                    "contract '{}' does not have function '{}'",
-                    ns.contracts[external_contract_no].id, func_name.name
-                ),
-            ));
+            diagnostics.extend(call_diagnostics);
+
+            if name_matches.is_empty() {
+                diagnostics.push(Diagnostic::error(
+                    *loc,
+                    format!(
+                        "contract '{}' does not have function '{}'",
+                        ns.contracts[external_contract_no].id, func_name.name
+                    ),
+                ));
+            }
+
+            Err(())
         }
-        1 => diagnostics.extend(errors),
+        1 => Ok(resolved_calls[0].1.clone()),
         _ => {
-            diagnostics.push(Diagnostic::error(
+            diagnostics.push(Diagnostic::error_with_notes(
                 *loc,
-                "cannot find overloaded function which matches signature".to_string(),
+                "function call can be resolved to multiple functions".into(),
+                resolved_calls
+                    .iter()
+                    .map(|(func_no, _)| {
+                        let func = &ns.functions[*func_no];
+
+                        Note {
+                            loc: func.loc,
+                            message: "candidate function".into(),
+                        }
+                    })
+                    .collect(),
             ));
+            Err(())
         }
     }
-    Err(())
 }
 
 /// Resolve call to contract with positional arguments
@@ -2805,13 +2937,24 @@ fn contract_call_pos_args(
         PreProcessedCall::Error => return Err(()),
     };
 
-    let mut errors = Diagnostics::default();
+    let mut call_diagnostics = Diagnostics::default();
+    let mut resolved_calls = Vec::new();
+
+    // try to resolve the arguments, give up if there are any errors
+    if args.iter().fold(false, |acc, arg| {
+        acc | expression(arg, context, ns, symtable, diagnostics, ResolveTo::Unknown).is_err()
+    }) {
+        return Err(());
+    }
 
     for function_no in &name_matches {
+        let mut candidate_diagnostics = Diagnostics::default();
+        let mut cast_args = Vec::new();
+
         let params_len = ns.functions[*function_no].params.len();
 
         if params_len != args.len() {
-            errors.push(Diagnostic::error(
+            candidate_diagnostics.push(Diagnostic::error(
                 *loc,
                 format!(
                     "function expects {} arguments, {} provided",
@@ -2819,92 +2962,116 @@ fn contract_call_pos_args(
                     args.len()
                 ),
             ));
-            continue;
-        }
-
-        let mut matches = true;
-        let mut cast_args = Vec::new();
-
-        // check if arguments can be implicitly casted
-        for (i, arg) in args.iter().enumerate() {
-            let ty = ns.functions[*function_no].params[i].ty.clone();
-
-            let arg = match expression(
-                arg,
-                context,
-                ns,
-                symtable,
-                &mut errors,
-                ResolveTo::Type(&ty),
-            ) {
-                Ok(e) => e,
-                Err(_) => {
-                    matches = false;
-                    continue;
-                }
-            };
+        } else {
+            // check if arguments can be implicitly casted
+            for (i, arg) in args.iter().enumerate() {
+                let ty = ns.functions[*function_no].params[i].ty.clone();
 
-            match arg.cast(&arg.loc(), &ty, true, ns, &mut errors) {
-                Ok(expr) => cast_args.push(expr),
-                Err(()) => {
-                    matches = false;
-                    continue;
-                }
+                evaluate_argument(
+                    arg,
+                    context,
+                    ns,
+                    symtable,
+                    &ty,
+                    &mut candidate_diagnostics,
+                    &mut cast_args,
+                );
             }
         }
 
-        if matches {
-            let resolved_call = contract_call_match(
-                loc,
-                func,
-                *function_no,
-                external_contract_no,
-                call_args,
-                cast_args,
-                var_expr,
-                ns,
-                diagnostics,
-                resolve_to,
-            )?;
-            return Ok(Some(resolved_call));
-        } else if name_matches.len() > 1 && diagnostics.extend_non_casting(&errors) {
-            return Err(());
-        }
-    }
+        if candidate_diagnostics.any_errors() {
+            if name_matches.len() != 1 {
+                candidate_diagnostics.iter_mut().for_each(|diagnostic| {
+                    diagnostic.notes.push(Note {
+                        loc: ns.functions[*function_no].loc_prototype,
+                        message: "candidate function".into(),
+                    })
+                });
 
-    if let Some(var) = var_expr {
-        // what about call args
-        match using::try_resolve_using_call(
+                // will be de-duped
+                candidate_diagnostics.push(Diagnostic::error(
+                    *loc,
+                    "cannot find overloaded function which matches signature".into(),
+                ));
+            }
+        } else if let Ok(resolved_call) = contract_call_match(
             loc,
             func,
-            var,
-            context,
-            args,
-            symtable,
-            diagnostics,
+            *function_no,
+            external_contract_no,
+            call_args.clone(),
+            cast_args,
+            var_expr,
             ns,
+            &mut candidate_diagnostics,
             resolve_to,
         ) {
-            Ok(Some(expr)) => {
-                return Ok(Some(expr));
+            resolved_calls.push((*function_no, resolved_call));
+            continue;
+        }
+
+        call_diagnostics.extend(candidate_diagnostics);
+    }
+
+    match resolved_calls.len() {
+        0 => {
+            if let Some(var) = var_expr {
+                // what about call args
+                match using::try_resolve_using_call(
+                    loc,
+                    func,
+                    var,
+                    context,
+                    args,
+                    symtable,
+                    diagnostics,
+                    ns,
+                    resolve_to,
+                ) {
+                    Ok(Some(expr)) => {
+                        return Ok(Some(expr));
+                    }
+                    Ok(None) => (),
+                    Err(_) => {
+                        return Err(());
+                    }
+                }
             }
-            Ok(None) => (),
-            Err(_) => {
-                return Err(());
+
+            diagnostics.extend(call_diagnostics);
+
+            if name_matches.is_empty() {
+                diagnostics.push(Diagnostic::error(
+                    func.loc,
+                    format!("unknown function '{}'", func.name),
+                ));
             }
+
+            Err(())
         }
-    }
+        1 => Ok(Some(resolved_calls[0].1.clone())),
+        _ => {
+            diagnostics.extend(call_diagnostics);
 
-    if name_matches.len() == 1 {
-        diagnostics.extend(errors);
-    } else if name_matches.len() != 1 {
-        diagnostics.push(Diagnostic::error(
-            *loc,
-            "cannot find overloaded function which matches signature".to_string(),
-        ));
-    }
+            diagnostics.push(Diagnostic::error_with_notes(
+                *loc,
+                "function call can be resolved to multiple functions".into(),
+                resolved_calls
+                    .iter()
+                    .map(|(func_no, _)| {
+                        let func = &ns.functions[*func_no];
+
+                        Note {
+                            loc: func.loc,
+                            message: "candidate function".into(),
+                        }
+                    })
+                    .collect(),
+            ));
 
-    Err(())
+            Err(())
+        }
+    }
 }
 
 /// Checks if an identifier path is an external call on Solana.

+ 2 - 3
src/sema/expression/literals.rs

@@ -796,15 +796,14 @@ pub(super) fn array_literal(
     } else {
         first.ty()
     };
-
     used_variable(ns, &first, symtable);
     let mut exprs = vec![first];
 
     for e in flattened {
-        let mut other = expression(e, context, ns, symtable, diagnostics, ResolveTo::Type(&ty))?;
+        let mut other = expression(e, context, ns, symtable, diagnostics, resolve_to)?;
         used_variable(ns, &other, symtable);
 
-        if other.ty() != ty {
+        if resolve_to != ResolveTo::Unknown && other.ty() != ty {
             other = other.cast(&e.loc(), &ty, true, ns, diagnostics)?;
         }
 

+ 2 - 0
src/sema/expression/mod.rs

@@ -72,6 +72,8 @@ pub struct ExprContext {
     pub loops: LoopScopes,
     /// Stack of currently active variable scopes
     pub active_scopes: Vec<VarScope>,
+    /// Solidity v0.5 and earlier don't complain about emit resolving to multiple events
+    pub ambiguous_emit: bool,
 }
 
 impl ExprContext {

+ 2 - 134
src/sema/mod.rs

@@ -32,6 +32,7 @@ mod function_annotation;
 mod functions;
 mod mutability;
 mod namespace;
+mod pragma;
 pub(crate) mod solana_accounts;
 mod statements;
 pub mod symtable;
@@ -121,7 +122,7 @@ fn sema_file(file: &ResolvedFile, resolver: &mut FileResolver, ns: &mut ast::Nam
         match &item.part {
             pt::SourceUnitPart::PragmaDirective(pragma) => {
                 annotions_not_allowed(&item.annotations, "pragma", ns);
-                resolve_pragma(pragma, ns);
+                pragma::resolve_pragma(pragma, ns);
             }
             pt::SourceUnitPart::ImportDirective(import) => {
                 annotions_not_allowed(&item.annotations, "import", ns);
@@ -416,139 +417,6 @@ fn resolve_import(
     }
 }
 
-/// Resolve pragma. We don't do anything with pragmas for now
-fn resolve_pragma(pragma: &pt::PragmaDirective, ns: &mut ast::Namespace) {
-    match pragma {
-        pt::PragmaDirective::Identifier(loc, Some(ident), Some(value)) => {
-            plain_pragma(loc, &ident.name, &value.name, ns);
-
-            ns.pragmas.push(ast::Pragma::Identifier {
-                loc: *loc,
-                name: ident.clone(),
-                value: value.clone(),
-            });
-        }
-        pt::PragmaDirective::StringLiteral(loc, ident, value) => {
-            plain_pragma(loc, &ident.name, &value.string, ns);
-
-            ns.pragmas.push(ast::Pragma::StringLiteral {
-                loc: *loc,
-                name: ident.clone(),
-                value: value.clone(),
-            });
-        }
-        pt::PragmaDirective::Version(loc, ident, versions) => {
-            if ident.name != "solidity" {
-                ns.diagnostics.push(ast::Diagnostic::error(
-                    ident.loc,
-                    format!("unknown pragma '{}'", ident.name),
-                ));
-            } else {
-                // parser versions
-                let mut res = Vec::new();
-
-                for version in versions {
-                    let Ok(v) = parse_version_comparator(version, ns) else {
-                        return;
-                    };
-                    res.push(v);
-                }
-
-                ns.pragmas.push(ast::Pragma::SolidityVersion {
-                    loc: *loc,
-                    versions: res,
-                });
-            }
-        }
-        pt::PragmaDirective::Identifier { .. } => (),
-    }
-}
-
-fn plain_pragma(loc: &pt::Loc, name: &str, value: &str, ns: &mut ast::Namespace) {
-    if name == "experimental" && value == "ABIEncoderV2" {
-        ns.diagnostics.push(ast::Diagnostic::debug(
-            *loc,
-            "pragma 'experimental' with value 'ABIEncoderV2' is ignored".to_string(),
-        ));
-    } else if name == "experimental" && value == "solidity" {
-        ns.diagnostics.push(ast::Diagnostic::error(
-            *loc,
-            "experimental solidity features are not supported".to_string(),
-        ));
-    } else if name == "abicoder" && value == "v2" {
-        ns.diagnostics.push(ast::Diagnostic::debug(
-            *loc,
-            "pragma 'abicoder' with value 'v2' is ignored".to_string(),
-        ));
-    } else {
-        ns.diagnostics.push(ast::Diagnostic::warning(
-            *loc,
-            format!("unknown pragma '{}' with value '{}' ignored", name, value),
-        ));
-    }
-}
-
-fn parse_version_comparator(
-    version: &pt::VersionComparator,
-    ns: &mut ast::Namespace,
-) -> Result<ast::VersionReq, ()> {
-    match version {
-        pt::VersionComparator::Plain { loc, version } => Ok(ast::VersionReq::Plain {
-            loc: *loc,
-            version: parse_version(loc, version, ns)?,
-        }),
-        pt::VersionComparator::Operator { loc, op, version } => Ok(ast::VersionReq::Operator {
-            loc: *loc,
-            op: *op,
-            version: parse_version(loc, version, ns)?,
-        }),
-        pt::VersionComparator::Range { loc, from, to } => Ok(ast::VersionReq::Range {
-            loc: *loc,
-            from: parse_version(loc, from, ns)?,
-            to: parse_version(loc, to, ns)?,
-        }),
-        pt::VersionComparator::Or { loc, left, right } => Ok(ast::VersionReq::Or {
-            loc: *loc,
-            left: parse_version_comparator(left, ns)?.into(),
-            right: parse_version_comparator(right, ns)?.into(),
-        }),
-    }
-}
-
-fn parse_version(
-    loc: &pt::Loc,
-    version: &[String],
-    ns: &mut ast::Namespace,
-) -> Result<ast::Version, ()> {
-    let mut res = Vec::new();
-
-    for v in version {
-        if let Ok(v) = v.parse() {
-            res.push(v);
-        } else {
-            ns.diagnostics.push(ast::Diagnostic::error(
-                *loc,
-                format!("'{v}' is not a valid number"),
-            ));
-            return Err(());
-        }
-    }
-
-    if version.len() > 3 {
-        ns.diagnostics.push(ast::Diagnostic::error(
-            *loc,
-            "no more than three numbers allowed - major.minor.patch".into(),
-        ));
-        return Err(());
-    }
-
-    Ok(ast::Version {
-        major: res[0],
-        minor: res.get(1).cloned(),
-        patch: res.get(2).cloned(),
-    })
-}
-
 /// Walk through the parse tree and collect all the annotations and doccomments for
 /// each item, also inside contracts.
 fn collect_annotations_doccomments<'a>(

+ 276 - 0
src/sema/pragma.rs

@@ -0,0 +1,276 @@
+// SPDX-License-Identifier: Apache-2.0
+
+use super::ast;
+use solang_parser::pt;
+use std::str;
+
+/// Resolve pragma from the parse tree
+pub fn resolve_pragma(pragma: &pt::PragmaDirective, ns: &mut ast::Namespace) {
+    match pragma {
+        pt::PragmaDirective::Identifier(loc, Some(ident), Some(value)) => {
+            plain_pragma(loc, &ident.name, &value.name, ns);
+
+            ns.pragmas.push(ast::Pragma::Identifier {
+                loc: *loc,
+                name: ident.clone(),
+                value: value.clone(),
+            });
+        }
+        pt::PragmaDirective::StringLiteral(loc, ident, value) => {
+            plain_pragma(loc, &ident.name, &value.string, ns);
+
+            ns.pragmas.push(ast::Pragma::StringLiteral {
+                loc: *loc,
+                name: ident.clone(),
+                value: value.clone(),
+            });
+        }
+        pt::PragmaDirective::Version(loc, ident, versions) => {
+            if ident.name != "solidity" {
+                ns.diagnostics.push(ast::Diagnostic::error(
+                    ident.loc,
+                    format!("unknown pragma '{}'", ident.name),
+                ));
+            } else {
+                // parser versions
+                let mut res = Vec::new();
+
+                for version in versions {
+                    let Ok(v) = parse_version_comparator(version, ns) else {
+                        return;
+                    };
+                    res.push(v);
+                }
+
+                if res.len() > 1
+                    && res
+                        .iter()
+                        .any(|v| matches!(v, ast::VersionReq::Range { .. }))
+                {
+                    ns.diagnostics.push(ast::Diagnostic::error(
+                        *loc,
+                        "version ranges can only be combined with the || operator".into(),
+                    ));
+                }
+
+                ns.pragmas.push(ast::Pragma::SolidityVersion {
+                    loc: *loc,
+                    versions: res,
+                });
+            }
+        }
+        // only occurs when there is a parse error, name or value is None
+        pt::PragmaDirective::Identifier { .. } => (),
+    }
+}
+
+fn plain_pragma(loc: &pt::Loc, name: &str, value: &str, ns: &mut ast::Namespace) {
+    if name == "experimental" && value == "ABIEncoderV2" {
+        ns.diagnostics.push(ast::Diagnostic::debug(
+            *loc,
+            "pragma 'experimental' with value 'ABIEncoderV2' is ignored".to_string(),
+        ));
+    } else if name == "experimental" && value == "solidity" {
+        ns.diagnostics.push(ast::Diagnostic::error(
+            *loc,
+            "experimental solidity features are not supported".to_string(),
+        ));
+    } else if name == "abicoder" && (value == "v1" || value == "v2") {
+        ns.diagnostics.push(ast::Diagnostic::debug(
+            *loc,
+            "pragma 'abicoder' ignored".to_string(),
+        ));
+    } else {
+        ns.diagnostics.push(ast::Diagnostic::error(
+            *loc,
+            format!("unknown pragma '{}' with value '{}'", name, value),
+        ));
+    }
+}
+
+fn parse_version_comparator(
+    version: &pt::VersionComparator,
+    ns: &mut ast::Namespace,
+) -> Result<ast::VersionReq, ()> {
+    match version {
+        pt::VersionComparator::Plain { loc, version } => Ok(ast::VersionReq::Plain {
+            loc: *loc,
+            version: parse_version(loc, version, ns)?,
+        }),
+        pt::VersionComparator::Operator { loc, op, version } => Ok(ast::VersionReq::Operator {
+            loc: *loc,
+            op: *op,
+            version: parse_version(loc, version, ns)?,
+        }),
+        pt::VersionComparator::Range { loc, from, to } => Ok(ast::VersionReq::Range {
+            loc: *loc,
+            from: parse_version(loc, from, ns)?,
+            to: parse_version(loc, to, ns)?,
+        }),
+        pt::VersionComparator::Or { loc, left, right } => Ok(ast::VersionReq::Or {
+            loc: *loc,
+            left: parse_version_comparator(left, ns)?.into(),
+            right: parse_version_comparator(right, ns)?.into(),
+        }),
+    }
+}
+
+fn parse_version(
+    loc: &pt::Loc,
+    version: &[String],
+    ns: &mut ast::Namespace,
+) -> Result<ast::Version, ()> {
+    let mut res = Vec::new();
+
+    for v in version {
+        if let Ok(v) = v.parse() {
+            res.push(v);
+        } else {
+            ns.diagnostics.push(ast::Diagnostic::error(
+                *loc,
+                format!("'{v}' is not a valid number"),
+            ));
+            return Err(());
+        }
+    }
+
+    if version.len() > 3 {
+        ns.diagnostics.push(ast::Diagnostic::error(
+            *loc,
+            "no more than three numbers allowed - major.minor.patch".into(),
+        ));
+        return Err(());
+    }
+
+    Ok(ast::Version {
+        major: res[0],
+        minor: res.get(1).cloned(),
+        patch: res.get(2).cloned(),
+    })
+}
+
+impl ast::VersionReq {
+    fn highest_version(&self) -> Vec<ast::Version> {
+        match self {
+            ast::VersionReq::Plain { version, .. } => vec![version.clone()],
+            ast::VersionReq::Operator { op, version, .. } => match op {
+                pt::VersionOp::Exact => vec![version.clone()],
+                pt::VersionOp::Less => {
+                    let mut version = version.clone();
+
+                    if let Some(patch) = &mut version.patch {
+                        if *patch != 0 {
+                            *patch -= 1;
+                            return vec![version];
+                        }
+                    }
+
+                    if let Some(minor) = &mut version.minor {
+                        if *minor != 0 {
+                            *minor -= 1;
+
+                            version.patch = None;
+                            return vec![version];
+                        }
+                    }
+
+                    if version.major > 0 {
+                        version.major -= 1;
+                        version.minor = None;
+                        version.patch = None;
+
+                        return vec![version];
+                    }
+
+                    vec![]
+                }
+                pt::VersionOp::LessEq => vec![version.clone()],
+                pt::VersionOp::Greater => vec![],
+                pt::VersionOp::GreaterEq => vec![],
+                pt::VersionOp::Caret if version.major == 0 => {
+                    if let Some(m) = version.minor {
+                        if m > 0 {
+                            let mut version = version.clone();
+                            version.patch = None;
+                            return vec![version];
+                        }
+                    }
+
+                    vec![]
+                }
+                pt::VersionOp::Caret => {
+                    let mut version = version.clone();
+                    version.minor = None;
+                    version.patch = None;
+                    vec![version]
+                }
+                pt::VersionOp::Tilde => {
+                    if let Some(m) = version.minor {
+                        if m > 0 {
+                            let mut version = version.clone();
+                            version.patch = None;
+                            return vec![version];
+                        }
+                    }
+
+                    vec![]
+                }
+                pt::VersionOp::Wildcard => vec![],
+            },
+            ast::VersionReq::Or { left, right, .. } => {
+                let mut v = Vec::new();
+                v.extend(left.highest_version());
+                v.extend(right.highest_version());
+                v
+            }
+            ast::VersionReq::Range { to, .. } => vec![to.clone()],
+        }
+    }
+}
+
+impl ast::Namespace {
+    /// Return the highest supported version of Solidity according the solidity
+    /// version pragmas
+    pub fn highest_solidity_version(&self, file_no: usize) -> Option<ast::Version> {
+        let mut v = Vec::new();
+
+        for pragma in &self.pragmas {
+            if let ast::Pragma::SolidityVersion { loc, versions } = pragma {
+                if file_no == loc.file_no() {
+                    versions
+                        .iter()
+                        .map(|v| v.highest_version())
+                        .for_each(|res| v.extend(res));
+                }
+            }
+        }
+
+        // pick the most specific version
+        v.sort_by(|a, b| {
+            let cmp = a.minor.is_some().cmp(&b.minor.is_some());
+
+            if cmp == std::cmp::Ordering::Equal {
+                a.patch.is_some().cmp(&b.patch.is_some())
+            } else {
+                cmp
+            }
+        });
+
+        v.pop()
+    }
+
+    /// Are we supporting minor_version at most?
+    pub fn solidity_minor_version(&self, file_no: usize, minor_version: u32) -> bool {
+        if let Some(version) = self.highest_solidity_version(file_no) {
+            if version.major == 0 {
+                if let Some(minor) = version.minor {
+                    if minor <= minor_version {
+                        return true;
+                    }
+                }
+            }
+        }
+
+        false
+    }
+}

+ 263 - 121
src/sema/statements.rs

@@ -44,6 +44,7 @@ pub fn resolve_function_body(
         file_no,
         contract_no,
         function_no: Some(function_no),
+        ambiguous_emit: ns.solidity_minor_version(file_no, 5),
         ..Default::default()
     };
     context.enter_scope();
@@ -339,7 +340,6 @@ pub fn resolve_function_body(
 }
 
 /// Resolve a statement
-#[allow(clippy::ptr_arg)]
 fn statement(
     stmt: &pt::Statement,
     res: &mut Vec<Statement>,
@@ -1186,7 +1186,7 @@ fn emit_event(
 ) -> Result<Statement, ()> {
     let function_no = context.function_no.unwrap();
     let to_stmt =
-        |ns: &mut Namespace, event_no: usize, event_loc: pt::Loc, cast_args: Vec<Expression>| {
+        |ns: &mut Namespace, event_no: usize, event_loc: pt::Loc, args: Vec<Expression>| {
             if !ns.functions[function_no].emits_events.contains(&event_no) {
                 ns.functions[function_no].emits_events.push(event_no);
             }
@@ -1194,44 +1194,55 @@ fn emit_event(
                 loc: *loc,
                 event_no,
                 event_loc,
-                args: cast_args,
+                args,
             }
         };
 
     match ty {
         pt::Expression::FunctionCall(_, ty, args) => {
             let event_loc = ty.loc();
+            let mut emit_diagnostics = Diagnostics::default();
+            let mut valid_args = Vec::new();
 
-            let mut errors = Diagnostics::default();
-
-            let event_nos =
-                match ns.resolve_event(context.file_no, context.contract_no, ty, diagnostics) {
-                    Ok(nos) => nos,
-                    Err(_) => {
-                        for arg in args {
-                            if let Ok(exp) = expression(
-                                arg,
-                                context,
-                                ns,
-                                symtable,
-                                diagnostics,
-                                ResolveTo::Unknown,
-                            ) {
-                                used_variable(ns, &exp, symtable);
-                            };
-                        }
-                        return Err(());
-                    }
+            for arg in args {
+                if let Ok(exp) = expression(
+                    arg,
+                    context,
+                    ns,
+                    symtable,
+                    &mut emit_diagnostics,
+                    ResolveTo::Unknown,
+                ) {
+                    used_variable(ns, &exp, symtable);
+                    valid_args.push(exp);
                 };
+            }
+
+            let Ok(event_nos) = ns.resolve_event(
+                context.file_no,
+                context.contract_no,
+                ty,
+                &mut emit_diagnostics,
+            ) else {
+                diagnostics.extend(emit_diagnostics);
+                return Err(());
+            };
+
+            if emit_diagnostics.any_errors() {
+                diagnostics.extend(emit_diagnostics);
+                return Ok(to_stmt(ns, event_nos[0], event_loc, valid_args));
+            }
+
+            let mut resolved_events = Vec::new();
 
             for event_no in &event_nos {
-                let event = &mut ns.events[*event_no];
-                event.used = true;
+                let mut candidate_diagnostics = Diagnostics::default();
 
-                let mut matches = true;
+                let event = &ns.events[*event_no];
+                let mut cast_args = Vec::new();
 
                 if args.len() != event.fields.len() {
-                    errors.push(Diagnostic::cast_error(
+                    candidate_diagnostics.push(Diagnostic::error_with_note(
                         *loc,
                         format!(
                             "event type '{}' has {} fields, {} provided",
@@ -1239,115 +1250,154 @@ fn emit_event(
                             event.fields.len(),
                             args.len()
                         ),
+                        event.id.loc,
+                        format!("definition of {}", event.id),
                     ));
-                    matches = false;
                 }
-                let mut cast_args = Vec::new();
 
                 // check if arguments can be implicitly casted
                 for (i, arg) in args.iter().enumerate() {
-                    let ty = ns.events[*event_no]
+                    if let Some(ty) = ns.events[*event_no]
                         .fields
                         .get(i)
-                        .map(|field| field.ty.clone());
-
-                    let resolve_to = ty
-                        .as_ref()
-                        .map(ResolveTo::Type)
-                        .unwrap_or(ResolveTo::Unknown);
-
-                    let arg = match expression(arg, context, ns, symtable, &mut errors, resolve_to)
+                        .map(|field| field.ty.clone())
                     {
-                        Ok(e) => e,
-                        Err(()) => {
-                            matches = false;
-                            break;
-                        }
-                    };
-                    used_variable(ns, &arg, symtable);
-
-                    if let Some(ty) = &ty {
-                        match arg.cast(&arg.loc(), ty, true, ns, &mut errors) {
-                            Ok(expr) => cast_args.push(expr),
-                            Err(_) => {
-                                matches = false;
-                            }
+                        if let Ok(expr) = expression(
+                            arg,
+                            context,
+                            ns,
+                            symtable,
+                            &mut candidate_diagnostics,
+                            ResolveTo::Type(&ty),
+                        )
+                        .and_then(|arg| {
+                            arg.cast(&arg.loc(), &ty, true, ns, &mut candidate_diagnostics)
+                        }) {
+                            used_variable(ns, &expr, symtable);
+                            cast_args.push(expr);
                         }
                     }
                 }
 
-                if matches {
-                    return Ok(to_stmt(ns, *event_no, event_loc, cast_args));
-                } else if event_nos.len() > 1 && diagnostics.extend_non_casting(&errors) {
-                    return Err(());
+                if candidate_diagnostics.any_errors() {
+                    if event_nos.len() != 1 {
+                        let event = &ns.events[*event_no];
+
+                        candidate_diagnostics.iter_mut().for_each(|diagnostic| {
+                            diagnostic.notes.push(Note {
+                                loc: event.loc,
+                                message: "candidate event".into(),
+                            })
+                        });
+
+                        // will be de-duped
+                        candidate_diagnostics.push(Diagnostic::error(
+                            *loc,
+                            "cannot find event with matching signature".into(),
+                        ));
+                    }
+
+                    emit_diagnostics.extend(candidate_diagnostics);
+                } else {
+                    resolved_events.push((
+                        *event_no,
+                        candidate_diagnostics,
+                        to_stmt(ns, *event_no, event_loc, cast_args),
+                    ));
                 }
             }
 
-            if event_nos.len() == 1 {
-                diagnostics.extend(errors);
+            remove_duplicate_events(loc, &mut resolved_events, context, diagnostics, ns);
+
+            let count = resolved_events.len();
+
+            if count == 0 {
+                diagnostics.extend(emit_diagnostics);
+            } else if count == 1 {
+                let (event_no, candidate_diagnostics, stmt) = resolved_events.remove(0);
+
+                let event = &mut ns.events[event_no];
+                event.used = true;
+
+                diagnostics.extend(candidate_diagnostics);
+
+                return Ok(stmt);
             } else {
-                diagnostics.push(Diagnostic::error(
+                diagnostics.push(Diagnostic::error_with_notes(
                     *loc,
-                    "cannot find event which matches signature".to_string(),
+                    "emit can be resolved to multiple events".into(),
+                    resolved_events
+                        .into_iter()
+                        .map(|(event_no, _, _)| {
+                            let event = &ns.events[event_no];
+
+                            Note {
+                                loc: event.id.loc,
+                                message: "candidate event".into(),
+                            }
+                        })
+                        .collect(),
                 ));
-            }
+            };
         }
         pt::Expression::NamedFunctionCall(_, ty, args) => {
             let event_loc = ty.loc();
 
-            let mut temp_diagnostics = Diagnostics::default();
+            let mut emit_diagnostics = Diagnostics::default();
             let mut arguments = HashMap::new();
+            let mut resolved_events = Vec::new();
+            let mut valid_args = Vec::new();
 
             for arg in args {
+                if let Ok(expr) = expression(
+                    &arg.expr,
+                    context,
+                    ns,
+                    symtable,
+                    &mut emit_diagnostics,
+                    ResolveTo::Unknown,
+                ) {
+                    used_variable(ns, &expr, symtable);
+                    valid_args.push(expr);
+                };
+
                 if arguments.contains_key(arg.name.name.as_str()) {
-                    diagnostics.push(Diagnostic::error(
+                    emit_diagnostics.push(Diagnostic::error(
                         arg.name.loc,
                         format!("duplicate argument with name '{}'", arg.name.name),
                     ));
-
-                    let _ = expression(
-                        &arg.expr,
-                        context,
-                        ns,
-                        symtable,
-                        diagnostics,
-                        ResolveTo::Unknown,
-                    );
-
                     continue;
                 }
 
                 arguments.insert(arg.name.name.as_str(), &arg.expr);
             }
 
-            let event_nos = match ns.resolve_event(
+            let Ok(event_nos) = ns.resolve_event(
                 context.file_no,
                 context.contract_no,
                 ty,
-                &mut temp_diagnostics,
-            ) {
-                Ok(nos) => nos,
-                Err(_) => {
-                    // check arguments for errors
-                    for (_, arg) in arguments {
-                        let _ =
-                            expression(arg, context, ns, symtable, diagnostics, ResolveTo::Unknown);
-                    }
-                    return Err(());
-                }
+                &mut emit_diagnostics,
+            ) else {
+                diagnostics.extend(emit_diagnostics);
+                return Err(());
             };
 
+            if emit_diagnostics.any_errors() {
+                diagnostics.extend(emit_diagnostics);
+                return Ok(to_stmt(ns, event_nos[0], event_loc, valid_args));
+            }
+
             for event_no in &event_nos {
-                let event = &mut ns.events[*event_no];
-                event.used = true;
+                let mut candidate_diagnostics = Diagnostics::default();
+                let event = &ns.events[*event_no];
                 let params_len = event.fields.len();
 
-                let mut matches = true;
-
                 let unnamed_fields = event.fields.iter().filter(|p| p.id.is_none()).count();
 
+                let mut cast_args = Vec::new();
+
                 if unnamed_fields > 0 {
-                    temp_diagnostics.push(Diagnostic::cast_error_with_note(
+                    candidate_diagnostics.push(Diagnostic::error_with_note(
                         *loc,
                         format!(
                             "event cannot be emitted with named fields as {unnamed_fields} of its fields do not have names"
@@ -1355,21 +1405,19 @@ fn emit_event(
                         event.id.loc,
                         format!("definition of {}", event.id),
                     ));
-                    matches = false;
                 } else if params_len != arguments.len() {
-                    temp_diagnostics.push(Diagnostic::error(
+                    candidate_diagnostics.push(Diagnostic::error_with_note(
                         *loc,
                         format!(
                             "event expects {} arguments, {} provided",
                             params_len,
                             arguments.len()
                         ),
+                        event.id.loc,
+                        format!("definition of {}", event.id),
                     ));
-                    matches = false;
                 }
 
-                let mut cast_args = Vec::new();
-
                 // check if arguments can be implicitly casted
                 for i in 0..params_len {
                     let param = ns.events[*event_no].fields[i].clone();
@@ -1381,8 +1429,7 @@ fn emit_event(
                     let arg = match arguments.get(param.name_as_str()) {
                         Some(a) => a,
                         None => {
-                            matches = false;
-                            temp_diagnostics.push(Diagnostic::cast_error(
+                            candidate_diagnostics.push(Diagnostic::error(
                                 *loc,
                                 format!(
                                     "missing argument '{}' to event '{}'",
@@ -1394,44 +1441,80 @@ fn emit_event(
                         }
                     };
 
-                    let arg = match expression(
+                    if let Ok(expr) = expression(
                         arg,
                         context,
                         ns,
                         symtable,
-                        &mut temp_diagnostics,
+                        &mut candidate_diagnostics,
                         ResolveTo::Type(&param.ty),
-                    ) {
-                        Ok(e) => e,
-                        Err(()) => {
-                            matches = false;
-                            continue;
-                        }
-                    };
+                    )
+                    .and_then(|arg| {
+                        arg.cast(&arg.loc(), &param.ty, true, ns, &mut candidate_diagnostics)
+                    }) {
+                        used_variable(ns, &expr, symtable);
+                        cast_args.push(expr);
+                    }
+                }
 
-                    used_variable(ns, &arg, symtable);
+                if candidate_diagnostics.any_errors() {
+                    if event_nos.len() != 1 {
+                        let event = &ns.events[*event_no];
 
-                    match arg.cast(&arg.loc(), &param.ty, true, ns, &mut temp_diagnostics) {
-                        Ok(expr) => cast_args.push(expr),
-                        Err(_) => {
-                            matches = false;
-                        }
+                        candidate_diagnostics.iter_mut().for_each(|diagnostic| {
+                            diagnostic.notes.push(Note {
+                                loc: event.loc,
+                                message: "candidate event".into(),
+                            })
+                        });
+
+                        // will be de-duped
+                        candidate_diagnostics.push(Diagnostic::error(
+                            *loc,
+                            "cannot find event with matching signature".into(),
+                        ));
                     }
-                }
 
-                if matches {
-                    return Ok(to_stmt(ns, *event_no, event_loc, cast_args));
-                } else if event_nos.len() > 1 && diagnostics.extend_non_casting(&temp_diagnostics) {
-                    return Err(());
+                    emit_diagnostics.extend(candidate_diagnostics);
+                } else {
+                    resolved_events.push((
+                        *event_no,
+                        candidate_diagnostics,
+                        to_stmt(ns, *event_no, event_loc, cast_args),
+                    ));
                 }
             }
 
-            if event_nos.len() == 1 {
-                diagnostics.extend(temp_diagnostics);
+            remove_duplicate_events(loc, &mut resolved_events, context, diagnostics, ns);
+
+            let count = resolved_events.len();
+
+            if count == 0 {
+                diagnostics.extend(emit_diagnostics);
+            } else if count == 1 {
+                let (event_no, candidate_diagnostics, stmt) = resolved_events.remove(0);
+
+                diagnostics.extend(candidate_diagnostics);
+
+                let event = &mut ns.events[event_no];
+                event.used = true;
+
+                return Ok(stmt);
             } else {
-                diagnostics.push(Diagnostic::error(
+                diagnostics.push(Diagnostic::error_with_notes(
                     *loc,
-                    "cannot find event which matches signature".to_string(),
+                    "emit can be resolved to multiple events".into(),
+                    resolved_events
+                        .into_iter()
+                        .map(|(event_no, _, _)| {
+                            let event = &ns.events[event_no];
+
+                            Note {
+                                loc: event.id.loc,
+                                message: "candidate event".into(),
+                            }
+                        })
+                        .collect(),
                 ));
             }
         }
@@ -1449,6 +1532,65 @@ fn emit_event(
     Err(())
 }
 
+fn remove_duplicate_events(
+    loc: &pt::Loc,
+    resolved_events: &mut Vec<(usize, Diagnostics, Statement)>,
+    context: &ExprContext,
+    diagnostics: &mut Diagnostics,
+    ns: &Namespace,
+) {
+    resolved_events.dedup_by(|a, b| ns.events[a.0].identical(&ns.events[b.0]));
+
+    if context.ambiguous_emit
+        && resolved_events.len() > 1
+        && resolved_events
+            .windows(2)
+            .all(|slice| ns.events[slice[0].0].identical_v0_5(&ns.events[slice[1].0]))
+    {
+        diagnostics.push(Diagnostic::warning_with_notes(
+            *loc,
+            "emit can be resolved to multiple incompatible events. This is permitted in Solidity v0.5 and earlier, however it could indicate a bug.".into(),
+            resolved_events
+                .iter()
+                .map(|(event_no, _, _)| {
+                    let event = &ns.events[*event_no];
+
+                    Note {
+                        loc: event.id.loc,
+                        message: "candidate event".into(),
+                    }
+                })
+                .collect(),
+        ));
+
+        resolved_events.truncate(1);
+    }
+}
+
+impl EventDecl {
+    // Are two events Identical?
+    pub fn identical(&self, other: &Self) -> bool {
+        self.id.name == other.id.name
+            && self.anonymous == other.anonymous
+            && self
+                .fields
+                .iter()
+                .zip(other.fields.iter())
+                .all(|(l, r)| l.indexed == r.indexed && l.ty == r.ty)
+    }
+
+    // Are two events Identical in Solidity v0.5?
+    // Solidity v0.5 erronously does not compare indexed-ness and anonymous-ness
+    pub fn identical_v0_5(&self, other: &Self) -> bool {
+        self.id.name == other.id.name
+            && self
+                .fields
+                .iter()
+                .zip(other.fields.iter())
+                .all(|(l, r)| l.ty == r.ty)
+    }
+}
+
 /// Resolve destructuring assignment
 fn destructure(
     loc: &pt::Loc,

+ 17 - 0
tests/contract_testcases/evm/builtin_overloaded.sol

@@ -0,0 +1,17 @@
+contract C {
+	bytes constant bs = "abcdefgh";
+	int8 constant v = bs.readInt8(0);
+
+	function test1() public {
+		bs.readInt8("foo");
+	}
+
+	function test2() public {
+		bs.readInt8(1, "foo");
+	}
+}
+
+// ---- Expect: diagnostics ----
+// error: 3:20-34: cannot call function in constant expression
+// error: 6:15-20: implicit conversion to uint32 from bytes3 not allowed
+// error: 10:6-14: builtin function 'readInt8' expects 1 arguments, 2 provided

+ 15 - 0
tests/contract_testcases/evm/contract-shadows-global.sol

@@ -0,0 +1,15 @@
+function f(int, int) pure {}
+function f(int, bool) pure {}
+contract C {
+	function f(int, int) public pure {}
+	function f(bool) public pure {}
+	function f(bool, int) public pure {}
+	function g() public pure {
+		f(1, 2);
+	}
+}
+
+// ---- Expect: diagnostics ----
+// warning: 4:11-12: f is already defined as a function
+// 	note 1:10-11: location of previous definition
+// 	note 2:1-27: location of previous definition

+ 47 - 0
tests/contract_testcases/evm/dup1.sol

@@ -0,0 +1,47 @@
+contract C {
+	function foo(int8 a, int8 b) public {}
+	function foo(int64 a, int8 b) public {}
+	function bar() public {
+		foo(1, 2);
+	}
+}
+
+contract D is C {
+	function test1() public {
+		C.foo(1, 2);
+	}
+
+	function test2(C c) public {
+		c.foo(1, 2);
+	}
+
+	function test3(C c) public {
+		c.foo(1);
+	}
+
+	function test4(C c) public {
+		c.foo(x, y);
+	}
+}
+
+// ---- Expect: diagnostics ----
+// warning: 2:20-21: function parameter 'a' is unused
+// warning: 2:28-29: function parameter 'b' is unused
+// warning: 3:21-22: function parameter 'a' is unused
+// warning: 3:29-30: function parameter 'b' is unused
+// error: 5:3-12: function call can be resolved to multiple functions
+// 	note 2:2-40: candidate function
+// 	note 3:2-41: candidate function
+// error: 11:3-14: function call can be resolved to multiple functions
+// 	note 2:2-40: candidate function
+// 	note 3:2-41: candidate function
+// error: 15:3-14: function call can be resolved to multiple functions
+// 	note 2:2-40: candidate function
+// 	note 3:2-41: candidate function
+// error: 19:3-11: cannot find overloaded function which matches signature
+// error: 19:3-11: function expects 2 arguments, 1 provided
+// 	note 2:2-37: candidate function
+// error: 19:3-11: function expects 2 arguments, 1 provided
+// 	note 3:2-38: candidate function
+// error: 23:9-10: 'x' not found
+// error: 23:12-13: 'y' not found

+ 17 - 0
tests/contract_testcases/evm/ev.sol

@@ -0,0 +1,17 @@
+contract C {
+	event E1(int);
+	event E1(int, int);
+
+	function e() public {
+		emit E1();
+	}
+}
+
+// ---- Expect: diagnostics ----
+// error: 6:3-12: event type 'E1' has 1 fields, 0 provided
+// 	note 2:8-10: definition of E1
+// 	note 2:2-15: candidate event
+// error: 6:3-12: event type 'E1' has 2 fields, 0 provided
+// 	note 3:8-10: definition of E1
+// 	note 3:2-20: candidate event
+// error: 6:3-12: cannot find event with matching signature

+ 25 - 0
tests/contract_testcases/evm/events_v0.5.sol

@@ -0,0 +1,25 @@
+pragma solidity 0.4.12;
+
+contract A {
+	event E(int indexed a, bool indexed b) anonymous;
+}
+
+contract B is A {
+	event E(int a, bool b);
+
+	function test1() public {
+		emit E(1, true);
+	}
+
+	function test2() public {
+		emit E({a: 1, b: true});
+	}
+}
+
+// ---- Expect: diagnostics ----
+// warning: 11:3-18: emit can be resolved to multiple incompatible events. This is permitted in Solidity v0.5 and earlier, however it could indicate a bug.
+// 	note 8:8-9: candidate event
+// 	note 4:8-9: candidate event
+// warning: 15:3-26: emit can be resolved to multiple incompatible events. This is permitted in Solidity v0.5 and earlier, however it could indicate a bug.
+// 	note 8:8-9: candidate event
+// 	note 4:8-9: candidate event

+ 25 - 0
tests/contract_testcases/evm/events_v0.6.sol

@@ -0,0 +1,25 @@
+pragma solidity ^0.6.5;
+
+contract A {
+	event E(int indexed a, bool indexed b) anonymous;
+}
+
+contract B is A {
+	event E(int a, bool b);
+
+	function test1() public {
+		emit E(1, true);
+	}
+
+	function test2() public {
+		emit E({a: 1, b: true});
+	}
+}
+
+// ---- Expect: diagnostics ----
+// error: 11:3-18: emit can be resolved to multiple events
+// 	note 8:8-9: candidate event
+// 	note 4:8-9: candidate event
+// error: 15:3-26: emit can be resolved to multiple events
+// 	note 8:8-9: candidate event
+// 	note 4:8-9: candidate event

+ 22 - 0
tests/contract_testcases/evm/issue1534.sol

@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.0;
+
+contract Overloaded {
+    // Version 1
+    function f(uint n) internal pure returns (uint) {
+        return n;
+    }
+
+    // Version 3
+    function f(int n, uint x) internal pure returns (int) {
+        uint result = f(uint256(n), x);
+        return n > 0 ? int(result) : -int(result);
+    }
+
+    // Version 2
+    function f(uint n, uint x) internal pure returns (uint) {
+        return n + x;
+    }
+}
+
+// ---- Expect: diagnostics ----

+ 112 - 0
tests/contract_testcases/evm/overloaded.sol

@@ -0,0 +1,112 @@
+contract C {
+	function foo(int) public {}
+	function foo(int a, bool b) public {}
+	function foo(bool b, int a) public {}
+	function test1() public {
+		foo({a: true, b: 1});
+	}
+
+	function test2() public {
+		foo({a: 1, b: true});
+	}
+
+	function test3() public {
+		foo({a: moo, b: meh});
+	}
+}
+
+contract B is C {
+	function test1() public {
+		B.foo({a: true, b: 1});
+	}
+
+	function test2() public {
+		B.foo({a: 1, b: true});
+	}
+
+	function test3(C c) public {
+		c.foo({a: true, b: 1});
+	}
+
+	function test4(C c) public {
+		c.foo({a: 1, b: true});
+	}
+
+	function test5(C c) public {
+		c.bar({a: 1, b: true});
+	}
+
+	function test6(C c) public {
+		c.bar({a: 1, b: x});
+	}
+}
+
+// ---- Expect: diagnostics ----
+// error: 5:2-25: function 'test1' with this signature already defined
+// 	note 19:2-25: previous definition of function 'test1'
+// error: 6:3-23: cannot find overloaded function which matches signature
+// 	note 2:2-29: candidate function
+// error: 6:3-23: cannot find overloaded function which matches signature
+// 	note 3:2-39: candidate function
+// error: 6:3-23: cannot find overloaded function which matches signature
+// 	note 4:2-39: candidate function
+// error: 6:3-23: function cannot be called with named arguments as 1 of its parameters do not have names
+// 	note 2:2-26: definition of foo
+// 	note 2:2-29: candidate function
+// error: 6:11-15: conversion from bool to int256 not possible
+// 	note 3:2-39: candidate function
+// error: 6:11-15: conversion from bool to int256 not possible
+// 	note 4:2-39: candidate function
+// error: 6:20-21: expected 'bool', found integer
+// 	note 3:2-39: candidate function
+// error: 6:20-21: expected 'bool', found integer
+// 	note 4:2-39: candidate function
+// error: 9:2-25: function 'test2' with this signature already defined
+// 	note 23:2-25: previous definition of function 'test2'
+// error: 10:3-23: function call can be resolved to multiple functions
+// 	note 3:2-39: candidate function
+// 	note 4:2-39: candidate function
+// error: 14:11-14: 'moo' not found
+// error: 14:19-22: 'meh' not found
+// error: 20:3-25: cannot find overloaded function which matches signature
+// 	note 2:2-29: candidate function
+// error: 20:3-25: cannot find overloaded function which matches signature
+// 	note 3:2-39: candidate function
+// error: 20:3-25: cannot find overloaded function which matches signature
+// 	note 4:2-39: candidate function
+// error: 20:3-25: function cannot be called with named arguments as 1 of its parameters do not have names
+// 	note 2:2-26: definition of foo
+// 	note 2:2-29: candidate function
+// error: 20:13-17: conversion from bool to int256 not possible
+// 	note 3:2-39: candidate function
+// error: 20:13-17: conversion from bool to int256 not possible
+// 	note 4:2-39: candidate function
+// error: 20:22-23: expected 'bool', found integer
+// 	note 3:2-39: candidate function
+// error: 20:22-23: expected 'bool', found integer
+// 	note 4:2-39: candidate function
+// error: 24:3-25: function call can be resolved to multiple functions
+// 	note 3:2-39: candidate function
+// 	note 4:2-39: candidate function
+// error: 28:3-25: cannot find overloaded function which matches signature
+// 	note 2:2-29: candidate function
+// error: 28:3-25: cannot find overloaded function which matches signature
+// 	note 3:2-39: candidate function
+// error: 28:3-25: cannot find overloaded function which matches signature
+// 	note 4:2-39: candidate function
+// error: 28:3-25: function cannot be called with named arguments as 1 of its parameters do not have names
+// 	note 2:2-26: definition of foo
+// 	note 2:2-29: candidate function
+// error: 28:13-17: conversion from bool to int256 not possible
+// 	note 3:2-39: candidate function
+// error: 28:13-17: conversion from bool to int256 not possible
+// 	note 4:2-39: candidate function
+// error: 28:22-23: expected 'bool', found integer
+// 	note 3:2-39: candidate function
+// error: 28:22-23: expected 'bool', found integer
+// 	note 4:2-39: candidate function
+// error: 32:3-25: function call can be resolved to multiple functions
+// 	note 3:2-39: candidate function
+// 	note 4:2-39: candidate function
+// error: 36:3-25: contract 'C' does not have function 'bar'
+// error: 40:19-20: 'x' not found

+ 45 - 29
tests/contract_testcases/evm/pragmas.dot

@@ -1,36 +1,52 @@
 strict digraph "tests/contract_testcases/evm/pragmas.sol" {
-	contract [label="contract C\ntests/contract_testcases/evm/pragmas.sol:6:1-14"]
+	contract [label="contract C\ntests/contract_testcases/evm/pragmas.sol:9:1-14"]
 	pragma [label="name: foo\nvalue: bar\ntests/contract_testcases/evm/pragmas.sol:1:1-15"]
-	pragma_4 [label="name: abicode\nvalue: v2\ntests/contract_testcases/evm/pragmas.sol:2:1-18"]
-	pragma_5 [label="name: abicode\nvalue: 'v2'\ntests/contract_testcases/evm/pragmas.sol:3:1-20"]
-	pragma_6 [label="name: solidity\ntests/contract_testcases/evm/pragmas.sol:4:1-65"]
-	operator [label="version: ^0.5.16\ntests/contract_testcases/evm/pragmas.sol:4:17-24"]
-	range [label="version: 0.4 - 1\ntests/contract_testcases/evm/pragmas.sol:4:25-32"]
-	or [label="||\ntests/contract_testcases/evm/pragmas.sol:4:33-52"]
-	operator_10 [label="version: =0.8.22\ntests/contract_testcases/evm/pragmas.sol:4:33-40"]
-	operator_11 [label="version: >=0.8.21\ntests/contract_testcases/evm/pragmas.sol:4:44-52"]
-	operator_12 [label="version: <=2\ntests/contract_testcases/evm/pragmas.sol:4:53-56"]
-	operator_13 [label="version: ~1\ntests/contract_testcases/evm/pragmas.sol:4:57-59"]
-	plain [label="version: 0.6.2\ntests/contract_testcases/evm/pragmas.sol:4:60-65"]
-	diagnostic [label="unknown pragma 'foo' with value 'bar' ignored\nlevel Warning\ntests/contract_testcases/evm/pragmas.sol:1:1-15"]
-	diagnostic_17 [label="unknown pragma 'abicode' with value 'v2' ignored\nlevel Warning\ntests/contract_testcases/evm/pragmas.sol:2:1-18"]
-	diagnostic_18 [label="unknown pragma 'abicode' with value 'v2' ignored\nlevel Warning\ntests/contract_testcases/evm/pragmas.sol:3:1-20"]
-	diagnostic_19 [label="found contract 'C'\nlevel Debug\ntests/contract_testcases/evm/pragmas.sol:6:1-14"]
+	pragma_4 [label="name: abicoder\nvalue: v2\ntests/contract_testcases/evm/pragmas.sol:2:1-19"]
+	pragma_5 [label="name: solidity\ntests/contract_testcases/evm/pragmas.sol:4:1-28"]
+	range [label="version: 0 - 1\ntests/contract_testcases/evm/pragmas.sol:4:17-22"]
+	range_7 [label="version: 0 - 2\ntests/contract_testcases/evm/pragmas.sol:4:23-28"]
+	pragma_8 [label="name: abicoder\nvalue: 'v2'\ntests/contract_testcases/evm/pragmas.sol:5:1-21"]
+	pragma_9 [label="name: solidity\ntests/contract_testcases/evm/pragmas.sol:6:1-57"]
+	operator [label="version: ^0.5.16\ntests/contract_testcases/evm/pragmas.sol:6:17-24"]
+	or [label="||\ntests/contract_testcases/evm/pragmas.sol:6:25-44"]
+	operator_12 [label="version: =0.8.22\ntests/contract_testcases/evm/pragmas.sol:6:25-32"]
+	operator_13 [label="version: >=0.8.21\ntests/contract_testcases/evm/pragmas.sol:6:36-44"]
+	operator_14 [label="version: <=2\ntests/contract_testcases/evm/pragmas.sol:6:45-48"]
+	operator_15 [label="version: ~1\ntests/contract_testcases/evm/pragmas.sol:6:49-51"]
+	plain [label="version: 0.6.2\ntests/contract_testcases/evm/pragmas.sol:6:52-57"]
+	pragma_17 [label="name: solidity\ntests/contract_testcases/evm/pragmas.sol:7:1-40"]
+	or_18 [label="||\ntests/contract_testcases/evm/pragmas.sol:7:17-40"]
+	range_19 [label="version: 0.4 - 1\ntests/contract_testcases/evm/pragmas.sol:7:17-24"]
+	range_20 [label="version: 0.3 - 0.5.16\ntests/contract_testcases/evm/pragmas.sol:7:28-40"]
+	diagnostic [label="unknown pragma 'foo' with value 'bar'\nlevel Error\ntests/contract_testcases/evm/pragmas.sol:1:1-15"]
+	diagnostic_23 [label="pragma 'abicoder' ignored\nlevel Debug\ntests/contract_testcases/evm/pragmas.sol:2:1-19"]
+	diagnostic_24 [label="'4294967296' is not a valid number\nlevel Error\ntests/contract_testcases/evm/pragmas.sol:3:17-28"]
+	diagnostic_25 [label="version ranges can only be combined with the || operator\nlevel Error\ntests/contract_testcases/evm/pragmas.sol:4:1-28"]
+	diagnostic_26 [label="pragma 'abicoder' ignored\nlevel Debug\ntests/contract_testcases/evm/pragmas.sol:5:1-21"]
+	diagnostic_27 [label="found contract 'C'\nlevel Debug\ntests/contract_testcases/evm/pragmas.sol:9:1-14"]
 	contracts -> contract
 	pragmas -> pragma
 	pragmas -> pragma_4
 	pragmas -> pragma_5
-	pragmas -> pragma_6
-	pragma_6 -> operator [label="version 0"]
-	pragma_6 -> range [label="version 1"]
-	pragma_6 -> or [label="version 2"]
-	or -> operator_10 [label="left"]
-	or -> operator_11 [label="right"]
-	pragma_6 -> operator_12 [label="version 3"]
-	pragma_6 -> operator_13 [label="version 4"]
-	pragma_6 -> plain [label="version 5"]
-	diagnostics -> diagnostic [label="Warning"]
-	diagnostics -> diagnostic_17 [label="Warning"]
-	diagnostics -> diagnostic_18 [label="Warning"]
-	diagnostics -> diagnostic_19 [label="Debug"]
+	pragma_5 -> range [label="version 0"]
+	pragma_5 -> range_7 [label="version 1"]
+	pragmas -> pragma_8
+	pragmas -> pragma_9
+	pragma_9 -> operator [label="version 0"]
+	pragma_9 -> or [label="version 1"]
+	or -> operator_12 [label="left"]
+	or -> operator_13 [label="right"]
+	pragma_9 -> operator_14 [label="version 2"]
+	pragma_9 -> operator_15 [label="version 3"]
+	pragma_9 -> plain [label="version 4"]
+	pragmas -> pragma_17
+	pragma_17 -> or_18 [label="version 0"]
+	or_18 -> range_19 [label="left"]
+	or_18 -> range_20 [label="right"]
+	diagnostics -> diagnostic [label="Error"]
+	diagnostics -> diagnostic_23 [label="Debug"]
+	diagnostics -> diagnostic_24 [label="Error"]
+	diagnostics -> diagnostic_25 [label="Error"]
+	diagnostics -> diagnostic_26 [label="Debug"]
+	diagnostics -> diagnostic_27 [label="Debug"]
 }

+ 9 - 6
tests/contract_testcases/evm/pragmas.sol

@@ -1,12 +1,15 @@
 pragma foo bar;
-pragma abicode v2;
-pragma abicode "v2";
-pragma solidity ^0.5.16 0.4 - 1 =0.8.22 || >=0.8.21 <=2 ~1 0.6.2;
+pragma abicoder v2;
+pragma solidity ^4294967296;
+pragma solidity 0 - 1 0 - 2;
+pragma abicoder "v2";
+pragma solidity ^0.5.16 =0.8.22 || >=0.8.21 <=2 ~1 0.6.2;
+pragma solidity 0.4 - 1 || 0.3 - 0.5.16;
 
 contract C {}
 
 // ---- Expect: dot ----
 // ---- Expect: diagnostics ----
-// warning: 1:1-15: unknown pragma 'foo' with value 'bar' ignored
-// warning: 2:1-18: unknown pragma 'abicode' with value 'v2' ignored
-// warning: 3:1-20: unknown pragma 'abicode' with value 'v2' ignored
+// error: 1:1-15: unknown pragma 'foo' with value 'bar'
+// error: 3:17-28: '4294967296' is not a valid number
+// error: 4:1-28: version ranges can only be combined with the || operator

+ 30 - 0
tests/contract_testcases/evm/super-resolves-to-multiple.sol

@@ -0,0 +1,30 @@
+abstract contract b1 {
+    function foo(int) internal virtual returns (uint64) {
+        return 100;
+    }
+}
+
+abstract contract b2 {
+    function foo(int) internal virtual returns (uint64) {
+        return 200;
+    }
+    function foo(int64) internal virtual returns (uint64) {
+        return 101;
+    }
+}
+
+contract a is b1, b2 {
+    function baz() public returns (uint64) {
+        // this will return 100
+        return super.foo(1);
+    }
+
+    function foo(int) internal override(b1, b2) returns (uint64) {
+        return 2;
+    }
+}
+
+// ---- Expect: diagnostics ----
+// error: 19:16-28: function call can be resolved to multiple functions
+// 	note 2:5-4:6: candidate function
+// 	note 11:5-13:6: candidate function

+ 3 - 2
tests/contract_testcases/evm/unresolved_constants.sol

@@ -1,4 +1,5 @@
 int constant C1 = LEN1;
+int constant C3 = foo();
 
 contract c {
 	int constant C2 = LEN1;
@@ -7,7 +8,7 @@ contract c {
 	bool[C2] var22;
 }
 
-
 // ---- Expect: diagnostics ----
 // error: 1:19-23: 'LEN1' not found
-// error: 4:20-24: 'LEN1' not found
+// error: 2:19-24: cannot call function in constant expression
+// error: 5:20-24: 'LEN1' not found

+ 2 - 0
tests/contract_testcases/polkadot/contracts/external_call_02.sol

@@ -18,8 +18,10 @@
                 return x * t;
             }
         }
+
 // ---- Expect: diagnostics ----
 // warning: 4:27-33: 'public': visibility for constructors is ignored
 // error: 8:24-52: function expects 1 arguments, 2 provided
 // error: 8:41-42: duplicate argument with name 't'
+// error: 8:44-49: conversion from bool to int32 not possible
 // warning: 14:34-40: 'public': visibility for constructors is ignored

+ 2 - 1
tests/contract_testcases/polkadot/contracts/external_call_03.sol

@@ -18,8 +18,9 @@
                 return x * t;
             }
         }
+
 // ---- Expect: diagnostics ----
 // warning: 4:27-33: 'public': visibility for constructors is ignored
-// error: 5:21-42: duplicate argument name 'a'
+// error: 5:35-36: duplicate argument with name 'a'
 // 	note 5:32-33: location of previous argument
 // warning: 14:34-40: 'public': visibility for constructors is ignored

+ 59 - 0
tests/contract_testcases/polkadot/contracts/overloaded_constructor.sol

@@ -0,0 +1,59 @@
+contract S {
+	constructor(int) {}
+	constructor(int a, bool b) {}
+	constructor(bool b, int a) {}
+
+	function p() public {}
+}
+
+contract T {
+	function test1() public {
+		new S();
+	}
+
+	function test2() public {
+		new S(true);
+	}
+
+	function test3() public {
+		new S({});
+	}
+
+	function test4() public {
+		new S({a: 1, b: true});
+	}
+
+}
+
+// ---- Expect: diagnostics ----
+// warning: 3:18-19: function parameter 'a' is unused
+// warning: 3:26-27: function parameter 'b' is unused
+// warning: 4:19-20: function parameter 'b' is unused
+// warning: 4:26-27: function parameter 'a' is unused
+// error: 11:3-10: cannot find overloaded constructor which matches signature
+// error: 11:3-10: constructor expects 1 arguments, 0 provided
+// 	note 2:2-21: candidate constructor
+// error: 11:3-10: constructor expects 2 arguments, 0 provided
+// 	note 3:2-31: candidate constructor
+// error: 11:3-10: constructor expects 2 arguments, 0 provided
+// 	note 4:2-31: candidate constructor
+// error: 15:3-14: cannot find overloaded constructor which matches signature
+// error: 15:3-14: constructor expects 2 arguments, 1 provided
+// 	note 3:2-31: candidate constructor
+// error: 15:3-14: constructor expects 2 arguments, 1 provided
+// 	note 4:2-31: candidate constructor
+// error: 15:9-13: conversion from bool to int256 not possible
+// 	note 2:2-21: candidate constructor
+// error: 19:3-12: cannot find overloaded constructor which matches signature
+// error: 19:3-12: constructor cannot be called with named arguments as 1 of its parameters do not have names
+// 	note 2:2-19: definition of constructor
+// 	note 2:2-21: candidate constructor
+// error: 19:3-12: constructor expects 2 arguments, 0 provided
+// 	note 3:2-29: definition of constructor
+// 	note 3:2-31: candidate constructor
+// error: 19:3-12: constructor expects 2 arguments, 0 provided
+// 	note 4:2-29: definition of constructor
+// 	note 4:2-31: candidate constructor
+// error: 23:3-25: can be resolved to multiple constructors
+// 	note 3:2-31: candidate constructor
+// 	note 4:2-31: candidate constructor

+ 53 - 0
tests/contract_testcases/polkadot/contracts/overloaded_constructor2.sol

@@ -0,0 +1,53 @@
+contract S {
+	constructor(int32 a) {}
+	constructor(int64 a) {}
+
+	function test1() public {}
+}
+
+contract T {
+	function test1() public {
+		new S(1);
+	}
+
+	function test2() public {
+		new S({a: 1});
+	}
+
+	function test3() public {
+		new S({b: 1});
+	}
+}
+
+contract R {
+	function test1() public {
+		new T({});
+	}
+
+	function test2() public {
+		new T({a: 1});
+	}
+
+	function test3() public {
+		new T(1);
+	}
+}
+
+// ---- Expect: diagnostics ----
+// warning: 2:20-21: function parameter 'a' is unused
+// warning: 3:20-21: function parameter 'a' is unused
+// error: 10:3-11: constructor can be resolved to multiple functions
+// 	note 2:2-25: candidate constructor
+// 	note 3:2-25: candidate constructor
+// error: 14:3-16: can be resolved to multiple constructors
+// 	note 2:2-25: candidate constructor
+// 	note 3:2-25: candidate constructor
+// error: 18:3-16: cannot find overloaded constructor which matches signature
+// error: 18:3-16: missing argument 'a' to constructor
+// 	note 2:2-23: definition of constructor
+// 	note 2:2-25: candidate constructor
+// error: 18:3-16: missing argument 'a' to constructor
+// 	note 3:2-23: definition of constructor
+// 	note 3:2-25: candidate constructor
+// error: 28:3-16: cannot find matching constructor
+// error: 32:3-11: default constructor does not take arguments

+ 2 - 2
tests/contract_testcases/polkadot/doccomments_everywhere.sol

@@ -103,5 +103,5 @@ contract Property {
 // error: 7:3-11: 'Counters' not found
 // error: 17:17-24: 'ERC1155' not found
 // error: 28:29-38: '_tokenIds' not found
-// error: 53:7-50: cannot find overloaded function which matches signature
-// error: 86:7-38: cannot find overloaded function which matches signature
+// error: 53:12-28: unknown function 'isApprovedForAll'
+// error: 86:12-21: unknown function 'balanceOf'

+ 1 - 0
tests/contract_testcases/polkadot/events/emit_02.sol

@@ -7,3 +7,4 @@
         }
 // ---- Expect: diagnostics ----
 // error: 5:17-32: event type 'foo' has 2 fields, 1 provided
+// 	note 3:19-22: definition of foo

+ 0 - 2
tests/contract_testcases/polkadot/events/emit_04.sol

@@ -6,6 +6,4 @@
             }
         }
 // ---- Expect: diagnostics ----
-// error: 5:17-44: event cannot be emitted with named fields as 2 of its fields do not have names
-// 	note 3:19-22: definition of foo
 // error: 5:36-37: duplicate argument with name 'a'

+ 2 - 0
tests/contract_testcases/polkadot/events/signatures.sol

@@ -10,6 +10,8 @@
                 emit foo(true, 1);
             }
         }
+
 // ---- Expect: diagnostics ----
 // warning: 3:15-18: event 'bar' has never been emitted
+// warning: 6:19-22: event 'foo' has never been emitted
 // warning: 7:19-22: event 'bar' has never been emitted

+ 2 - 0
tests/contract_testcases/polkadot/events/signatures_01.sol

@@ -9,5 +9,7 @@
                 emit foo(true, 1);
             }
         }
+
 // ---- Expect: diagnostics ----
 // warning: 3:15-18: event 'foo' has never been emitted
+// warning: 6:19-22: event 'foo' has never been emitted

+ 13 - 8
tests/contract_testcases/polkadot/events/signatures_02.sol

@@ -1,12 +1,17 @@
+pragma solidity 0.5.4;
 
-        event foo(bool a, int b);
+event foo(bool a, int b);
 
-        contract c {
-            event foo(int b);
-            event foo(int x);
+contract c {
+	event foo(int b);
+	event foo(int x);
+
+	function f() public {
+		// resolves to multiple events, but solc 0.5 permits this
+		emit foo(true, 1);
+	}
+}
 
-            function f() public {
-                emit foo(true, 1);
-            }
-        }
 // ---- Expect: diagnostics ----
+// warning: 6:8-11: event 'foo' has never been emitted
+// warning: 7:8-11: event 'foo' has never been emitted

+ 4 - 1
tests/contract_testcases/polkadot/events/signatures_03.sol

@@ -1,5 +1,5 @@
 
-        event foo(bool a, int b);
+        event foo(bool a, int32 b);
 
         contract c {
             event foo(bool x, int y);
@@ -9,3 +9,6 @@
             }
         }
 // ---- Expect: diagnostics ----
+// error: 8:17-34: emit can be resolved to multiple events
+// 	note 5:19-22: candidate event
+// 	note 2:15-18: candidate event

+ 3 - 0
tests/contract_testcases/polkadot/inheritance/call_inherited_function_02.sol

@@ -18,3 +18,6 @@
 // ---- Expect: diagnostics ----
 // error: 3:13-59: function 'foo' with this signature already defined
 // 	note 9:13-59: previous definition of function 'foo'
+// error: 14:24-35: function call can be resolved to multiple functions
+// 	note 3:13-5:14: candidate function
+// 	note 9:13-11:14: candidate function

+ 26 - 0
tests/contract_testcases/solana/dup1.sol

@@ -0,0 +1,26 @@
+@program_id("F1ipperKF9EfD821ZbbYjS319LXYiBmjhzkkf5a26rC")
+contract C {
+	function foo(int8 a, int8 b) external {}
+	function foo(int64 a, int8 b) external {}
+}
+
+contract D {
+	function test1() external {
+		C.foo(1, 2);
+	}
+
+	function test2() external {
+		C.foo(x, y);
+	}
+}
+
+// ---- Expect: diagnostics ----
+// warning: 3:20-21: function parameter 'a' is unused
+// warning: 3:28-29: function parameter 'b' is unused
+// warning: 4:21-22: function parameter 'a' is unused
+// warning: 4:29-30: function parameter 'b' is unused
+// error: 9:3-14: function call can be resolved to multiple functions
+// 	note 3:2-42: candidate function
+// 	note 4:2-43: candidate function
+// error: 13:9-10: 'x' not found
+// error: 13:12-13: 'y' not found

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

@@ -7,15 +7,21 @@ contract c {
         emit Foo(a, b);
         emit Foo({ no: a, name: b, no: c});
         emit Foo2({ no: a, name: b, no: c});
+        emit Foo({ no: 1, name: "name", foo: 5});
     }
 }
+
 // ---- Expect: diagnostics ----
 // error: 7:18-19: 'a' not found
+// error: 7:21-22: 'b' not found
 // error: 8:24-25: 'a' not found
 // error: 8:33-34: 'b' not found
 // error: 8:36-38: duplicate argument with name 'no'
 // error: 8:40-41: 'c' is a contract
+// error: 9:14-18: event 'Foo2' not found
 // error: 9:25-26: 'a' not found
 // error: 9:34-35: 'b' not found
 // error: 9:37-39: duplicate argument with name 'no'
 // error: 9:41-42: 'c' is a contract
+// error: 10:9-49: event expects 2 arguments, 3 provided
+// 	note 3:7-10: definition of Foo

+ 2 - 2
tests/contract_testcases/solana/now.sol

@@ -28,5 +28,5 @@ contract c {
 // error: 7:3-6: unknown function or type 'now'
 // error: 10:10-13: 'now' not found. 'now' was an alias for 'block.timestamp' in older versions of the Solidity language. Please use 'block.timestamp' instead.
 // error: 13:10-13: 'now' not found. 'now' was an alias for 'block.timestamp' in older versions of the Solidity language. Please use 'block.timestamp' instead.
-// error: 21:34-37: 'now' not found. 'now' was an alias for 'block.timestamp' in older versions of the Solidity language. Please use 'block.timestamp' instead.
-// error: 22:41-44: 'now' not found. 'now' was an alias for 'block.timestamp' in older versions of the Solidity language. Please use 'block.timestamp' instead.
+// error: 21:34-37: 'now' not found
+// error: 22:41-44: 'now' not found

+ 3 - 1
tests/evm.rs

@@ -9,6 +9,8 @@ use std::{
 };
 use walkdir::WalkDir;
 
+mod evm_tests;
+
 fn test_solidity(src: &str) -> ast::Namespace {
     let mut cache = FileResolver::default();
 
@@ -253,7 +255,7 @@ fn ethereum_solidity_tests() {
         })
         .sum();
 
-    assert_eq!(errors, 933);
+    assert_eq!(errors, 916);
 }
 
 fn set_file_contents(source: &str, path: &Path) -> (FileResolver, Vec<String>) {

+ 3 - 0
tests/evm_tests/mod.rs

@@ -0,0 +1,3 @@
+// SPDX-License-Identifier: Apache-2.0
+
+mod pragma;

+ 192 - 0
tests/evm_tests/pragma.rs

@@ -0,0 +1,192 @@
+// SPDX-License-Identifier: Apache-2.0
+
+use crate::test_solidity;
+use solang::sema::ast;
+
+#[test]
+fn version_match() {
+    let ns = test_solidity("pragma solidity 0.5.16; pragma solidity 0.5;");
+
+    assert_eq!(
+        ns.highest_solidity_version(0),
+        Some(ast::Version {
+            major: 0,
+            minor: Some(5),
+            patch: Some(16)
+        })
+    );
+
+    let ns = test_solidity("pragma solidity 0.5; pragma solidity 0.5.16 <1 >0;");
+
+    assert_eq!(
+        ns.highest_solidity_version(0),
+        Some(ast::Version {
+            major: 0,
+            minor: Some(5),
+            patch: Some(16)
+        })
+    );
+
+    let ns = test_solidity("pragma solidity 0.5 || 0.5.16 || <1 || >0;");
+
+    assert_eq!(
+        ns.highest_solidity_version(0),
+        Some(ast::Version {
+            major: 0,
+            minor: Some(5),
+            patch: Some(16)
+        })
+    );
+
+    let ns = test_solidity("pragma solidity =0.5; pragma solidity <=0.5.16 >= 0;");
+
+    assert_eq!(
+        ns.highest_solidity_version(0),
+        Some(ast::Version {
+            major: 0,
+            minor: Some(5),
+            patch: Some(16)
+        })
+    );
+
+    let ns = test_solidity("pragma solidity <0.5.17;");
+
+    assert_eq!(
+        ns.highest_solidity_version(0),
+        Some(ast::Version {
+            major: 0,
+            minor: Some(5),
+            patch: Some(16)
+        })
+    );
+
+    let ns = test_solidity("pragma solidity <0.5;");
+
+    assert_eq!(
+        ns.highest_solidity_version(0),
+        Some(ast::Version {
+            major: 0,
+            minor: Some(4),
+            patch: None
+        })
+    );
+
+    let ns = test_solidity("pragma solidity <0;");
+
+    assert_eq!(ns.highest_solidity_version(0), None);
+
+    let ns = test_solidity("pragma solidity ~0.0;");
+
+    assert_eq!(ns.highest_solidity_version(0), None);
+
+    let ns = test_solidity("pragma solidity <0.5.0;");
+
+    assert_eq!(
+        ns.highest_solidity_version(0),
+        Some(ast::Version {
+            major: 0,
+            minor: Some(4),
+            patch: None
+        })
+    );
+
+    let ns = test_solidity("pragma solidity <1;");
+
+    assert_eq!(
+        ns.highest_solidity_version(0),
+        Some(ast::Version {
+            major: 0,
+            minor: None,
+            patch: None
+        })
+    );
+
+    let ns = test_solidity("pragma solidity <1.0.0;");
+
+    assert_eq!(
+        ns.highest_solidity_version(0),
+        Some(ast::Version {
+            major: 0,
+            minor: None,
+            patch: None
+        })
+    );
+
+    let ns = test_solidity("pragma solidity ^1.2.0;");
+
+    assert_eq!(
+        ns.highest_solidity_version(0),
+        Some(ast::Version {
+            major: 1,
+            minor: None,
+            patch: None
+        })
+    );
+
+    let ns = test_solidity("pragma solidity ^0.5.16;");
+
+    assert_eq!(
+        ns.highest_solidity_version(0),
+        Some(ast::Version {
+            major: 0,
+            minor: Some(5),
+            patch: None
+        })
+    );
+
+    let ns = test_solidity("pragma solidity ~0.5.16 *1.0.0;");
+
+    assert_eq!(
+        ns.highest_solidity_version(0),
+        Some(ast::Version {
+            major: 0,
+            minor: Some(5),
+            patch: None
+        })
+    );
+
+    let ns = test_solidity("pragma solidity 0.5.0 - 0.5.18;");
+
+    assert_eq!(
+        ns.highest_solidity_version(0),
+        Some(ast::Version {
+            major: 0,
+            minor: Some(5),
+            patch: Some(18)
+        })
+    );
+
+    assert!(ns.solidity_minor_version(0, 5));
+
+    let ns = test_solidity("pragma solidity 0.4 - 0.5 ^0;");
+
+    assert_eq!(
+        ns.highest_solidity_version(0),
+        Some(ast::Version {
+            major: 0,
+            minor: Some(5),
+            patch: None
+        })
+    );
+
+    assert!(ns.solidity_minor_version(0, 5));
+
+    let ns = test_solidity("pragma solidity 0.4 - 0.5 ~0;");
+
+    assert_eq!(
+        ns.highest_solidity_version(0),
+        Some(ast::Version {
+            major: 0,
+            minor: Some(5),
+            patch: None
+        })
+    );
+
+    assert!(ns.solidity_minor_version(0, 5));
+
+    let ns = test_solidity("pragma solidity ~0;");
+
+    assert_eq!(ns.highest_solidity_version(0), None);
+
+    assert!(!ns.solidity_minor_version(0, 5));
+}

+ 0 - 1
tests/solana.rs

@@ -296,7 +296,6 @@ fn serialize_parameters(
     let mut refs = Vec::new();
     let mut v: Vec<u8> = Vec::new();
 
-    #[allow(clippy::ptr_arg)]
     fn serialize_account(
         v: &mut Vec<u8>,
         refs: &mut Vec<AccountRef>,

+ 10 - 12
tests/unused_variable_detection.rs

@@ -39,7 +39,7 @@ fn emit_event() {
     let case_2 = r#"
     event Hey(uint8 n);
     contract usedEvent {
-        event Hey(uint8 n);
+        event Hey(bool);
         event Hello(uint8 n);
         function emitEvent(uint8 n) public {
             emit Hey(n);
@@ -48,16 +48,15 @@ fn emit_event() {
     "#;
 
     let ns = parse(case_2);
-    assert_eq!(ns.diagnostics.count_warnings(), 1);
-    assert_eq!(
-        ns.diagnostics.first_warning().message,
-        "event 'Hello' has never been emitted"
-    );
+    let warnings = ns.diagnostics.warnings();
+    assert_eq!(warnings.len(), 2);
+    assert_eq!(warnings[0].message, "event 'Hey' has never been emitted");
+    assert_eq!(warnings[1].message, "event 'Hello' has never been emitted");
 
     // Unused event
     let case_2 = r#"
     contract F {
-        event Hey(uint8 n);
+        event Hey(bool);
         event Hello(uint8 n);
     }
     contract usedEvent is F {
@@ -69,11 +68,10 @@ fn emit_event() {
     "#;
 
     let ns = parse(case_2);
-    assert_eq!(ns.diagnostics.count_warnings(), 1);
-    assert_eq!(
-        ns.diagnostics.first_warning().message,
-        "event 'Hello' has never been emitted"
-    );
+    let warnings = ns.diagnostics.warnings();
+    assert_eq!(warnings.len(), 2);
+    assert_eq!(warnings[0].message, "event 'Hey' has never been emitted");
+    assert_eq!(warnings[1].message, "event 'Hello' has never been emitted");
 
     // Unused event
     let case_2 = r#"

+ 8 - 8
vscode/src/test/suite/extension.test.ts

@@ -48,14 +48,14 @@ suite('Extension Test Suite', function () {
   test('Testing for diagnostics warnings.', async () => {
     await testdiagnos(diagnosdoc4, [
       {
-        message: `unknown pragma 'foo' with value 'bar' ignored`,
-        range: toRange(0, 0, 0, 14),
+        message: `function can be declared 'pure'`,
+        range: toRange(1, 5, 1, 48),
         severity: vscode.DiagnosticSeverity.Warning,
         source: `solidity`,
       },
       {
-        message: `function can be declared 'pure'`,
-        range: toRange(3, 5, 3, 40),
+        message: `function parameter 'b' is unused`,
+        range: toRange(1, 22, 1, 23),
         severity: vscode.DiagnosticSeverity.Warning,
         source: `solidity`,
       },
@@ -548,7 +548,7 @@ async function testcompletion(docUri: vscode.Uri) {
   const labels1 = get_labels(suggestions1);
   labels1.includes('second');
   assert.ok(labels1.includes('VAL') && labels1.includes('value') && !labels1.includes('first') && labels1.includes('second'));
-  
+
   const pos2 = new vscode.Position(11, 10);
   const suggestions2 = (await vscode.commands.executeCommand(
     'vscode.executeCompletionItemProvider',
@@ -566,8 +566,8 @@ async function testcompletion(docUri: vscode.Uri) {
     '.'
   )) as vscode.CompletionList;
   const labels3 = get_labels(suggestions3);
-  assert.ok(labels3.includes('field1') && labels3.includes('field2') && !labels3.includes('value')&& !labels3.includes('first') && !labels3.includes('second') && !labels3.includes('VAL'));
-  
+  assert.ok(labels3.includes('field1') && labels3.includes('field2') && !labels3.includes('value') && !labels3.includes('first') && !labels3.includes('second') && !labels3.includes('VAL'));
+
   const pos4 = new vscode.Position(18, 36);
   const suggestions4 = (await vscode.commands.executeCommand(
     'vscode.executeCompletionItemProvider',
@@ -576,7 +576,7 @@ async function testcompletion(docUri: vscode.Uri) {
     '.'
   )) as vscode.CompletionList;
   const labels4 = get_labels(suggestions4);
-  assert.ok(labels4.includes('aaa') && labels4.includes('bbbb') && !labels4.includes('field1') && !labels4.includes('field2') && !labels4.includes('value')&& !labels4.includes('first') && !labels4.includes('second') && !labels4.includes('VAL'));
+  assert.ok(labels4.includes('aaa') && labels4.includes('bbbb') && !labels4.includes('field1') && !labels4.includes('field2') && !labels4.includes('value') && !labels4.includes('first') && !labels4.includes('second') && !labels4.includes('VAL'));
 }
 
 async function testhover(docUri: vscode.Uri) {

+ 2 - 4
vscode/src/testFixture/four.sol

@@ -1,7 +1,5 @@
-pragma foo bar;
-
 contract a {
-     function foo() public returns (int) {
+     function foo(int b) public returns (int256) {
           return 1;
      }
-}
+}