mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Refactor TypeChecker to assign different IDs to different errors
This commit is contained in:
parent
7e1f26270b
commit
936ea6f950
@ -1994,19 +1994,16 @@ void TypeChecker::typeCheckFunctionGeneralChecks(
|
|||||||
bool const isStructConstructorCall =
|
bool const isStructConstructorCall =
|
||||||
_functionCall.annotation().kind == FunctionCallKind::StructConstructorCall;
|
_functionCall.annotation().kind == FunctionCallKind::StructConstructorCall;
|
||||||
|
|
||||||
string msg;
|
auto [errorId, description] = [&]() -> tuple<ErrorId, string> {
|
||||||
|
string msg = isVariadic ?
|
||||||
if (isVariadic)
|
|
||||||
msg +=
|
|
||||||
"Need at least " +
|
"Need at least " +
|
||||||
toString(parameterTypes.size()) +
|
toString(parameterTypes.size()) +
|
||||||
" arguments for " +
|
" arguments for " +
|
||||||
string(isStructConstructorCall ? "struct constructor" : "function call") +
|
string(isStructConstructorCall ? "struct constructor" : "function call") +
|
||||||
", but provided only " +
|
", but provided only " +
|
||||||
toString(arguments.size()) +
|
toString(arguments.size()) +
|
||||||
".";
|
"."
|
||||||
else
|
:
|
||||||
msg +=
|
|
||||||
"Wrong argument count for " +
|
"Wrong argument count for " +
|
||||||
string(isStructConstructorCall ? "struct constructor" : "function call") +
|
string(isStructConstructorCall ? "struct constructor" : "function call") +
|
||||||
": " +
|
": " +
|
||||||
@ -2016,49 +2013,64 @@ void TypeChecker::typeCheckFunctionGeneralChecks(
|
|||||||
toString(parameterTypes.size()) +
|
toString(parameterTypes.size()) +
|
||||||
".";
|
".";
|
||||||
|
|
||||||
// Extend error message in case we try to construct a struct with mapping member.
|
// Extend error message in case we try to construct a struct with mapping member.
|
||||||
if (isStructConstructorCall)
|
if (isStructConstructorCall)
|
||||||
{
|
|
||||||
/// For error message: Struct members that were removed during conversion to memory.
|
|
||||||
TypePointer const expressionType = type(_functionCall.expression());
|
|
||||||
TypeType const& t = dynamic_cast<TypeType const&>(*expressionType);
|
|
||||||
auto const& structType = dynamic_cast<StructType const&>(*t.actualType());
|
|
||||||
set<string> membersRemovedForStructConstructor = structType.membersMissingInMemory();
|
|
||||||
|
|
||||||
if (!membersRemovedForStructConstructor.empty())
|
|
||||||
{
|
{
|
||||||
msg += " Members that have to be skipped in memory:";
|
/// For error message: Struct members that were removed during conversion to memory.
|
||||||
for (auto const& member: membersRemovedForStructConstructor)
|
TypePointer const expressionType = type(_functionCall.expression());
|
||||||
msg += " " + member;
|
auto const& t = dynamic_cast<TypeType const&>(*expressionType);
|
||||||
|
auto const& structType = dynamic_cast<StructType const&>(*t.actualType());
|
||||||
|
set<string> membersRemovedForStructConstructor = structType.membersMissingInMemory();
|
||||||
|
|
||||||
|
if (!membersRemovedForStructConstructor.empty())
|
||||||
|
{
|
||||||
|
msg += " Members that have to be skipped in memory:";
|
||||||
|
for (auto const& member: membersRemovedForStructConstructor)
|
||||||
|
msg += " " + member;
|
||||||
|
}
|
||||||
|
|
||||||
|
return { isVariadic ? 1123_error : 9755_error, msg };
|
||||||
}
|
}
|
||||||
}
|
else if (
|
||||||
else if (
|
_functionType->kind() == FunctionType::Kind::BareCall ||
|
||||||
_functionType->kind() == FunctionType::Kind::BareCall ||
|
_functionType->kind() == FunctionType::Kind::BareCallCode ||
|
||||||
_functionType->kind() == FunctionType::Kind::BareCallCode ||
|
_functionType->kind() == FunctionType::Kind::BareDelegateCall ||
|
||||||
_functionType->kind() == FunctionType::Kind::BareDelegateCall ||
|
_functionType->kind() == FunctionType::Kind::BareStaticCall
|
||||||
_functionType->kind() == FunctionType::Kind::BareStaticCall
|
)
|
||||||
)
|
{
|
||||||
{
|
if (arguments.empty())
|
||||||
if (arguments.empty())
|
return {
|
||||||
msg +=
|
isVariadic ? 7653_error : 6138_error,
|
||||||
|
msg +
|
||||||
|
" This function requires a single bytes argument."
|
||||||
|
" Use \"\" as argument to provide empty calldata."
|
||||||
|
};
|
||||||
|
else
|
||||||
|
return {
|
||||||
|
isVariadic ? 9390_error : 8922_error,
|
||||||
|
msg +
|
||||||
|
" This function requires a single bytes argument."
|
||||||
|
" If all your arguments are value types, you can use"
|
||||||
|
" abi.encode(...) to properly generate it."
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else if (
|
||||||
|
_functionType->kind() == FunctionType::Kind::KECCAK256 ||
|
||||||
|
_functionType->kind() == FunctionType::Kind::SHA256 ||
|
||||||
|
_functionType->kind() == FunctionType::Kind::RIPEMD160
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
isVariadic ? 1220_error : 4323_error,
|
||||||
|
msg +
|
||||||
" This function requires a single bytes argument."
|
" This function requires a single bytes argument."
|
||||||
" Use \"\" as argument to provide empty calldata.";
|
" Use abi.encodePacked(...) to obtain the pre-0.5.0"
|
||||||
|
" behaviour or abi.encode(...) to use ABI encoding."
|
||||||
|
};
|
||||||
else
|
else
|
||||||
msg +=
|
return { isVariadic ? 9308_error : 6160_error, msg };
|
||||||
" This function requires a single bytes argument."
|
}();
|
||||||
" If all your arguments are value types, you can use"
|
|
||||||
" abi.encode(...) to properly generate it.";
|
m_errorReporter.typeError(errorId, _functionCall.location(), description);
|
||||||
}
|
|
||||||
else if (
|
|
||||||
_functionType->kind() == FunctionType::Kind::KECCAK256 ||
|
|
||||||
_functionType->kind() == FunctionType::Kind::SHA256 ||
|
|
||||||
_functionType->kind() == FunctionType::Kind::RIPEMD160
|
|
||||||
)
|
|
||||||
msg +=
|
|
||||||
" This function requires a single bytes argument."
|
|
||||||
" Use abi.encodePacked(...) to obtain the pre-0.5.0"
|
|
||||||
" behaviour or abi.encode(...) to use ABI encoding.";
|
|
||||||
m_errorReporter.typeError(1093_error, _functionCall.location(), msg);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2134,33 +2146,43 @@ void TypeChecker::typeCheckFunctionGeneralChecks(
|
|||||||
solAssert(!!paramArgMap[i], "unmapped parameter");
|
solAssert(!!paramArgMap[i], "unmapped parameter");
|
||||||
if (!type(*paramArgMap[i])->isImplicitlyConvertibleTo(*parameterTypes[i]))
|
if (!type(*paramArgMap[i])->isImplicitlyConvertibleTo(*parameterTypes[i]))
|
||||||
{
|
{
|
||||||
string msg =
|
auto [errorId, description] = [&]() -> tuple<ErrorId, string> {
|
||||||
"Invalid type for argument in function call. "
|
string msg =
|
||||||
"Invalid implicit conversion from " +
|
"Invalid type for argument in function call. "
|
||||||
type(*paramArgMap[i])->toString() +
|
"Invalid implicit conversion from " +
|
||||||
" to " +
|
type(*paramArgMap[i])->toString() +
|
||||||
parameterTypes[i]->toString() +
|
" to " +
|
||||||
" requested.";
|
parameterTypes[i]->toString() +
|
||||||
if (
|
" requested.";
|
||||||
_functionType->kind() == FunctionType::Kind::BareCall ||
|
if (
|
||||||
_functionType->kind() == FunctionType::Kind::BareCallCode ||
|
_functionType->kind() == FunctionType::Kind::BareCall ||
|
||||||
_functionType->kind() == FunctionType::Kind::BareDelegateCall ||
|
_functionType->kind() == FunctionType::Kind::BareCallCode ||
|
||||||
_functionType->kind() == FunctionType::Kind::BareStaticCall
|
_functionType->kind() == FunctionType::Kind::BareDelegateCall ||
|
||||||
)
|
_functionType->kind() == FunctionType::Kind::BareStaticCall
|
||||||
msg +=
|
)
|
||||||
" This function requires a single bytes argument."
|
return {
|
||||||
" If all your arguments are value types, you can"
|
8051_error,
|
||||||
" use abi.encode(...) to properly generate it.";
|
msg +
|
||||||
else if (
|
" This function requires a single bytes argument."
|
||||||
_functionType->kind() == FunctionType::Kind::KECCAK256 ||
|
" If all your arguments are value types, you can"
|
||||||
_functionType->kind() == FunctionType::Kind::SHA256 ||
|
" use abi.encode(...) to properly generate it."
|
||||||
_functionType->kind() == FunctionType::Kind::RIPEMD160
|
};
|
||||||
)
|
else if (
|
||||||
msg +=
|
_functionType->kind() == FunctionType::Kind::KECCAK256 ||
|
||||||
" This function requires a single bytes argument."
|
_functionType->kind() == FunctionType::Kind::SHA256 ||
|
||||||
" Use abi.encodePacked(...) to obtain the pre-0.5.0"
|
_functionType->kind() == FunctionType::Kind::RIPEMD160
|
||||||
" behaviour or abi.encode(...) to use ABI encoding.";
|
)
|
||||||
m_errorReporter.typeError(6706_error, paramArgMap[i]->location(), msg);
|
return {
|
||||||
|
7556_error,
|
||||||
|
msg +
|
||||||
|
" This function requires a single bytes argument."
|
||||||
|
" Use abi.encodePacked(...) to obtain the pre-0.5.0"
|
||||||
|
" behaviour or abi.encode(...) to use ABI encoding."
|
||||||
|
};
|
||||||
|
else
|
||||||
|
return { 9553_error, msg };
|
||||||
|
}();
|
||||||
|
m_errorReporter.typeError(errorId, paramArgMap[i]->location(), description);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2549,61 +2571,70 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
|
|||||||
" outside of storage."
|
" outside of storage."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
string errorMsg = "Member \"" + memberName + "\" not found or not visible "
|
|
||||||
|
auto [errorId, description] = [&]() -> tuple<ErrorId, string> {
|
||||||
|
string errorMsg = "Member \"" + memberName + "\" not found or not visible "
|
||||||
"after argument-dependent lookup in " + exprType->toString() + ".";
|
"after argument-dependent lookup in " + exprType->toString() + ".";
|
||||||
|
|
||||||
if (auto const& funType = dynamic_cast<FunctionType const*>(exprType))
|
if (auto const* funType = dynamic_cast<FunctionType const*>(exprType))
|
||||||
{
|
|
||||||
auto const& t = funType->returnParameterTypes();
|
|
||||||
|
|
||||||
if (memberName == "value")
|
|
||||||
{
|
{
|
||||||
if (funType->kind() == FunctionType::Kind::Creation)
|
TypePointers const& t = funType->returnParameterTypes();
|
||||||
errorMsg = "Constructor for " + t.front()->toString() + " must be payable for member \"value\" to be available.";
|
|
||||||
else if (
|
if (memberName == "value")
|
||||||
funType->kind() == FunctionType::Kind::DelegateCall ||
|
|
||||||
funType->kind() == FunctionType::Kind::BareDelegateCall
|
|
||||||
)
|
|
||||||
errorMsg = "Member \"value\" is not allowed in delegated calls due to \"msg.value\" persisting.";
|
|
||||||
else
|
|
||||||
errorMsg = "Member \"value\" is only available for payable functions.";
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
t.size() == 1 &&
|
|
||||||
(t.front()->category() == Type::Category::Struct ||
|
|
||||||
t.front()->category() == Type::Category::Contract)
|
|
||||||
)
|
|
||||||
errorMsg += " Did you intend to call the function?";
|
|
||||||
}
|
|
||||||
else if (exprType->category() == Type::Category::Contract)
|
|
||||||
{
|
|
||||||
for (auto const& addressMember: TypeProvider::payableAddress()->nativeMembers(nullptr))
|
|
||||||
if (addressMember.name == memberName)
|
|
||||||
{
|
{
|
||||||
Identifier const* var = dynamic_cast<Identifier const*>(&_memberAccess.expression());
|
if (funType->kind() == FunctionType::Kind::Creation)
|
||||||
string varName = var ? var->name() : "...";
|
return {
|
||||||
errorMsg += " Use \"address(" + varName + ")." + memberName + "\" to access this address member.";
|
8827_error,
|
||||||
break;
|
"Constructor for " + t.front()->toString() + " must be payable for member \"value\" to be available."
|
||||||
|
};
|
||||||
|
else if (
|
||||||
|
funType->kind() == FunctionType::Kind::DelegateCall ||
|
||||||
|
funType->kind() == FunctionType::Kind::BareDelegateCall
|
||||||
|
)
|
||||||
|
return { 8477_error, "Member \"value\" is not allowed in delegated calls due to \"msg.value\" persisting." };
|
||||||
|
else
|
||||||
|
return { 8820_error, "Member \"value\" is only available for payable functions." };
|
||||||
}
|
}
|
||||||
}
|
else if (
|
||||||
else if (auto addressType = dynamic_cast<AddressType const*>(exprType))
|
t.size() == 1 && (
|
||||||
{
|
t.front()->category() == Type::Category::Struct ||
|
||||||
// Trigger error when using send or transfer with a non-payable fallback function.
|
t.front()->category() == Type::Category::Contract
|
||||||
if (memberName == "send" || memberName == "transfer")
|
)
|
||||||
{
|
)
|
||||||
solAssert(
|
return { 6005_error, errorMsg + " Did you intend to call the function?" };
|
||||||
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() + "\".";
|
|
||||||
}
|
}
|
||||||
}
|
else if (exprType->category() == Type::Category::Contract)
|
||||||
|
{
|
||||||
|
for (MemberList::Member const& addressMember: TypeProvider::payableAddress()->nativeMembers(nullptr))
|
||||||
|
if (addressMember.name == memberName)
|
||||||
|
{
|
||||||
|
auto const* var = dynamic_cast<Identifier const*>(&_memberAccess.expression());
|
||||||
|
string varName = var ? var->name() : "...";
|
||||||
|
errorMsg += " Use \"address(" + varName + ")." + memberName + "\" to access this address member.";
|
||||||
|
return { 5256_error, errorMsg };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (auto const* addressType = dynamic_cast<AddressType const*>(exprType))
|
||||||
|
{
|
||||||
|
// 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"
|
||||||
|
);
|
||||||
|
|
||||||
|
return { 2604_error, "\"send\" and \"transfer\" are only available for objects of type \"address payable\", not \"" + exprType->toString() + "\"." };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { 5856_error, errorMsg };
|
||||||
|
}();
|
||||||
|
|
||||||
m_errorReporter.fatalTypeError(
|
m_errorReporter.fatalTypeError(
|
||||||
4035_error,
|
errorId,
|
||||||
_memberAccess.location(),
|
_memberAccess.location(),
|
||||||
errorMsg
|
description
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else if (possibleMembers.size() > 1)
|
else if (possibleMembers.size() > 1)
|
||||||
@ -3165,17 +3196,17 @@ void TypeChecker::requireLValue(Expression const& _expression, bool _ordinaryAss
|
|||||||
if (_expression.annotation().isLValue)
|
if (_expression.annotation().isLValue)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
return m_errorReporter.typeError(1123_error, _expression.location(), [&]() {
|
auto [errorId, description] = [&]() -> tuple<ErrorId, string> {
|
||||||
if (_expression.annotation().isConstant)
|
if (_expression.annotation().isConstant)
|
||||||
return "Cannot assign to a constant variable.";
|
return { 6520_error, "Cannot assign to a constant variable." };
|
||||||
|
|
||||||
if (auto indexAccess = dynamic_cast<IndexAccess const*>(&_expression))
|
if (auto indexAccess = dynamic_cast<IndexAccess const*>(&_expression))
|
||||||
{
|
{
|
||||||
if (type(indexAccess->baseExpression())->category() == Type::Category::FixedBytes)
|
if (type(indexAccess->baseExpression())->category() == Type::Category::FixedBytes)
|
||||||
return "Single bytes in fixed bytes arrays cannot be modified.";
|
return { 9222_error, "Single bytes in fixed bytes arrays cannot be modified." };
|
||||||
else if (auto arrayType = dynamic_cast<ArrayType const*>(type(indexAccess->baseExpression())))
|
else if (auto arrayType = dynamic_cast<ArrayType const*>(type(indexAccess->baseExpression())))
|
||||||
if (arrayType->dataStoredIn(DataLocation::CallData))
|
if (arrayType->dataStoredIn(DataLocation::CallData))
|
||||||
return "Calldata arrays are read-only.";
|
return { 3335_error, "Calldata arrays are read-only." };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto memberAccess = dynamic_cast<MemberAccess const*>(&_expression))
|
if (auto memberAccess = dynamic_cast<MemberAccess const*>(&_expression))
|
||||||
@ -3183,18 +3214,20 @@ void TypeChecker::requireLValue(Expression const& _expression, bool _ordinaryAss
|
|||||||
if (auto structType = dynamic_cast<StructType const*>(type(memberAccess->expression())))
|
if (auto structType = dynamic_cast<StructType const*>(type(memberAccess->expression())))
|
||||||
{
|
{
|
||||||
if (structType->dataStoredIn(DataLocation::CallData))
|
if (structType->dataStoredIn(DataLocation::CallData))
|
||||||
return "Calldata structs are read-only.";
|
return { 9942_error, "Calldata structs are read-only." };
|
||||||
}
|
}
|
||||||
else if (dynamic_cast<ArrayType const*>(type(memberAccess->expression())))
|
else if (dynamic_cast<ArrayType const*>(type(memberAccess->expression())))
|
||||||
if (memberAccess->memberName() == "length")
|
if (memberAccess->memberName() == "length")
|
||||||
return "Member \"length\" is read-only and cannot be used to resize arrays.";
|
return { 7567_error, "Member \"length\" is read-only and cannot be used to resize arrays." };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto identifier = dynamic_cast<Identifier const*>(&_expression))
|
if (auto identifier = dynamic_cast<Identifier const*>(&_expression))
|
||||||
if (auto varDecl = dynamic_cast<VariableDeclaration const*>(identifier->annotation().referencedDeclaration))
|
if (auto varDecl = dynamic_cast<VariableDeclaration const*>(identifier->annotation().referencedDeclaration))
|
||||||
if (varDecl->isExternalCallableParameter() && dynamic_cast<ReferenceType const*>(identifier->annotation().type))
|
if (varDecl->isExternalCallableParameter() && dynamic_cast<ReferenceType const*>(identifier->annotation().type))
|
||||||
return "External function arguments of reference type are read-only.";
|
return { 7128_error, "External function arguments of reference type are read-only." };
|
||||||
|
|
||||||
return "Expression has to be an lvalue.";
|
return { 4247_error, "Expression has to be an lvalue." };
|
||||||
}());
|
}();
|
||||||
|
|
||||||
|
m_errorReporter.typeError(errorId, _expression.location(), description);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user