Merge pull request #9001 from a3d4/partfix-5819-refactor-typechecker

Refactor TypeChecker to assign different IDs to different errors
This commit is contained in:
chriseth 2020-05-26 11:59:54 +02:00 committed by GitHub
commit 5fedb4eab0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

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") +
": " + ": " +
@ -2021,7 +2018,7 @@ void TypeChecker::typeCheckFunctionGeneralChecks(
{ {
/// For error message: Struct members that were removed during conversion to memory. /// For error message: Struct members that were removed during conversion to memory.
TypePointer const expressionType = type(_functionCall.expression()); TypePointer const expressionType = type(_functionCall.expression());
TypeType const& t = dynamic_cast<TypeType const&>(*expressionType); auto const& t = dynamic_cast<TypeType const&>(*expressionType);
auto const& structType = dynamic_cast<StructType const&>(*t.actualType()); auto const& structType = dynamic_cast<StructType const&>(*t.actualType());
set<string> membersRemovedForStructConstructor = structType.membersMissingInMemory(); set<string> membersRemovedForStructConstructor = structType.membersMissingInMemory();
@ -2031,6 +2028,8 @@ void TypeChecker::typeCheckFunctionGeneralChecks(
for (auto const& member: membersRemovedForStructConstructor) for (auto const& member: membersRemovedForStructConstructor)
msg += " " + member; msg += " " + member;
} }
return { isVariadic ? 1123_error : 9755_error, msg };
} }
else if ( else if (
_functionType->kind() == FunctionType::Kind::BareCall || _functionType->kind() == FunctionType::Kind::BareCall ||
@ -2040,25 +2039,38 @@ void TypeChecker::typeCheckFunctionGeneralChecks(
) )
{ {
if (arguments.empty()) if (arguments.empty())
msg += return {
isVariadic ? 7653_error : 6138_error,
msg +
" This function requires a single bytes argument." " This function requires a single bytes argument."
" Use \"\" as argument to provide empty calldata."; " Use \"\" as argument to provide empty calldata."
};
else else
msg += return {
isVariadic ? 9390_error : 8922_error,
msg +
" This function requires a single bytes argument." " This function requires a single bytes argument."
" If all your arguments are value types, you can use" " If all your arguments are value types, you can use"
" abi.encode(...) to properly generate it."; " abi.encode(...) to properly generate it."
};
} }
else if ( else if (
_functionType->kind() == FunctionType::Kind::KECCAK256 || _functionType->kind() == FunctionType::Kind::KECCAK256 ||
_functionType->kind() == FunctionType::Kind::SHA256 || _functionType->kind() == FunctionType::Kind::SHA256 ||
_functionType->kind() == FunctionType::Kind::RIPEMD160 _functionType->kind() == FunctionType::Kind::RIPEMD160
) )
msg += return {
isVariadic ? 1220_error : 4323_error,
msg +
" This function requires a single bytes argument." " This function requires a single bytes argument."
" Use abi.encodePacked(...) to obtain the pre-0.5.0" " Use abi.encodePacked(...) to obtain the pre-0.5.0"
" behaviour or abi.encode(...) to use ABI encoding."; " behaviour or abi.encode(...) to use ABI encoding."
m_errorReporter.typeError(1093_error, _functionCall.location(), msg); };
else
return { isVariadic ? 9308_error : 6160_error, msg };
}();
m_errorReporter.typeError(errorId, _functionCall.location(), description);
return; return;
} }
@ -2134,6 +2146,7 @@ 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]))
{ {
auto [errorId, description] = [&]() -> tuple<ErrorId, string> {
string msg = string msg =
"Invalid type for argument in function call. " "Invalid type for argument in function call. "
"Invalid implicit conversion from " + "Invalid implicit conversion from " +
@ -2147,20 +2160,29 @@ void TypeChecker::typeCheckFunctionGeneralChecks(
_functionType->kind() == FunctionType::Kind::BareDelegateCall || _functionType->kind() == FunctionType::Kind::BareDelegateCall ||
_functionType->kind() == FunctionType::Kind::BareStaticCall _functionType->kind() == FunctionType::Kind::BareStaticCall
) )
msg += return {
8051_error,
msg +
" This function requires a single bytes argument." " This function requires a single bytes argument."
" If all your arguments are value types, you can" " If all your arguments are value types, you can"
" use abi.encode(...) to properly generate it."; " use abi.encode(...) to properly generate it."
};
else if ( else if (
_functionType->kind() == FunctionType::Kind::KECCAK256 || _functionType->kind() == FunctionType::Kind::KECCAK256 ||
_functionType->kind() == FunctionType::Kind::SHA256 || _functionType->kind() == FunctionType::Kind::SHA256 ||
_functionType->kind() == FunctionType::Kind::RIPEMD160 _functionType->kind() == FunctionType::Kind::RIPEMD160
) )
msg += return {
7556_error,
msg +
" This function requires a single bytes argument." " This function requires a single bytes argument."
" Use abi.encodePacked(...) to obtain the pre-0.5.0" " Use abi.encodePacked(...) to obtain the pre-0.5.0"
" behaviour or abi.encode(...) to use ABI encoding."; " behaviour or abi.encode(...) to use ABI encoding."
m_errorReporter.typeError(6706_error, paramArgMap[i]->location(), msg); };
else
return { 9553_error, msg };
}();
m_errorReporter.typeError(errorId, paramArgMap[i]->location(), description);
} }
} }
} }
@ -2549,44 +2571,50 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
" outside of storage." " outside of storage."
); );
} }
auto [errorId, description] = [&]() -> tuple<ErrorId, string> {
string errorMsg = "Member \"" + memberName + "\" not found or not visible " 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(); TypePointers const& t = funType->returnParameterTypes();
if (memberName == "value") if (memberName == "value")
{ {
if (funType->kind() == FunctionType::Kind::Creation) if (funType->kind() == FunctionType::Kind::Creation)
errorMsg = "Constructor for " + t.front()->toString() + " must be payable for member \"value\" to be available."; return {
8827_error,
"Constructor for " + t.front()->toString() + " must be payable for member \"value\" to be available."
};
else if ( else if (
funType->kind() == FunctionType::Kind::DelegateCall || funType->kind() == FunctionType::Kind::DelegateCall ||
funType->kind() == FunctionType::Kind::BareDelegateCall funType->kind() == FunctionType::Kind::BareDelegateCall
) )
errorMsg = "Member \"value\" is not allowed in delegated calls due to \"msg.value\" persisting."; return { 8477_error, "Member \"value\" is not allowed in delegated calls due to \"msg.value\" persisting." };
else else
errorMsg = "Member \"value\" is only available for payable functions."; return { 8820_error, "Member \"value\" is only available for payable functions." };
} }
else if ( else if (
t.size() == 1 && t.size() == 1 && (
(t.front()->category() == Type::Category::Struct || t.front()->category() == Type::Category::Struct ||
t.front()->category() == Type::Category::Contract) t.front()->category() == Type::Category::Contract
) )
errorMsg += " Did you intend to call the function?"; )
return { 6005_error, errorMsg + " Did you intend to call the function?" };
} }
else if (exprType->category() == Type::Category::Contract) else if (exprType->category() == Type::Category::Contract)
{ {
for (auto const& addressMember: TypeProvider::payableAddress()->nativeMembers(nullptr)) for (MemberList::Member const& addressMember: TypeProvider::payableAddress()->nativeMembers(nullptr))
if (addressMember.name == memberName) if (addressMember.name == memberName)
{ {
Identifier const* var = dynamic_cast<Identifier const*>(&_memberAccess.expression()); auto const* var = dynamic_cast<Identifier const*>(&_memberAccess.expression());
string varName = var ? var->name() : "..."; string varName = var ? var->name() : "...";
errorMsg += " Use \"address(" + varName + ")." + memberName + "\" to access this address member."; errorMsg += " Use \"address(" + varName + ")." + memberName + "\" to access this address member.";
break; return { 5256_error, errorMsg };
} }
} }
else if (auto addressType = dynamic_cast<AddressType const*>(exprType)) else if (auto const* addressType = dynamic_cast<AddressType const*>(exprType))
{ {
// Trigger error when using send or transfer with a non-payable fallback function. // Trigger error when using send or transfer with a non-payable fallback function.
if (memberName == "send" || memberName == "transfer") if (memberName == "send" || memberName == "transfer")
@ -2596,14 +2624,17 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
"Expected address not-payable as members were not found" "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() + "\"."; 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);
} }