Coverage Report

Created: 2026-06-30 07:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}