Skip to content

Commit 5cd0866

Browse files
committed
feat(errors): add coroutine template diagnostics
1 parent f6eb2a1 commit 5cd0866

8 files changed

Lines changed: 893 additions & 0 deletions

include/vix/cli/errors/template/ITemplateErrorRule.hpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,13 @@ namespace vix::cli::errors::template_rules
4343
std::unique_ptr<ITemplateErrorRule> makeRequiresExpressionFailureRule();
4444
std::unique_ptr<ITemplateErrorRule> makeNoMatchingOverloadWithConstraintsRule();
4545
std::unique_ptr<ITemplateErrorRule> makeLambdaCaptureLifetimeRule();
46+
std::unique_ptr<ITemplateErrorRule> makeCoroutineReturnTypeRule();
47+
std::unique_ptr<ITemplateErrorRule> makeMissingCoReturnRule();
48+
std::unique_ptr<ITemplateErrorRule> makeInvalidAwaitableRule();
49+
std::unique_ptr<ITemplateErrorRule> makeInvalidPromiseTypeRule();
50+
std::unique_ptr<ITemplateErrorRule> makeNoMemberAwaitReadyRule();
51+
std::unique_ptr<ITemplateErrorRule> makeNoMemberAwaitSuspendRule();
52+
std::unique_ptr<ITemplateErrorRule> makeNoMemberAwaitResumeRule();
4653
} // namespace vix::cli::errors::template_rules
4754

4855
#endif
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
/**
2+
*
3+
* @file CoroutineReturnTypeRule.cpp
4+
* @author Gaspard Kirira
5+
*
6+
* Copyright 2025, Gaspard Kirira. All rights reserved.
7+
* https://github.com/vixcpp/vix
8+
* Use of this source code is governed by a MIT license
9+
* that can be found in the License file.
10+
*
11+
* Vix.cpp
12+
*
13+
*/
14+
#include <vix/cli/errors/template/ITemplateErrorRule.hpp>
15+
#include <vix/cli/errors/CodeFrame.hpp>
16+
17+
#include <filesystem>
18+
#include <iostream>
19+
#include <memory>
20+
#include <string>
21+
22+
#include <vix/cli/Style.hpp>
23+
24+
using namespace vix::cli::style;
25+
26+
namespace vix::cli::errors::template_rules
27+
{
28+
namespace
29+
{
30+
bool icontains(const std::string &text, const std::string &needle)
31+
{
32+
if (needle.empty())
33+
return true;
34+
35+
auto lower = [](unsigned char c) -> char
36+
{
37+
if (c >= 'A' && c <= 'Z')
38+
return static_cast<char>(c + ('a' - 'A'));
39+
return static_cast<char>(c);
40+
};
41+
42+
if (text.size() < needle.size())
43+
return false;
44+
45+
for (std::size_t i = 0; i + needle.size() <= text.size(); ++i)
46+
{
47+
bool ok = true;
48+
49+
for (std::size_t j = 0; j < needle.size(); ++j)
50+
{
51+
if (lower(static_cast<unsigned char>(text[i + j])) !=
52+
lower(static_cast<unsigned char>(needle[j])))
53+
{
54+
ok = false;
55+
break;
56+
}
57+
}
58+
59+
if (ok)
60+
return true;
61+
}
62+
63+
return false;
64+
}
65+
} // namespace
66+
67+
class CoroutineReturnTypeRule final : public ITemplateErrorRule
68+
{
69+
public:
70+
bool match(const vix::cli::errors::CompilerError &err) const override
71+
{
72+
const std::string &m = err.message;
73+
74+
return (icontains(m, "coroutine") &&
75+
icontains(m, "return type")) ||
76+
(icontains(m, "unable to find the promise type")) ||
77+
(icontains(m, "this function cannot be a coroutine")) ||
78+
(icontains(m, "std::coroutine_traits") &&
79+
icontains(m, "promise_type")) ||
80+
(icontains(m, "no member named") &&
81+
icontains(m, "promise_type")) ||
82+
(icontains(m, "coroutine") &&
83+
icontains(m, "promise_type"));
84+
}
85+
86+
bool handle(
87+
const vix::cli::errors::CompilerError &err,
88+
const vix::cli::errors::ErrorContext &ctx) const override
89+
{
90+
std::filesystem::path filePath(err.file);
91+
const std::string fileName = filePath.filename().string();
92+
93+
std::cerr << RED
94+
<< "error: "
95+
<< RESET
96+
<< "invalid coroutine return type"
97+
<< "\n";
98+
99+
printCodeFrame(err, ctx);
100+
101+
std::cerr << YELLOW
102+
<< "hint: "
103+
<< RESET
104+
<< "the return type used by this coroutine does not provide the coroutine interface expected by the compiler"
105+
<< "\n";
106+
107+
std::cerr << YELLOW
108+
<< "hint: "
109+
<< RESET
110+
<< "check that the return type exposes a valid promise_type and that it is designed to be used as a coroutine return type"
111+
<< "\n";
112+
113+
std::cerr << GREEN
114+
<< "at: "
115+
<< RESET
116+
<< fileName << ":" << err.line << ":" << err.column
117+
<< "\n";
118+
119+
return true;
120+
}
121+
};
122+
123+
std::unique_ptr<ITemplateErrorRule> makeCoroutineReturnTypeRule()
124+
{
125+
return std::make_unique<CoroutineReturnTypeRule>();
126+
}
127+
} // namespace vix::cli::errors::template_rules
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
/**
2+
*
3+
* @file InvalidAwaitableRule.cpp
4+
* @author Gaspard Kirira
5+
*
6+
* Copyright 2025, Gaspard Kirira. All rights reserved.
7+
* https://github.com/vixcpp/vix
8+
* Use of this source code is governed by a MIT license
9+
* that can be found in the License file.
10+
*
11+
* Vix.cpp
12+
*
13+
*/
14+
#include <vix/cli/errors/template/ITemplateErrorRule.hpp>
15+
#include <vix/cli/errors/CodeFrame.hpp>
16+
17+
#include <filesystem>
18+
#include <iostream>
19+
#include <memory>
20+
#include <string>
21+
22+
#include <vix/cli/Style.hpp>
23+
24+
using namespace vix::cli::style;
25+
26+
namespace vix::cli::errors::template_rules
27+
{
28+
namespace
29+
{
30+
bool icontains(const std::string &text, const std::string &needle)
31+
{
32+
if (needle.empty())
33+
return true;
34+
35+
auto lower = [](unsigned char c) -> char
36+
{
37+
if (c >= 'A' && c <= 'Z')
38+
return static_cast<char>(c + ('a' - 'A'));
39+
return static_cast<char>(c);
40+
};
41+
42+
if (text.size() < needle.size())
43+
return false;
44+
45+
for (std::size_t i = 0; i + needle.size() <= text.size(); ++i)
46+
{
47+
bool ok = true;
48+
49+
for (std::size_t j = 0; j < needle.size(); ++j)
50+
{
51+
if (lower(static_cast<unsigned char>(text[i + j])) !=
52+
lower(static_cast<unsigned char>(needle[j])))
53+
{
54+
ok = false;
55+
break;
56+
}
57+
}
58+
59+
if (ok)
60+
return true;
61+
}
62+
63+
return false;
64+
}
65+
} // namespace
66+
67+
class InvalidAwaitableRule final : public ITemplateErrorRule
68+
{
69+
public:
70+
bool match(const vix::cli::errors::CompilerError &err) const override
71+
{
72+
const std::string &m = err.message;
73+
74+
return (icontains(m, "co_await") &&
75+
icontains(m, "awaitable")) ||
76+
(icontains(m, "co_await") &&
77+
icontains(m, "cannot be used")) ||
78+
(icontains(m, "no member named") &&
79+
(icontains(m, "await_ready") ||
80+
icontains(m, "await_suspend") ||
81+
icontains(m, "await_resume"))) ||
82+
(icontains(m, "no matching") &&
83+
icontains(m, "operator co_await")) ||
84+
(icontains(m, "not awaitable")) ||
85+
(icontains(m, "awaiter") &&
86+
icontains(m, "invalid"));
87+
}
88+
89+
bool handle(
90+
const vix::cli::errors::CompilerError &err,
91+
const vix::cli::errors::ErrorContext &ctx) const override
92+
{
93+
std::filesystem::path filePath(err.file);
94+
const std::string fileName = filePath.filename().string();
95+
96+
std::cerr << RED
97+
<< "error: "
98+
<< RESET
99+
<< "invalid awaitable used with co_await"
100+
<< "\n";
101+
102+
printCodeFrame(err, ctx);
103+
104+
std::cerr << YELLOW
105+
<< "hint: "
106+
<< RESET
107+
<< "the object used with co_await does not satisfy the awaitable protocol expected by the compiler"
108+
<< "\n";
109+
110+
std::cerr << YELLOW
111+
<< "hint: "
112+
<< RESET
113+
<< "check operator co_await, await_ready(), await_suspend(), and await_resume() on the awaited type"
114+
<< "\n";
115+
116+
std::cerr << GREEN
117+
<< "at: "
118+
<< RESET
119+
<< fileName << ":" << err.line << ":" << err.column
120+
<< "\n";
121+
122+
return true;
123+
}
124+
};
125+
126+
std::unique_ptr<ITemplateErrorRule> makeInvalidAwaitableRule()
127+
{
128+
return std::make_unique<InvalidAwaitableRule>();
129+
}
130+
} // namespace vix::cli::errors::template_rules

0 commit comments

Comments
 (0)