Skip to content

Commit b6c97dc

Browse files
committed
In several PRs, such as microsoft#908 and microsoft#1210 , it has become clear that we need a thread safe channel through which errors and warnings can both be reasonably reported. Now that microsoft#1279 is landed and functionally everything in the codebase uses ExpectedL, we can look at what the new thing that fixes issues is.
Consider the following: ```c++ ExpectedL<T> example_api(int a); ExpectedL<std::unique_ptr<SourceControlFile>> try_load_port_manifest_text(StringView text, StringView control_path, MessageSink& warning_sink); ``` The reason this can't return the warnings through the ExpectedL channel is that we don't want the 'error' state to be engaged when there are merely warnings. Moreover, that these channels are different channels means that situations that might want to return errors and warnings together, as happens when parsing files, means that order relationships between errors and warnings is lost. It is probably a good idea in general to put warnings and errors about the same location next to each other in the output, but that's hard to do with this interface. Rather than multiplexing everything through the return value, this proposal is to multiplex only the success or failure through the return value, and report any specific error information through an out parameter. 1. Distinguish whether an overall operation succeeded or failed in the return value, but 2. record any errors or warnings via an out parameter. Applying this to the above gives: ```c++ Optional<T> example_api(MessageContext& context, int a); // unique_ptr is already 'optional' std::unique_ptr<SourceControlFile> try_load_port_manifest_text(MessageContext& context, StringView text, StringView control_path); ``` Issues this new mechanism fixes: * Errors and warnings can share the same channel and thus be printed together * The interface between code wanting to report events and the code wanting to consume them is a natural thread synchronization boundary. Other attempts to fix this have been incorrect by synchronizing individual print calls ( microsoft#1290 ) or complex enough that we are not sure they are correct by trying to recover boundaries by reparsing our own error output ( microsoft#908 ) * This shuts down the "error: error:" and similar bugs where it isn't clear who is formatting the overall error message vs. talking about individual components Known issues that are not fixed by this change: * This still doesn't make it easy for callers to programmatically handle specific types of errors. Currently, we have some APIs that still use explicit `std::error_code` because they want to do different things for 'file does not exist' vs. 'there was an I/O error'. Given that this condition isn't well served by the ExpectedL mechanism I don't want to wait until we have a better solution to it to proceed. * Because we aren't making the context parameter the 'success carrier' it's more complex to implement 'warnings as errors' or similar functionality where the caller decides how 'important' something is. I would be in favor of moving all success tests to the context parameter but I'm not proposing that because the other vcpkg maintainers do not like it. * Contextual information / stack problems aren't solved. However, the context parameter might be extended in the future to help with this.
1 parent bfda089 commit b6c97dc

File tree

11 files changed

+93
-52
lines changed

11 files changed

+93
-52
lines changed

include/vcpkg/base/diagnostics.h

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
#pragma once
2+
3+
#include <vcpkg/base/messages.h>
4+
#include <vcpkg/base/optional.h>
5+
6+
#include <string>
7+
8+
namespace vcpkg
9+
{
10+
enum class DiagKind
11+
{
12+
None, // foo.h: localized
13+
Message, // foo.h: message: localized
14+
Error, // foo.h: error: localized
15+
Warning, // foo.h: warning: localized
16+
Note // foo.h: note: localized
17+
};
18+
19+
struct TextPosition
20+
{
21+
// '0' indicates uninitialized; '1' is the first row/column
22+
int row = 0;
23+
int column = 0;
24+
};
25+
26+
struct DiagnosticLine
27+
{
28+
DiagnosticLine(MessageKind kind, LocalizedString&& message)
29+
: m_kind(kind), m_origin(), m_position(), m_message(std::move(message))
30+
{
31+
}
32+
33+
DiagnosticLine(MessageKind kind, StringView origin, LocalizedString&& message)
34+
: m_kind(kind), m_origin(origin.to_string()), m_position(), m_message(std::move(message))
35+
{
36+
}
37+
38+
DiagnosticLine(MessageKind kind, StringView origin, TextPosition position, LocalizedString&& message)
39+
: m_kind(kind), m_origin(origin.to_string()), m_position(position), m_message(std::move(message))
40+
{
41+
}
42+
43+
private:
44+
MessageKind m_kind;
45+
Optional<std::string> m_origin;
46+
TextPosition m_position;
47+
LocalizedString m_message;
48+
};
49+
50+
struct DiagnosticContext
51+
{
52+
virtual void report(const DiagnosticLine& line);
53+
virtual void report(DiagnosticLine&& line) { report(line); }
54+
55+
protected:
56+
~DiagnosticContext() = default;
57+
};
58+
}

include/vcpkg/base/parse.h

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,12 @@
22

33
#include <vcpkg/base/fwd/parse.h>
44

5+
#include <vcpkg/base/diagnostics.h>
56
#include <vcpkg/base/messages.h>
67
#include <vcpkg/base/optional.h>
78
#include <vcpkg/base/stringview.h>
89
#include <vcpkg/base/unicode.h>
910

10-
#include <vcpkg/textrowcol.h>
11-
1211
#include <memory>
1312
#include <string>
1413

@@ -75,7 +74,7 @@ namespace vcpkg
7574

7675
struct ParserBase
7776
{
78-
ParserBase(StringView text, StringView origin, TextRowCol init_rowcol = {});
77+
ParserBase(StringView text, StringView origin, TextPosition init_rowcol = {1, 1});
7978

8079
static constexpr bool is_whitespace(char32_t ch) { return ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n'; }
8180
static constexpr bool is_lower_alpha(char32_t ch) { return ch >= 'a' && ch <= 'z'; }
@@ -131,7 +130,7 @@ namespace vcpkg
131130
Unicode::Utf8Decoder it() const { return m_it; }
132131
char32_t cur() const { return m_it == m_it.end() ? Unicode::end_of_file : *m_it; }
133132
SourceLoc cur_loc() const { return {m_it, m_start_of_line, m_row, m_column}; }
134-
TextRowCol cur_rowcol() const { return {m_row, m_column}; }
133+
TextPosition cur_rowcol() const { return {m_row, m_column}; }
135134
char32_t next();
136135
bool at_eof() const { return m_it == m_it.end(); }
137136

include/vcpkg/paragraphparser.h

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22

33
#include <vcpkg/fwd/paragraphparser.h>
44

5+
#include <vcpkg/base/diagnostics.h>
56
#include <vcpkg/base/expected.h>
67
#include <vcpkg/base/messages.h>
78
#include <vcpkg/base/stringview.h>
89

910
#include <vcpkg/packagespec.h>
10-
#include <vcpkg/textrowcol.h>
1111

1212
#include <map>
1313
#include <memory>
@@ -16,7 +16,7 @@
1616

1717
namespace vcpkg
1818
{
19-
using Paragraph = std::map<std::string, std::pair<std::string, TextRowCol>, std::less<>>;
19+
using Paragraph = std::map<std::string, std::pair<std::string, TextPosition>, std::less<>>;
2020

2121
struct ParagraphParser
2222
{
@@ -28,9 +28,9 @@ namespace vcpkg
2828
std::string required_field(StringLiteral fieldname);
2929

3030
std::string optional_field(StringLiteral fieldname);
31-
std::string optional_field(StringLiteral fieldname, TextRowCol& position);
31+
std::string optional_field(StringLiteral fieldname, TextPosition& position);
3232

33-
void add_error(TextRowCol position, msg::MessageT<> error_content);
33+
void add_error(TextPosition position, msg::MessageT<> error_content);
3434

3535
Optional<LocalizedString> error() const;
3636

@@ -42,8 +42,8 @@ namespace vcpkg
4242

4343
ExpectedL<std::vector<std::string>> parse_default_features_list(const std::string& str,
4444
StringView origin = "<unknown>",
45-
TextRowCol textrowcol = {});
45+
TextPosition position = {1, 1});
4646
ExpectedL<std::vector<ParsedQualifiedSpecifier>> parse_qualified_specifier_list(const std::string& str,
4747
StringView origin = "<unknown>",
48-
TextRowCol textrowcol = {});
48+
TextPosition position = {1, 1});
4949
}

include/vcpkg/sourceparagraph.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,5 +223,5 @@ namespace vcpkg
223223
// Exposed for testing
224224
ExpectedL<std::vector<Dependency>> parse_dependencies_list(const std::string& str,
225225
StringView origin,
226-
TextRowCol textrowcol = {});
226+
TextPosition position = {1, 1});
227227
}

include/vcpkg/textrowcol.h

Lines changed: 0 additions & 17 deletions
This file was deleted.

src/vcpkg-test/paragraph.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ namespace
1515
{
1616
pghs.emplace_back();
1717
for (auto&& kv : p)
18-
pghs.back().emplace(kv.first, std::make_pair(kv.second, vcpkg::TextRowCol{}));
18+
pghs.back().emplace(kv.first, std::make_pair(kv.second, vcpkg::TextPosition{}));
1919
}
2020
return vcpkg::SourceControlFile::parse_control_file("", std::move(pghs));
2121
}
@@ -24,7 +24,7 @@ namespace
2424
{
2525
Paragraph pgh;
2626
for (auto&& kv : v)
27-
pgh.emplace(kv.first, std::make_pair(kv.second, vcpkg::TextRowCol{}));
27+
pgh.emplace(kv.first, std::make_pair(kv.second, vcpkg::TextPosition{}));
2828

2929
return vcpkg::BinaryParagraph("test", std::move(pgh));
3030
}

src/vcpkg-test/util.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ namespace vcpkg::Test
6363
pghs.emplace_back();
6464
for (auto&& kv : p)
6565
{
66-
pghs.back().emplace(kv.first, std::make_pair(kv.second, vcpkg::TextRowCol{}));
66+
pghs.back().emplace(kv.first, std::make_pair(kv.second, vcpkg::TextPosition{}));
6767
}
6868
}
6969
return vcpkg::SourceControlFile::parse_control_file("", std::move(pghs));

src/vcpkg/base/parse.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,11 +90,11 @@ namespace vcpkg
9090
}
9191
}
9292

93-
ParserBase::ParserBase(StringView text, StringView origin, TextRowCol init_rowcol)
93+
ParserBase::ParserBase(StringView text, StringView origin, TextPosition init_rowcol)
9494
: m_it(text.begin(), text.end())
9595
, m_start_of_line(m_it)
96-
, m_row(init_rowcol.row_or(1))
97-
, m_column(init_rowcol.column_or(1))
96+
, m_row(init_rowcol.row)
97+
, m_column(init_rowcol.column)
9898
, m_text(text)
9999
, m_origin(origin)
100100
{

src/vcpkg/binaryparagraph.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ namespace vcpkg
3535

3636
// one or the other
3737
this->version.text = parser.optional_field(Fields::VERSION);
38-
TextRowCol pv_position;
38+
TextPosition pv_position;
3939
auto pv_str = parser.optional_field(Fields::PORT_VERSION, pv_position);
4040
this->version.port_version = 0;
4141
if (!pv_str.empty())

src/vcpkg/paragraphs.cpp

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ static std::atomic<uint64_t> g_load_ports_stats(0);
2020

2121
namespace vcpkg
2222
{
23-
static Optional<std::pair<std::string, TextRowCol>> remove_field(Paragraph* fields, StringView fieldname)
23+
static Optional<std::pair<std::string, TextPosition>> remove_field(Paragraph* fields, StringView fieldname)
2424
{
2525
auto it = fields->find(fieldname.to_string());
2626
if (it == fields->end())
@@ -33,7 +33,7 @@ namespace vcpkg
3333
return value;
3434
}
3535

36-
std::string ParagraphParser::optional_field(StringLiteral fieldname, TextRowCol& position)
36+
std::string ParagraphParser::optional_field(StringLiteral fieldname, TextPosition& position)
3737
{
3838
auto maybe_field = remove_field(&fields, fieldname);
3939
if (auto field = maybe_field.get())
@@ -47,7 +47,7 @@ namespace vcpkg
4747

4848
std::string ParagraphParser::optional_field(StringLiteral fieldname)
4949
{
50-
TextRowCol ignore;
50+
TextPosition ignore;
5151
return optional_field(fieldname, ignore);
5252
}
5353

@@ -66,7 +66,7 @@ namespace vcpkg
6666
return std::string();
6767
}
6868

69-
void ParagraphParser::add_error(TextRowCol position, msg::MessageT<> error_content)
69+
void ParagraphParser::add_error(TextPosition position, msg::MessageT<> error_content)
7070
{
7171
errors.emplace_back(LocalizedString::from_raw(origin)
7272
.append_raw(fmt::format("{}:{}: ", position.row, position.column))
@@ -167,18 +167,18 @@ namespace vcpkg
167167

168168
ExpectedL<std::vector<std::string>> parse_default_features_list(const std::string& str,
169169
StringView origin,
170-
TextRowCol textrowcol)
170+
TextPosition position)
171171
{
172-
auto parser = ParserBase(str, origin, textrowcol);
172+
auto parser = ParserBase(str, origin, position);
173173
auto opt = parse_list_until_eof<std::string>(msgExpectedDefaultFeaturesList, parser, &parse_feature_name);
174174
if (!opt) return {LocalizedString::from_raw(parser.get_error()->to_string()), expected_right_tag};
175175
return {std::move(opt).value_or_exit(VCPKG_LINE_INFO), expected_left_tag};
176176
}
177177
ExpectedL<std::vector<ParsedQualifiedSpecifier>> parse_qualified_specifier_list(const std::string& str,
178178
StringView origin,
179-
TextRowCol textrowcol)
179+
TextPosition position)
180180
{
181-
auto parser = ParserBase(str, origin, textrowcol);
181+
auto parser = ParserBase(str, origin, position);
182182
auto opt = parse_list_until_eof<ParsedQualifiedSpecifier>(
183183
msgExpectedDependenciesList, parser, [](ParserBase& parser) { return parse_qualified_specifier(parser); });
184184
if (!opt) return {LocalizedString::from_raw(parser.get_error()->to_string()), expected_right_tag};
@@ -187,9 +187,9 @@ namespace vcpkg
187187
}
188188
ExpectedL<std::vector<Dependency>> parse_dependencies_list(const std::string& str,
189189
StringView origin,
190-
TextRowCol textrowcol)
190+
TextPosition position)
191191
{
192-
auto parser = ParserBase(str, origin, textrowcol);
192+
auto parser = ParserBase(str, origin, position);
193193
auto opt = parse_list_until_eof<Dependency>(msgExpectedDependenciesList, parser, [](ParserBase& parser) {
194194
auto loc = parser.cur_loc();
195195
return parse_qualified_specifier(parser).then([&](ParsedQualifiedSpecifier&& pqs) -> Optional<Dependency> {

0 commit comments

Comments
 (0)