aboutsummaryrefslogtreecommitdiffstats
path: root/libsolidity/analysis
diff options
context:
space:
mode:
Diffstat (limited to 'libsolidity/analysis')
-rw-r--r--libsolidity/analysis/ConstantEvaluator.cpp93
-rw-r--r--libsolidity/analysis/ConstantEvaluator.h20
-rw-r--r--libsolidity/analysis/DeclarationContainer.cpp22
-rw-r--r--libsolidity/analysis/DeclarationContainer.h4
-rw-r--r--libsolidity/analysis/NameAndTypeResolver.cpp6
-rw-r--r--libsolidity/analysis/NameAndTypeResolver.h3
-rw-r--r--libsolidity/analysis/PostTypeChecker.h2
-rw-r--r--libsolidity/analysis/ReferencesResolver.cpp55
-rw-r--r--libsolidity/analysis/StaticAnalyzer.h2
-rw-r--r--libsolidity/analysis/SyntaxChecker.cpp16
-rw-r--r--libsolidity/analysis/SyntaxChecker.h4
-rw-r--r--libsolidity/analysis/TypeChecker.cpp56
-rw-r--r--libsolidity/analysis/TypeChecker.h2
-rw-r--r--libsolidity/analysis/ViewPureChecker.cpp39
-rw-r--r--libsolidity/analysis/ViewPureChecker.h1
15 files changed, 240 insertions, 85 deletions
diff --git a/libsolidity/analysis/ConstantEvaluator.cpp b/libsolidity/analysis/ConstantEvaluator.cpp
index bc3b7cf1..83f37f47 100644
--- a/libsolidity/analysis/ConstantEvaluator.cpp
+++ b/libsolidity/analysis/ConstantEvaluator.cpp
@@ -28,49 +28,78 @@ using namespace std;
using namespace dev;
using namespace dev::solidity;
-/// FIXME: this is pretty much a copy of TypeChecker::endVisit(BinaryOperation)
void ConstantEvaluator::endVisit(UnaryOperation const& _operation)
{
- TypePointer const& subType = _operation.subExpression().annotation().type;
- if (!dynamic_cast<RationalNumberType const*>(subType.get()))
- m_errorReporter.fatalTypeError(_operation.subExpression().location(), "Invalid constant expression.");
- TypePointer t = subType->unaryOperatorResult(_operation.getOperator());
- _operation.annotation().type = t;
+ auto sub = type(_operation.subExpression());
+ if (sub)
+ setType(_operation, sub->unaryOperatorResult(_operation.getOperator()));
}
-/// FIXME: this is pretty much a copy of TypeChecker::endVisit(BinaryOperation)
void ConstantEvaluator::endVisit(BinaryOperation const& _operation)
{
- TypePointer const& leftType = _operation.leftExpression().annotation().type;
- TypePointer const& rightType = _operation.rightExpression().annotation().type;
- if (!dynamic_cast<RationalNumberType const*>(leftType.get()))
- m_errorReporter.fatalTypeError(_operation.leftExpression().location(), "Invalid constant expression.");
- if (!dynamic_cast<RationalNumberType const*>(rightType.get()))
- m_errorReporter.fatalTypeError(_operation.rightExpression().location(), "Invalid constant expression.");
- TypePointer commonType = leftType->binaryOperatorResult(_operation.getOperator(), rightType);
- if (!commonType)
+ auto left = type(_operation.leftExpression());
+ auto right = type(_operation.rightExpression());
+ if (left && right)
{
- m_errorReporter.typeError(
- _operation.location(),
- "Operator " +
- string(Token::toString(_operation.getOperator())) +
- " not compatible with types " +
- leftType->toString() +
- " and " +
- rightType->toString()
+ auto commonType = left->binaryOperatorResult(_operation.getOperator(), right);
+ if (!commonType)
+ m_errorReporter.fatalTypeError(
+ _operation.location(),
+ "Operator " +
+ string(Token::toString(_operation.getOperator())) +
+ " not compatible with types " +
+ left->toString() +
+ " and " +
+ right->toString()
+ );
+ setType(
+ _operation,
+ Token::isCompareOp(_operation.getOperator()) ?
+ make_shared<BoolType>() :
+ commonType
);
- commonType = leftType;
}
- _operation.annotation().commonType = commonType;
- _operation.annotation().type =
- Token::isCompareOp(_operation.getOperator()) ?
- make_shared<BoolType>() :
- commonType;
}
void ConstantEvaluator::endVisit(Literal const& _literal)
{
- _literal.annotation().type = Type::forLiteral(_literal);
- if (!_literal.annotation().type)
- m_errorReporter.fatalTypeError(_literal.location(), "Invalid literal value.");
+ setType(_literal, Type::forLiteral(_literal));
+}
+
+void ConstantEvaluator::endVisit(Identifier const& _identifier)
+{
+ VariableDeclaration const* variableDeclaration = dynamic_cast<VariableDeclaration const*>(_identifier.annotation().referencedDeclaration);
+ if (!variableDeclaration)
+ return;
+ if (!variableDeclaration->isConstant())
+ return;
+
+ ASTPointer<Expression> const& value = variableDeclaration->value();
+ if (!value)
+ return;
+ else if (!m_types->count(value.get()))
+ {
+ if (m_depth > 32)
+ m_errorReporter.fatalTypeError(_identifier.location(), "Cyclic constant definition (or maximum recursion depth exhausted).");
+ ConstantEvaluator(m_errorReporter, m_depth + 1, m_types).evaluate(*value);
+ }
+
+ setType(_identifier, type(*value));
+}
+
+void ConstantEvaluator::setType(ASTNode const& _node, TypePointer const& _type)
+{
+ if (_type && _type->category() == Type::Category::RationalNumber)
+ (*m_types)[&_node] = _type;
+}
+
+TypePointer ConstantEvaluator::type(ASTNode const& _node)
+{
+ return (*m_types)[&_node];
+}
+
+TypePointer ConstantEvaluator::evaluate(Expression const& _expr)
+{
+ _expr.accept(*this);
+ return type(_expr);
}
diff --git a/libsolidity/analysis/ConstantEvaluator.h b/libsolidity/analysis/ConstantEvaluator.h
index 90bceb5d..77a357b6 100644
--- a/libsolidity/analysis/ConstantEvaluator.h
+++ b/libsolidity/analysis/ConstantEvaluator.h
@@ -38,18 +38,32 @@ class TypeChecker;
class ConstantEvaluator: private ASTConstVisitor
{
public:
- ConstantEvaluator(Expression const& _expr, ErrorReporter& _errorReporter):
- m_errorReporter(_errorReporter)
+ ConstantEvaluator(
+ ErrorReporter& _errorReporter,
+ size_t _newDepth = 0,
+ std::shared_ptr<std::map<ASTNode const*, TypePointer>> _types = std::make_shared<std::map<ASTNode const*, TypePointer>>()
+ ):
+ m_errorReporter(_errorReporter),
+ m_depth(_newDepth),
+ m_types(_types)
{
- _expr.accept(*this);
}
+ TypePointer evaluate(Expression const& _expr);
+
private:
virtual void endVisit(BinaryOperation const& _operation);
virtual void endVisit(UnaryOperation const& _operation);
virtual void endVisit(Literal const& _literal);
+ virtual void endVisit(Identifier const& _identifier);
+
+ void setType(ASTNode const& _node, TypePointer const& _type);
+ TypePointer type(ASTNode const& _node);
ErrorReporter& m_errorReporter;
+ /// Current recursion depth.
+ size_t m_depth = 0;
+ std::shared_ptr<std::map<ASTNode const*, TypePointer>> m_types;
};
}
diff --git a/libsolidity/analysis/DeclarationContainer.cpp b/libsolidity/analysis/DeclarationContainer.cpp
index b33c8568..7508ad9e 100644
--- a/libsolidity/analysis/DeclarationContainer.cpp
+++ b/libsolidity/analysis/DeclarationContainer.cpp
@@ -23,6 +23,7 @@
#include <libsolidity/analysis/DeclarationContainer.h>
#include <libsolidity/ast/AST.h>
#include <libsolidity/ast/Types.h>
+#include <libdevcore/StringUtils.h>
using namespace std;
using namespace dev;
@@ -105,7 +106,7 @@ bool DeclarationContainer::registerDeclaration(
return true;
}
-std::vector<Declaration const*> DeclarationContainer::resolveName(ASTString const& _name, bool _recursive) const
+vector<Declaration const*> DeclarationContainer::resolveName(ASTString const& _name, bool _recursive) const
{
solAssert(!_name.empty(), "Attempt to resolve empty name.");
auto result = m_declarations.find(_name);
@@ -115,3 +116,22 @@ std::vector<Declaration const*> DeclarationContainer::resolveName(ASTString cons
return m_enclosingContainer->resolveName(_name, true);
return vector<Declaration const*>({});
}
+
+vector<ASTString> DeclarationContainer::similarNames(ASTString const& _name) const
+{
+ static size_t const MAXIMUM_EDIT_DISTANCE = 2;
+
+ vector<ASTString> similar;
+
+ for (auto const& declaration: m_declarations)
+ {
+ string const& declarationName = declaration.first;
+ if (stringWithinDistance(_name, declarationName, MAXIMUM_EDIT_DISTANCE))
+ similar.push_back(declarationName);
+ }
+
+ if (m_enclosingContainer)
+ similar += m_enclosingContainer->similarNames(_name);
+
+ return similar;
+}
diff --git a/libsolidity/analysis/DeclarationContainer.h b/libsolidity/analysis/DeclarationContainer.h
index 301998b7..f9b1bda4 100644
--- a/libsolidity/analysis/DeclarationContainer.h
+++ b/libsolidity/analysis/DeclarationContainer.h
@@ -58,6 +58,10 @@ public:
/// @returns whether declaration is valid, and if not also returns previous declaration.
Declaration const* conflictingDeclaration(Declaration const& _declaration, ASTString const* _name = nullptr) const;
+ /// @returns existing declaration names similar to @a _name.
+ /// Searches this and all parent containers.
+ std::vector<ASTString> similarNames(ASTString const& _name) const;
+
private:
ASTNode const* m_enclosingNode;
DeclarationContainer const* m_enclosingContainer;
diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp
index 5d010693..5e4d414b 100644
--- a/libsolidity/analysis/NameAndTypeResolver.cpp
+++ b/libsolidity/analysis/NameAndTypeResolver.cpp
@@ -25,6 +25,7 @@
#include <libsolidity/ast/AST.h>
#include <libsolidity/analysis/TypeChecker.h>
#include <libsolidity/interface/ErrorReporter.h>
+#include <libdevcore/StringUtils.h>
#include <boost/algorithm/string.hpp>
@@ -425,6 +426,11 @@ vector<_T const*> NameAndTypeResolver::cThreeMerge(list<list<_T const*>>& _toMer
return result;
}
+string NameAndTypeResolver::similarNameSuggestions(ASTString const& _name) const
+{
+ return quotedAlternativesList(m_currentScope->similarNames(_name));
+}
+
DeclarationRegistrationHelper::DeclarationRegistrationHelper(
map<ASTNode const*, shared_ptr<DeclarationContainer>>& _scopes,
ASTNode& _astRoot,
diff --git a/libsolidity/analysis/NameAndTypeResolver.h b/libsolidity/analysis/NameAndTypeResolver.h
index d83697cd..9aea07ab 100644
--- a/libsolidity/analysis/NameAndTypeResolver.h
+++ b/libsolidity/analysis/NameAndTypeResolver.h
@@ -93,6 +93,9 @@ public:
/// Generate and store warnings about variables that are named like instructions.
void warnVariablesNamedLikeInstructions();
+ /// @returns a list of similar identifiers in the current and enclosing scopes. May return empty string if no suggestions.
+ std::string similarNameSuggestions(ASTString const& _name) const;
+
private:
/// Internal version of @a resolveNamesAndTypes (called from there) throws exceptions on fatal errors.
bool resolveNamesAndTypesInternal(ASTNode& _node, bool _resolveInsideCode = true);
diff --git a/libsolidity/analysis/PostTypeChecker.h b/libsolidity/analysis/PostTypeChecker.h
index 91d2b0b9..bafc1ae6 100644
--- a/libsolidity/analysis/PostTypeChecker.h
+++ b/libsolidity/analysis/PostTypeChecker.h
@@ -38,7 +38,7 @@ class ErrorReporter;
class PostTypeChecker: private ASTConstVisitor
{
public:
- /// @param _errors the reference to the list of errors and warnings to add them found during type checking.
+ /// @param _errorReporter provides the error logging functionality.
PostTypeChecker(ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {}
bool check(ASTNode const& _astRoot);
diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp
index f22c95cc..0bb5e3fe 100644
--- a/libsolidity/analysis/ReferencesResolver.cpp
+++ b/libsolidity/analysis/ReferencesResolver.cpp
@@ -47,7 +47,13 @@ bool ReferencesResolver::visit(Identifier const& _identifier)
{
auto declarations = m_resolver.nameFromCurrentScope(_identifier.name());
if (declarations.empty())
- fatalDeclarationError(_identifier.location(), "Undeclared identifier.");
+ {
+ string suggestions = m_resolver.similarNameSuggestions(_identifier.name());
+ string errorMessage =
+ "Undeclared identifier." +
+ (suggestions.empty()? "": " Did you mean " + std::move(suggestions) + "?");
+ declarationError(_identifier.location(), errorMessage);
+ }
else if (declarations.size() == 1)
_identifier.annotation().referencedDeclaration = declarations.front();
else
@@ -90,7 +96,10 @@ void ReferencesResolver::endVisit(UserDefinedTypeName const& _typeName)
{
Declaration const* declaration = m_resolver.pathFromCurrentScope(_typeName.namePath());
if (!declaration)
- fatalDeclarationError(_typeName.location(), "Identifier not found or not unique.");
+ {
+ declarationError(_typeName.location(), "Identifier not found or not unique.");
+ return;
+ }
_typeName.annotation().referencedDeclaration = declaration;
@@ -101,7 +110,7 @@ void ReferencesResolver::endVisit(UserDefinedTypeName const& _typeName)
else if (ContractDefinition const* contract = dynamic_cast<ContractDefinition const*>(declaration))
_typeName.annotation().type = make_shared<ContractType>(*contract);
else
- fatalTypeError(_typeName.location(), "Name has to refer to a struct, enum or contract.");
+ typeError(_typeName.location(), "Name has to refer to a struct, enum or contract.");
}
void ReferencesResolver::endVisit(FunctionTypeName const& _typeName)
@@ -112,17 +121,25 @@ void ReferencesResolver::endVisit(FunctionTypeName const& _typeName)
case VariableDeclaration::Visibility::External:
break;
default:
- fatalTypeError(_typeName.location(), "Invalid visibility, can only be \"external\" or \"internal\".");
+ typeError(_typeName.location(), "Invalid visibility, can only be \"external\" or \"internal\".");
+ return;
}
if (_typeName.isPayable() && _typeName.visibility() != VariableDeclaration::Visibility::External)
- fatalTypeError(_typeName.location(), "Only external function types can be payable.");
+ {
+ typeError(_typeName.location(), "Only external function types can be payable.");
+ return;
+ }
+
if (_typeName.visibility() == VariableDeclaration::Visibility::External)
for (auto const& t: _typeName.parameterTypes() + _typeName.returnParameterTypes())
{
solAssert(t->annotation().type, "Type not set for parameter.");
if (!t->annotation().type->canBeUsedExternally(false))
- fatalTypeError(t->location(), "Internal type cannot be used for external function type.");
+ {
+ typeError(t->location(), "Internal type cannot be used for external function type.");
+ return;
+ }
}
_typeName.annotation().type = make_shared<FunctionType>(_typeName);
@@ -142,15 +159,21 @@ void ReferencesResolver::endVisit(Mapping const& _typeName)
void ReferencesResolver::endVisit(ArrayTypeName const& _typeName)
{
TypePointer baseType = _typeName.baseType().annotation().type;
+ if (!baseType)
+ {
+ solAssert(!m_errorReporter.errors().empty(), "");
+ return;
+ }
if (baseType->storageBytes() == 0)
fatalTypeError(_typeName.baseType().location(), "Illegal base type of storage size zero for array.");
if (Expression const* length = _typeName.length())
{
- if (!length->annotation().type)
- ConstantEvaluator e(*length, m_errorReporter);
- auto const* lengthType = dynamic_cast<RationalNumberType const*>(length->annotation().type.get());
+ TypePointer lengthTypeGeneric = length->annotation().type;
+ if (!lengthTypeGeneric)
+ lengthTypeGeneric = ConstantEvaluator(m_errorReporter).evaluate(*length);
+ RationalNumberType const* lengthType = dynamic_cast<RationalNumberType const*>(lengthTypeGeneric.get());
if (!lengthType || !lengthType->mobileType())
- fatalTypeError(length->location(), "Invalid array length, expected integer literal.");
+ fatalTypeError(length->location(), "Invalid array length, expected integer literal or constant expression.");
else if (lengthType->isFractional())
fatalTypeError(length->location(), "Array with fractional length specified.");
else if (lengthType->isNegative())
@@ -206,7 +229,7 @@ bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly)
// Will be re-generated later with correct information
assembly::AsmAnalysisInfo analysisInfo;
- assembly::AsmAnalyzer(analysisInfo, errorsIgnored, false, resolver).analyze(_inlineAssembly.operations());
+ assembly::AsmAnalyzer(analysisInfo, errorsIgnored, assembly::AsmFlavour::Loose, resolver).analyze(_inlineAssembly.operations());
return false;
}
@@ -321,17 +344,13 @@ void ReferencesResolver::endVisit(VariableDeclaration const& _variable)
type = ref->copyForLocation(typeLoc, isPointer);
}
else if (varLoc != Location::Default && !ref)
- fatalTypeError(_variable.location(), "Storage location can only be given for array or struct types.");
-
- if (!type)
- fatalTypeError(_variable.location(), "Invalid type name.");
+ typeError(_variable.location(), "Storage location can only be given for array or struct types.");
+ _variable.annotation().type = type;
}
else if (!_variable.canHaveAutoType())
- fatalTypeError(_variable.location(), "Explicit type needed.");
+ typeError(_variable.location(), "Explicit type needed.");
// otherwise we have a "var"-declaration whose type is resolved by the first assignment
-
- _variable.annotation().type = type;
}
void ReferencesResolver::typeError(SourceLocation const& _location, string const& _description)
diff --git a/libsolidity/analysis/StaticAnalyzer.h b/libsolidity/analysis/StaticAnalyzer.h
index 24ed119f..124c4e7c 100644
--- a/libsolidity/analysis/StaticAnalyzer.h
+++ b/libsolidity/analysis/StaticAnalyzer.h
@@ -43,7 +43,7 @@ namespace solidity
class StaticAnalyzer: private ASTConstVisitor
{
public:
- /// @param _errors the reference to the list of errors and warnings to add them found during static analysis.
+ /// @param _errorReporter provides the error logging functionality.
explicit StaticAnalyzer(ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {}
/// Performs static analysis on the given source unit and all of its sub-nodes.
diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp
index 0ca4b86c..5a3745b0 100644
--- a/libsolidity/analysis/SyntaxChecker.cpp
+++ b/libsolidity/analysis/SyntaxChecker.cpp
@@ -54,7 +54,7 @@ void SyntaxChecker::endVisit(SourceUnit const& _sourceUnit)
string(".") +
to_string(recommendedVersion.minor()) +
string(".") +
- to_string(recommendedVersion.patch());
+ to_string(recommendedVersion.patch()) +
string(";\"");
m_errorReporter.warning(_sourceUnit.location(), errorString);
@@ -224,3 +224,17 @@ bool SyntaxChecker::visit(FunctionTypeName const& _node)
return true;
}
+
+bool SyntaxChecker::visit(VariableDeclaration const& _declaration)
+{
+ bool const v050 = m_sourceUnit->annotation().experimentalFeatures.count(ExperimentalFeature::V050);
+
+ if (!_declaration.typeName())
+ {
+ if (v050)
+ m_errorReporter.syntaxError(_declaration.location(), "Use of the \"var\" keyword is deprecated.");
+ else
+ m_errorReporter.warning(_declaration.location(), "Use of the \"var\" keyword is deprecated.");
+ }
+ return true;
+}
diff --git a/libsolidity/analysis/SyntaxChecker.h b/libsolidity/analysis/SyntaxChecker.h
index 7fffbec0..871bf0a9 100644
--- a/libsolidity/analysis/SyntaxChecker.h
+++ b/libsolidity/analysis/SyntaxChecker.h
@@ -38,7 +38,7 @@ namespace solidity
class SyntaxChecker: private ASTConstVisitor
{
public:
- /// @param _errors the reference to the list of errors and warnings to add them found during type checking.
+ /// @param _errorReporter provides the error logging functionality.
SyntaxChecker(ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {}
bool checkSyntax(ASTNode const& _astRoot);
@@ -69,6 +69,8 @@ private:
virtual bool visit(FunctionDefinition const& _function) override;
virtual bool visit(FunctionTypeName const& _node) override;
+ virtual bool visit(VariableDeclaration const& _declaration) override;
+
ErrorReporter& m_errorReporter;
/// Flag that indicates whether a function modifier actually contains '_'.
diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp
index 746e762e..f62ddeb9 100644
--- a/libsolidity/analysis/TypeChecker.cpp
+++ b/libsolidity/analysis/TypeChecker.cpp
@@ -171,13 +171,7 @@ void TypeChecker::checkContractDuplicateFunctions(ContractDefinition const& _con
ssl.append("Another declaration is here:", (*it)->location());
string msg = "More than one constructor defined.";
- size_t occurrences = ssl.infos.size();
- if (occurrences > 32)
- {
- ssl.infos.resize(32);
- msg += " Truncated from " + boost::lexical_cast<string>(occurrences) + " to the first 32 occurrences.";
- }
-
+ ssl.limitSize(msg);
m_errorReporter.declarationError(
functions[_contract.name()].front()->location(),
ssl,
@@ -219,12 +213,7 @@ void TypeChecker::findDuplicateDefinitions(map<string, vector<T>> const& _defini
if (ssl.infos.size() > 0)
{
- size_t occurrences = ssl.infos.size();
- if (occurrences > 32)
- {
- ssl.infos.resize(32);
- _message += " Truncated from " + boost::lexical_cast<string>(occurrences) + " to the first 32 occurrences.";
- }
+ ssl.limitSize(_message);
m_errorReporter.declarationError(
overloads[i]->location(),
@@ -570,6 +559,17 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
m_errorReporter.typeError(var->location(), "Type is required to live outside storage.");
if (_function.visibility() >= FunctionDefinition::Visibility::Public && !(type(*var)->interfaceType(isLibraryFunction)))
m_errorReporter.fatalTypeError(var->location(), "Internal or recursive type is not allowed for public or external functions.");
+ if (
+ _function.visibility() > FunctionDefinition::Visibility::Internal &&
+ type(*var)->category() == Type::Category::Struct &&
+ !type(*var)->dataStoredIn(DataLocation::Storage) &&
+ !_function.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2)
+ )
+ m_errorReporter.typeError(
+ var->location(),
+ "Structs are only supported in the new experimental ABI encoder. "
+ "Use \"pragma experimental ABIEncoderV2;\" to enable the feature."
+ );
var->accept(*this);
}
@@ -604,6 +604,8 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
{
if (_function.visibility() < FunctionDefinition::Visibility::Public)
m_errorReporter.typeError(_function.location(), "Functions in interfaces cannot be internal or private.");
+ else if (_function.visibility() != FunctionDefinition::Visibility::External)
+ m_errorReporter.warning(_function.location(), "Functions in interfaces should be declared external.");
}
if (_function.isConstructor())
m_errorReporter.typeError(_function.location(), "Constructor cannot be defined in interfaces.");
@@ -873,7 +875,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
assembly::AsmAnalyzer analyzer(
*_inlineAssembly.annotation().analysisInfo,
m_errorReporter,
- false,
+ assembly::AsmFlavour::Loose,
identifierAccess
);
if (!analyzer.analyze(_inlineAssembly.operations()))
@@ -970,7 +972,11 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
string errorText{"Uninitialized storage pointer."};
if (varDecl.referenceLocation() == VariableDeclaration::Location::Default)
errorText += " Did you mean '<type> memory " + varDecl.name() + "'?";
- m_errorReporter.warning(varDecl.location(), errorText);
+ solAssert(m_scope, "");
+ if (m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050))
+ m_errorReporter.declarationError(varDecl.location(), errorText);
+ else
+ m_errorReporter.warning(varDecl.location(), errorText);
}
}
else if (dynamic_cast<MappingType const*>(type(varDecl).get()))
@@ -1060,7 +1066,7 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
_statement.initialValue()->location(),
"Invalid rational " +
valueComponentType->toString() +
- " (absolute value too large or divison by zero)."
+ " (absolute value too large or division by zero)."
);
else
solAssert(false, "");
@@ -1122,7 +1128,7 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
var.annotation().type->toString() +
". Try converting to type " +
valueComponentType->mobileType()->toString() +
- " or use an explicit conversion."
+ " or use an explicit conversion."
);
else
m_errorReporter.typeError(
@@ -1320,7 +1326,7 @@ bool TypeChecker::visit(TupleExpression const& _tuple)
_tuple.annotation().isPure = isPure;
if (_tuple.isInlineArray())
{
- if (!inlineArrayType)
+ if (!inlineArrayType)
m_errorReporter.fatalTypeError(_tuple.location(), "Unable to deduce common type for array elements.");
_tuple.annotation().type = make_shared<ArrayType>(DataLocation::Memory, inlineArrayType, types.size());
}
@@ -1551,8 +1557,12 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
if (!functionType->takesArbitraryParameters() && parameterTypes.size() != arguments.size())
{
+ bool isStructConstructorCall = _functionCall.annotation().kind == FunctionCallKind::StructConstructorCall;
+
string msg =
- "Wrong argument count for function call: " +
+ "Wrong argument count for " +
+ string(isStructConstructorCall ? "struct constructor" : "function call") +
+ ": " +
toString(arguments.size()) +
" arguments given but expected " +
toString(parameterTypes.size()) +
@@ -1668,10 +1678,12 @@ void TypeChecker::endVisit(NewExpression const& _newExpression)
SecondarySourceLocation ssl;
for (auto function: contract->annotation().unimplementedFunctions)
ssl.append("Missing implementation:", function->location());
+ string msg = "Trying to create an instance of an abstract contract.";
+ ssl.limitSize(msg);
m_errorReporter.typeError(
_newExpression.location(),
ssl,
- "Trying to create an instance of an abstract contract."
+ msg
);
}
if (!contract->constructorIsPublic())
@@ -2000,7 +2012,9 @@ void TypeChecker::endVisit(Literal const& _literal)
m_errorReporter.warning(
_literal.location(),
"This looks like an address but has an invalid checksum. "
- "If this is not used as an address, please prepend '00'."
+ "If this is not used as an address, please prepend '00'. " +
+ (!_literal.getChecksummedAddress().empty() ? "Correct checksummed address: '" + _literal.getChecksummedAddress() + "'. " : "") +
+ "For more information please see https://solidity.readthedocs.io/en/develop/types.html#address-literals"
);
}
if (!_literal.annotation().type)
diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h
index abe6dac1..344b019d 100644
--- a/libsolidity/analysis/TypeChecker.h
+++ b/libsolidity/analysis/TypeChecker.h
@@ -42,7 +42,7 @@ class ErrorReporter;
class TypeChecker: private ASTConstVisitor
{
public:
- /// @param _errors the reference to the list of errors and warnings to add them found during type checking.
+ /// @param _errorReporter provides the error logging functionality.
TypeChecker(ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {}
/// Performs type checking on the given contract and all of its sub-nodes.
diff --git a/libsolidity/analysis/ViewPureChecker.cpp b/libsolidity/analysis/ViewPureChecker.cpp
index 7f28c7d2..13c3ab68 100644
--- a/libsolidity/analysis/ViewPureChecker.cpp
+++ b/libsolidity/analysis/ViewPureChecker.cpp
@@ -40,19 +40,20 @@ public:
void operator()(assembly::Label const&) { }
void operator()(assembly::Instruction const& _instruction)
{
- if (eth::SemanticInformation::invalidInViewFunctions(_instruction.instruction))
- m_reportMutability(StateMutability::NonPayable, _instruction.location);
- else if (eth::SemanticInformation::invalidInPureFunctions(_instruction.instruction))
- m_reportMutability(StateMutability::View, _instruction.location);
+ checkInstruction(_instruction.location, _instruction.instruction);
}
void operator()(assembly::Literal const&) {}
void operator()(assembly::Identifier const&) {}
void operator()(assembly::FunctionalInstruction const& _instr)
{
- (*this)(_instr.instruction);
+ checkInstruction(_instr.location, _instr.instruction);
for (auto const& arg: _instr.arguments)
boost::apply_visitor(*this, arg);
}
+ void operator()(assembly::ExpressionStatement const& _expr)
+ {
+ boost::apply_visitor(*this, _expr.expression);
+ }
void operator()(assembly::StackAssignment const&) {}
void operator()(assembly::Assignment const& _assignment)
{
@@ -72,6 +73,11 @@ public:
for (auto const& arg: _funCall.arguments)
boost::apply_visitor(*this, arg);
}
+ void operator()(assembly::If const& _if)
+ {
+ boost::apply_visitor(*this, *_if.condition);
+ (*this)(_if.body);
+ }
void operator()(assembly::Switch const& _switch)
{
boost::apply_visitor(*this, *_switch.expression);
@@ -97,6 +103,13 @@ public:
private:
std::function<void(StateMutability, SourceLocation const&)> m_reportMutability;
+ void checkInstruction(SourceLocation _location, solidity::Instruction _instruction)
+ {
+ if (eth::SemanticInformation::invalidInViewFunctions(_instruction))
+ m_reportMutability(StateMutability::NonPayable, _location);
+ else if (eth::SemanticInformation::invalidInPureFunctions(_instruction))
+ m_reportMutability(StateMutability::View, _location);
+ }
};
}
@@ -262,6 +275,22 @@ void ViewPureChecker::endVisit(FunctionCall const& _functionCall)
reportMutability(mut, _functionCall.location());
}
+bool ViewPureChecker::visit(MemberAccess const& _memberAccess)
+{
+ // Catch the special case of `this.f.selector` which is a pure expression.
+ ASTString const& member = _memberAccess.memberName();
+ if (
+ _memberAccess.expression().annotation().type->category() == Type::Category::Function &&
+ member == "selector"
+ )
+ if (auto const* expr = dynamic_cast<MemberAccess const*>(&_memberAccess.expression()))
+ if (auto const* exprInt = dynamic_cast<Identifier const*>(&expr->expression()))
+ if (exprInt->name() == "this")
+ // Do not continue visiting.
+ return false;
+ return true;
+}
+
void ViewPureChecker::endVisit(MemberAccess const& _memberAccess)
{
StateMutability mutability = StateMutability::Pure;
diff --git a/libsolidity/analysis/ViewPureChecker.h b/libsolidity/analysis/ViewPureChecker.h
index fec060b6..0b882cd8 100644
--- a/libsolidity/analysis/ViewPureChecker.h
+++ b/libsolidity/analysis/ViewPureChecker.h
@@ -56,6 +56,7 @@ private:
virtual bool visit(ModifierDefinition const& _modifierDef) override;
virtual void endVisit(ModifierDefinition const& _modifierDef) override;
virtual void endVisit(Identifier const& _identifier) override;
+ virtual bool visit(MemberAccess const& _memberAccess) override;
virtual void endVisit(MemberAccess const& _memberAccess) override;
virtual void endVisit(IndexAccess const& _indexAccess) override;
virtual void endVisit(ModifierInvocation const& _modifier) override;