Refactor TypeChecker to assign different IDs to different errors

This commit is contained in:
a3d4 2020-05-21 03:49:31 +02:00
parent 7e1f26270b
commit 936ea6f950

View File

@ -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);
} }