aboutsummaryrefslogtreecommitdiffstats
path: root/libsolidity/analysis/ContractLevelChecker.cpp
blob: a2e76edfc8fcfd07cd3d651ea03fde3df87b9d0f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
/*
    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/>.
*/
/**
 * Component that verifies overloads, abstract contracts, function clashes and others
 * checks at contract or function level.
 */

#include <libsolidity/analysis/ContractLevelChecker.h>
#include <libsolidity/ast/AST.h>

#include <liblangutil/ErrorReporter.h>


using namespace std;
using namespace dev;
using namespace langutil;
using namespace dev::solidity;


bool ContractLevelChecker::check(ContractDefinition const& _contract)
{
    checkContractDuplicateFunctions(_contract);
    checkContractDuplicateEvents(_contract);

    return Error::containsOnlyWarnings(m_errorReporter.errors());
}

void ContractLevelChecker::checkContractDuplicateFunctions(ContractDefinition const& _contract)
{
    /// Checks that two functions with the same name defined in this contract have different
    /// argument types and that there is at most one constructor.
    map<string, vector<FunctionDefinition const*>> functions;
    FunctionDefinition const* constructor = nullptr;
    FunctionDefinition const* fallback = nullptr;
    for (FunctionDefinition const* function: _contract.definedFunctions())
        if (function->isConstructor())
        {
            if (constructor)
                m_errorReporter.declarationError(
                    function->location(),
                    SecondarySourceLocation().append("Another declaration is here:", constructor->location()),
                    "More than one constructor defined."
                );
            constructor = function;
        }
        else if (function->isFallback())
        {
            if (fallback)
                m_errorReporter.declarationError(
                    function->location(),
                    SecondarySourceLocation().append("Another declaration is here:", fallback->location()),
                    "Only one fallback function is allowed."
                );
            fallback = function;
        }
        else
        {
            solAssert(!function->name().empty(), "");
            functions[function->name()].push_back(function);
        }

    findDuplicateDefinitions(functions, "Function with same name and arguments defined twice.");
}

void ContractLevelChecker::checkContractDuplicateEvents(ContractDefinition const& _contract)
{
    /// Checks that two events with the same name defined in this contract have different
    /// argument types
    map<string, vector<EventDefinition const*>> events;
    for (EventDefinition const* event: _contract.events())
        events[event->name()].push_back(event);

    findDuplicateDefinitions(events, "Event with same name and arguments defined twice.");
}

template <class T>
void ContractLevelChecker::findDuplicateDefinitions(map<string, vector<T>> const& _definitions, string _message)
{
    for (auto const& it: _definitions)
    {
        vector<T> const& overloads = it.second;
        set<size_t> reported;
        for (size_t i = 0; i < overloads.size() && !reported.count(i); ++i)
        {
            SecondarySourceLocation ssl;

            for (size_t j = i + 1; j < overloads.size(); ++j)
                if (FunctionType(*overloads[i]).asCallableFunction(false)->hasEqualParameterTypes(
                    *FunctionType(*overloads[j]).asCallableFunction(false))
                )
                {
                    ssl.append("Other declaration is here:", overloads[j]->location());
                    reported.insert(j);
                }

            if (ssl.infos.size() > 0)
            {
                ssl.limitSize(_message);

                m_errorReporter.declarationError(
                    overloads[i]->location(),
                    ssl,
                    _message
                );
            }
        }
    }
}