aboutsummaryrefslogtreecommitdiffstats
path: root/libsolidity/analysis
diff options
context:
space:
mode:
Diffstat (limited to 'libsolidity/analysis')
-rw-r--r--libsolidity/analysis/DeclarationContainer.h1
-rw-r--r--libsolidity/analysis/NameAndTypeResolver.cpp125
-rw-r--r--libsolidity/analysis/NameAndTypeResolver.h11
-rw-r--r--libsolidity/analysis/SyntaxChecker.cpp43
-rw-r--r--libsolidity/analysis/SyntaxChecker.h2
-rw-r--r--libsolidity/analysis/TypeChecker.cpp48
6 files changed, 163 insertions, 67 deletions
diff --git a/libsolidity/analysis/DeclarationContainer.h b/libsolidity/analysis/DeclarationContainer.h
index 9c7c89e7..301998b7 100644
--- a/libsolidity/analysis/DeclarationContainer.h
+++ b/libsolidity/analysis/DeclarationContainer.h
@@ -53,6 +53,7 @@ public:
bool registerDeclaration(Declaration const& _declaration, ASTString const* _name = nullptr, bool _invisible = false, bool _update = false);
std::vector<Declaration const*> resolveName(ASTString const& _name, bool _recursive = false) const;
ASTNode const* enclosingNode() const { return m_enclosingNode; }
+ DeclarationContainer const* enclosingContainer() const { return m_enclosingContainer; }
std::map<ASTString, std::vector<Declaration const*>> const& declarations() const { return m_declarations; }
/// @returns whether declaration is valid, and if not also returns previous declaration.
Declaration const* conflictingDeclaration(Declaration const& _declaration, ASTString const* _name = nullptr) const;
diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp
index aac90311..523e7176 100644
--- a/libsolidity/analysis/NameAndTypeResolver.cpp
+++ b/libsolidity/analysis/NameAndTypeResolver.cpp
@@ -104,29 +104,18 @@ bool NameAndTypeResolver::performImports(SourceUnit& _sourceUnit, map<string, So
}
else
for (Declaration const* declaration: declarations)
- {
- ASTString const* name = alias.second ? alias.second.get() : &declaration->name();
- if (!target.registerDeclaration(*declaration, name))
- {
- m_errorReporter.declarationError(
- imp->location(),
- "Identifier \"" + *name + "\" already declared."
- );
+ if (!DeclarationRegistrationHelper::registerDeclaration(
+ target, *declaration, alias.second.get(), &imp->location(), true, m_errorReporter
+ ))
error = true;
- }
- }
}
else if (imp->name().empty())
for (auto const& nameAndDeclaration: scope->second->declarations())
for (auto const& declaration: nameAndDeclaration.second)
- if (!target.registerDeclaration(*declaration, &nameAndDeclaration.first))
- {
- m_errorReporter.declarationError(
- imp->location(),
- "Identifier \"" + nameAndDeclaration.first + "\" already declared."
- );
- error = true;
- }
+ if (!DeclarationRegistrationHelper::registerDeclaration(
+ target, *declaration, &nameAndDeclaration.first, &imp->location(), true, m_errorReporter
+ ))
+ error = true;
}
return !error;
}
@@ -450,6 +439,71 @@ DeclarationRegistrationHelper::DeclarationRegistrationHelper(
solAssert(m_currentScope == _currentScope, "Scopes not correctly closed.");
}
+bool DeclarationRegistrationHelper::registerDeclaration(
+ DeclarationContainer& _container,
+ Declaration const& _declaration,
+ string const* _name,
+ SourceLocation const* _errorLocation,
+ bool _warnOnShadow,
+ ErrorReporter& _errorReporter
+)
+{
+ if (!_errorLocation)
+ _errorLocation = &_declaration.location();
+
+ Declaration const* shadowedDeclaration = nullptr;
+ if (_warnOnShadow && !_declaration.name().empty() && _container.enclosingContainer())
+ for (auto const* decl: _container.enclosingContainer()->resolveName(_declaration.name(), true))
+ shadowedDeclaration = decl;
+
+ if (!_container.registerDeclaration(_declaration, _name, !_declaration.isVisibleInContract()))
+ {
+ SourceLocation firstDeclarationLocation;
+ SourceLocation secondDeclarationLocation;
+ Declaration const* conflictingDeclaration = _container.conflictingDeclaration(_declaration, _name);
+ solAssert(conflictingDeclaration, "");
+ bool const comparable =
+ _errorLocation->sourceName &&
+ conflictingDeclaration->location().sourceName &&
+ *_errorLocation->sourceName == *conflictingDeclaration->location().sourceName;
+ if (comparable && _errorLocation->start < conflictingDeclaration->location().start)
+ {
+ firstDeclarationLocation = *_errorLocation;
+ secondDeclarationLocation = conflictingDeclaration->location();
+ }
+ else
+ {
+ firstDeclarationLocation = conflictingDeclaration->location();
+ secondDeclarationLocation = *_errorLocation;
+ }
+
+ _errorReporter.declarationError(
+ secondDeclarationLocation,
+ SecondarySourceLocation().append("The previous declaration is here:", firstDeclarationLocation),
+ "Identifier already declared."
+ );
+ return false;
+ }
+ else if (shadowedDeclaration)
+ {
+ if (dynamic_cast<MagicVariableDeclaration const*>(shadowedDeclaration))
+ _errorReporter.warning(
+ _declaration.location(),
+ "This declaration shadows a builtin symbol."
+ );
+ else
+ {
+ auto shadowedLocation = shadowedDeclaration->location();
+ _errorReporter.warning(
+ _declaration.location(),
+ "This declaration shadows an existing declaration.",
+ SecondarySourceLocation().append("The shadowed declaration is here:", shadowedLocation)
+ );
+ }
+ }
+ return true;
+}
+
bool DeclarationRegistrationHelper::visit(SourceUnit& _sourceUnit)
{
if (!m_scopes[&_sourceUnit])
@@ -590,30 +644,21 @@ void DeclarationRegistrationHelper::closeCurrentScope()
void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaration, bool _opensScope)
{
solAssert(m_currentScope && m_scopes.count(m_currentScope), "No current scope.");
- if (!m_scopes[m_currentScope]->registerDeclaration(_declaration, nullptr, !_declaration.isVisibleInContract()))
- {
- SourceLocation firstDeclarationLocation;
- SourceLocation secondDeclarationLocation;
- Declaration const* conflictingDeclaration = m_scopes[m_currentScope]->conflictingDeclaration(_declaration);
- solAssert(conflictingDeclaration, "");
- if (_declaration.location().start < conflictingDeclaration->location().start)
- {
- firstDeclarationLocation = _declaration.location();
- secondDeclarationLocation = conflictingDeclaration->location();
- }
- else
- {
- firstDeclarationLocation = conflictingDeclaration->location();
- secondDeclarationLocation = _declaration.location();
- }
+ bool warnAboutShadowing = true;
+ // Do not warn about shadowing for structs and enums because their members are
+ // not accessible without prefixes.
+ if (
+ dynamic_cast<StructDefinition const*>(m_currentScope) ||
+ dynamic_cast<EnumDefinition const*>(m_currentScope)
+ )
+ warnAboutShadowing = false;
+ // Do not warn about the constructor shadowing the contract.
+ if (auto fun = dynamic_cast<FunctionDefinition const*>(&_declaration))
+ if (fun->isConstructor())
+ warnAboutShadowing = false;
- m_errorReporter.declarationError(
- secondDeclarationLocation,
- SecondarySourceLocation().append("The previous declaration is here:", firstDeclarationLocation),
- "Identifier already declared."
- );
- }
+ registerDeclaration(*m_scopes[m_currentScope], _declaration, nullptr, nullptr, warnAboutShadowing, m_errorReporter);
_declaration.setScope(m_currentScope);
if (_opensScope)
diff --git a/libsolidity/analysis/NameAndTypeResolver.h b/libsolidity/analysis/NameAndTypeResolver.h
index 84628778..59bd3b1f 100644
--- a/libsolidity/analysis/NameAndTypeResolver.h
+++ b/libsolidity/analysis/NameAndTypeResolver.h
@@ -136,6 +136,15 @@ public:
ASTNode const* _currentScope = nullptr
);
+ static bool registerDeclaration(
+ DeclarationContainer& _container,
+ Declaration const& _declaration,
+ std::string const* _name,
+ SourceLocation const* _errorLocation,
+ bool _warnOnShadow,
+ ErrorReporter& _errorReporter
+ );
+
private:
bool visit(SourceUnit& _sourceUnit) override;
void endVisit(SourceUnit& _sourceUnit) override;
@@ -160,6 +169,8 @@ private:
void closeCurrentScope();
void registerDeclaration(Declaration& _declaration, bool _opensScope);
+ static bool isOverloadedFunction(Declaration const& _declaration1, Declaration const& _declaration2);
+
/// @returns the canonical name of the current scope.
std::string currentCanonicalName() const;
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 23f01752..6852c13d 100644
--- a/libsolidity/analysis/TypeChecker.cpp
+++ b/libsolidity/analysis/TypeChecker.cpp
@@ -93,7 +93,7 @@ bool TypeChecker::visit(ContractDefinition const& _contract)
FunctionDefinition const* fallbackFunction = nullptr;
for (FunctionDefinition const* function: _contract.definedFunctions())
{
- if (function->name().empty())
+ if (function->isFallback())
{
if (fallbackFunction)
{
@@ -112,8 +112,6 @@ bool TypeChecker::visit(ContractDefinition const& _contract)
m_errorReporter.typeError(fallbackFunction->returnParameterList()->location(), "Fallback function cannot return values.");
}
}
- if (!function->isImplemented())
- _contract.annotation().isFullyImplemented = false;
}
for (auto const& n: _contract.subNodes())
@@ -188,20 +186,13 @@ void TypeChecker::checkContractAbstractFunctions(ContractDefinition const& _cont
using FunTypeAndFlag = std::pair<FunctionTypePointer, bool>;
map<string, vector<FunTypeAndFlag>> functions;
- bool allBaseConstructorsImplemented = true;
// Search from base to derived
for (ContractDefinition const* contract: boost::adaptors::reverse(_contract.annotation().linearizedBaseContracts))
for (FunctionDefinition const* function: contract->definedFunctions())
{
// Take constructors out of overload hierarchy
if (function->isConstructor())
- {
- if (!function->isImplemented())
- // Base contract's constructor is not fully implemented, no way to get
- // out of this.
- allBaseConstructorsImplemented = false;
continue;
- }
auto& overloads = functions[function->name()];
FunctionTypePointer funType = make_shared<FunctionType>(*function);
auto it = find_if(overloads.begin(), overloads.end(), [&](FunTypeAndFlag const& _funAndFlag)
@@ -219,16 +210,15 @@ void TypeChecker::checkContractAbstractFunctions(ContractDefinition const& _cont
it->second = true;
}
- if (!allBaseConstructorsImplemented)
- _contract.annotation().isFullyImplemented = false;
-
// Set to not fully implemented if at least one flag is false.
for (auto const& it: functions)
for (auto const& funAndFlag: it.second)
if (!funAndFlag.second)
{
- _contract.annotation().isFullyImplemented = false;
- return;
+ FunctionDefinition const* function = dynamic_cast<FunctionDefinition const*>(&funAndFlag.first->declaration());
+ solAssert(function, "");
+ _contract.annotation().unimplementedFunctions.push_back(function);
+ break;
}
}
@@ -266,7 +256,8 @@ void TypeChecker::checkContractAbstractConstructors(ContractDefinition const& _c
}
}
if (!argumentsNeeded.empty())
- _contract.annotation().isFullyImplemented = false;
+ for (ContractDefinition const* contract: argumentsNeeded)
+ _contract.annotation().unimplementedFunctions.push_back(contract->constructor());
}
void TypeChecker::checkContractIllegalOverrides(ContractDefinition const& _contract)
@@ -482,7 +473,7 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
{
if (isLibraryFunction)
m_errorReporter.typeError(_function.location(), "Library functions cannot be payable.");
- if (!_function.isConstructor() && !_function.name().empty() && !_function.isPartOfExternalInterface())
+ 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.");
@@ -510,8 +501,6 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
{
if (dynamic_cast<ContractDefinition const*>(decl))
m_errorReporter.declarationError(modifier->location(), "Base constructor already provided.");
- else
- m_errorReporter.declarationError(modifier->location(), "Modifier already used for this function.");
}
else
modifiers.insert(decl);
@@ -527,6 +516,10 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
}
if (_function.isImplemented())
_function.body().accept(*this);
+ else if (_function.isConstructor())
+ m_errorReporter.typeError(_function.location(), "Constructor must be implemented if declared.");
+ else if (isLibraryFunction && _function.visibility() <= FunctionDefinition::Visibility::Internal)
+ m_errorReporter.typeError(_function.location(), "Internal library function must be implemented if declared.");
return false;
}
@@ -1052,7 +1045,7 @@ void TypeChecker::endVisit(ExpressionStatement const& _statement)
{
auto kind = callType->kind();
if (
- kind == FunctionType::Kind::Bare ||
+ kind == FunctionType::Kind::BareCall ||
kind == FunctionType::Kind::BareCallCode ||
kind == FunctionType::Kind::BareDelegateCall
)
@@ -1124,7 +1117,9 @@ bool TypeChecker::visit(Assignment const& _assignment)
_assignment.annotation().type = make_shared<TupleType>();
expectType(_assignment.rightHandSide(), *tupleType);
- checkDoubleStorageAssignment(_assignment);
+ // expectType does not cause fatal errors, so we have to check again here.
+ if (dynamic_cast<TupleType const*>(type(_assignment.rightHandSide()).get()))
+ checkDoubleStorageAssignment(_assignment);
}
else if (t->category() == Type::Category::Mapping)
{
@@ -1523,8 +1518,15 @@ void TypeChecker::endVisit(NewExpression const& _newExpression)
if (!contract)
m_errorReporter.fatalTypeError(_newExpression.location(), "Identifier is not a contract.");
- if (!contract->annotation().isFullyImplemented)
- m_errorReporter.typeError(_newExpression.location(), "Trying to create an instance of an abstract contract.");
+ if (!contract->annotation().unimplementedFunctions.empty())
+ m_errorReporter.typeError(
+ _newExpression.location(),
+ SecondarySourceLocation().append(
+ "Missing implementation:",
+ contract->annotation().unimplementedFunctions.front()->location()
+ ),
+ "Trying to create an instance of an abstract contract."
+ );
if (!contract->constructorIsPublic())
m_errorReporter.typeError(_newExpression.location(), "Contract with internal constructor cannot be created directly.");