aboutsummaryrefslogtreecommitdiffstats
path: root/libsolidity/analysis
diff options
context:
space:
mode:
Diffstat (limited to 'libsolidity/analysis')
-rw-r--r--libsolidity/analysis/StaticAnalyzer.cpp44
-rw-r--r--libsolidity/analysis/StaticAnalyzer.h3
-rw-r--r--libsolidity/analysis/SyntaxChecker.cpp43
-rw-r--r--libsolidity/analysis/SyntaxChecker.h2
-rw-r--r--libsolidity/analysis/TypeChecker.cpp91
-rw-r--r--libsolidity/analysis/TypeChecker.h3
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