@@ -186,6 +186,183 @@ auto starts_with_identifier_colon(std::string const& line) -> bool
186
186
}
187
187
188
188
189
+ // ---------------------------------------------------------------------------
190
+ // braces_tracker: to track brace depth
191
+ //
192
+ // Normally we don't emit diagnostics for Cpp1 code, but we do for a
193
+ // brace mismatch since we're relying on balanced { } to find Cpp2 code
194
+ //
195
+ class braces_tracker
196
+ {
197
+ // to track preprocessor #if brace depth and brace counts
198
+ //
199
+ class pre_if_depth_info
200
+ {
201
+ int if_net_braces = 0 ;
202
+ bool found_else = false ;
203
+ int else_net_braces = 0 ;
204
+
205
+ public:
206
+ auto found_open_brace () -> void {
207
+ if (!found_else) { ++if_net_braces; }
208
+ else { ++else_net_braces; }
209
+ }
210
+
211
+ auto found_close_brace () -> void {
212
+ if (!found_else) { --if_net_braces; }
213
+ else { --else_net_braces; }
214
+ }
215
+
216
+ auto found_preprocessor_else () -> void {
217
+ assert (!found_else);
218
+ found_else = true ;
219
+ }
220
+
221
+ // If the "if" and "else" branches opened/closed the same net number
222
+ // of unbalanced braces, they were double-counted in the brace
223
+ // matching and to try to keep going we can apply this adjustment
224
+ auto braces_to_ignore () -> int {
225
+ if (if_net_braces >= 0 && if_net_braces == else_net_braces) {
226
+ return if_net_braces;
227
+ }
228
+ else {
229
+ return 0 ;
230
+ }
231
+ }
232
+ };
233
+ std::vector<pre_if_depth_info> preprocessor = { {} }; // sentinel
234
+ std::vector<lineno_t > open_braces;
235
+ std::vector<error>& errors;
236
+
237
+ public:
238
+ braces_tracker ( std::vector<error>& errors ) : errors{errors} { }
239
+
240
+ // --- Brace matching functions - { and }
241
+
242
+ auto found_open_brace (lineno_t lineno) -> void {
243
+ assert (std::ssize (preprocessor) > 0 );
244
+ open_braces.push_back (lineno);
245
+ preprocessor.back ().found_open_brace ();
246
+ }
247
+
248
+ auto found_close_brace (source_position pos) -> void {
249
+ assert (std::ssize (preprocessor) > 0 );
250
+
251
+ if (std::ssize (open_braces) < 1 ) {
252
+ errors.emplace_back (
253
+ pos,
254
+ " closing } does not match a prior {"
255
+ );
256
+ }
257
+ else {
258
+ open_braces.pop_back ();
259
+ }
260
+
261
+ preprocessor.back ().found_close_brace ();
262
+ }
263
+
264
+ auto found_eof (source_position pos) const -> void {
265
+ // Emit diagnostic if braces didn't match
266
+ //
267
+ if (current_depth () != 0 ) {
268
+ std::string unmatched_brace_lines;
269
+ for (auto i = 0 ; i < std::ssize (open_braces); ++i) {
270
+ if (i > 0 && std::size (open_braces)>2 ) { unmatched_brace_lines += " ," ; };
271
+ if (i > 0 && i == std::ssize (open_braces)-1 ) { unmatched_brace_lines += " and" ; };
272
+ unmatched_brace_lines += " " + std::to_string (open_braces[i]);
273
+ }
274
+ errors.emplace_back (
275
+ pos,
276
+ std::string (" end of file reached with " )
277
+ + std::to_string (current_depth ())
278
+ + " missing } to match earlier { on line"
279
+ + (current_depth () > 1 ? " s" : " " )
280
+ + unmatched_brace_lines
281
+ );
282
+ }
283
+ }
284
+
285
+ auto current_depth () const -> int {
286
+ return std::ssize (open_braces);
287
+ }
288
+
289
+ // --- Preprocessor matching functions - #if/#else/#endif
290
+
291
+ // Entering an #if
292
+ auto found_pre_if () -> void {
293
+ assert (std::ssize (preprocessor) > 0 );
294
+ preprocessor.push_back ({});
295
+ }
296
+
297
+ // Encountered an #else
298
+ auto found_pre_else () -> void {
299
+ assert (std::ssize (preprocessor) > 1 );
300
+ preprocessor.back ().found_preprocessor_else ();
301
+ }
302
+
303
+ // Exiting an #endif
304
+ auto found_pre_endif () -> void {
305
+ assert (std::ssize (preprocessor) > 1 );
306
+
307
+ // If the #if/#else/#endif introduced the same net number of braces,
308
+ // then we will have recorded that number too many open braces, and
309
+ // braces_to_ignore() will be the positive number of those net open braces
310
+ // that this loop will now throw away
311
+ for (auto i = 0 ; i < preprocessor.back ().braces_to_ignore (); ++i) {
312
+ found_close_brace ( source_position{} );
313
+ }
314
+
315
+ preprocessor.pop_back ();
316
+ }
317
+ };
318
+
319
+
320
+ // ---------------------------------------------------------------------------
321
+ // starts_with_whitespace_slash_slash: is this a "// comment" line
322
+ //
323
+ // line current line being processed
324
+ //
325
+ enum class preprocessor_conditional {
326
+ none = 0 , pre_if, pre_else, pre_endif
327
+ };
328
+ auto starts_with_preprocessor_if_else_endif (
329
+ std::string const & line
330
+ )
331
+ -> preprocessor_conditional
332
+ {
333
+ auto i = 0 ;
334
+
335
+ // find first non-whitespace character
336
+ if (!move_next (line, i, isspace)) {
337
+ return preprocessor_conditional::none;
338
+ }
339
+
340
+ // if it's not #, this isn't an #if/#else/#endif
341
+ if (line[i] != ' #' ) {
342
+ return preprocessor_conditional::none;
343
+ }
344
+
345
+ // find next non-whitespace character
346
+ ++i;
347
+ if (!move_next (line, i, isspace)) {
348
+ return preprocessor_conditional::none;
349
+ }
350
+
351
+ if (line.substr (i).starts_with (" if" )) {
352
+ return preprocessor_conditional::pre_if;
353
+ }
354
+ else if (line.substr (i).starts_with (" else" )) {
355
+ return preprocessor_conditional::pre_else;
356
+ }
357
+ else if (line.substr (i).starts_with (" endif" )) {
358
+ return preprocessor_conditional::pre_endif;
359
+ }
360
+ else {
361
+ return preprocessor_conditional::none;
362
+ }
363
+ }
364
+
365
+
189
366
// ---------------------------------------------------------------------------
190
367
// process_cpp_line: just enough to know what to skip over
191
368
//
@@ -204,19 +381,20 @@ auto process_cpp_line(
204
381
bool & in_string_literal,
205
382
bool & in_raw_string_literal,
206
383
std::string& raw_string_closing_seq,
207
- std::vector< int > & brace_depth ,
384
+ braces_tracker & braces ,
208
385
lineno_t lineno,
209
386
std::vector<error>& errors
210
387
)
211
388
-> process_line_ret
212
389
{
213
- if (!in_comment && !in_string_literal && !in_raw_string_literal && starts_with_whitespace_slash_slash (line)) {
214
- return { true , false , false };
215
- }
216
-
217
- if (!in_comment && !in_string_literal && !in_raw_string_literal && starts_with_whitespace_slash_star_and_no_star_slash (line)) {
218
- in_comment = true ;
219
- return { true , false , false };
390
+ if (!in_comment && !in_string_literal && !in_raw_string_literal) {
391
+ if (starts_with_whitespace_slash_slash (line)) {
392
+ return { true , false , false };
393
+ }
394
+ else if (starts_with_whitespace_slash_star_and_no_star_slash (line)) {
395
+ in_comment = true ;
396
+ return { true , false , false };
397
+ }
220
398
}
221
399
222
400
struct process_line_ret r { in_comment, true , in_raw_string_literal};
@@ -273,22 +451,12 @@ auto process_cpp_line(
273
451
274
452
break ;case ' {' :
275
453
if (!in_literal ()) {
276
- brace_depth. push_back (lineno);
454
+ braces. found_open_brace (lineno);
277
455
}
278
456
279
457
break ;case ' }' :
280
458
if (!in_literal ()) {
281
- if (std::ssize (brace_depth) < 1 ) {
282
- // Might as well give a diagnostic in Cpp1 code since
283
- // we're relying on balanced { } to find Cpp2 code
284
- errors.emplace_back (
285
- source_position (lineno, i),
286
- " closing } does not match a prior {"
287
- );
288
- }
289
- else {
290
- brace_depth.pop_back ();
291
- }
459
+ braces.found_close_brace (source_position (lineno, i));
292
460
}
293
461
294
462
break ;case ' *' :
@@ -323,7 +491,7 @@ auto process_cpp_line(
323
491
auto process_cpp2_line (
324
492
std::string const & line,
325
493
bool & in_comment,
326
- std::vector< int > & brace_depth ,
494
+ braces_tracker & braces ,
327
495
lineno_t lineno,
328
496
std::vector<error>& errors
329
497
)
@@ -344,24 +512,16 @@ auto process_cpp2_line(
344
512
else {
345
513
switch (line[i]) {
346
514
break ;case ' {' :
347
- brace_depth. push_back (lineno);
515
+ braces. found_open_brace (lineno);
348
516
349
517
break ;case ' }' :
350
- if (std::ssize (brace_depth) < 1 ) {
351
- errors.emplace_back (
352
- source_position (lineno, i),
353
- " closing } does not match a prior {"
354
- );
355
- }
356
- else {
357
- brace_depth.pop_back ();
358
- if (std::ssize (brace_depth) < 1 ) {
359
- found_end = true ;
360
- }
518
+ braces.found_close_brace ( source_position (lineno, i) );
519
+ if (braces.current_depth () < 1 ) {
520
+ found_end = true ;
361
521
}
362
522
363
523
break ;case ' ;' :
364
- if (std::ssize (brace_depth) == 0 ) { found_end = true ; }
524
+ if (braces. current_depth () < 1 ) { found_end = true ; }
365
525
366
526
break ;case ' *' :
367
527
if (prev == ' /' ) {
@@ -463,7 +623,23 @@ class source
463
623
auto in_raw_string_literal = false ;
464
624
std::string raw_string_closing_seq;
465
625
466
- auto brace_depth = std::vector<int >();
626
+ auto braces = braces_tracker (errors);
627
+
628
+ auto add_preprocessor_line = [&] {
629
+ lines.push_back ({ &buf[0 ], source_line::category::preprocessor });
630
+ if (auto pre = starts_with_preprocessor_if_else_endif (lines.back ().text );
631
+ pre != preprocessor_conditional::none
632
+ ) {
633
+ switch (pre) {
634
+ break ;case preprocessor_conditional::pre_if:
635
+ braces.found_pre_if ();
636
+ break ;case preprocessor_conditional::pre_else:
637
+ braces.found_pre_else ();
638
+ break ;case preprocessor_conditional::pre_endif:
639
+ braces.found_pre_endif ();
640
+ }
641
+ }
642
+ };
467
643
468
644
while (in.getline (&buf[0 ], max_line_len)) {
469
645
@@ -472,10 +648,12 @@ class source
472
648
if (auto pre = is_preprocessor (buf, true ); pre.is_preprocessor )
473
649
{
474
650
cpp1_found = true ;
475
- lines.push_back ({ &buf[0 ], source_line::category::preprocessor });
651
+ // lines.push_back({ &buf[0], source_line::category::preprocessor });
652
+ add_preprocessor_line ();
476
653
while (pre.has_continuation && in.getline (&buf[0 ], max_line_len))
477
654
{
478
- lines.push_back ({ &buf[0 ], source_line::category::preprocessor });
655
+ // lines.push_back({ &buf[0], source_line::category::preprocessor });
656
+ add_preprocessor_line ();
479
657
pre = is_preprocessor (buf, false );
480
658
}
481
659
}
@@ -487,7 +665,9 @@ class source
487
665
// Switch to cpp2 mode if we're not in a comment, not inside nested { },
488
666
// and the line starts with "nonwhitespace :" but not "::"
489
667
//
490
- if (!in_comment && !in_raw_string_literal && std::ssize (brace_depth) == 0 && starts_with_identifier_colon (lines.back ().text ))
668
+ if (!in_comment && !in_raw_string_literal &&
669
+ braces.current_depth () < 1 &&
670
+ starts_with_identifier_colon (lines.back ().text ))
491
671
{
492
672
cpp2_found= true ;
493
673
@@ -508,7 +688,7 @@ class source
508
688
!process_cpp2_line (
509
689
lines.back ().text ,
510
690
in_comment,
511
- brace_depth ,
691
+ braces ,
512
692
std::ssize (lines)-1 ,
513
693
errors
514
694
)
@@ -533,7 +713,7 @@ class source
533
713
in_string_literal,
534
714
in_raw_string_literal,
535
715
raw_string_closing_seq,
536
- brace_depth ,
716
+ braces ,
537
717
std::ssize (lines) - 1 ,
538
718
errors
539
719
);
@@ -578,22 +758,7 @@ class source
578
758
return false ;
579
759
}
580
760
581
- // Emit diagnostic if braces didn't match
582
- //
583
- if (std::ssize (brace_depth) != 0 ) {
584
- std::string unmatched_brace_lines;
585
- for (auto line : brace_depth) {
586
- unmatched_brace_lines = std::to_string (line) + " " + unmatched_brace_lines;
587
- }
588
- errors.emplace_back (
589
- source_position (lineno_t (std::ssize (lines)), 0 ),
590
- std::string (" end of file reached with " )
591
- + std::to_string (std::ssize (brace_depth))
592
- + " missing } to match earlier { on line"
593
- + (std::size (brace_depth)>1 ? " s " : " " )
594
- + unmatched_brace_lines
595
- );
596
- }
761
+ braces.found_eof ( source_position (lineno_t (std::ssize (lines)), 0 ) );
597
762
598
763
return true ;
599
764
}
0 commit comments