diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 0802f5c22e95..79ccad507128 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -3186,6 +3186,46 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) ++it; } + // When both a native property and a library function share the same name, + // disambiguate based on call syntax: + // - With parentheses (function call): prefer function members (library) + // - Without parentheses (property access): prefer non-function members (native) + // This mirrors the identifier resolution pattern at lines 3693-3745 where + // VariableDeclarations are preferred without parentheses. + if (possibleMembers.size() > 1) + { + bool hasFunction = false; + bool hasNonFunction = false; + for (auto const& member: possibleMembers) + { + if (member.type->category() == Type::Category::Function) + hasFunction = true; + else + hasNonFunction = true; + } + + if (hasFunction && hasNonFunction) + { + MemberList::MemberMap filtered; + if (arguments) + { + // Called with parentheses - keep only functions (library) + for (auto const& member: possibleMembers) + if (member.type->category() == Type::Category::Function) + filtered.push_back(member); + } + else + { + // Accessed without parentheses - keep only non-functions (native property) + for (auto const& member: possibleMembers) + if (member.type->category() != Type::Category::Function) + filtered.push_back(member); + } + if (filtered.size() == 1) + possibleMembers = std::move(filtered); + } + } + annotation.isConstant = false; if (possibleMembers.empty()) diff --git a/test/libsolidity/syntaxTests/memberLookup/library_function_vs_native_property_with_parens.sol b/test/libsolidity/syntaxTests/memberLookup/library_function_vs_native_property_with_parens.sol new file mode 100644 index 000000000000..ecac94c4d24b --- /dev/null +++ b/test/libsolidity/syntaxTests/memberLookup/library_function_vs_native_property_with_parens.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.8.0; + +// Test that library function is preferred over native property when called with parentheses. +// This allows OpenZeppelin's Address.isContract() to work alongside Tron's native address.isContract property. +library Address { + function isContract(address account) internal view returns (bool) { + return account.code.length > 0; + } +} + +contract C { + using Address for address; + + function check(address a) public view returns (bool) { + // With parentheses - should use library function + return a.isContract(); + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/memberLookup/library_function_vs_native_property_without_parens.sol b/test/libsolidity/syntaxTests/memberLookup/library_function_vs_native_property_without_parens.sol new file mode 100644 index 000000000000..dd46794aacdb --- /dev/null +++ b/test/libsolidity/syntaxTests/memberLookup/library_function_vs_native_property_without_parens.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.8.0; + +// Test that native property is preferred over library function when accessed without parentheses. +// This allows Tron's native address.isContract property to work alongside library functions. +library Address { + function isContract(address account) internal view returns (bool) { + return account.code.length > 0; + } +} + +contract C { + using Address for address; + + function check(address a) public view returns (bool) { + // Without parentheses - should use native property + return a.isContract; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/memberLookup/native_isContract_property.sol b/test/libsolidity/syntaxTests/memberLookup/native_isContract_property.sol new file mode 100644 index 000000000000..1f78572a80e5 --- /dev/null +++ b/test/libsolidity/syntaxTests/memberLookup/native_isContract_property.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.8.0; + +// Test Tron's native address.isContract property works without library. +contract C { + function check(address a) public view returns (bool) { + return a.isContract; + } +} +// ----