diff options
Diffstat (limited to 'libsolidity/analysis')
| -rw-r--r-- | libsolidity/analysis/StaticAnalyzer.cpp | 44 | ||||
| -rw-r--r-- | libsolidity/analysis/StaticAnalyzer.h | 3 | ||||
| -rw-r--r-- | libsolidity/analysis/SyntaxChecker.cpp | 43 | ||||
| -rw-r--r-- | libsolidity/analysis/SyntaxChecker.h | 2 | ||||
| -rw-r--r-- | libsolidity/analysis/TypeChecker.cpp | 91 | ||||
| -rw-r--r-- | libsolidity/analysis/TypeChecker.h | 3 |
6 files changed, 157 insertions, 29 deletions
diff --git a/libsolidity/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp index 46477e1e..2f130414 100644 --- a/libsolidity/analysis/StaticAnalyzer.cpp +++ b/libsolidity/analysis/StaticAnalyzer.cpp @@ -57,6 +57,8 @@ bool StaticAnalyzer::visit(FunctionDefinition const& _function) solAssert(m_localVarUseCount.empty(), ""); m_nonPayablePublic = _function.isPublic() && !_function.isPayable(); m_constructor = _function.isConstructor(); + if (_function.stateMutability() == StateMutability::Pure) + m_errorReporter.warning(_function.location(), "Function is marked pure. Be careful, pureness is not enforced yet."); return true; } @@ -92,6 +94,17 @@ bool StaticAnalyzer::visit(VariableDeclaration const& _variable) // This is not a no-op, the entry might pre-exist. m_localVarUseCount[&_variable] += 0; } + else if (_variable.isStateVariable()) + { + set<StructDefinition const*> structsSeen; + if (structureSizeEstimate(*_variable.type(), structsSeen) >= bigint(1) << 64) + m_errorReporter.warning( + _variable.location(), + "Variable covers a large part of storage and thus makes collisions likely. " + "Either use mappings or dynamic arrays and allow their size to be increased only " + "in small quantities per transaction." + ); + } return true; } @@ -160,3 +173,34 @@ bool StaticAnalyzer::visit(InlineAssembly const& _inlineAssembly) return true; } + +bigint StaticAnalyzer::structureSizeEstimate(Type const& _type, set<StructDefinition const*>& _structsSeen) +{ + switch (_type.category()) + { + case Type::Category::Array: + { + auto const& t = dynamic_cast<ArrayType const&>(_type); + return structureSizeEstimate(*t.baseType(), _structsSeen) * (t.isDynamicallySized() ? 1 : t.length()); + } + case Type::Category::Struct: + { + auto const& t = dynamic_cast<StructType const&>(_type); + bigint size = 1; + if (!_structsSeen.count(&t.structDefinition())) + { + _structsSeen.insert(&t.structDefinition()); + for (auto const& m: t.members(nullptr)) + size += structureSizeEstimate(*m.type, _structsSeen); + } + return size; + } + case Type::Category::Mapping: + { + return structureSizeEstimate(*dynamic_cast<MappingType const&>(_type).valueType(), _structsSeen); + } + default: + break; + } + return bigint(1); +} diff --git a/libsolidity/analysis/StaticAnalyzer.h b/libsolidity/analysis/StaticAnalyzer.h index 21a487df..a3080b42 100644 --- a/libsolidity/analysis/StaticAnalyzer.h +++ b/libsolidity/analysis/StaticAnalyzer.h @@ -65,6 +65,9 @@ private: virtual bool visit(MemberAccess const& _memberAccess) override; virtual bool visit(InlineAssembly const& _inlineAssembly) override; + /// @returns the size of this type in storage, including all sub-types. + static bigint structureSizeEstimate(Type const& _type, std::set<StructDefinition const*>& _structsSeen); + ErrorReporter& m_errorReporter; /// Flag that indicates whether the current contract definition is a library. diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index bde0e616..d2571cd3 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -18,6 +18,7 @@ #include <libsolidity/analysis/SyntaxChecker.h> #include <memory> #include <libsolidity/ast/AST.h> +#include <libsolidity/ast/ExperimentalFeatures.h> #include <libsolidity/analysis/SemVerHandler.h> #include <libsolidity/interface/ErrorReporter.h> #include <libsolidity/interface/Version.h> @@ -33,9 +34,10 @@ bool SyntaxChecker::checkSyntax(ASTNode const& _astRoot) return Error::containsOnlyWarnings(m_errorReporter.errors()); } -bool SyntaxChecker::visit(SourceUnit const&) +bool SyntaxChecker::visit(SourceUnit const& _sourceUnit) { m_versionPragmaFound = false; + m_sourceUnit = &_sourceUnit; return true; } @@ -57,15 +59,46 @@ void SyntaxChecker::endVisit(SourceUnit const& _sourceUnit) m_errorReporter.warning(_sourceUnit.location(), errorString); } + m_sourceUnit = nullptr; } bool SyntaxChecker::visit(PragmaDirective const& _pragma) { solAssert(!_pragma.tokens().empty(), ""); solAssert(_pragma.tokens().size() == _pragma.literals().size(), ""); - if (_pragma.tokens()[0] != Token::Identifier || _pragma.literals()[0] != "solidity") - m_errorReporter.syntaxError(_pragma.location(), "Unknown pragma \"" + _pragma.literals()[0] + "\""); - else + if (_pragma.tokens()[0] != Token::Identifier) + m_errorReporter.syntaxError(_pragma.location(), "Invalid pragma \"" + _pragma.literals()[0] + "\""); + else if (_pragma.literals()[0] == "experimental") + { + solAssert(m_sourceUnit, ""); + vector<string> literals(_pragma.literals().begin() + 1, _pragma.literals().end()); + if (literals.size() == 0) + m_errorReporter.syntaxError( + _pragma.location(), + "Experimental feature name is missing." + ); + else if (literals.size() > 1) + m_errorReporter.syntaxError( + _pragma.location(), + "Stray arguments." + ); + else + { + string const literal = literals[0]; + if (literal.empty()) + m_errorReporter.syntaxError(_pragma.location(), "Empty experimental feature name is invalid."); + else if (!ExperimentalFeatureNames.count(literal)) + m_errorReporter.syntaxError(_pragma.location(), "Unsupported experimental feature name."); + else if (m_sourceUnit->annotation().experimentalFeatures.count(ExperimentalFeatureNames.at(literal))) + m_errorReporter.syntaxError(_pragma.location(), "Duplicate experimental feature name."); + else + { + m_sourceUnit->annotation().experimentalFeatures.insert(ExperimentalFeatureNames.at(literal)); + m_errorReporter.warning(_pragma.location(), "Experimental features are turned on. Do not use experimental features on live deployments."); + } + } + } + else if (_pragma.literals()[0] == "solidity") { vector<Token::Value> tokens(_pragma.tokens().begin() + 1, _pragma.tokens().end()); vector<string> literals(_pragma.literals().begin() + 1, _pragma.literals().end()); @@ -81,6 +114,8 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma) ); m_versionPragmaFound = true; } + else + m_errorReporter.syntaxError(_pragma.location(), "Unknown pragma \"" + _pragma.literals()[0] + "\""); return true; } diff --git a/libsolidity/analysis/SyntaxChecker.h b/libsolidity/analysis/SyntaxChecker.h index fb5cc6d7..fa34bab3 100644 --- a/libsolidity/analysis/SyntaxChecker.h +++ b/libsolidity/analysis/SyntaxChecker.h @@ -77,6 +77,8 @@ private: bool m_versionPragmaFound = false; int m_inLoopDepth = 0; + + SourceUnit const* m_sourceUnit = nullptr; }; } diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 6852c13d..99f3c64c 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -84,8 +84,13 @@ bool TypeChecker::visit(ContractDefinition const& _contract) { if (!function->returnParameters().empty()) m_errorReporter.typeError(function->returnParameterList()->location(), "Non-empty \"returns\" directive for constructor."); - if (function->isDeclaredConst()) - m_errorReporter.typeError(function->location(), "Constructor cannot be defined as constant."); + if (function->stateMutability() != StateMutability::NonPayable && function->stateMutability() != StateMutability::Payable) + m_errorReporter.typeError( + function->location(), + "Constructor must be payable or non-payable, but is \"" + + stateMutabilityToString(function->stateMutability()) + + "\"." + ); if (function->visibility() != FunctionDefinition::Visibility::Public && function->visibility() != FunctionDefinition::Visibility::Internal) m_errorReporter.typeError(function->location(), "Constructor must be public or internal."); } @@ -104,8 +109,13 @@ bool TypeChecker::visit(ContractDefinition const& _contract) fallbackFunction = function; if (_contract.isLibrary()) m_errorReporter.typeError(fallbackFunction->location(), "Libraries cannot have fallback functions."); - if (fallbackFunction->isDeclaredConst()) - m_errorReporter.typeError(fallbackFunction->location(), "Fallback function cannot be declared constant."); + if (function->stateMutability() != StateMutability::NonPayable && function->stateMutability() != StateMutability::Payable) + m_errorReporter.typeError( + function->location(), + "Fallback function must be payable or non-payable, but is \"" + + stateMutabilityToString(function->stateMutability()) + + "\"." + ); if (!fallbackFunction->parameters().empty()) m_errorReporter.typeError(fallbackFunction->parameterList().location(), "Fallback function cannot take parameters."); if (!fallbackFunction->returnParameters().empty()) @@ -277,21 +287,10 @@ void TypeChecker::checkContractIllegalOverrides(ContractDefinition const& _contr string const& name = function->name(); if (modifiers.count(name)) m_errorReporter.typeError(modifiers[name]->location(), "Override changes function to modifier."); - FunctionType functionType(*function); - // function should not change the return type + for (FunctionDefinition const* overriding: functions[name]) - { - FunctionType overridingType(*overriding); - if (!overridingType.hasEqualArgumentTypes(functionType)) - continue; - if ( - overriding->visibility() != function->visibility() || - overriding->isDeclaredConst() != function->isDeclaredConst() || - overriding->isPayable() != function->isPayable() || - overridingType != functionType - ) - m_errorReporter.typeError(overriding->location(), "Override changes extended function signature."); - } + checkFunctionOverride(*overriding, *function); + functions[name].push_back(function); } for (ModifierDefinition const* modifier: contract->functionModifiers()) @@ -308,6 +307,41 @@ void TypeChecker::checkContractIllegalOverrides(ContractDefinition const& _contr } } +void TypeChecker::checkFunctionOverride(FunctionDefinition const& function, FunctionDefinition const& super) +{ + FunctionType functionType(function); + FunctionType superType(super); + + if (!functionType.hasEqualArgumentTypes(superType)) + return; + + if (function.visibility() != super.visibility()) + overrideError(function, super, "Overriding function visibility differs."); + + else if (function.stateMutability() != super.stateMutability()) + overrideError( + function, + super, + "Overriding function changes state mutability from \"" + + stateMutabilityToString(super.stateMutability()) + + "\" to \"" + + stateMutabilityToString(function.stateMutability()) + + "\"." + ); + + else if (functionType != superType) + overrideError(function, super, "Overriding function return types differ."); +} + +void TypeChecker::overrideError(FunctionDefinition const& function, FunctionDefinition const& super, string message) +{ + m_errorReporter.typeError( + function.location(), + SecondarySourceLocation().append("Overriden function is here:", super.location()), + message + ); +} + void TypeChecker::checkContractExternalTypeClashes(ContractDefinition const& _contract) { map<string, vector<pair<Declaration const*, FunctionTypePointer>>> externalDeclarations; @@ -396,7 +430,11 @@ void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance) m_errorReporter.typeError(_inheritance.location(), "Libraries cannot be inherited from."); auto const& arguments = _inheritance.arguments(); - TypePointers parameterTypes = ContractType(*base).newExpressionType()->parameterTypes(); + TypePointers parameterTypes; + if (base->contractKind() != ContractDefinition::ContractKind::Interface) + // Interfaces do not have constructors, so there are zero parameters. + parameterTypes = ContractType(*base).newExpressionType()->parameterTypes(); + if (!arguments.empty() && parameterTypes.size() != arguments.size()) { m_errorReporter.typeError( @@ -429,7 +467,7 @@ void TypeChecker::endVisit(UsingForDirective const& _usingFor) _usingFor.libraryName().annotation().referencedDeclaration ); if (!library || !library->isLibrary()) - m_errorReporter.typeError(_usingFor.libraryName().location(), "Library name expected."); + m_errorReporter.fatalTypeError(_usingFor.libraryName().location(), "Library name expected."); } bool TypeChecker::visit(StructDefinition const& _struct) @@ -475,8 +513,6 @@ bool TypeChecker::visit(FunctionDefinition const& _function) m_errorReporter.typeError(_function.location(), "Library functions cannot be payable."); if (!_function.isConstructor() && !_function.isFallback() && !_function.isPartOfExternalInterface()) m_errorReporter.typeError(_function.location(), "Internal functions cannot be payable."); - if (_function.isDeclaredConst()) - m_errorReporter.typeError(_function.location(), "Functions cannot be constant and payable at the same time."); } for (ASTPointer<VariableDeclaration> const& var: _function.parameters() + _function.returnParameters()) { @@ -514,6 +550,9 @@ bool TypeChecker::visit(FunctionDefinition const& _function) if (_function.isConstructor()) m_errorReporter.typeError(_function.location(), "Constructor cannot be defined in interfaces."); } + else if (m_scope->contractKind() == ContractDefinition::ContractKind::Library) + if (_function.isConstructor()) + m_errorReporter.typeError(_function.location(), "Constructor cannot be defined in libraries."); if (_function.isImplemented()) _function.body().accept(*this); else if (_function.isConstructor()) @@ -1282,8 +1321,9 @@ void TypeChecker::endVisit(BinaryOperation const& _operation) _operation.leftExpression().annotation().isPure && _operation.rightExpression().annotation().isPure; - if (_operation.getOperator() == Token::Exp) + if (_operation.getOperator() == Token::Exp || _operation.getOperator() == Token::SHL) { + string operation = _operation.getOperator() == Token::Exp ? "exponentiation" : "shift"; if ( leftType->category() == Type::Category::RationalNumber && rightType->category() != Type::Category::RationalNumber @@ -1297,7 +1337,7 @@ void TypeChecker::endVisit(BinaryOperation const& _operation) )) m_errorReporter.warning( _operation.location(), - "Result of exponentiation has type " + commonType->toString() + " and thus " + "Result of " + operation + " has type " + commonType->toString() + " and thus " "might overflow. Silence this warning by converting the literal to the " "expected type." ); @@ -1518,6 +1558,8 @@ void TypeChecker::endVisit(NewExpression const& _newExpression) if (!contract) m_errorReporter.fatalTypeError(_newExpression.location(), "Identifier is not a contract."); + if (contract->contractKind() == ContractDefinition::ContractKind::Interface) + m_errorReporter.fatalTypeError(_newExpression.location(), "Cannot instantiate an interface."); if (!contract->annotation().unimplementedFunctions.empty()) m_errorReporter.typeError( _newExpression.location(), @@ -1949,4 +1991,3 @@ void TypeChecker::requireLValue(Expression const& _expression) else if (!_expression.annotation().isLValue) m_errorReporter.typeError(_expression.location(), "Expression has to be an lvalue."); } - diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h index ee43d13a..f2e13765 100644 --- a/libsolidity/analysis/TypeChecker.h +++ b/libsolidity/analysis/TypeChecker.h @@ -62,6 +62,9 @@ private: /// arguments and that there is at most one constructor. void checkContractDuplicateFunctions(ContractDefinition const& _contract); void checkContractIllegalOverrides(ContractDefinition const& _contract); + /// Reports a type error with an appropiate message if overriden function signature differs. + void checkFunctionOverride(FunctionDefinition const& function, FunctionDefinition const& super); + void overrideError(FunctionDefinition const& function, FunctionDefinition const& super, std::string message); void checkContractAbstractFunctions(ContractDefinition const& _contract); void checkContractAbstractConstructors(ContractDefinition const& _contract); /// Checks that different functions with external visibility end up having different |
