diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 1bc89ce17..1d30a1910 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -2064,7 +2064,8 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) errorMsg += " Did you intend to call the function?"; } } - if (exprType->category() == Type::Category::Contract) + else if (exprType->category() == Type::Category::Contract) + { for (auto const& addressMember: AddressType::addressPayable().nativeMembers(nullptr)) if (addressMember.name == memberName) { @@ -2073,6 +2074,21 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) errorMsg += " Use \"address(" + varName + ")." + memberName + "\" to access this address member."; break; } + } + else if (auto addressType = dynamic_cast(exprType.get())) + { + // Trigger error when using send or transfer with a non-payable fallback function. + if (memberName == "send" || memberName == "transfer") + { + solAssert( + addressType->stateMutability() != StateMutability::Payable, + "Expected address not-payable as members were not found" + ); + + errorMsg = "\"send\" and \"transfer\" are only available for objects of type \"address payable\", not \"" + exprType->toString() + "\"."; + } + } + m_errorReporter.fatalTypeError( _memberAccess.location(), errorMsg @@ -2115,26 +2131,6 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) annotation.isLValue = annotation.referencedDeclaration->isLValue(); } - if (exprType->category() == Type::Category::Contract) - { - // Warn about using send or transfer with a non-payable fallback function. - if (auto callType = dynamic_cast(type(_memberAccess).get())) - { - auto kind = callType->kind(); - auto contractType = dynamic_cast(exprType.get()); - solAssert(!!contractType, "Should be contract type."); - - if ( - (kind == FunctionType::Kind::Send || kind == FunctionType::Kind::Transfer) && - !contractType->isPayable() - ) - m_errorReporter.typeError( - _memberAccess.location(), - "Value transfer to a contract without a payable fallback function." - ); - } - } - // TODO some members might be pure, but for example `address(0x123).balance` is not pure // although every subexpression is, so leaving this limited for now. if (auto tt = dynamic_cast(exprType.get())) diff --git a/test/libsolidity/syntaxTests/memberLookup/contract_not_payable_send.sol b/test/libsolidity/syntaxTests/memberLookup/contract_not_payable_send.sol new file mode 100644 index 000000000..56ad89286 --- /dev/null +++ b/test/libsolidity/syntaxTests/memberLookup/contract_not_payable_send.sol @@ -0,0 +1,8 @@ +contract C { + function f() public { + address(this).send(10); + } +} + +// ---- +// TypeError: (47-65): "send" and "transfer" are only available for objects of type "address payable", not "address". diff --git a/test/libsolidity/syntaxTests/memberLookup/contract_not_payable_transfer.sol b/test/libsolidity/syntaxTests/memberLookup/contract_not_payable_transfer.sol new file mode 100644 index 000000000..21d792ee4 --- /dev/null +++ b/test/libsolidity/syntaxTests/memberLookup/contract_not_payable_transfer.sol @@ -0,0 +1,8 @@ +contract C { + function f() public { + address(this).transfer(10); + } +} + +// ---- +// TypeError: (47-69): "send" and "transfer" are only available for objects of type "address payable", not "address".