Coverage Report

Created: 2022-08-24 06:43

/src/solidity/libsolidity/analysis/ReferencesResolver.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
 * @author Christian <c@ethdev.com>
20
 * @date 2015
21
 * Component that resolves type names to types and annotates the AST accordingly.
22
 */
23
24
#include <libsolidity/analysis/ReferencesResolver.h>
25
#include <libsolidity/analysis/NameAndTypeResolver.h>
26
#include <libsolidity/ast/AST.h>
27
28
#include <libyul/AsmAnalysis.h>
29
#include <libyul/AsmAnalysisInfo.h>
30
#include <libyul/AST.h>
31
#include <libyul/backends/evm/EVMDialect.h>
32
33
#include <liblangutil/ErrorReporter.h>
34
#include <liblangutil/Exceptions.h>
35
36
#include <libsolutil/StringUtils.h>
37
#include <libsolutil/CommonData.h>
38
39
#include <boost/algorithm/string.hpp>
40
#include <boost/algorithm/string/split.hpp>
41
42
using namespace std;
43
using namespace solidity;
44
using namespace solidity::langutil;
45
using namespace solidity::frontend;
46
47
48
bool ReferencesResolver::resolve(ASTNode const& _root)
49
70.3k
{
50
70.3k
  auto errorWatcher = m_errorReporter.errorWatcher();
51
70.3k
  _root.accept(*this);
52
70.3k
  return errorWatcher.ok();
53
70.3k
}
54
55
bool ReferencesResolver::visit(Block const& _block)
56
30.1k
{
57
30.1k
  if (!m_resolveInsideCode)
58
12.7k
    return false;
59
17.4k
  m_resolver.setScope(&_block);
60
17.4k
  return true;
61
30.1k
}
62
63
void ReferencesResolver::endVisit(Block const& _block)
64
30.1k
{
65
30.1k
  if (!m_resolveInsideCode)
66
12.7k
    return;
67
68
17.4k
  m_resolver.setScope(_block.scope());
69
17.4k
}
70
71
bool ReferencesResolver::visit(TryCatchClause const& _tryCatchClause)
72
417
{
73
417
  if (!m_resolveInsideCode)
74
0
    return false;
75
417
  m_resolver.setScope(&_tryCatchClause);
76
417
  return true;
77
417
}
78
79
void ReferencesResolver::endVisit(TryCatchClause const& _tryCatchClause)
80
414
{
81
414
  if (!m_resolveInsideCode)
82
0
    return;
83
84
414
  m_resolver.setScope(_tryCatchClause.scope());
85
414
}
86
87
bool ReferencesResolver::visit(ForStatement const& _for)
88
192
{
89
192
  if (!m_resolveInsideCode)
90
0
    return false;
91
192
  m_resolver.setScope(&_for);
92
192
  return true;
93
192
}
94
95
void ReferencesResolver::endVisit(ForStatement const& _for)
96
190
{
97
190
  if (!m_resolveInsideCode)
98
0
    return;
99
190
  m_resolver.setScope(_for.scope());
100
190
}
101
102
void ReferencesResolver::endVisit(VariableDeclarationStatement const& _varDeclStatement)
103
5.56k
{
104
5.56k
  if (!m_resolveInsideCode)
105
0
    return;
106
5.56k
  for (auto const& var: _varDeclStatement.declarations())
107
6.85k
    if (var)
108
6.19k
      m_resolver.activateVariable(var->name());
109
5.56k
}
110
111
bool ReferencesResolver::visit(VariableDeclaration const& _varDecl)
112
65.5k
{
113
65.5k
  if (_varDecl.documentation())
114
181
    resolveInheritDoc(*_varDecl.documentation(), _varDecl.annotation());
115
116
65.5k
  return true;
117
65.5k
}
118
119
bool ReferencesResolver::visit(Identifier const& _identifier)
120
42.4k
{
121
42.4k
  auto declarations = m_resolver.nameFromCurrentScope(_identifier.name());
122
42.4k
  if (declarations.empty())
123
1.90k
  {
124
1.90k
    string suggestions = m_resolver.similarNameSuggestions(_identifier.name());
125
1.90k
    string errorMessage = "Undeclared identifier.";
126
1.90k
    if (!suggestions.empty())
127
496
    {
128
496
      if ("\"" + _identifier.name() + "\"" == suggestions)
129
26
        errorMessage += " " + std::move(suggestions) + " is not (or not yet) visible at this point.";
130
470
      else
131
470
        errorMessage += " Did you mean " + std::move(suggestions) + "?";
132
496
    }
133
1.90k
    m_errorReporter.declarationError(7576_error, _identifier.location(), errorMessage);
134
1.90k
  }
135
40.5k
  else if (declarations.size() == 1)
136
40.0k
    _identifier.annotation().referencedDeclaration = declarations.front();
137
471
  else
138
471
    _identifier.annotation().candidateDeclarations = declarations;
139
42.4k
  return false;
140
42.4k
}
141
142
bool ReferencesResolver::visit(FunctionDefinition const& _functionDefinition)
143
27.0k
{
144
27.0k
  m_returnParameters.push_back(_functionDefinition.returnParameterList().get());
145
146
27.0k
  if (_functionDefinition.documentation())
147
345
    resolveInheritDoc(*_functionDefinition.documentation(), _functionDefinition.annotation());
148
149
27.0k
  return true;
150
27.0k
}
151
152
void ReferencesResolver::endVisit(FunctionDefinition const&)
153
26.8k
{
154
26.8k
  solAssert(!m_returnParameters.empty(), "");
155
26.8k
  m_returnParameters.pop_back();
156
26.8k
}
157
158
bool ReferencesResolver::visit(ModifierDefinition const& _modifierDefinition)
159
920
{
160
920
  m_returnParameters.push_back(nullptr);
161
162
920
  if (_modifierDefinition.documentation())
163
17
    resolveInheritDoc(*_modifierDefinition.documentation(), _modifierDefinition.annotation());
164
165
920
  return true;
166
920
}
167
168
void ReferencesResolver::endVisit(ModifierDefinition const&)
169
913
{
170
913
  solAssert(!m_returnParameters.empty(), "");
171
913
  m_returnParameters.pop_back();
172
913
}
173
174
void ReferencesResolver::endVisit(IdentifierPath const& _path)
175
11.6k
{
176
11.6k
  std::vector<Declaration const*> declarations = m_resolver.pathFromCurrentScopeWithAllDeclarations(_path.path());
177
11.6k
  if (declarations.empty())
178
395
  {
179
395
    m_errorReporter.fatalDeclarationError(7920_error, _path.location(), "Identifier not found or not unique.");
180
395
    return;
181
395
  }
182
183
11.2k
  _path.annotation().referencedDeclaration = declarations.back();
184
11.2k
  _path.annotation().pathDeclarations = std::move(declarations);
185
11.2k
}
186
187
bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly)
188
1.02k
{
189
1.02k
  m_yulAnnotation = &_inlineAssembly.annotation();
190
1.02k
  (*this)(_inlineAssembly.operations());
191
1.02k
  m_yulAnnotation = nullptr;
192
193
1.02k
  return false;
194
1.02k
}
195
196
bool ReferencesResolver::visit(Return const& _return)
197
2.64k
{
198
2.64k
  solAssert(!m_returnParameters.empty(), "");
199
2.64k
  _return.annotation().functionReturnParameters = m_returnParameters.back();
200
2.64k
  return true;
201
2.64k
}
202
203
void ReferencesResolver::operator()(yul::FunctionDefinition const& _function)
204
56
{
205
56
  solAssert(nativeLocationOf(_function) == originLocationOf(_function), "");
206
56
  validateYulIdentifierName(_function.name, nativeLocationOf(_function));
207
56
  for (yul::TypedName const& varName: _function.parameters + _function.returnVariables)
208
74
  {
209
74
    solAssert(nativeLocationOf(varName) == originLocationOf(varName), "");
210
74
    validateYulIdentifierName(varName.name, nativeLocationOf(varName));
211
74
  }
212
213
56
  bool wasInsideFunction = m_yulInsideFunction;
214
56
  m_yulInsideFunction = true;
215
56
  this->operator()(_function.body);
216
56
  m_yulInsideFunction = wasInsideFunction;
217
56
}
218
219
void ReferencesResolver::operator()(yul::Identifier const& _identifier)
220
1.47k
{
221
1.47k
  solAssert(nativeLocationOf(_identifier) == originLocationOf(_identifier), "");
222
223
1.47k
  static set<string> suffixes{"slot", "offset", "length", "address", "selector"};
224
1.47k
  string suffix;
225
1.47k
  for (string const& s: suffixes)
226
7.36k
    if (boost::algorithm::ends_with(_identifier.name.str(), "." + s))
227
142
      suffix = s;
228
229
  // Could also use `pathFromCurrentScope`, split by '.'.
230
  // If we do that, suffix should only be set for when it has a special
231
  // meaning, not for normal identifierPaths.
232
1.47k
  auto declarations = m_resolver.nameFromCurrentScope(_identifier.name.str());
233
1.47k
  if (!suffix.empty())
234
142
  {
235
    // special mode to access storage variables
236
142
    if (!declarations.empty())
237
      // the special identifier exists itself, we should not allow that.
238
0
      return;
239
142
    string realName = _identifier.name.str().substr(0, _identifier.name.str().size() - suffix.size() - 1);
240
142
    solAssert(!realName.empty(), "Empty name.");
241
142
    declarations = m_resolver.nameFromCurrentScope(realName);
242
142
    if (!declarations.empty())
243
      // To support proper path resolution, we have to use pathFromCurrentScope.
244
142
      solAssert(!util::contains(realName, '.'), "");
245
142
  }
246
1.47k
  if (declarations.size() > 1)
247
1
  {
248
1
    m_errorReporter.declarationError(
249
1
      4718_error,
250
1
      nativeLocationOf(_identifier),
251
1
      "Multiple matching identifiers. Resolving overloaded identifiers is not supported."
252
1
    );
253
1
    return;
254
1
  }
255
1.47k
  else if (declarations.size() == 0)
256
704
  {
257
704
    if (
258
704
      boost::algorithm::ends_with(_identifier.name.str(), "_slot") ||
259
704
      boost::algorithm::ends_with(_identifier.name.str(), "_offset")
260
704
    )
261
3
      m_errorReporter.declarationError(
262
3
        9467_error,
263
3
        nativeLocationOf(_identifier),
264
3
        "Identifier not found. Use \".slot\" and \".offset\" to access storage variables."
265
3
      );
266
704
    return;
267
704
  }
268
767
  if (auto var = dynamic_cast<VariableDeclaration const*>(declarations.front()))
269
682
    if (var->isLocalVariable() && m_yulInsideFunction)
270
2
    {
271
2
      m_errorReporter.declarationError(
272
2
        6578_error,
273
2
        nativeLocationOf(_identifier),
274
2
        "Cannot access local Solidity variables from inside an inline assembly function."
275
2
      );
276
2
      return;
277
2
    }
278
279
765
  m_yulAnnotation->externalReferences[&_identifier].suffix = move(suffix);
280
765
  m_yulAnnotation->externalReferences[&_identifier].declaration = declarations.front();
281
765
}
282
283
void ReferencesResolver::operator()(yul::VariableDeclaration const& _varDecl)
284
185
{
285
185
  for (auto const& identifier: _varDecl.variables)
286
337
  {
287
337
    solAssert(nativeLocationOf(identifier) == originLocationOf(identifier), "");
288
337
    validateYulIdentifierName(identifier.name, nativeLocationOf(identifier));
289
290
337
    if (
291
337
      auto declarations = m_resolver.nameFromCurrentScope(identifier.name.str());
292
337
      !declarations.empty()
293
337
    )
294
4
    {
295
4
      SecondarySourceLocation ssl;
296
4
      for (auto const* decl: declarations)
297
4
        ssl.append("The shadowed declaration is here:", decl->location());
298
4
      if (!ssl.infos.empty())
299
4
        m_errorReporter.declarationError(
300
4
          3859_error,
301
4
          nativeLocationOf(identifier),
302
4
          ssl,
303
4
          "This declaration shadows a declaration outside the inline assembly block."
304
4
        );
305
4
    }
306
337
  }
307
308
185
  if (_varDecl.value)
309
90
    visit(*_varDecl.value);
310
185
}
311
312
void ReferencesResolver::resolveInheritDoc(StructuredDocumentation const& _documentation, StructurallyDocumentedAnnotation& _annotation)
313
543
{
314
543
  switch (_annotation.docTags.count("inheritdoc"))
315
543
  {
316
487
  case 0:
317
487
    break;
318
47
  case 1:
319
47
  {
320
47
    string const& name = _annotation.docTags.find("inheritdoc")->second.content;
321
47
    if (name.empty())
322
3
    {
323
3
      m_errorReporter.docstringParsingError(
324
3
        1933_error,
325
3
        _documentation.location(),
326
3
        "Expected contract name following documentation tag @inheritdoc."
327
3
      );
328
3
      return;
329
3
    }
330
331
44
    vector<string> path;
332
44
    boost::split(path, name, boost::is_any_of("."));
333
68
    if (any_of(path.begin(), path.end(), [](auto& _str) { return _str.empty(); }))
334
3
    {
335
3
      m_errorReporter.docstringParsingError(
336
3
        5967_error,
337
3
        _documentation.location(),
338
3
        "Documentation tag @inheritdoc reference \"" +
339
3
        name +
340
3
        "\" is malformed."
341
3
      );
342
3
      return;
343
3
    }
344
41
    Declaration const* result = m_resolver.pathFromCurrentScope(path);
345
346
41
    if (result == nullptr)
347
12
    {
348
12
      m_errorReporter.docstringParsingError(
349
12
        9397_error,
350
12
        _documentation.location(),
351
12
        "Documentation tag @inheritdoc references inexistent contract \"" +
352
12
        name +
353
12
        "\"."
354
12
      );
355
12
      return;
356
12
    }
357
29
    else
358
29
    {
359
29
      _annotation.inheritdocReference = dynamic_cast<ContractDefinition const*>(result);
360
361
29
      if (!_annotation.inheritdocReference)
362
1
        m_errorReporter.docstringParsingError(
363
1
          1430_error,
364
1
          _documentation.location(),
365
1
          "Documentation tag @inheritdoc reference \"" +
366
1
          name +
367
1
          "\" is not a contract."
368
1
        );
369
29
    }
370
29
    break;
371
41
  }
372
29
  default:
373
9
    m_errorReporter.docstringParsingError(
374
9
      5142_error,
375
9
      _documentation.location(),
376
9
      "Documentation tag @inheritdoc can only be given once."
377
9
    );
378
9
    break;
379
543
  }
380
543
}
381
382
void ReferencesResolver::validateYulIdentifierName(yul::YulString _name, SourceLocation const& _location)
383
467
{
384
467
  if (util::contains(_name.str(), '.'))
385
14
    m_errorReporter.declarationError(
386
14
      3927_error,
387
14
      _location,
388
14
      "User-defined identifiers in inline assembly cannot contain '.'."
389
14
    );
390
391
467
  if (set<string>{"this", "super", "_"}.count(_name.str()))
392
7
    m_errorReporter.declarationError(
393
7
      4113_error,
394
7
      _location,
395
7
      "The identifier name \"" + _name.str() + "\" is reserved."
396
7
    );
397
467
}