|
29 | 29 | #include <libsolutil/FunctionSelector.h>
|
30 | 30 | #include <liblangutil/ErrorReporter.h>
|
31 | 31 |
|
| 32 | +#include <range/v3/action/reverse.hpp> |
| 33 | + |
32 | 34 | #include <limits>
|
33 | 35 |
|
34 | 36 | using namespace solidity;
|
@@ -76,6 +78,8 @@ bool PostTypeContractLevelChecker::check(ContractDefinition const& _contract)
|
76 | 78 | if (_contract.storageLayoutSpecifier())
|
77 | 79 | checkStorageLayoutSpecifier(_contract);
|
78 | 80 |
|
| 81 | + warnStorageLayoutBaseNearStorageEnd(_contract); |
| 82 | + |
79 | 83 | return !Error::containsErrors(m_errorReporter.errors());
|
80 | 84 | }
|
81 | 85 |
|
@@ -145,3 +149,60 @@ void PostTypeContractLevelChecker::checkStorageLayoutSpecifier(ContractDefinitio
|
145 | 149 | "Contract extends past the end of storage when this base slot value is specified."
|
146 | 150 | );
|
147 | 151 | }
|
| 152 | + |
| 153 | +namespace |
| 154 | +{ |
| 155 | + |
| 156 | +VariableDeclaration const* findLastStorageVariable(ContractDefinition const& _contract) |
| 157 | +{ |
| 158 | + for (ContractDefinition const* baseContract: ranges::actions::reverse(_contract.annotation().linearizedBaseContracts)) |
| 159 | + for (VariableDeclaration const* stateVariable: ranges::actions::reverse(baseContract->stateVariables())) |
| 160 | + if (stateVariable->referenceLocation() == VariableDeclaration::Location::Unspecified) |
| 161 | + return stateVariable; |
| 162 | + |
| 163 | + return nullptr; |
| 164 | +} |
| 165 | + |
| 166 | +} |
| 167 | + |
| 168 | +void PostTypeContractLevelChecker::warnStorageLayoutBaseNearStorageEnd(ContractDefinition const& _contract) |
| 169 | +{ |
| 170 | + // In case of most errors the warning is pointless. E.g. if we're already past storage end. |
| 171 | + // If the errors were in the layout specifier, we may not even be able to get values to validate. |
| 172 | + if (Error::containsErrors(m_errorReporter.errors())) |
| 173 | + return; |
| 174 | + |
| 175 | + bigint storageSize = contractStorageSizeUpperBound(_contract, VariableDeclaration::Location::Unspecified); |
| 176 | + u256 baseSlot = layoutBaseForInheritanceHierarchy(_contract, DataLocation::Storage); |
| 177 | + solAssert(baseSlot + storageSize <= std::numeric_limits<u256>::max()); |
| 178 | + |
| 179 | + if ( |
| 180 | + u256 slotsLeft = std::numeric_limits<u256>::max() - baseSlot - u256(storageSize); |
| 181 | + slotsLeft <= u256(1) << 64 |
| 182 | + ) |
| 183 | + { |
| 184 | + auto const& location = _contract.storageLayoutSpecifier() ? |
| 185 | + _contract.storageLayoutSpecifier()->location() : |
| 186 | + _contract.location(); |
| 187 | + |
| 188 | + VariableDeclaration const* lastStorageVariable = findLastStorageVariable(_contract); |
| 189 | + |
| 190 | + auto errorID = 3495_error; |
| 191 | + std::string errorMsg = "This contract is very close to the end of storage. This limits its future upgradability."; |
| 192 | + if (lastStorageVariable) |
| 193 | + m_errorReporter.warning( |
| 194 | + errorID, |
| 195 | + location, |
| 196 | + errorMsg, |
| 197 | + SecondarySourceLocation{}.append( |
| 198 | + fmt::format( |
| 199 | + "There are {} storage slots between this state variable and the end of storage.", |
| 200 | + formatNumberReadable(slotsLeft) |
| 201 | + ), |
| 202 | + lastStorageVariable->location() |
| 203 | + ) |
| 204 | + ); |
| 205 | + else |
| 206 | + m_errorReporter.warning(errorID, location, errorMsg); |
| 207 | + } |
| 208 | +} |
0 commit comments