Skip to content

Commit 950f1d1

Browse files
committed
fix(parser): Validate the reported diagnostic messages
When `cxx-frontend` is invoked with the option `-verify` the reported diagnostic messages are validated against the expected diagnostics defined using comment-directives matchig the following regex: ``` //\s*expected-(error|warning)(@[+-]?\d+)?\s*{{.+}} ``` Implements #38
1 parent e0def8f commit 950f1d1

File tree

5 files changed

+139
-18
lines changed

5 files changed

+139
-18
lines changed

src/cxx/preprocessor.cc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1731,4 +1731,11 @@ std::string_view Preprocessor::getTextLine(const Token &token) const {
17311731
return textLine;
17321732
}
17331733

1734+
std::string_view Preprocessor::getTokenText(const Token &token) const {
1735+
if (token.fileId() == 0) return std::string_view();
1736+
const SourceFile *file = d->sourceFiles_[token.fileId() - 1].get();
1737+
std::string_view source = file->source;
1738+
return source.substr(token.offset(), token.length());
1739+
}
1740+
17341741
} // namespace cxx

src/cxx/preprocessor.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ class Preprocessor {
8686

8787
std::string_view getTextLine(const Token &token) const;
8888

89+
std::string_view getTokenText(const Token &token) const;
90+
8991
void squeeze();
9092

9193
private:

src/frontend/cli.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,8 @@ std::vector<CLIOptionDescr> options{
145145
"Set the toolchain to 'linux', 'darwin' or 'windows'",
146146
CLIOptionDescrKind::kSeparated},
147147

148+
{"-verify", "Verify the diagnostic messages", &CLI::opt_verify},
149+
148150
{"-v", "Show commands to run and use verbose output", &CLI::opt_v},
149151

150152
};

src/frontend/cli.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ class CLI {
6161
bool opt_nostdincpp = false;
6262
bool opt_S = false;
6363
bool opt_c = false;
64+
bool opt_verify = false;
6465
bool opt_v = false;
6566

6667
bool checkTypes() const { return opt_ir_dump || opt_S || opt_c; }

src/frontend/frontend.cc

Lines changed: 127 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,109 @@
4444
#include <fstream>
4545
#include <iomanip>
4646
#include <iostream>
47+
#include <list>
48+
#include <regex>
4749
#include <sstream>
4850
#include <string>
4951

5052
#include "cli.h"
5153

5254
namespace cxx {
5355

56+
struct ExpectedDiagnostic {
57+
Severity severity = Severity::Error;
58+
std::string_view fileName;
59+
unsigned line = 0;
60+
std::string message;
61+
};
62+
63+
struct VerifyCommentHandler : CommentHandler {
64+
std::regex rx{
65+
"^//\\s*expected-(error|warning)(?:@([+-]?\\d+))?\\s*\\{\\{(.+)\\}\\}"};
66+
std::list<ExpectedDiagnostic> expectedDiagnostics;
67+
68+
void handleComment(Preprocessor* preprocessor, const Token& token) override {
69+
const std::string text{preprocessor->getTokenText(token)};
70+
71+
std::smatch match;
72+
73+
if (std::regex_match(text, match, rx)) {
74+
std::string_view fileName;
75+
unsigned line = 0;
76+
unsigned column = 0;
77+
78+
preprocessor->getTokenStartPosition(token, &line, &column, &fileName);
79+
80+
Severity severity = Severity::Error;
81+
82+
if (match[1] == "warning") severity = Severity::Warning;
83+
84+
std::string offset = match[2];
85+
86+
if (!offset.empty()) line += std::stoi(offset);
87+
88+
const auto& message = match[3];
89+
90+
expectedDiagnostics.push_back({severity, fileName, line, message});
91+
}
92+
}
93+
};
94+
95+
struct VerifyDiagnostics : DiagnosticsClient {
96+
std::list<Diagnostic> reportedDiagnostics;
97+
bool verify = false;
98+
99+
void report(const Diagnostic& diagnostic) override {
100+
if (!verify) {
101+
DiagnosticsClient::report(diagnostic);
102+
return;
103+
}
104+
105+
reportedDiagnostics.push_back(diagnostic);
106+
}
107+
108+
void verifyExpectedDiagnostics(
109+
const std::list<ExpectedDiagnostic>& expectedDiagnostics) {
110+
if (!verify) return;
111+
112+
for (const auto& expected : expectedDiagnostics) {
113+
if (auto it = findDiagnostic(expected); it != cend(reportedDiagnostics)) {
114+
reportedDiagnostics.erase(it);
115+
}
116+
}
117+
118+
for (const auto& diag : reportedDiagnostics) {
119+
DiagnosticsClient::report(diag);
120+
}
121+
}
122+
123+
private:
124+
std::list<Diagnostic>::const_iterator findDiagnostic(
125+
const ExpectedDiagnostic& expected) const {
126+
return std::find_if(reportedDiagnostics.begin(), reportedDiagnostics.end(),
127+
[&](const Diagnostic& d) {
128+
if (d.severity() != expected.severity) {
129+
return false;
130+
}
131+
132+
unsigned line = 0;
133+
unsigned column = 0;
134+
std::string_view fileName;
135+
136+
preprocessor()->getTokenStartPosition(
137+
d.token(), &line, &column, &fileName);
138+
139+
if (line != expected.line) return false;
140+
141+
if (fileName != expected.fileName) return false;
142+
143+
if (d.message() != expected.message) return false;
144+
145+
return true;
146+
});
147+
}
148+
};
149+
54150
std::string readAll(const std::string& fileName, std::istream& in) {
55151
std::string code;
56152
char buffer[4 * 1024];
@@ -95,13 +191,21 @@ void dumpTokens(const CLI& cli, TranslationUnit& unit, std::ostream& output) {
95191

96192
bool runOnFile(const CLI& cli, const std::string& fileName) {
97193
Control control;
98-
DiagnosticsClient diagnosticsClient;
194+
VerifyDiagnostics diagnosticsClient;
99195
TranslationUnit unit(&control, &diagnosticsClient);
100196

101197
auto preprocesor = unit.preprocessor();
102198

103199
std::unique_ptr<Toolchain> toolchain;
104200

201+
VerifyCommentHandler verifyCommentHandler;
202+
203+
diagnosticsClient.verify = cli.opt_verify;
204+
205+
if (cli.opt_verify) {
206+
preprocesor->setCommentHandler(&verifyCommentHandler);
207+
}
208+
105209
auto toolchainId = cli.getSingle("-toolchain");
106210

107211
if (!toolchainId) {
@@ -176,24 +280,29 @@ bool runOnFile(const CLI& cli, const std::string& fileName) {
176280

177281
auto& output = outfile ? *outfile : std::cout;
178282

283+
bool shouldExit = false;
284+
179285
if (cli.opt_E && !cli.opt_dM) {
180286
preprocesor->preprocess(readAll(fileName), fileName, output);
181-
return true;
182-
}
183-
184-
unit.setSource(readAll(fileName), fileName);
185-
186-
if (cli.opt_dM) {
187-
preprocesor->printMacros(output);
188-
return true;
287+
shouldExit = true;
288+
} else {
289+
unit.setSource(readAll(fileName), fileName);
290+
291+
if (cli.opt_dM) {
292+
preprocesor->printMacros(output);
293+
shouldExit = true;
294+
} else if (cli.opt_dump_tokens) {
295+
dumpTokens(cli, unit, output);
296+
shouldExit = true;
297+
} else if (cli.opt_Eonly) {
298+
shouldExit = true;
299+
}
189300
}
190301

191-
if (cli.opt_dump_tokens) {
192-
dumpTokens(cli, unit, output);
193-
return true;
194-
}
302+
if (shouldExit) {
303+
diagnosticsClient.verifyExpectedDiagnostics(
304+
verifyCommentHandler.expectedDiagnostics);
195305

196-
if (cli.opt_Eonly) {
197306
return true;
198307
}
199308

@@ -204,10 +313,7 @@ bool runOnFile(const CLI& cli, const std::string& fileName) {
204313
if (cli.opt_ast_dump) {
205314
ASTPrinter print(&unit);
206315
output << print(unit.ast(), /*print locations*/ true);
207-
return result;
208-
}
209-
210-
if (cli.opt_S || cli.opt_ir_dump || cli.opt_c) {
316+
} else if (cli.opt_S || cli.opt_ir_dump || cli.opt_c) {
211317
Codegen cg;
212318

213319
auto module = cg(&unit);
@@ -221,6 +327,9 @@ bool runOnFile(const CLI& cli, const std::string& fileName) {
221327
}
222328
}
223329

330+
diagnosticsClient.verifyExpectedDiagnostics(
331+
verifyCommentHandler.expectedDiagnostics);
332+
224333
return result;
225334
}
226335

0 commit comments

Comments
 (0)