/src/solidity/libyul/ScopeFiller.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | * Module responsible for registering identifiers inside their scopes. |
20 | | */ |
21 | | |
22 | | #include <libyul/ScopeFiller.h> |
23 | | |
24 | | #include <libyul/AST.h> |
25 | | #include <libyul/Scope.h> |
26 | | #include <libyul/AsmAnalysisInfo.h> |
27 | | #include <libyul/Exceptions.h> |
28 | | |
29 | | #include <liblangutil/ErrorReporter.h> |
30 | | |
31 | | #include <libsolutil/CommonData.h> |
32 | | |
33 | | #include <memory> |
34 | | #include <functional> |
35 | | |
36 | | using namespace std; |
37 | | using namespace solidity; |
38 | | using namespace solidity::yul; |
39 | | using namespace solidity::util; |
40 | | using namespace solidity::langutil; |
41 | | |
42 | | ScopeFiller::ScopeFiller(AsmAnalysisInfo& _info, ErrorReporter& _errorReporter): |
43 | | m_info(_info), m_errorReporter(_errorReporter) |
44 | 63.5k | { |
45 | 63.5k | m_currentScope = &scope(nullptr); |
46 | 63.5k | } |
47 | | |
48 | | bool ScopeFiller::operator()(ExpressionStatement const& _expr) |
49 | 513k | { |
50 | 513k | return std::visit(*this, _expr.expression); |
51 | 513k | } |
52 | | |
53 | | bool ScopeFiller::operator()(VariableDeclaration const& _varDecl) |
54 | 326k | { |
55 | 326k | for (auto const& variable: _varDecl.variables) |
56 | 330k | if (!registerVariable(variable, nativeLocationOf(_varDecl), *m_currentScope)) |
57 | 15 | return false; |
58 | 326k | return true; |
59 | 326k | } |
60 | | |
61 | | bool ScopeFiller::operator()(FunctionDefinition const& _funDef) |
62 | 271k | { |
63 | 271k | auto virtualBlock = m_info.virtualBlocks[&_funDef] = make_shared<Block>(); |
64 | 271k | Scope& varScope = scope(virtualBlock.get()); |
65 | 271k | varScope.superScope = m_currentScope; |
66 | 271k | m_currentScope = &varScope; |
67 | 271k | varScope.functionScope = true; |
68 | | |
69 | 271k | bool success = true; |
70 | 271k | for (auto const& var: _funDef.parameters + _funDef.returnVariables) |
71 | 512k | if (!registerVariable(var, nativeLocationOf(_funDef), varScope)) |
72 | 1 | success = false; |
73 | | |
74 | 271k | if (!(*this)(_funDef.body)) |
75 | 0 | success = false; |
76 | | |
77 | 271k | yulAssert(m_currentScope == &varScope, ""); |
78 | 271k | m_currentScope = m_currentScope->superScope; |
79 | | |
80 | 271k | return success; |
81 | 271k | } |
82 | | |
83 | | bool ScopeFiller::operator()(If const& _if) |
84 | 182k | { |
85 | 182k | return (*this)(_if.body); |
86 | 182k | } |
87 | | |
88 | | bool ScopeFiller::operator()(Switch const& _switch) |
89 | 9.58k | { |
90 | 9.58k | bool success = true; |
91 | 9.58k | for (auto const& _case: _switch.cases) |
92 | 19.9k | if (!(*this)(_case.body)) |
93 | 0 | success = false; |
94 | 9.58k | return success; |
95 | 9.58k | } |
96 | | |
97 | | bool ScopeFiller::operator()(ForLoop const& _forLoop) |
98 | 28.8k | { |
99 | 28.8k | Scope* originalScope = m_currentScope; |
100 | | |
101 | 28.8k | bool success = true; |
102 | 28.8k | if (!(*this)(_forLoop.pre)) |
103 | 0 | success = false; |
104 | 28.8k | m_currentScope = &scope(&_forLoop.pre); |
105 | 28.8k | if (!std::visit(*this, *_forLoop.condition)) |
106 | 0 | success = false; |
107 | 28.8k | if (!(*this)(_forLoop.body)) |
108 | 0 | success = false; |
109 | 28.8k | if (!(*this)(_forLoop.post)) |
110 | 0 | success = false; |
111 | | |
112 | 28.8k | m_currentScope = originalScope; |
113 | | |
114 | 28.8k | return success; |
115 | 28.8k | } |
116 | | |
117 | | bool ScopeFiller::operator()(Block const& _block) |
118 | 654k | { |
119 | 654k | bool success = true; |
120 | 654k | scope(&_block).superScope = m_currentScope; |
121 | 654k | m_currentScope = &scope(&_block); |
122 | | |
123 | | // First visit all functions to make them create |
124 | | // an entry in the scope according to their visibility. |
125 | 654k | for (auto const& s: _block.statements) |
126 | 1.76M | if (holds_alternative<FunctionDefinition>(s)) |
127 | 271k | if (!registerFunction(std::get<FunctionDefinition>(s))) |
128 | 0 | success = false; |
129 | 654k | for (auto const& s: _block.statements) |
130 | 1.76M | if (!std::visit(*this, s)) |
131 | 17 | success = false; |
132 | | |
133 | 654k | m_currentScope = m_currentScope->superScope; |
134 | 654k | return success; |
135 | 654k | } |
136 | | |
137 | | bool ScopeFiller::registerVariable(TypedName const& _name, SourceLocation const& _location, Scope& _scope) |
138 | 843k | { |
139 | 843k | if (!_scope.registerVariable(_name.name, _name.type)) |
140 | 16 | { |
141 | | //@TODO secondary location |
142 | 16 | m_errorReporter.declarationError( |
143 | 16 | 1395_error, |
144 | 16 | _location, |
145 | 16 | "Variable name " + _name.name.str() + " already taken in this scope." |
146 | 16 | ); |
147 | 16 | return false; |
148 | 16 | } |
149 | 843k | return true; |
150 | 843k | } |
151 | | |
152 | | bool ScopeFiller::registerFunction(FunctionDefinition const& _funDef) |
153 | 271k | { |
154 | 271k | vector<Scope::YulType> parameters; |
155 | 271k | for (auto const& parameter: _funDef.parameters) |
156 | 313k | parameters.emplace_back(parameter.type); |
157 | 271k | vector<Scope::YulType> returns; |
158 | 271k | for (auto const& returnVariable: _funDef.returnVariables) |
159 | 199k | returns.emplace_back(returnVariable.type); |
160 | 271k | if (!m_currentScope->registerFunction(_funDef.name, std::move(parameters), std::move(returns))) |
161 | 0 | { |
162 | | //@TODO secondary location |
163 | 0 | m_errorReporter.declarationError( |
164 | 0 | 6052_error, |
165 | 0 | nativeLocationOf(_funDef), |
166 | 0 | "Function name " + _funDef.name.str() + " already taken in this scope." |
167 | 0 | ); |
168 | 0 | return false; |
169 | 0 | } |
170 | 271k | return true; |
171 | 271k | } |
172 | | |
173 | | Scope& ScopeFiller::scope(Block const* _block) |
174 | 1.67M | { |
175 | 1.67M | auto& scope = m_info.scopes[_block]; |
176 | 1.67M | if (!scope) |
177 | 989k | scope = make_shared<Scope>(); |
178 | 1.67M | return *scope; |
179 | 1.67M | } |