Coverage Report

Created: 2026-09-14 06:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/solidity/libsolidity/analysis/ReferencesResolver.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 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
168k
{
49
168k
  auto errorWatcher = m_errorReporter.errorWatcher();
50
168k
  _root.accept(*this);
51
168k
  return errorWatcher.ok();
52
168k
}
53
54
bool ReferencesResolver::visit(Block const& _block)
55
57.0k
{
56
57.0k
  if (!m_resolveInsideCode)
57
26.2k
    return false;
58
30.7k
  m_resolver.setScope(&_block);
59
30.7k
  return true;
60
57.0k
}
61
62
void ReferencesResolver::endVisit(Block const& _block)
63
57.0k
{
64
57.0k
  if (!m_resolveInsideCode)
65
26.2k
    return;
66
67
30.7k
  m_resolver.setScope(_block.scope());
68
30.7k
}
69
70
bool ReferencesResolver::visit(TryCatchClause const& _tryCatchClause)
71
758
{
72
758
  if (!m_resolveInsideCode)
73
0
    return false;
74
758
  m_resolver.setScope(&_tryCatchClause);
75
758
  return true;
76
758
}
77
78
void ReferencesResolver::endVisit(TryCatchClause const& _tryCatchClause)
79
755
{
80
755
  if (!m_resolveInsideCode)
81
0
    return;
82
83
755
  m_resolver.setScope(_tryCatchClause.scope());
84
755
}
85
86
bool ReferencesResolver::visit(ForStatement const& _for)
87
3.46k
{
88
3.46k
  if (!m_resolveInsideCode)
89
0
    return false;
90
3.46k
  m_resolver.setScope(&_for);
91
3.46k
  return true;
92
3.46k
}
93
94
void ReferencesResolver::endVisit(ForStatement const& _for)
95
3.45k
{
96
3.45k
  if (!m_resolveInsideCode)
97
0
    return;
98
3.45k
  m_resolver.setScope(_for.scope());
99
3.45k
}
100
101
void ReferencesResolver::endVisit(VariableDeclarationStatement const& _varDeclStatement)
102
11.6k
{
103
11.6k
  if (!m_resolveInsideCode)
104
0
    return;
105
11.6k
  for (auto const& var: _varDeclStatement.declarations())
106
13.2k
    if (var)
107
12.5k
      m_resolver.activateVariable(var->name());
108
11.6k
}
109
110
bool ReferencesResolver::visit(VariableDeclaration const& _varDecl)
111
166k
{
112
166k
  if (_varDecl.documentation())
113
697
    resolveInheritDoc(*_varDecl.documentation(), _varDecl.annotation());
114
115
166k
  return true;
116
166k
}
117
118
bool ReferencesResolver::visit(Identifier const& _identifier)
119
183k
{
120
183k
  auto declarations = m_resolver.nameFromCurrentScope(_identifier.name());
121
183k
  if (declarations.empty())
122
3.05k
  {
123
3.05k
    std::string suggestions = m_resolver.similarNameSuggestions(_identifier.name());
124
3.05k
    std::string errorMessage = "Undeclared identifier.";
125
3.05k
    if (!suggestions.empty())
126
1.02k
    {
127
1.02k
      if ("\"" + _identifier.name() + "\"" == suggestions)
128
83
        errorMessage += " " + std::move(suggestions) + " is not (or not yet) visible at this point.";
129
944
      else
130
944
        errorMessage += " Did you mean " + std::move(suggestions) + "?";
131
1.02k
    }
132
3.05k
    m_errorReporter.declarationError(7576_error, _identifier.location(), errorMessage);
133
3.05k
  }
134
180k
  else if (declarations.size() == 1)
135
178k
    _identifier.annotation().referencedDeclaration = declarations.front();
136
1.27k
  else
137
1.27k
    _identifier.annotation().candidateDeclarations = declarations;
138
183k
  return false;
139
183k
}
140
141
bool ReferencesResolver::visit(FunctionDefinition const& _functionDefinition)
142
57.9k
{
143
57.9k
  m_functionDefinitions.push_back(&_functionDefinition);
144
145
57.9k
  if (_functionDefinition.documentation())
146
2.32k
    resolveInheritDoc(*_functionDefinition.documentation(), _functionDefinition.annotation());
147
148
57.9k
  return true;
149
57.9k
}
150
151
void ReferencesResolver::endVisit(FunctionDefinition const&)
152
57.5k
{
153
57.5k
  solAssert(!m_functionDefinitions.empty(), "");
154
57.5k
  m_functionDefinitions.pop_back();
155
57.5k
}
156
157
bool ReferencesResolver::visit(ModifierDefinition const& _modifierDefinition)
158
6.84k
{
159
6.84k
  m_functionDefinitions.push_back(nullptr);
160
161
6.84k
  if (_modifierDefinition.documentation())
162
12
    resolveInheritDoc(*_modifierDefinition.documentation(), _modifierDefinition.annotation());
163
164
6.84k
  return true;
165
6.84k
}
166
167
void ReferencesResolver::endVisit(ModifierDefinition const&)
168
6.83k
{
169
6.83k
  solAssert(!m_functionDefinitions.empty(), "");
170
6.83k
  m_functionDefinitions.pop_back();
171
6.83k
}
172
173
void ReferencesResolver::endVisit(IdentifierPath const& _path)
174
48.5k
{
175
  // Note that library/functions names in "using {} for" directive are resolved separately in visit(UsingForDirective)
176
48.5k
  std::vector<Declaration const*> declarations = m_resolver.pathFromCurrentScopeWithAllDeclarations(_path.path());
177
48.5k
  if (declarations.empty())
178
1.01k
  {
179
1.01k
    m_errorReporter.fatalDeclarationError(7920_error, _path.location(), "Identifier not found or not unique.");
180
1.01k
    return;
181
1.01k
  }
182
183
47.5k
  _path.annotation().referencedDeclaration = declarations.back();
184
47.5k
  _path.annotation().pathDeclarations = std::move(declarations);
185
47.5k
}
186
187
bool ReferencesResolver::visit(UsingForDirective const& _usingFor)
188
1.07k
{
189
1.07k
  for (ASTPointer<IdentifierPath> const& path: _usingFor.functionsOrLibrary())
190
1.14k
  {
191
    // _includeInvisibles is enabled here because external library functions are marked invisible.
192
    // As unintended side-effects other invisible names (eg.: super, this) may be returned as well.
193
    // DeclarationTypeChecker should detect and report such situations.
194
1.14k
    std::vector<Declaration const*> declarations = m_resolver.pathFromCurrentScopeWithAllDeclarations(path->path(), true /* _includeInvisibles */);
195
1.14k
    if (declarations.empty())
196
15
    {
197
15
      std::string libraryOrFunctionNameErrorMessage =
198
15
        _usingFor.usesBraces() ?
199
11
        "Identifier is not a function name or not unique." :
200
15
        "Identifier is not a library name.";
201
15
      m_errorReporter.fatalDeclarationError(
202
15
        9589_error,
203
15
        path->location(),
204
15
        libraryOrFunctionNameErrorMessage
205
15
      );
206
15
      break;
207
15
    }
208
209
1.12k
    path->annotation().referencedDeclaration = declarations.back();
210
1.12k
    path->annotation().pathDeclarations = std::move(declarations);
211
1.12k
  }
212
213
1.07k
  if (_usingFor.typeName())
214
806
    _usingFor.typeName()->accept(*this);
215
216
1.07k
  return false;
217
1.07k
}
218
219
bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly)
220
4.09k
{
221
4.09k
  m_yulAnnotation = &_inlineAssembly.annotation();
222
4.09k
  (*this)(_inlineAssembly.operations().root());
223
4.09k
  m_yulAnnotation = nullptr;
224
225
4.09k
  return false;
226
4.09k
}
227
228
bool ReferencesResolver::visit(Return const& _return)
229
56.5k
{
230
56.5k
  solAssert(!m_functionDefinitions.empty(), "");
231
56.5k
  _return.annotation().function = m_functionDefinitions.back();
232
56.5k
  _return.annotation().functionReturnParameters = m_functionDefinitions.back() ? m_functionDefinitions.back()->returnParameterList().get() : nullptr;
233
56.5k
  return true;
234
56.5k
}
235
236
void ReferencesResolver::operator()(yul::FunctionDefinition const& _function)
237
296
{
238
296
  solAssert(nativeLocationOf(_function) == originLocationOf(_function), "");
239
296
  validateYulIdentifierName(_function.name, nativeLocationOf(_function));
240
296
  for (yul::NameWithDebugData const& varName: _function.parameters + _function.returnVariables)
241
309
  {
242
309
    solAssert(nativeLocationOf(varName) == originLocationOf(varName), "");
243
309
    validateYulIdentifierName(varName.name, nativeLocationOf(varName));
244
309
  }
245
246
296
  bool wasInsideFunction = m_yulInsideFunction;
247
296
  m_yulInsideFunction = true;
248
296
  this->operator()(_function.body);
249
296
  m_yulInsideFunction = wasInsideFunction;
250
296
}
251
252
void ReferencesResolver::operator()(yul::Identifier const& _identifier)
253
2.51k
{
254
2.51k
  solAssert(nativeLocationOf(_identifier) == originLocationOf(_identifier), "");
255
256
2.51k
  static std::set<std::string> suffixes{"slot", "offset", "length", "address", "selector"};
257
2.51k
  std::string suffix;
258
2.51k
  for (std::string const& s: suffixes)
259
12.5k
    if (boost::algorithm::ends_with(_identifier.name.str(), "." + s))
260
297
      suffix = s;
261
262
  // Could also use `pathFromCurrentScope`, split by '.'.
263
  // If we do that, suffix should only be set for when it has a special
264
  // meaning, not for normal identifierPaths.
265
2.51k
  auto declarations = m_resolver.nameFromCurrentScope(_identifier.name.str());
266
2.51k
  if (!suffix.empty())
267
297
  {
268
    // special mode to access storage variables
269
297
    if (!declarations.empty())
270
      // the special identifier exists itself, we should not allow that.
271
0
      return;
272
297
    std::string realName = _identifier.name.str().substr(0, _identifier.name.str().size() - suffix.size() - 1);
273
297
    solAssert(!realName.empty(), "Empty name.");
274
297
    declarations = m_resolver.nameFromCurrentScope(realName);
275
297
    if (!declarations.empty())
276
      // To support proper path resolution, we have to use pathFromCurrentScope.
277
297
      solAssert(!util::contains(realName, '.'), "");
278
297
  }
279
2.51k
  if (declarations.size() > 1)
280
2
  {
281
2
    m_errorReporter.declarationError(
282
2
      4718_error,
283
2
      nativeLocationOf(_identifier),
284
2
      "Multiple matching identifiers. Resolving overloaded identifiers is not supported."
285
2
    );
286
2
    return;
287
2
  }
288
2.51k
  else if (declarations.size() == 0)
289
760
  {
290
760
    if (
291
760
      boost::algorithm::ends_with(_identifier.name.str(), "_slot") ||
292
755
      boost::algorithm::ends_with(_identifier.name.str(), "_offset")
293
760
    )
294
10
      m_errorReporter.declarationError(
295
10
        9467_error,
296
10
        nativeLocationOf(_identifier),
297
10
        "Identifier not found. Use \".slot\" and \".offset\" to access storage or transient storage variables."
298
10
      );
299
760
    return;
300
760
  }
301
1.75k
  if (auto var = dynamic_cast<VariableDeclaration const*>(declarations.front()))
302
1.71k
    if (var->isLocalVariable() && m_yulInsideFunction)
303
2
    {
304
2
      m_errorReporter.declarationError(
305
2
        6578_error,
306
2
        nativeLocationOf(_identifier),
307
2
        "Cannot access local Solidity variables from inside an inline assembly function."
308
2
      );
309
2
      return;
310
2
    }
311
312
1.75k
  m_yulAnnotation->externalReferences[&_identifier].suffix = std::move(suffix);
313
1.75k
  m_yulAnnotation->externalReferences[&_identifier].declaration = declarations.front();
314
1.75k
}
315
316
void ReferencesResolver::operator()(yul::VariableDeclaration const& _varDecl)
317
232
{
318
232
  for (auto const& identifier: _varDecl.variables)
319
280
  {
320
280
    solAssert(nativeLocationOf(identifier) == originLocationOf(identifier), "");
321
280
    validateYulIdentifierName(identifier.name, nativeLocationOf(identifier));
322
323
280
    if (
324
280
      auto declarations = m_resolver.nameFromCurrentScope(identifier.name.str());
325
280
      !declarations.empty()
326
280
    )
327
6
    {
328
6
      SecondarySourceLocation ssl;
329
6
      for (auto const* decl: declarations)
330
6
        ssl.append("The shadowed declaration is here:", decl->location());
331
6
      if (!ssl.infos.empty())
332
6
        m_errorReporter.declarationError(
333
6
          3859_error,
334
6
          nativeLocationOf(identifier),
335
6
          ssl,
336
6
          "This declaration shadows a declaration outside the inline assembly block."
337
6
        );
338
6
    }
339
280
  }
340
341
232
  if (_varDecl.value)
342
208
    visit(*_varDecl.value);
343
232
}
344
345
void ReferencesResolver::resolveInheritDoc(StructuredDocumentation const& _documentation, StructurallyDocumentedAnnotation& _annotation)
346
3.03k
{
347
3.03k
  switch (_annotation.docTags.count("inheritdoc"))
348
3.03k
  {
349
2.88k
  case 0:
350
2.88k
    break;
351
97
  case 1:
352
97
  {
353
97
    std::string const& name = _annotation.docTags.find("inheritdoc")->second.content;
354
97
    if (name.empty())
355
1
    {
356
1
      m_errorReporter.docstringParsingError(
357
1
        1933_error,
358
1
        _documentation.location(),
359
1
        "Expected contract name following documentation tag @inheritdoc."
360
1
      );
361
1
      return;
362
1
    }
363
364
96
    std::vector<std::string> path;
365
96
    boost::split(path, name, boost::is_any_of("."));
366
161
    if (any_of(path.begin(), path.end(), [](auto& _str) { return _str.empty(); }))
367
2
    {
368
2
      m_errorReporter.docstringParsingError(
369
2
        5967_error,
370
2
        _documentation.location(),
371
2
        "Documentation tag @inheritdoc reference \"" +
372
2
        name +
373
2
        "\" is malformed."
374
2
      );
375
2
      return;
376
2
    }
377
94
    Declaration const* result = m_resolver.pathFromCurrentScope(path);
378
379
94
    if (result == nullptr)
380
75
    {
381
75
      m_errorReporter.docstringParsingError(
382
75
        9397_error,
383
75
        _documentation.location(),
384
75
        "Documentation tag @inheritdoc references inexistent contract \"" +
385
75
        name +
386
75
        "\"."
387
75
      );
388
75
      return;
389
75
    }
390
19
    else
391
19
    {
392
19
      _annotation.inheritdocReference = dynamic_cast<ContractDefinition const*>(result);
393
394
19
      if (!_annotation.inheritdocReference)
395
2
        m_errorReporter.docstringParsingError(
396
2
          1430_error,
397
2
          _documentation.location(),
398
2
          "Documentation tag @inheritdoc reference \"" +
399
2
          name +
400
2
          "\" is not a contract."
401
2
        );
402
19
    }
403
19
    break;
404
94
  }
405
58
  default:
406
58
    m_errorReporter.docstringParsingError(
407
58
      5142_error,
408
58
      _documentation.location(),
409
58
      "Documentation tag @inheritdoc can only be given once."
410
58
    );
411
58
    break;
412
3.03k
  }
413
3.03k
}
414
415
void ReferencesResolver::validateYulIdentifierName(yul::YulName _name, SourceLocation const& _location)
416
885
{
417
885
  if (util::contains(_name.str(), '.'))
418
25
    m_errorReporter.declarationError(
419
25
      3927_error,
420
25
      _location,
421
25
      "User-defined identifiers in inline assembly cannot contain '.'."
422
25
    );
423
424
885
  if (std::set<std::string>{"this", "super", "_"}.count(_name.str()))
425
11
    m_errorReporter.declarationError(
426
11
      4113_error,
427
11
      _location,
428
11
      "The identifier name \"" + _name.str() + "\" is reserved."
429
11
    );
430
885
}