Skip to content

Commit 11e00ac

Browse files
committed
improve concept constraint diagnostics
2 parents 2e0a954 + 79a292c commit 11e00ac

1 file changed

Lines changed: 128 additions & 11 deletions

File tree

src/errors/template/ConceptConstraintFailureRule.cpp

Lines changed: 128 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,13 @@
1414
#include <vix/cli/errors/template/ITemplateErrorRule.hpp>
1515
#include <vix/cli/errors/CodeFrame.hpp>
1616

17+
#include <filesystem>
1718
#include <iostream>
1819
#include <memory>
20+
#include <optional>
21+
#include <regex>
1922
#include <string>
23+
#include <string_view>
2024

2125
#include <vix/cli/Style.hpp>
2226

@@ -62,6 +66,122 @@ namespace vix::cli::errors::template_rules
6266

6367
return false;
6468
}
69+
70+
bool is_system_path(std::string_view path) noexcept
71+
{
72+
return path.find("/usr/include/") != std::string_view::npos ||
73+
path.find("/usr/lib/") != std::string_view::npos ||
74+
path.find("/usr/local/include/") != std::string_view::npos ||
75+
path.find("/usr/local/lib/") != std::string_view::npos ||
76+
path.find("/lib/") != std::string_view::npos ||
77+
path.find("/lib64/") != std::string_view::npos;
78+
}
79+
80+
bool is_user_path(std::string_view path) noexcept
81+
{
82+
if (path.empty())
83+
return false;
84+
85+
if (is_system_path(path))
86+
return false;
87+
88+
return path.find(".cpp") != std::string_view::npos ||
89+
path.find(".cc") != std::string_view::npos ||
90+
path.find(".cxx") != std::string_view::npos ||
91+
path.find(".hpp") != std::string_view::npos ||
92+
path.find(".hh") != std::string_view::npos ||
93+
path.find(".h") != std::string_view::npos;
94+
}
95+
96+
vix::cli::errors::CompilerError make_location(
97+
const std::string &file,
98+
int line,
99+
int column,
100+
const std::string &message)
101+
{
102+
vix::cli::errors::CompilerError out;
103+
out.file = file;
104+
out.line = line;
105+
out.column = column > 0 ? column : 1;
106+
out.message = message;
107+
return out;
108+
}
109+
110+
std::optional<vix::cli::errors::CompilerError>
111+
try_extract_first_user_location_from_log(
112+
const std::string &log,
113+
const std::string &message)
114+
{
115+
static const std::regex re(
116+
R"((/[^:\n]+?\.(?:c|cc|cpp|cxx|h|hpp|hh)):(\d+):(\d+))",
117+
std::regex::ECMAScript);
118+
119+
std::smatch match;
120+
std::string::const_iterator it = log.begin();
121+
122+
while (std::regex_search(it, log.cend(), match, re))
123+
{
124+
const std::string file = match[1].str();
125+
const int line = std::stoi(match[2].str());
126+
const int column = std::stoi(match[3].str());
127+
128+
if (is_user_path(file))
129+
return make_location(file, line, column, message);
130+
131+
it = match.suffix().first;
132+
}
133+
134+
return std::nullopt;
135+
}
136+
137+
const std::string &compiler_log_from_context(
138+
const vix::cli::errors::ErrorContext &ctx)
139+
{
140+
return ctx.buildLog;
141+
}
142+
143+
vix::cli::errors::CompilerError best_location(
144+
const vix::cli::errors::CompilerError &err,
145+
const vix::cli::errors::ErrorContext &ctx)
146+
{
147+
const std::string &log = compiler_log_from_context(ctx);
148+
149+
if (auto user_location =
150+
try_extract_first_user_location_from_log(log, err.message))
151+
{
152+
return *user_location;
153+
}
154+
155+
if (is_user_path(err.file))
156+
return err;
157+
158+
return err;
159+
}
160+
161+
void print_hint_and_at(
162+
const vix::cli::errors::CompilerError &location,
163+
const vix::cli::errors::CompilerError &original)
164+
{
165+
std::cerr << YELLOW
166+
<< "hint: "
167+
<< RESET
168+
<< "check the type used at the call site; it does not provide the operation required by this concept"
169+
<< "\n";
170+
171+
if (location.file != original.file)
172+
{
173+
std::cerr << GRAY
174+
<< "note: original compiler location was "
175+
<< original.file << ":" << original.line << ":" << original.column
176+
<< RESET << "\n";
177+
}
178+
179+
std::cerr << GREEN
180+
<< "at: "
181+
<< RESET
182+
<< location.file << ":" << location.line << ":" << location.column
183+
<< "\n";
184+
}
65185
} // namespace
66186

67187
class ConceptConstraintFailureRule final : public ITemplateErrorRule
@@ -84,23 +204,20 @@ namespace vix::cli::errors::template_rules
84204
const vix::cli::errors::CompilerError &err,
85205
const vix::cli::errors::ErrorContext &ctx) const override
86206
{
207+
const auto location = best_location(err, ctx);
208+
87209
std::cerr << RED
88210
<< "error: concept constraint not satisfied"
89211
<< RESET << "\n";
90212

91-
printCodeFrame(err, ctx);
213+
CodeFrameOptions options;
214+
options.contextLines = 2;
215+
options.maxLineWidth = 120;
216+
options.tabWidth = 4;
92217

93-
std::cerr << YELLOW
94-
<< "hint: "
95-
<< RESET
96-
<< "check that the type provides the operations, nested types, and return types required by the concept"
97-
<< "\n";
218+
printCodeFrame(location, ctx, options);
98219

99-
std::cerr << GREEN
100-
<< "at: "
101-
<< RESET
102-
<< err.file << ":" << err.line << ":" << err.column
103-
<< "\n";
220+
print_hint_and_at(location, err);
104221

105222
return true;
106223
}

0 commit comments

Comments
 (0)