/src/solidity/libsolidity/analysis/DeclarationContainer.cpp
Line | Count | Source |
1 | | /* |
2 | | This file is part of solidity. |
3 | | |
4 | | solidity is free software: you can redistribute it and/or modify |
5 | | it under the terms of the GNU General Public License as published by |
6 | | the Free Software Foundation, either version 3 of the License, or |
7 | | (at your option) any later version. |
8 | | |
9 | | solidity is distributed in the hope that it will be useful, |
10 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | | GNU General Public License for more details. |
13 | | |
14 | | You should have received a copy of the GNU General Public License |
15 | | along with solidity. If not, see <http://www.gnu.org/licenses/>. |
16 | | */ |
17 | | // SPDX-License-Identifier: GPL-3.0 |
18 | | /** |
19 | | * @author Christian <c@ethdev.com> |
20 | | * @date 2014 |
21 | | * Scope - object that holds declaration of names. |
22 | | */ |
23 | | |
24 | | #include <libsolidity/analysis/DeclarationContainer.h> |
25 | | |
26 | | #include <libsolidity/ast/AST.h> |
27 | | #include <libsolutil/StringUtils.h> |
28 | | |
29 | | #include <range/v3/view/filter.hpp> |
30 | | #include <range/v3/range/conversion.hpp> |
31 | | |
32 | | using namespace solidity; |
33 | | using namespace solidity::frontend; |
34 | | |
35 | | Declaration const* DeclarationContainer::conflictingDeclaration( |
36 | | Declaration const& _declaration, |
37 | | ASTString const* _name |
38 | | ) const |
39 | 662k | { |
40 | 662k | if (!_name) |
41 | 9.65k | _name = &_declaration.name(); |
42 | 662k | solAssert(!_name->empty(), ""); |
43 | 662k | std::vector<Declaration const*> declarations; |
44 | 662k | if (m_declarations.count(*_name)) |
45 | 85.0k | declarations += m_declarations.at(*_name); |
46 | 662k | if (m_invisibleDeclarations.count(*_name)) |
47 | 2.03k | declarations += m_invisibleDeclarations.at(*_name); |
48 | | |
49 | 662k | if ( |
50 | 662k | dynamic_cast<FunctionDefinition const*>(&_declaration) || |
51 | 638k | dynamic_cast<EventDefinition const*>(&_declaration) || |
52 | 635k | dynamic_cast<MagicVariableDeclaration const*>(&_declaration) |
53 | 662k | ) |
54 | 541k | { |
55 | | // check that all other declarations are of the same kind (in which |
56 | | // case the type checker will ensure that the signatures are different) |
57 | 541k | for (Declaration const* declaration: declarations) |
58 | 99.1k | { |
59 | 99.1k | if ( |
60 | 99.1k | dynamic_cast<FunctionDefinition const*>(&_declaration) && |
61 | 7.97k | !dynamic_cast<FunctionDefinition const*>(declaration) |
62 | 99.1k | ) |
63 | 68 | return declaration; |
64 | 99.1k | if ( |
65 | 99.1k | dynamic_cast<EventDefinition const*>(&_declaration) && |
66 | 6.38k | !dynamic_cast<EventDefinition const*>(declaration) |
67 | 99.1k | ) |
68 | 2 | return declaration; |
69 | 99.1k | if ( |
70 | 99.1k | dynamic_cast<MagicVariableDeclaration const*>(&_declaration) && |
71 | 84.8k | !dynamic_cast<MagicVariableDeclaration const*>(declaration) |
72 | 99.1k | ) |
73 | 0 | return declaration; |
74 | | // Or, continue. |
75 | 99.1k | } |
76 | 541k | } |
77 | 120k | else if (declarations.size() == 1 && declarations.front() == &_declaration) |
78 | 874 | return nullptr; |
79 | 119k | else if (!declarations.empty()) |
80 | 19.2k | return declarations.front(); |
81 | | |
82 | 642k | return nullptr; |
83 | 662k | } |
84 | | |
85 | | void DeclarationContainer::activateVariable(ASTString const& _name) |
86 | 10.4k | { |
87 | 10.4k | solAssert( |
88 | 10.4k | m_invisibleDeclarations.count(_name) && m_invisibleDeclarations.at(_name).size() == 1, |
89 | 10.4k | "Tried to activate a non-inactive variable or multiple inactive variables with the same name." |
90 | 10.4k | ); |
91 | 10.4k | solAssert(m_declarations.count(_name) == 0 || m_declarations.at(_name).empty(), ""); |
92 | 10.4k | m_declarations[_name].emplace_back(m_invisibleDeclarations.at(_name).front()); |
93 | 10.4k | m_invisibleDeclarations.erase(_name); |
94 | 10.4k | } |
95 | | |
96 | | bool DeclarationContainer::isInvisible(ASTString const& _name) const |
97 | 11.2k | { |
98 | 11.2k | return m_invisibleDeclarations.count(_name); |
99 | 11.2k | } |
100 | | |
101 | | bool DeclarationContainer::registerDeclaration( |
102 | | Declaration const& _declaration, |
103 | | ASTString const* _name, |
104 | | langutil::SourceLocation const* _location, |
105 | | bool _invisible, |
106 | | bool _update |
107 | | ) |
108 | 852k | { |
109 | 852k | if (!_name) |
110 | 850k | _name = &_declaration.name(); |
111 | 852k | if (_name->empty()) |
112 | 19.4k | return true; |
113 | | |
114 | 832k | if (_update) |
115 | 180k | { |
116 | 180k | solAssert(!dynamic_cast<FunctionDefinition const*>(&_declaration), "Attempt to update function definition."); |
117 | 180k | m_declarations.erase(*_name); |
118 | 180k | m_invisibleDeclarations.erase(*_name); |
119 | 180k | } |
120 | 652k | else |
121 | 652k | { |
122 | 652k | if (conflictingDeclaration(_declaration, _name)) |
123 | 9.67k | return false; |
124 | | |
125 | 642k | if (m_enclosingContainer && _declaration.isVisibleAsUnqualifiedName()) |
126 | 105k | m_homonymCandidates.emplace_back(*_name, _location ? _location : &_declaration.location()); |
127 | 642k | } |
128 | | |
129 | 823k | std::vector<Declaration const*>& decls = _invisible ? m_invisibleDeclarations[*_name] : m_declarations[*_name]; |
130 | 823k | if (!util::contains(decls, &_declaration)) |
131 | 822k | decls.push_back(&_declaration); |
132 | 823k | return true; |
133 | 832k | } |
134 | | |
135 | | bool DeclarationContainer::registerDeclaration( |
136 | | Declaration const& _declaration, |
137 | | bool _invisible, |
138 | | bool _update |
139 | | ) |
140 | 700k | { |
141 | 700k | return registerDeclaration(_declaration, nullptr, nullptr, _invisible, _update); |
142 | 700k | } |
143 | | |
144 | | std::vector<Declaration const*> DeclarationContainer::resolveName( |
145 | | ASTString const& _name, |
146 | | ResolvingSettings _settings |
147 | | ) const |
148 | 633k | { |
149 | 633k | solAssert(!_name.empty(), "Attempt to resolve empty name."); |
150 | 633k | std::vector<Declaration const*> result; |
151 | | |
152 | 633k | if (m_declarations.count(_name)) |
153 | 187k | { |
154 | 187k | if (_settings.onlyVisibleAsUnqualifiedNames) |
155 | 40.8k | result += m_declarations.at(_name) | ranges::views::filter(&Declaration::isVisibleAsUnqualifiedName) | ranges::to_vector; |
156 | 146k | else |
157 | 146k | result += m_declarations.at(_name); |
158 | 187k | } |
159 | | |
160 | 633k | if (_settings.alsoInvisible && m_invisibleDeclarations.count(_name)) |
161 | 308 | { |
162 | 308 | if (_settings.onlyVisibleAsUnqualifiedNames) |
163 | 0 | result += m_invisibleDeclarations.at(_name) | ranges::views::filter(&Declaration::isVisibleAsUnqualifiedName) | ranges::to_vector; |
164 | 308 | else |
165 | 308 | result += m_invisibleDeclarations.at(_name); |
166 | 308 | } |
167 | | |
168 | 633k | if (result.empty() && _settings.recursive && m_enclosingContainer) |
169 | 339k | result = m_enclosingContainer->resolveName(_name, _settings); |
170 | | |
171 | 633k | return result; |
172 | 633k | } |
173 | | |
174 | | std::vector<ASTString> DeclarationContainer::similarNames(ASTString const& _name) const |
175 | 10.1k | { |
176 | | |
177 | | // because the function below has quadratic runtime - it will not magically improve once a better algorithm is discovered ;) |
178 | | // since 80 is the suggested line length limit, we use 80^2 as length threshold |
179 | 10.1k | static size_t const MAXIMUM_LENGTH_THRESHOLD = 80 * 80; |
180 | | |
181 | 10.1k | std::vector<ASTString> similar; |
182 | 10.1k | size_t maximumEditDistance = _name.size() > 3 ? 2 : _name.size() / 2; |
183 | 10.1k | for (auto const& declaration: m_declarations) |
184 | 79.4k | { |
185 | 79.4k | std::string const& declarationName = declaration.first; |
186 | 79.4k | if (util::stringWithinDistance(_name, declarationName, maximumEditDistance, MAXIMUM_LENGTH_THRESHOLD)) |
187 | 1.79k | similar.push_back(declarationName); |
188 | 79.4k | } |
189 | 10.1k | for (auto const& declaration: m_invisibleDeclarations) |
190 | 2.51k | { |
191 | 2.51k | std::string const& declarationName = declaration.first; |
192 | 2.51k | if (util::stringWithinDistance(_name, declarationName, maximumEditDistance, MAXIMUM_LENGTH_THRESHOLD)) |
193 | 580 | similar.push_back(declarationName); |
194 | 2.51k | } |
195 | | |
196 | 10.1k | if (m_enclosingContainer) |
197 | 6.90k | similar += m_enclosingContainer->similarNames(_name); |
198 | | |
199 | 10.1k | return similar; |
200 | 10.1k | } |
201 | | |
202 | | void DeclarationContainer::populateHomonyms(std::back_insert_iterator<Homonyms> _it) const |
203 | 138k | { |
204 | 138k | for (DeclarationContainer const* innerContainer: m_innerContainers) |
205 | 117k | innerContainer->populateHomonyms(_it); |
206 | | |
207 | 138k | for (auto [name, location]: m_homonymCandidates) |
208 | 101k | { |
209 | 101k | ResolvingSettings settings; |
210 | 101k | settings.recursive = true; |
211 | 101k | settings.alsoInvisible = true; |
212 | 101k | std::vector<Declaration const*> const& declarations = m_enclosingContainer->resolveName(name, std::move(settings)); |
213 | 101k | if (!declarations.empty()) |
214 | 658 | _it = make_pair(location, declarations); |
215 | 101k | } |
216 | 138k | } |