diff options
Diffstat (limited to 'libsolidity/analysis')
-rw-r--r-- | libsolidity/analysis/DeclarationContainer.h | 1 | ||||
-rw-r--r-- | libsolidity/analysis/NameAndTypeResolver.cpp | 125 | ||||
-rw-r--r-- | libsolidity/analysis/NameAndTypeResolver.h | 11 | ||||
-rw-r--r-- | libsolidity/analysis/SyntaxChecker.cpp | 43 | ||||
-rw-r--r-- | libsolidity/analysis/SyntaxChecker.h | 2 | ||||
-rw-r--r-- | libsolidity/analysis/TypeChecker.cpp | 48 |
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."); |