Skip to content

Commit 2b8826b

Browse files
committed
feat(errors): add polymorphism diagnostics
1 parent 5cd0866 commit 2b8826b

6 files changed

Lines changed: 520 additions & 0 deletions

File tree

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ namespace vix::cli::errors::template_rules
5050
std::unique_ptr<ITemplateErrorRule> makeNoMemberAwaitReadyRule();
5151
std::unique_ptr<ITemplateErrorRule> makeNoMemberAwaitSuspendRule();
5252
std::unique_ptr<ITemplateErrorRule> makeNoMemberAwaitResumeRule();
53+
std::unique_ptr<ITemplateErrorRule> makeNonVirtualDestructorDeleteRule();
54+
std::unique_ptr<ITemplateErrorRule> makeObjectSlicingRule();
55+
std::unique_ptr<ITemplateErrorRule> makeBadOverrideRule();
56+
std::unique_ptr<ITemplateErrorRule> makeInvalidDowncastRule();
5357
} // namespace vix::cli::errors::template_rules
5458

5559
#endif

src/errors/ErrorPipeline.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,17 @@ namespace vix::cli::errors
2929
templateRules_.push_back(vix::cli::errors::template_rules::makeConceptConstraintFailureRule());
3030
templateRules_.push_back(vix::cli::errors::template_rules::makeNoMatchingOverloadWithConstraintsRule());
3131
templateRules_.push_back(vix::cli::errors::template_rules::makeLambdaCaptureLifetimeRule());
32+
templateRules_.push_back(vix::cli::errors::template_rules::makeNonVirtualDestructorDeleteRule());
33+
templateRules_.push_back(vix::cli::errors::template_rules::makeObjectSlicingRule());
34+
templateRules_.push_back(vix::cli::errors::template_rules::makeBadOverrideRule());
35+
templateRules_.push_back(vix::cli::errors::template_rules::makeInvalidDowncastRule());
36+
templateRules_.push_back(vix::cli::errors::template_rules::makeCoroutineReturnTypeRule());
37+
templateRules_.push_back(vix::cli::errors::template_rules::makeMissingCoReturnRule());
38+
templateRules_.push_back(vix::cli::errors::template_rules::makeInvalidAwaitableRule());
39+
templateRules_.push_back(vix::cli::errors::template_rules::makeNoMemberAwaitReadyRule());
40+
templateRules_.push_back(vix::cli::errors::template_rules::makeNoMemberAwaitSuspendRule());
41+
templateRules_.push_back(vix::cli::errors::template_rules::makeNoMemberAwaitResumeRule());
42+
templateRules_.push_back(vix::cli::errors::template_rules::makeInvalidPromiseTypeRule());
3243
templateRules_.push_back(vix::cli::errors::template_rules::makeSubstitutionFailureRule());
3344

3445
// Beginner / syntax / common mistakes
@@ -49,6 +60,7 @@ namespace vix::cli::errors
4960
rules_.push_back(makeReturnLocalRefRule());
5061
rules_.push_back(makeUseOfUninitializedRule());
5162
}
63+
5264
static bool isSystemPath(const std::string &p)
5365
{
5466
return p.rfind("/usr/include/", 0) == 0 ||
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
/**
2+
*
3+
* @file BadOverrideRule.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 BadOverrideRule 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, "override") &&
75+
icontains(m, "does not override")) ||
76+
(icontains(m, "marked") &&
77+
icontains(m, "override") &&
78+
icontains(m, "but does not override")) ||
79+
(icontains(m, "no member function declared in") &&
80+
icontains(m, "override")) ||
81+
(icontains(m, "hides overloaded virtual function")) ||
82+
(icontains(m, "different qualifiers") &&
83+
icontains(m, "override"));
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 override"
97+
<< "\n";
98+
99+
printCodeFrame(err, ctx);
100+
101+
std::cerr << YELLOW
102+
<< "hint: "
103+
<< RESET
104+
<< "this function is marked override, but its signature does not match any virtual function in the base class"
105+
<< "\n";
106+
107+
std::cerr << YELLOW
108+
<< "hint: "
109+
<< RESET
110+
<< "check parameter types, const qualifiers, ref qualifiers, noexcept, and return type compatibility"
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> makeBadOverrideRule()
124+
{
125+
return std::make_unique<BadOverrideRule>();
126+
}
127+
} // namespace vix::cli::errors::template_rules
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
/**
2+
*
3+
* @file InvalidDowncastRule.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 InvalidDowncastRule 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, "dynamic_cast") &&
75+
icontains(m, "not polymorphic")) ||
76+
(icontains(m, "invalid static_cast") &&
77+
icontains(m, "base") &&
78+
icontains(m, "derived")) ||
79+
(icontains(m, "cannot dynamic_cast")) ||
80+
(icontains(m, "downcast") &&
81+
icontains(m, "invalid")) ||
82+
(icontains(m, "source type is not polymorphic")) ||
83+
(icontains(m, "cannot cast") &&
84+
icontains(m, "base") &&
85+
icontains(m, "derived"));
86+
}
87+
88+
bool handle(
89+
const vix::cli::errors::CompilerError &err,
90+
const vix::cli::errors::ErrorContext &ctx) const override
91+
{
92+
std::filesystem::path filePath(err.file);
93+
const std::string fileName = filePath.filename().string();
94+
95+
std::cerr << RED
96+
<< "error: "
97+
<< RESET
98+
<< "invalid downcast"
99+
<< "\n";
100+
101+
printCodeFrame(err, ctx);
102+
103+
std::cerr << YELLOW
104+
<< "hint: "
105+
<< RESET
106+
<< "this cast tries to convert a base object or pointer into a derived type in a way that is not safe or not allowed"
107+
<< "\n";
108+
109+
std::cerr << YELLOW
110+
<< "hint: "
111+
<< RESET
112+
<< "use dynamic_cast with a polymorphic base when runtime checking is needed, or redesign the ownership and type flow to avoid unsafe downcasts"
113+
<< "\n";
114+
115+
std::cerr << GREEN
116+
<< "at: "
117+
<< RESET
118+
<< fileName << ":" << err.line << ":" << err.column
119+
<< "\n";
120+
121+
return true;
122+
}
123+
};
124+
125+
std::unique_ptr<ITemplateErrorRule> makeInvalidDowncastRule()
126+
{
127+
return std::make_unique<InvalidDowncastRule>();
128+
}
129+
} // namespace vix::cli::errors::template_rules

0 commit comments

Comments
 (0)