aboutsummaryrefslogtreecommitdiffstats
path: root/libsolidity/analysis/ViewPureChecker.cpp
diff options
context:
space:
mode:
authorchriseth <chris@ethereum.org>2017-08-29 01:48:34 +0800
committerchriseth <chris@ethereum.org>2017-09-06 19:50:49 +0800
commit5470da4d9adc8ef07aa1c2a758b7062be843cca4 (patch)
tree824e0012f236a9d959c0eb46e5a1fdb02ece43ec /libsolidity/analysis/ViewPureChecker.cpp
parent16526ad554256bde3e185c99c1651b0f47725165 (diff)
downloaddexon-solidity-5470da4d9adc8ef07aa1c2a758b7062be843cca4.tar.gz
dexon-solidity-5470da4d9adc8ef07aa1c2a758b7062be843cca4.tar.zst
dexon-solidity-5470da4d9adc8ef07aa1c2a758b7062be843cca4.zip
View-pure checker.
Diffstat (limited to 'libsolidity/analysis/ViewPureChecker.cpp')
-rw-r--r--libsolidity/analysis/ViewPureChecker.cpp227
1 files changed, 227 insertions, 0 deletions
diff --git a/libsolidity/analysis/ViewPureChecker.cpp b/libsolidity/analysis/ViewPureChecker.cpp
new file mode 100644
index 00000000..0e2cfacf
--- /dev/null
+++ b/libsolidity/analysis/ViewPureChecker.cpp
@@ -0,0 +1,227 @@
+/*
+ This file is part of solidity.
+
+ solidity is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ solidity is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with solidity. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <libsolidity/analysis/ViewPureChecker.h>
+
+using namespace std;
+using namespace dev;
+using namespace dev::solidity;
+
+bool ViewPureChecker::check()
+{
+ vector<ContractDefinition const*> contracts;
+
+ for (auto const& node: m_ast)
+ {
+ SourceUnit const* source = dynamic_cast<SourceUnit const*>(node.get());
+ solAssert(source, "");
+ for (auto const& topLevelNode: source->nodes())
+ {
+ ContractDefinition const* contract = dynamic_cast<ContractDefinition const*>(topLevelNode.get());
+ if (contract)
+ contracts.push_back(contract);
+ }
+ }
+
+ // Check modifiers first to infer their state mutability.
+ for (auto const* contract: contracts)
+ for (ModifierDefinition const* mod: contract->functionModifiers())
+ mod->accept(*this);
+
+ for (auto const* contract: contracts)
+ contract->accept(*this);
+
+ return !m_errors;
+}
+
+
+
+bool ViewPureChecker::visit(FunctionDefinition const& _funDef)
+{
+ solAssert(!m_currentFunction, "");
+ m_currentFunction = &_funDef;
+ m_currentBestMutability = StateMutability::Pure;
+ return true;
+}
+
+void ViewPureChecker::endVisit(FunctionDefinition const& _funDef)
+{
+ solAssert(m_currentFunction == &_funDef, "");
+ if (
+ m_currentBestMutability < _funDef.stateMutability() &&
+ _funDef.stateMutability() != StateMutability::Payable &&
+ _funDef.isImplemented() &&
+ !_funDef.isConstructor() &&
+ !_funDef.isFallback() &&
+ !_funDef.isConstructor() &&
+ !_funDef.annotation().superFunction
+ )
+ m_errorReporter.warning(
+ _funDef.location(),
+ "Function state mutability can be restricted to " + stateMutabilityToString(m_currentBestMutability)
+ );
+ m_currentFunction = nullptr;
+}
+
+bool ViewPureChecker::visit(ModifierDefinition const&)
+{
+ solAssert(m_currentFunction == nullptr, "");
+ m_currentBestMutability = StateMutability::Pure;
+ return true;
+}
+
+void ViewPureChecker::endVisit(ModifierDefinition const& _modifierDef)
+{
+ solAssert(m_currentFunction == nullptr, "");
+ m_inferredMutability[&_modifierDef] = m_currentBestMutability;
+}
+
+void ViewPureChecker::endVisit(Identifier const& _identifier)
+{
+ Declaration const* declaration = _identifier.annotation().referencedDeclaration;
+ solAssert(declaration, "");
+
+ StateMutability mutability = StateMutability::Pure;
+
+ bool writes = _identifier.annotation().lValueRequested;
+ if (VariableDeclaration const* varDecl = dynamic_cast<VariableDeclaration const*>(declaration))
+ {
+ if (varDecl->isStateVariable())
+ mutability = writes ? StateMutability::NonPayable : StateMutability::View;
+ }
+ else if (MagicVariableDeclaration const* magicVar = dynamic_cast<MagicVariableDeclaration const*>(declaration))
+ {
+ switch (magicVar->type()->category())
+ {
+ case Type::Category::Contract:
+ solAssert(_identifier.name() == "this" || _identifier.name() == "super", "");
+ if (!dynamic_cast<ContractType const&>(*magicVar->type()).isSuper())
+ // reads the address
+ mutability = StateMutability::View;
+ break;
+ case Type::Category::Integer:
+ solAssert(_identifier.name() == "now", "");
+ mutability = StateMutability::View;
+ break;
+ default:
+ break;
+ }
+ }
+
+ reportMutability(mutability, _identifier);
+}
+
+void ViewPureChecker::endVisit(InlineAssembly const& _inlineAssembly)
+{
+ // @TOOD we can and should analyze it further.
+ reportMutability(StateMutability::NonPayable, _inlineAssembly);
+}
+
+void ViewPureChecker::reportMutability(StateMutability _mutability, const ASTNode& _node)
+{
+ if (m_currentFunction && m_currentFunction->stateMutability() < _mutability)
+ {
+ m_errors = true;
+ if (_mutability == StateMutability::View)
+ m_errorReporter.typeError(
+ _node.location(),
+ "Function declared as pure, but this expression reads from the environment or state and thus "
+ "requires \"view\"."
+ );
+ else if (_mutability == StateMutability::NonPayable)
+ m_errorReporter.typeError(
+ _node.location(),
+ "Function declared as " +
+ stateMutabilityToString(m_currentFunction->stateMutability()) +
+ ", but this expression modifies the state and thus "
+ "requires non-payable (the default) or payable."
+ );
+ else
+ solAssert(false, "");
+ }
+ if (_mutability >= m_currentBestMutability)
+ m_currentBestMutability = _mutability;
+}
+
+void ViewPureChecker::endVisit(FunctionCall const& _functionCall)
+{
+ if (_functionCall.annotation().kind != FunctionCallKind::FunctionCall)
+ return;
+
+ StateMutability mut = dynamic_cast<FunctionType const&>(*_functionCall.expression().annotation().type).stateMutability();
+ // We only require "nonpayable" to call a payble function.
+ if (mut == StateMutability::Payable)
+ mut = StateMutability::NonPayable;
+ reportMutability(mut, _functionCall);
+}
+
+void ViewPureChecker::endVisit(MemberAccess const& _memberAccess)
+{
+ StateMutability mutability = StateMutability::Pure;
+ bool writes = _memberAccess.annotation().lValueRequested;
+
+ ASTString const& member = _memberAccess.memberName();
+ switch (_memberAccess.expression().annotation().type->category())
+ {
+ case Type::Category::Contract:
+ case Type::Category::Integer:
+ if (member == "balance" && !_memberAccess.annotation().referencedDeclaration)
+ mutability = StateMutability::View;
+ break;
+ case Type::Category::Magic:
+ // we can ignore the kind of magic and only look at the name of the member
+ if (member != "data" && member != "sig" && member != "blockhash")
+ mutability = StateMutability::View;
+ break;
+ case Type::Category::Struct:
+ {
+ if (_memberAccess.expression().annotation().type->dataStoredIn(DataLocation::Storage))
+ mutability = writes ? StateMutability::NonPayable : StateMutability::View;
+ break;
+ }
+ case Type::Category::Array:
+ {
+ auto const& type = dynamic_cast<ArrayType const&>(*_memberAccess.expression().annotation().type);
+ if (member == "length" && type.isDynamicallySized() && type.dataStoredIn(DataLocation::Storage))
+ mutability = writes ? StateMutability::NonPayable : StateMutability::View;
+ break;
+ }
+ default:
+ break;
+ }
+ reportMutability(mutability, _memberAccess);
+}
+
+void ViewPureChecker::endVisit(IndexAccess const& _indexAccess)
+{
+ solAssert(_indexAccess.indexExpression(), "");
+
+ bool writes = _indexAccess.annotation().lValueRequested;
+ if (_indexAccess.baseExpression().annotation().type->dataStoredIn(DataLocation::Storage))
+ reportMutability(writes ? StateMutability::NonPayable : StateMutability::View, _indexAccess);
+}
+
+void ViewPureChecker::endVisit(ModifierInvocation const& _modifier)
+{
+ solAssert(_modifier.name(), "");
+ ModifierDefinition const* mod = dynamic_cast<decltype(mod)>(_modifier.name()->annotation().referencedDeclaration);
+ solAssert(mod, "");
+ solAssert(m_inferredMutability.count(mod), "");
+
+ reportMutability(m_inferredMutability.at(mod), _modifier);
+}
+