| 371 | } |
| 372 | |
| 373 | void StaticAnalyzer::checkDoubleStorageAssignment(Assignment const& _assignment) |
| 374 | { |
| 375 | size_t storageToStorageCopies = 0; |
| 376 | size_t toStorageCopies = 0; |
| 377 | size_t storageByteArrayPushes = 0; |
| 378 | size_t storageByteAccesses = 0; |
| 379 | auto count = [&](TupleExpression const& _lhs, TupleType const& _rhs, auto _recurse) -> void { |
| 380 | TupleType const& lhsType = dynamic_cast<TupleType const&>(*type(_lhs)); |
| 381 | TupleExpression const* lhsResolved = dynamic_cast<TupleExpression const*>(resolveOuterUnaryTuples(&_lhs)); |
| 382 | |
| 383 | solAssert(lhsResolved && lhsResolved->components().size() == lhsType.components().size()); |
| 384 | if (lhsType.components().size() != _rhs.components().size()) |
| 385 | { |
| 386 | solAssert(m_errorReporter.hasErrors(), ""); |
| 387 | return; |
| 388 | } |
| 389 | |
| 390 | for (auto&& [index, componentType]: lhsType.components() | ranges::views::enumerate) |
| 391 | { |
| 392 | if (ReferenceType const* ref = dynamic_cast<ReferenceType const*>(componentType)) |
| 393 | { |
| 394 | if (ref->dataStoredIn(DataLocation::Storage) && !ref->isPointer()) |
| 395 | { |
| 396 | toStorageCopies++; |
| 397 | if (_rhs.components()[index]->dataStoredIn(DataLocation::Storage)) |
| 398 | storageToStorageCopies++; |
| 399 | } |
| 400 | } |
| 401 | else if (FixedBytesType const* bytesType = dynamic_cast<FixedBytesType const*>(componentType)) |
| 402 | { |
| 403 | if (bytesType->numBytes() == 1) |
| 404 | { |
| 405 | if (FunctionCall const* lhsCall = dynamic_cast<FunctionCall const*>(resolveOuterUnaryTuples(lhsResolved->components().at(index).get()))) |
| 406 | { |
| 407 | FunctionType const& callType = dynamic_cast<FunctionType const&>(*type(lhsCall->expression())); |
| 408 | if (callType.kind() == FunctionType::Kind::ArrayPush) |
| 409 | { |
| 410 | ArrayType const& arrayType = dynamic_cast<ArrayType const&>(*callType.selfType()); |
| 411 | if (arrayType.isByteArray() && arrayType.dataStoredIn(DataLocation::Storage)) |
| 412 | { |
| 413 | ++storageByteAccesses; |
| 414 | ++storageByteArrayPushes; |
| 415 | } |
| 416 | } |
| 417 | } |
| 418 | else if (IndexAccess const* indexAccess = dynamic_cast<IndexAccess const*>(resolveOuterUnaryTuples(lhsResolved->components().at(index).get()))) |
| 419 | { |
| 420 | if (ArrayType const* arrayType = dynamic_cast<ArrayType const*>(type(indexAccess->baseExpression()))) |
| 421 | if (arrayType->isByteArray() && arrayType->dataStoredIn(DataLocation::Storage)) |
| 422 | ++storageByteAccesses; |
| 423 | } |
| 424 | } |
| 425 | } |
| 426 | else if (dynamic_cast<TupleType const*>(componentType)) |
| 427 | if (auto const* lhsNested = dynamic_cast<TupleExpression const*>(lhsResolved->components().at(index).get())) |
| 428 | if (auto const* rhsNestedType = dynamic_cast<TupleType const*>(_rhs.components().at(index))) |
| 429 | _recurse( |
| 430 | *lhsNested, |
nothing calls this directly
no test coverage detected