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
5254namespace 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+
54150std::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
96192bool 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