Coverage Report

Created: 2026-10-26 06:53

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/solidity/liblangutil/SourceReferenceExtractor.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
#include <liblangutil/SourceReferenceExtractor.h>
19
#include <liblangutil/Exceptions.h>
20
#include <liblangutil/CharStreamProvider.h>
21
#include <liblangutil/CharStream.h>
22
23
#include <algorithm>
24
#include <cmath>
25
#include <cstddef>
26
#include <string_view>
27
#include <variant>
28
29
using namespace solidity;
30
using namespace solidity::langutil;
31
32
namespace
33
{
34
35
// If `_position` points into the middle of a multi-byte character, yield the beginning of the character. Otherwise,
36
// this is the identity.
37
std::string::size_type toUTF8SequenceStart(std::string_view const _text, std::string::size_type _position)
38
0
{
39
0
  while (
40
0
    _position > 0 &&
41
0
    _position < _text.size() &&
42
0
    (static_cast<std::byte>(_text[_position]) & std::byte{0xc0}) == std::byte{0x80}
43
0
  )
44
0
    --_position;
45
0
  return _position;
46
0
}
47
48
}
49
50
SourceReferenceExtractor::Message SourceReferenceExtractor::extract(
51
  CharStreamProvider const& _charStreamProvider,
52
  util::Exception const& _exception,
53
  std::variant<Error::Type, Error::Severity> _typeOrSeverity
54
)
55
0
{
56
0
  SourceLocation const* location = boost::get_error_info<errinfo_sourceLocation>(_exception);
57
58
0
  std::string const* message = boost::get_error_info<util::errinfo_comment>(_exception);
59
0
  SourceReference primary = extract(_charStreamProvider, location, message ? *message : "");
60
61
0
  std::vector<SourceReference> secondary;
62
0
  auto secondaryLocation = boost::get_error_info<errinfo_secondarySourceLocation>(_exception);
63
0
  if (secondaryLocation && !secondaryLocation->infos.empty())
64
0
    for (auto const& info: secondaryLocation->infos)
65
0
      secondary.emplace_back(extract(_charStreamProvider, &info.second, info.first));
66
67
0
  return Message{std::move(primary), _typeOrSeverity, std::move(secondary), std::nullopt};
68
0
}
69
70
SourceReferenceExtractor::Message SourceReferenceExtractor::extract(
71
  CharStreamProvider const& _charStreamProvider,
72
  Error const& _error,
73
  std::variant<Error::Type, Error::Severity> _typeOrSeverity
74
)
75
0
{
76
0
  Message message = extract(_charStreamProvider, static_cast<util::Exception>(_error), _typeOrSeverity);
77
0
  message.errorId = _error.errorId();
78
0
  return message;
79
0
}
80
81
SourceReference SourceReferenceExtractor::extract(
82
  CharStreamProvider const& _charStreamProvider,
83
  SourceLocation const* _location,
84
  std::string message
85
)
86
0
{
87
0
  if (!_location || !_location->sourceName) // Nothing we can extract here
88
0
    return SourceReference::MessageOnly(std::move(message));
89
90
0
  if (!_location->hasText()) // No source text, so we can only extract the source name
91
0
    return SourceReference::MessageOnly(std::move(message), *_location->sourceName);
92
93
0
  CharStream const& charStream = _charStreamProvider.charStream(*_location->sourceName);
94
95
0
  LineColumn const interest = charStream.translatePositionToLineColumn(_location->start);
96
0
  LineColumn start = interest;
97
0
  LineColumn end = charStream.translatePositionToLineColumn(_location->end);
98
0
  bool const isMultiline = start.line != end.line;
99
100
0
  std::string line = charStream.lineAtPosition(_location->start);
101
102
0
  int locationLength =
103
0
    isMultiline ?
104
0
      int(line.length()) - start.column :
105
0
      end.column - start.column;
106
107
0
  if (locationLength > 150)
108
0
  {
109
0
    std::string::size_type const lhs = toUTF8SequenceStart(line, static_cast<std::string::size_type>(start.column) + 35);
110
0
    std::string::size_type const rhs = toUTF8SequenceStart(
111
0
      line,
112
0
      (isMultiline ? line.length() : static_cast<size_t>(end.column)) - 35
113
0
    );
114
0
    line = line.substr(0, lhs) + " ... " + line.substr(rhs);
115
0
    end.column = start.column + 75;
116
0
    locationLength = 75;
117
0
  }
118
119
0
  if (line.length() > 150)
120
0
  {
121
0
    int const len = static_cast<int>(line.length());
122
0
    std::string::size_type const cutStart = toUTF8SequenceStart(line, static_cast<std::string::size_type>(std::max(0, start.column - 35)));
123
0
    std::string::size_type const cutEnd = toUTF8SequenceStart(
124
0
      line,
125
0
      static_cast<std::string::size_type>(start.column) + static_cast<std::string::size_type>(std::min(locationLength + 35, len - start.column))
126
0
    );
127
0
    solAssert(cutEnd >= cutStart);
128
0
    line = line.substr(cutStart, cutEnd - cutStart);
129
0
    if (start.column + locationLength + 35 < len)
130
0
      line += " ...";
131
0
    if (start.column > 35)
132
0
    {
133
0
      line = " ... " + line;
134
0
      start.column = 40;
135
0
    }
136
0
    end.column = start.column + static_cast<int>(locationLength);
137
0
  }
138
139
0
  return SourceReference{
140
0
    std::move(message),
141
0
    *_location->sourceName,
142
0
    interest,
143
0
    isMultiline,
144
0
    line,
145
0
    std::min(start.column, static_cast<int>(line.length())),
146
0
    std::min(end.column, static_cast<int>(line.length()))
147
0
  };
148
0
}