diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 1bc89ce17..ce1910129 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -2051,20 +2051,18 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) errorMsg.pop_back(); errorMsg += " - did you forget the \"payable\" modifier?"; } - else if (exprType->category() == Type::Category::Function) + else if (auto const& funType = dynamic_pointer_cast(exprType)) { - if (auto const& funType = dynamic_pointer_cast(exprType)) - { - auto const& t = funType->returnParameterTypes(); - if (t.size() == 1) - if ( - t.front()->category() == Type::Category::Contract || - t.front()->category() == Type::Category::Struct - ) - errorMsg += " Did you intend to call the function?"; - } + auto const& t = funType->returnParameterTypes(); + if (t.size() == 1) + if ( + t.front()->category() == Type::Category::Contract || + t.front()->category() == Type::Category::Struct + ) + 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 +2071,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 +2128,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".