Coverage Report

Created: 2025-09-04 07:34

/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 solidity;
43
using namespace solidity::langutil;
44
using namespace solidity::frontend;
45
46
47
bool ReferencesResolver::resolve(ASTNode const& _root)
48
160k
{
49
160k
  auto errorWatcher = m_errorReporter.errorWatcher();
50
160k
  _root.accept(*this);
51
160k
  return errorWatcher.ok();
52
160k
}
53
54
bool ReferencesResolver::visit(Block const& _block)
55
54.1k
{
56
54.1k
  if (!m_resolveInsideCode)
57
25.1k
    return false;
58
29.0k
  m_resolver.setScope(&_block);
59
29.0k
  return true;
60
54.1k
}
61
62
void ReferencesResolver::endVisit(Block const& _block)
63
54.1k
{
64
54.1k
  if (!m_resolveInsideCode)
65
25.1k
    return;
66
67
28.9k
  m_resolver.setScope(_block.scope());
68
28.9k
}
69
70
bool ReferencesResolver::visit(TryCatchClause const& _tryCatchClause)
71
672
{
72
672
  if (!m_resolveInsideCode)
73
0
    return false;
74
672
  m_resolver.setScope(&_tryCatchClause);
75
672
  return true;
76
672
}
77
78
void ReferencesResolver::endVisit(TryCatchClause const& _tryCatchClause)
79
669
{
80
669
  if (!m_resolveInsideCode)
81
0
    return;
82
83
669
  m_resolver.setScope(_tryCatchClause.scope());
84
669
}
85
86
bool ReferencesResolver::visit(ForStatement const& _for)
87
2.77k
{
88
2.77k
  if (!m_resolveInsideCode)
89
0
    return false;
90
2.77k
  m_resolver.setScope(&_for);
91
2.77k
  return true;
92
2.77k
}
93
94
void ReferencesResolver::endVisit(ForStatement const& _for)
95
2.77k
{
96
2.77k
  if (!m_resolveInsideCode)
97
0
    return;
98
2.77k
  m_resolver.setScope(_for.scope());
99
2.77k
}
100
101
void ReferencesResolver::endVisit(VariableDeclarationStatement const& _varDeclStatement)
102
11.1k
{
103
11.1k
  if (!m_resolveInsideCode)
104
0
    return;
105
11.1k
  for (auto const& var: _varDeclStatement.declarations())
106
12.2k
    if (var)
107
11.9k
      m_resolver.activateVariable(var->name());
108
11.1k
}
109
110
bool ReferencesResolver::visit(VariableDeclaration const& _varDecl)
111
161k
{
112
161k
  if (_varDecl.documentation())
113
806
    resolveInheritDoc(*_varDecl.documentation(), _varDecl.annotation());
114
115
161k
  if (m_resolver.experimentalSolidity())
116
306
  {
117
306
    solAssert(!_varDecl.hasTypeName());
118
306
    if (_varDecl.typeExpression())
119
169
    {
120
169
      ScopedSaveAndRestore typeContext{m_typeContext, true};
121
169
      _varDecl.typeExpression()->accept(*this);
122
169
    }
123
306
    if (_varDecl.overrides())
124
0
      _varDecl.overrides()->accept(*this);
125
306
    if (_varDecl.value())
126
0
      _varDecl.value()->accept(*this);
127
306
    return false;
128
306
  }
129
130
161k
  return true;
131
161k
}
132
133
bool ReferencesResolver::visit(Identifier const& _identifier)
134
162k
{
135
162k
  auto declarations = m_resolver.nameFromCurrentScope(_identifier.name());
136
162k
  if (declarations.empty())
137
5.86k
  {
138
5.86k
    if (m_resolver.experimentalSolidity() && m_typeContext)
139
87
      return false;
140
5.77k
    std::string suggestions = m_resolver.similarNameSuggestions(_identifier.name());
141
5.77k
    std::string errorMessage = "Undeclared identifier.";
142
5.77k
    if (!suggestions.empty())
143
763
    {
144
763
      if ("\"" + _identifier.name() + "\"" == suggestions)
145
77
        errorMessage += " " + std::move(suggestions) + " is not (or not yet) visible at this point.";
146
686
      else
147
686
        errorMessage += " Did you mean " + std::move(suggestions) + "?";
148
763
    }
149
5.77k
    m_errorReporter.declarationError(7576_error, _identifier.location(), errorMessage);
150
5.77k
  }
151
157k
  else if (declarations.size() == 1)
152
155k
    _identifier.annotation().referencedDeclaration = declarations.front();
153
1.09k
  else
154
1.09k
    _identifier.annotation().candidateDeclarations = declarations;
155
162k
  return false;
156
162k
}
157
158
bool ReferencesResolver::visit(FunctionDefinition const& _functionDefinition)
159
53.8k
{
160
53.8k
  m_functionDefinitions.push_back(&_functionDefinition);
161
162
53.8k
  if (_functionDefinition.documentation())
163
2.09k
    resolveInheritDoc(*_functionDefinition.documentation(), _functionDefinition.annotation());
164
165
53.8k
  return true;
166
53.8k
}
167
168
void ReferencesResolver::endVisit(FunctionDefinition const&)
169
53.4k
{
170
53.4k
  solAssert(!m_functionDefinitions.empty(), "");
171
53.4k
  m_functionDefinitions.pop_back();
172
53.4k
}
173
174
bool ReferencesResolver::visit(ModifierDefinition const& _modifierDefinition)
175
4.78k
{
176
4.78k
  m_functionDefinitions.push_back(nullptr);
177
178
4.78k
  if (_modifierDefinition.documentation())
179
13
    resolveInheritDoc(*_modifierDefinition.documentation(), _modifierDefinition.annotation());
180
181
4.78k
  return true;
182
4.78k
}
183
184
void ReferencesResolver::endVisit(ModifierDefinition const&)
185
4.76k
{
186
4.76k
  solAssert(!m_functionDefinitions.empty(), "");
187
4.76k
  m_functionDefinitions.pop_back();
188
4.76k
}
189
190
void ReferencesResolver::endVisit(IdentifierPath const& _path)
191
46.5k
{
192
  // Note that library/functions names in "using {} for" directive are resolved separately in visit(UsingForDirective)
193
46.5k
  std::vector<Declaration const*> declarations = m_resolver.pathFromCurrentScopeWithAllDeclarations(_path.path());
194
46.5k
  if (declarations.empty())
195
1.46k
  {
196
1.46k
    m_errorReporter.fatalDeclarationError(7920_error, _path.location(), "Identifier not found or not unique.");
197
1.46k
    return;
198
1.46k
  }
199
200
45.1k
  _path.annotation().referencedDeclaration = declarations.back();
201
45.1k
  _path.annotation().pathDeclarations = std::move(declarations);
202
45.1k
}
203
204
bool ReferencesResolver::visit(UsingForDirective const& _usingFor)
205
915
{
206
915
  for (ASTPointer<IdentifierPath> const& path: _usingFor.functionsOrLibrary())
207
985
  {
208
    // _includeInvisibles is enabled here because external library functions are marked invisible.
209
    // As unintended side-effects other invisible names (eg.: super, this) may be returned as well.
210
    // DeclarationTypeChecker should detect and report such situations.
211
985
    std::vector<Declaration const*> declarations = m_resolver.pathFromCurrentScopeWithAllDeclarations(path->path(), true /* _includeInvisibles */);
212
985
    if (declarations.empty())
213
13
    {
214
13
      std::string libraryOrFunctionNameErrorMessage =
215
13
        _usingFor.usesBraces() ?
216
8
        "Identifier is not a function name or not unique." :
217
13
        "Identifier is not a library name.";
218
13
      m_errorReporter.fatalDeclarationError(
219
13
        9589_error,
220
13
        path->location(),
221
13
        libraryOrFunctionNameErrorMessage
222
13
      );
223
13
      break;
224
13
    }
225
226
972
    path->annotation().referencedDeclaration = declarations.back();
227
972
    path->annotation().pathDeclarations = std::move(declarations);
228
972
  }
229
230
915
  if (_usingFor.typeName())
231
667
    _usingFor.typeName()->accept(*this);
232
233
915
  return false;
234
915
}
235
236
bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly)
237
2.73k
{
238
2.73k
  m_yulAnnotation = &_inlineAssembly.annotation();
239
2.73k
  (*this)(_inlineAssembly.operations().root());
240
2.73k
  m_yulAnnotation = nullptr;
241
242
2.73k
  return false;
243
2.73k
}
244
245
bool ReferencesResolver::visit(Return const& _return)
246
46.4k
{
247
46.4k
  solAssert(!m_functionDefinitions.empty(), "");
248
46.4k
  _return.annotation().function = m_functionDefinitions.back();
249
46.4k
  _return.annotation().functionReturnParameters = m_functionDefinitions.back() ? m_functionDefinitions.back()->returnParameterList().get() : nullptr;
250
46.4k
  return true;
251
46.4k
}
252
253
bool ReferencesResolver::visit(BinaryOperation const& _binaryOperation)
254
111k
{
255
111k
  if (m_resolver.experimentalSolidity())
256
16
  {
257
16
    _binaryOperation.leftExpression().accept(*this);
258
16
    if (_binaryOperation.getOperator() == Token::Colon)
259
3
    {
260
3
      ScopedSaveAndRestore typeContext(m_typeContext, !m_typeContext);
261
3
      _binaryOperation.rightExpression().accept(*this);
262
3
    }
263
13
    else
264
13
      _binaryOperation.rightExpression().accept(*this);
265
16
    return false;
266
16
  }
267
111k
  else
268
111k
    return ASTConstVisitor::visit(_binaryOperation);
269
111k
}
270
271
void ReferencesResolver::operator()(yul::FunctionDefinition const& _function)
272
259
{
273
259
  solAssert(nativeLocationOf(_function) == originLocationOf(_function), "");
274
259
  validateYulIdentifierName(_function.name, nativeLocationOf(_function));
275
259
  for (yul::NameWithDebugData const& varName: _function.parameters + _function.returnVariables)
276
274
  {
277
274
    solAssert(nativeLocationOf(varName) == originLocationOf(varName), "");
278
274
    validateYulIdentifierName(varName.name, nativeLocationOf(varName));
279
274
  }
280
281
259
  bool wasInsideFunction = m_yulInsideFunction;
282
259
  m_yulInsideFunction = true;
283
259
  this->operator()(_function.body);
284
259
  m_yulInsideFunction = wasInsideFunction;
285
259
}
286
287
void ReferencesResolver::operator()(yul::Identifier const& _identifier)
288
2.55k
{
289
2.55k
  solAssert(nativeLocationOf(_identifier) == originLocationOf(_identifier), "");
290
291
2.55k
  if (m_resolver.experimentalSolidity())
292
0
  {
293
0
    std::vector<std::string> splitName;
294
0
    boost::split(splitName, _identifier.name.str(), boost::is_any_of("."));
295
0
    solAssert(!splitName.empty());
296
0
    if (splitName.size() > 2)
297
0
    {
298
0
      m_errorReporter.declarationError(
299
0
        4955_error,
300
0
        nativeLocationOf(_identifier),
301
0
        "Unsupported identifier in inline assembly."
302
0
      );
303
0
      return;
304
0
    }
305
0
    std::string name = splitName.front();
306
0
    auto declarations = m_resolver.nameFromCurrentScope(name);
307
0
    switch (declarations.size())
308
0
    {
309
0
    case 0:
310
0
      if (splitName.size() > 1)
311
0
        m_errorReporter.declarationError(
312
0
          7531_error,
313
0
          nativeLocationOf(_identifier),
314
0
          "Unsupported identifier in inline assembly."
315
0
        );
316
0
      break;
317
0
    case 1:
318
0
      m_yulAnnotation->externalReferences[&_identifier].declaration = declarations.front();
319
0
      m_yulAnnotation->externalReferences[&_identifier].suffix = splitName.size() > 1 ? splitName.back() : "";
320
0
      break;
321
0
    default:
322
0
      m_errorReporter.declarationError(
323
0
        5387_error,
324
0
        nativeLocationOf(_identifier),
325
0
        "Multiple matching identifiers. Resolving overloaded identifiers is not supported."
326
0
      );
327
0
      break;
328
0
    }
329
0
    return;
330
0
  }
331
332
2.55k
  static std::set<std::string> suffixes{"slot", "offset", "length", "address", "selector"};
333
2.55k
  std::string suffix;
334
2.55k
  for (std::string const& s: suffixes)
335
12.7k
    if (boost::algorithm::ends_with(_identifier.name.str(), "." + s))
336
329
      suffix = s;
337
338
  // Could also use `pathFromCurrentScope`, split by '.'.
339
  // If we do that, suffix should only be set for when it has a special
340
  // meaning, not for normal identifierPaths.
341
2.55k
  auto declarations = m_resolver.nameFromCurrentScope(_identifier.name.str());
342
2.55k
  if (!suffix.empty())
343
329
  {
344
    // special mode to access storage variables
345
329
    if (!declarations.empty())
346
      // the special identifier exists itself, we should not allow that.
347
0
      return;
348
329
    std::string realName = _identifier.name.str().substr(0, _identifier.name.str().size() - suffix.size() - 1);
349
329
    solAssert(!realName.empty(), "Empty name.");
350
329
    declarations = m_resolver.nameFromCurrentScope(realName);
351
329
    if (!declarations.empty())
352
      // To support proper path resolution, we have to use pathFromCurrentScope.
353
329
      solAssert(!util::contains(realName, '.'), "");
354
329
  }
355
2.55k
  if (declarations.size() > 1)
356
2
  {
357
2
    m_errorReporter.declarationError(
358
2
      4718_error,
359
2
      nativeLocationOf(_identifier),
360
2
      "Multiple matching identifiers. Resolving overloaded identifiers is not supported."
361
2
    );
362
2
    return;
363
2
  }
364
2.55k
  else if (declarations.size() == 0)
365
887
  {
366
887
    if (
367
887
      boost::algorithm::ends_with(_identifier.name.str(), "_slot") ||
368
887
      boost::algorithm::ends_with(_identifier.name.str(), "_offset")
369
887
    )
370
11
      m_errorReporter.declarationError(
371
11
        9467_error,
372
11
        nativeLocationOf(_identifier),
373
11
        "Identifier not found. Use \".slot\" and \".offset\" to access storage or transient storage variables."
374
11
      );
375
887
    return;
376
887
  }
377
1.66k
  if (auto var = dynamic_cast<VariableDeclaration const*>(declarations.front()))
378
1.64k
    if (var->isLocalVariable() && m_yulInsideFunction)
379
2
    {
380
2
      m_errorReporter.declarationError(
381
2
        6578_error,
382
2
        nativeLocationOf(_identifier),
383
2
        "Cannot access local Solidity variables from inside an inline assembly function."
384
2
      );
385
2
      return;
386
2
    }
387
388
1.66k
  m_yulAnnotation->externalReferences[&_identifier].suffix = std::move(suffix);
389
1.66k
  m_yulAnnotation->externalReferences[&_identifier].declaration = declarations.front();
390
1.66k
}
391
392
void ReferencesResolver::operator()(yul::VariableDeclaration const& _varDecl)
393
268
{
394
268
  for (auto const& identifier: _varDecl.variables)
395
318
  {
396
318
    solAssert(nativeLocationOf(identifier) == originLocationOf(identifier), "");
397
318
    validateYulIdentifierName(identifier.name, nativeLocationOf(identifier));
398
399
318
    if (
400
318
      auto declarations = m_resolver.nameFromCurrentScope(identifier.name.str());
401
318
      !declarations.empty()
402
318
    )
403
4
    {
404
4
      SecondarySourceLocation ssl;
405
4
      for (auto const* decl: declarations)
406
4
        ssl.append("The shadowed declaration is here:", decl->location());
407
4
      if (!ssl.infos.empty())
408
4
        m_errorReporter.declarationError(
409
4
          3859_error,
410
4
          nativeLocationOf(identifier),
411
4
          ssl,
412
4
          "This declaration shadows a declaration outside the inline assembly block."
413
4
        );
414
4
    }
415
318
  }
416
417
268
  if (_varDecl.value)
418
209
    visit(*_varDecl.value);
419
268
}
420
421
void ReferencesResolver::resolveInheritDoc(StructuredDocumentation const& _documentation, StructurallyDocumentedAnnotation& _annotation)
422
2.91k
{
423
2.91k
  switch (_annotation.docTags.count("inheritdoc"))
424
2.91k
  {
425
2.77k
  case 0:
426
2.77k
    break;
427
111
  case 1:
428
111
  {
429
111
    std::string const& name = _annotation.docTags.find("inheritdoc")->second.content;
430
111
    if (name.empty())
431
1
    {
432
1
      m_errorReporter.docstringParsingError(
433
1
        1933_error,
434
1
        _documentation.location(),
435
1
        "Expected contract name following documentation tag @inheritdoc."
436
1
      );
437
1
      return;
438
1
    }
439
440
110
    std::vector<std::string> path;
441
110
    boost::split(path, name, boost::is_any_of("."));
442
146
    if (any_of(path.begin(), path.end(), [](auto& _str) { return _str.empty(); }))
443
1
    {
444
1
      m_errorReporter.docstringParsingError(
445
1
        5967_error,
446
1
        _documentation.location(),
447
1
        "Documentation tag @inheritdoc reference \"" +
448
1
        name +
449
1
        "\" is malformed."
450
1
      );
451
1
      return;
452
1
    }
453
109
    Declaration const* result = m_resolver.pathFromCurrentScope(path);
454
455
109
    if (result == nullptr)
456
91
    {
457
91
      m_errorReporter.docstringParsingError(
458
91
        9397_error,
459
91
        _documentation.location(),
460
91
        "Documentation tag @inheritdoc references inexistent contract \"" +
461
91
        name +
462
91
        "\"."
463
91
      );
464
91
      return;
465
91
    }
466
18
    else
467
18
    {
468
18
      _annotation.inheritdocReference = dynamic_cast<ContractDefinition const*>(result);
469
470
18
      if (!_annotation.inheritdocReference)
471
2
        m_errorReporter.docstringParsingError(
472
2
          1430_error,
473
2
          _documentation.location(),
474
2
          "Documentation tag @inheritdoc reference \"" +
475
2
          name +
476
2
          "\" is not a contract."
477
2
        );
478
18
    }
479
18
    break;
480
109
  }
481
31
  default:
482
31
    m_errorReporter.docstringParsingError(
483
31
      5142_error,
484
31
      _documentation.location(),
485
31
      "Documentation tag @inheritdoc can only be given once."
486
31
    );
487
31
    break;
488
2.91k
  }
489
2.91k
}
490
491
void ReferencesResolver::validateYulIdentifierName(yul::YulName _name, SourceLocation const& _location)
492
851
{
493
851
  if (util::contains(_name.str(), '.'))
494
21
    m_errorReporter.declarationError(
495
21
      3927_error,
496
21
      _location,
497
21
      "User-defined identifiers in inline assembly cannot contain '.'."
498
21
    );
499
500
851
  if (std::set<std::string>{"this", "super", "_"}.count(_name.str()))
501
9
    m_errorReporter.declarationError(
502
9
      4113_error,
503
9
      _location,
504
9
      "The identifier name \"" + _name.str() + "\" is reserved."
505
9
    );
506
851
}