diff options
Diffstat (limited to 'libsolidity/analysis/StaticAnalyzer.cpp')
-rw-r--r-- | libsolidity/analysis/StaticAnalyzer.cpp | 153 |
1 files changed, 153 insertions, 0 deletions
diff --git a/libsolidity/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp new file mode 100644 index 00000000..b1b31163 --- /dev/null +++ b/libsolidity/analysis/StaticAnalyzer.cpp @@ -0,0 +1,153 @@ +/* + 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/>. +*/ +/** + * @author Federico Bond <federicobond@gmail.com> + * @date 2016 + * Static analyzer and checker. + */ + +#include <libsolidity/analysis/StaticAnalyzer.h> +#include <libsolidity/ast/AST.h> +#include <libsolidity/interface/ErrorReporter.h> +#include <memory> + +using namespace std; +using namespace dev; +using namespace dev::solidity; + +bool StaticAnalyzer::analyze(SourceUnit const& _sourceUnit) +{ + _sourceUnit.accept(*this); + return Error::containsOnlyWarnings(m_errorReporter.errors()); +} + +bool StaticAnalyzer::visit(ContractDefinition const& _contract) +{ + m_library = _contract.isLibrary(); + return true; +} + +void StaticAnalyzer::endVisit(ContractDefinition const&) +{ + m_library = false; +} + +bool StaticAnalyzer::visit(FunctionDefinition const& _function) +{ + if (_function.isImplemented()) + m_currentFunction = &_function; + else + solAssert(!m_currentFunction, ""); + solAssert(m_localVarUseCount.empty(), ""); + m_nonPayablePublic = _function.isPublic() && !_function.isPayable(); + return true; +} + +void StaticAnalyzer::endVisit(FunctionDefinition const&) +{ + m_currentFunction = nullptr; + m_nonPayablePublic = false; + for (auto const& var: m_localVarUseCount) + if (var.second == 0) + m_errorReporter.warning(var.first->location(), "Unused local variable"); + m_localVarUseCount.clear(); +} + +bool StaticAnalyzer::visit(Identifier const& _identifier) +{ + if (m_currentFunction) + if (auto var = dynamic_cast<VariableDeclaration const*>(_identifier.annotation().referencedDeclaration)) + { + solAssert(!var->name().empty(), ""); + if (var->isLocalVariable()) + m_localVarUseCount[var] += 1; + } + return true; +} + +bool StaticAnalyzer::visit(VariableDeclaration const& _variable) +{ + if (m_currentFunction) + { + solAssert(_variable.isLocalVariable(), ""); + if (_variable.name() != "") + // This is not a no-op, the entry might pre-exist. + m_localVarUseCount[&_variable] += 0; + } + return true; +} + +bool StaticAnalyzer::visit(Return const& _return) +{ + // If the return has an expression, it counts as + // a "use" of the return parameters. + if (m_currentFunction && _return.expression()) + for (auto const& var: m_currentFunction->returnParameters()) + if (!var->name().empty()) + m_localVarUseCount[var.get()] += 1; + return true; +} + +bool StaticAnalyzer::visit(ExpressionStatement const& _statement) +{ + if (_statement.expression().annotation().isPure) + m_errorReporter.warning( + _statement.location(), + "Statement has no effect." + ); + + return true; +} + +bool StaticAnalyzer::visit(MemberAccess const& _memberAccess) +{ + if (m_nonPayablePublic && !m_library) + if (MagicType const* type = dynamic_cast<MagicType const*>(_memberAccess.expression().annotation().type.get())) + if (type->kind() == MagicType::Kind::Message && _memberAccess.memberName() == "value") + m_errorReporter.warning( + _memberAccess.location(), + "\"msg.value\" used in non-payable function. Do you want to add the \"payable\" modifier to this function?" + ); + + if (_memberAccess.memberName() == "callcode") + if (auto const* type = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type.get())) + if (type->kind() == FunctionType::Kind::BareCallCode) + m_errorReporter.warning( + _memberAccess.location(), + "\"callcode\" has been deprecated in favour of \"delegatecall\"." + ); + + return true; +} + +bool StaticAnalyzer::visit(InlineAssembly const& _inlineAssembly) +{ + if (!m_currentFunction) + return true; + + for (auto const& ref: _inlineAssembly.annotation().externalReferences) + { + if (auto var = dynamic_cast<VariableDeclaration const*>(ref.second.declaration)) + { + solAssert(!var->name().empty(), ""); + if (var->isLocalVariable()) + m_localVarUseCount[var] += 1; + } + } + + return true; +} |